From bad161a5c1d87d335ab88b7ad3ddc0297f2520a6 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 9 Sep 2021 10:28:34 +1200 Subject: [PATCH 001/207] Bump version to 2021.10.0-dev --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 07d0079172..f342609538 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.9.0-dev" +__version__ = "2021.10.0-dev" ESP_PLATFORM_ESP32 = "ESP32" ESP_PLATFORM_ESP8266 = "ESP8266" From affaaf7d2c86c5b356886098876ad4b970ba848c Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 10 Sep 2021 12:10:28 +1200 Subject: [PATCH 002/207] Fix a few ESP32-C3 compiler issues (#2265) * Fix using Serial when using ESP32-C3 standard pins * Force type for std::min in pn532 * Fix variable size where size_t is different on exp32-c3 --- esphome/components/api/api_frame_helper.cpp | 2 +- esphome/components/pn532/pn532.cpp | 2 +- esphome/components/uart/uart_esp32.cpp | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/esphome/components/api/api_frame_helper.cpp b/esphome/components/api/api_frame_helper.cpp index 26fbf1269f..520a5c2caf 100644 --- a/esphome/components/api/api_frame_helper.cpp +++ b/esphome/components/api/api_frame_helper.cpp @@ -707,7 +707,7 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) { } size_t i = 1; - size_t consumed = 0; + uint32_t consumed = 0; auto msg_size_varint = ProtoVarInt::parse(&rx_header_buf_[i], rx_header_buf_.size() - i, &consumed); if (!msg_size_varint.has_value()) { // not enough data there yet diff --git a/esphome/components/pn532/pn532.cpp b/esphome/components/pn532/pn532.cpp index fc84f30078..fcab9872c4 100644 --- a/esphome/components/pn532/pn532.cpp +++ b/esphome/components/pn532/pn532.cpp @@ -49,7 +49,7 @@ void PN532::setup() { } // Set up SAM (secure access module) - uint8_t sam_timeout = std::min(255u, this->update_interval_ / 50); + uint8_t sam_timeout = std::min(255u, this->update_interval_ / 50); if (!this->write_command_({ PN532_COMMAND_SAMCONFIGURATION, 0x01, // normal mode diff --git a/esphome/components/uart/uart_esp32.cpp b/esphome/components/uart/uart_esp32.cpp index 16d683e4a6..c672a34c36 100644 --- a/esphome/components/uart/uart_esp32.cpp +++ b/esphome/components/uart/uart_esp32.cpp @@ -73,7 +73,11 @@ void UARTComponent::setup() { // Use Arduino HardwareSerial UARTs if all used pins match the ones // preconfigured by the platform. For example if RX disabled but TX pin // is 1 we still want to use Serial. +#ifdef CONFIG_IDF_TARGET_ESP32C3 + if (this->tx_pin_.value_or(21) == 21 && this->rx_pin_.value_or(20) == 20) { +#else if (this->tx_pin_.value_or(1) == 1 && this->rx_pin_.value_or(3) == 3) { +#endif this->hw_serial_ = &Serial; } else { this->hw_serial_ = new HardwareSerial(next_uart_num++); From 3d71e2e1890a94d0caee3dda2403184b98fdbfde Mon Sep 17 00:00:00 2001 From: poptix Date: Fri, 10 Sep 2021 04:05:25 -0500 Subject: [PATCH 003/207] sm300d2: Accept (undocumented) 0x80 checksum offset. (#2263) Co-authored-by: Matt Hallacy --- esphome/components/sm300d2/sm300d2.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/sm300d2/sm300d2.cpp b/esphome/components/sm300d2/sm300d2.cpp index 34d80349f9..b1787581ae 100644 --- a/esphome/components/sm300d2/sm300d2.cpp +++ b/esphome/components/sm300d2/sm300d2.cpp @@ -27,7 +27,8 @@ void SM300D2Sensor::update() { } uint16_t calculated_checksum = this->sm300d2_checksum_(response); - if (calculated_checksum != response[SM300D2_RESPONSE_LENGTH - 1]) { + if ((calculated_checksum != response[SM300D2_RESPONSE_LENGTH - 1]) && + (calculated_checksum - 0x80 != response[SM300D2_RESPONSE_LENGTH - 1])) { ESP_LOGW(TAG, "SM300D2 Checksum doesn't match: 0x%02X!=0x%02X", response[SM300D2_RESPONSE_LENGTH - 1], calculated_checksum); this->status_set_warning(); From ede1de9021c2ffe9533ad7f05c929554834a6c13 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Sun, 12 Sep 2021 22:04:35 +0200 Subject: [PATCH 004/207] Drop obsolete comments from CONTRIBUTING.md (#2271) --- CONTRIBUTING.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 43d5e79074..e96bb5745b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,10 +1,6 @@ # Contributing to ESPHome -This python project is responsible for reading in YAML configuration files, -converting them to C++ code. This code is then converted to a platformio project and compiled -with [esphome-core](https://github.com/esphome/esphome-core), the C++ framework behind the project. - -For a detailed guide, please see https://esphome.io/guides/contributing.html#contributing-to-esphomeyaml +For a detailed guide, please see https://esphome.io/guides/contributing.html#contributing-to-esphome Things to note when contributing: From 97a18717e6d3b39a281cd1403d03a304404321a0 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Mon, 13 Sep 2021 02:44:39 +0200 Subject: [PATCH 005/207] Disable automatic usage of SNTP servers from DHCP (#2273) --- esphome/components/wifi/wifi_component_esp32.cpp | 6 ++++++ esphome/components/wifi/wifi_component_esp8266.cpp | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/esphome/components/wifi/wifi_component_esp32.cpp b/esphome/components/wifi/wifi_component_esp32.cpp index 57c4efcdd5..b56030db56 100644 --- a/esphome/components/wifi/wifi_component_esp32.cpp +++ b/esphome/components/wifi/wifi_component_esp32.cpp @@ -11,6 +11,7 @@ #endif #include "lwip/err.h" #include "lwip/dns.h" +#include "lwip/apps/sntp.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" @@ -92,6 +93,11 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { tcpip_adapter_dhcp_status_t dhcp_status; tcpip_adapter_dhcpc_get_status(TCPIP_ADAPTER_IF_STA, &dhcp_status); if (!manual_ip.has_value()) { + // lwIP starts the SNTP client if it gets an SNTP server from DHCP. We don't need the time, and more importantly, + // the built-in SNTP client has a memory leak in certain situations. Disable this feature. + // https://github.com/esphome/issues/issues/2299 + sntp_servermode_dhcp(false); + // Use DHCP client if (dhcp_status != TCPIP_ADAPTER_DHCP_STARTED) { esp_err_t err = tcpip_adapter_dhcpc_start(TCPIP_ADAPTER_IF_STA); diff --git a/esphome/components/wifi/wifi_component_esp8266.cpp b/esphome/components/wifi/wifi_component_esp8266.cpp index ad1a64d1f4..de529ee3aa 100644 --- a/esphome/components/wifi/wifi_component_esp8266.cpp +++ b/esphome/components/wifi/wifi_component_esp8266.cpp @@ -16,6 +16,7 @@ extern "C" { #include "lwip/dns.h" #include "lwip/dhcp.h" #include "lwip/init.h" // LWIP_VERSION_ +#include "lwip/apps/sntp.h" #if LWIP_IPV6 #include "lwip/netif.h" // struct netif #endif @@ -112,6 +113,11 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { enum dhcp_status dhcp_status = wifi_station_dhcpc_status(); if (!manual_ip.has_value()) { + // lwIP starts the SNTP client if it gets an SNTP server from DHCP. We don't need the time, and more importantly, + // the built-in SNTP client has a memory leak in certain situations. Disable this feature. + // https://github.com/esphome/issues/issues/2299 + sntp_servermode_dhcp(false); + // Use DHCP client if (dhcp_status != DHCP_STARTED) { bool ret = wifi_station_dhcpc_start(); From f0b6aabc964722d3d0da3e49c84cec4125d9e7d4 Mon Sep 17 00:00:00 2001 From: irtimaled Date: Mon, 13 Sep 2021 00:33:20 -0700 Subject: [PATCH 006/207] Support inverting color temperature on tuya lights (#2277) --- esphome/components/tuya/light/__init__.py | 4 ++++ esphome/components/tuya/light/tuya_light.cpp | 9 ++++++++- esphome/components/tuya/light/tuya_light.h | 4 ++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/esphome/components/tuya/light/__init__.py b/esphome/components/tuya/light/__init__.py index 979082d636..f43cc570ca 100644 --- a/esphome/components/tuya/light/__init__.py +++ b/esphome/components/tuya/light/__init__.py @@ -18,6 +18,7 @@ DEPENDENCIES = ["tuya"] CONF_DIMMER_DATAPOINT = "dimmer_datapoint" CONF_MIN_VALUE_DATAPOINT = "min_value_datapoint" CONF_COLOR_TEMPERATURE_DATAPOINT = "color_temperature_datapoint" +CONF_COLOR_TEMPERATURE_INVERT = "color_temperature_invert" CONF_COLOR_TEMPERATURE_MAX_VALUE = "color_temperature_max_value" TuyaLight = tuya_ns.class_("TuyaLight", light.LightOutput, cg.Component) @@ -33,6 +34,7 @@ CONFIG_SCHEMA = cv.All( cv.Inclusive( CONF_COLOR_TEMPERATURE_DATAPOINT, "color_temperature" ): cv.uint8_t, + cv.Optional(CONF_COLOR_TEMPERATURE_INVERT, default=False): cv.boolean, cv.Optional(CONF_MIN_VALUE): cv.int_, cv.Optional(CONF_MAX_VALUE): cv.int_, cv.Optional(CONF_COLOR_TEMPERATURE_MAX_VALUE): cv.int_, @@ -67,6 +69,8 @@ async def to_code(config): cg.add(var.set_switch_id(config[CONF_SWITCH_DATAPOINT])) if CONF_COLOR_TEMPERATURE_DATAPOINT in config: cg.add(var.set_color_temperature_id(config[CONF_COLOR_TEMPERATURE_DATAPOINT])) + cg.add(var.set_color_temperature_invert(config[CONF_COLOR_TEMPERATURE_INVERT])) + cg.add( var.set_cold_white_temperature(config[CONF_COLD_WHITE_COLOR_TEMPERATURE]) ) diff --git a/esphome/components/tuya/light/tuya_light.cpp b/esphome/components/tuya/light/tuya_light.cpp index 8ad78aacea..6f3adfcdfd 100644 --- a/esphome/components/tuya/light/tuya_light.cpp +++ b/esphome/components/tuya/light/tuya_light.cpp @@ -9,10 +9,14 @@ static const char *const TAG = "tuya.light"; void TuyaLight::setup() { if (this->color_temperature_id_.has_value()) { this->parent_->register_listener(*this->color_temperature_id_, [this](const TuyaDatapoint &datapoint) { + auto datapoint_value = datapoint.value_uint; + if (this->color_temperature_invert_) { + datapoint_value = this->color_temperature_max_value_ - datapoint_value; + } auto call = this->state_->make_call(); call.set_color_temperature(this->cold_white_temperature_ + (this->warm_white_temperature_ - this->cold_white_temperature_) * - (float(datapoint.value_uint) / float(this->color_temperature_max_value_))); + (float(datapoint_value) / this->color_temperature_max_value_)); call.perform(); }); } @@ -78,6 +82,9 @@ void TuyaLight::write_state(light::LightState *state) { static_cast(this->color_temperature_max_value_ * (state->current_values.get_color_temperature() - this->cold_white_temperature_) / (this->warm_white_temperature_ - this->cold_white_temperature_)); + if (this->color_temperature_invert_) { + color_temp_int = this->color_temperature_max_value_ - color_temp_int; + } parent_->set_integer_datapoint_value(*this->color_temperature_id_, color_temp_int); } diff --git a/esphome/components/tuya/light/tuya_light.h b/esphome/components/tuya/light/tuya_light.h index 72422bc9e7..20753fa90b 100644 --- a/esphome/components/tuya/light/tuya_light.h +++ b/esphome/components/tuya/light/tuya_light.h @@ -17,6 +17,9 @@ class TuyaLight : public Component, public light::LightOutput { } void set_switch_id(uint8_t switch_id) { this->switch_id_ = switch_id; } void set_color_temperature_id(uint8_t color_temperature_id) { this->color_temperature_id_ = color_temperature_id; } + void set_color_temperature_invert(bool color_temperature_invert) { + this->color_temperature_invert_ = color_temperature_invert; + } void set_tuya_parent(Tuya *parent) { this->parent_ = parent; } void set_min_value(uint32_t min_value) { min_value_ = min_value; } void set_max_value(uint32_t max_value) { max_value_ = max_value; } @@ -47,6 +50,7 @@ class TuyaLight : public Component, public light::LightOutput { uint32_t color_temperature_max_value_ = 255; float cold_white_temperature_; float warm_white_temperature_; + bool color_temperature_invert_{false}; light::LightState *state_{nullptr}; }; From f31e0532c4e502de7913f086452a3ac4eebb3473 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Mon, 13 Sep 2021 09:33:29 +0200 Subject: [PATCH 007/207] Untangle core headers (part 1) (#2276) --- esphome/components/ade7953/ade7953.h | 1 + esphome/components/am43/am43_base.cpp | 2 ++ esphome/components/anova/anova_base.cpp | 2 ++ esphome/components/as3935/as3935.h | 1 + esphome/components/climate/climate_traits.cpp | 2 +- esphome/components/deep_sleep/deep_sleep_component.h | 1 + esphome/components/dht/dht.h | 1 + esphome/components/esp32_ble/ble_uuid.h | 1 + .../gpio/binary_sensor/gpio_binary_sensor.h | 1 + esphome/components/gpio/switch/gpio_switch.h | 1 + esphome/components/nfc/nfc.cpp | 1 + esphome/components/rc522/rc522.h | 1 + .../remote_receiver/remote_receiver_esp8266.cpp | 1 + esphome/components/sx1509/sx1509.h | 1 + esphome/components/ttp229_bsf/ttp229_bsf.h | 1 + esphome/components/tx20/tx20.h | 1 + esphome/components/vl53l0x/vl53l0x_sensor.h | 1 + esphome/core/esphal.h | 4 +--- esphome/core/helpers.h | 5 ++++- esphome/core/log.h | 12 +++++++----- esphome/core/preferences.h | 2 +- 21 files changed, 32 insertions(+), 11 deletions(-) diff --git a/esphome/components/ade7953/ade7953.h b/esphome/components/ade7953/ade7953.h index e0fadf37c3..5a582a991a 100644 --- a/esphome/components/ade7953/ade7953.h +++ b/esphome/components/ade7953/ade7953.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/esphal.h" #include "esphome/components/i2c/i2c.h" #include "esphome/components/sensor/sensor.h" diff --git a/esphome/components/am43/am43_base.cpp b/esphome/components/am43/am43_base.cpp index a17df55571..af474dcb79 100644 --- a/esphome/components/am43/am43_base.cpp +++ b/esphome/components/am43/am43_base.cpp @@ -1,4 +1,6 @@ #include "am43_base.h" +#include +#include namespace esphome { namespace am43 { diff --git a/esphome/components/anova/anova_base.cpp b/esphome/components/anova/anova_base.cpp index ad581b1e6c..811a34a27a 100644 --- a/esphome/components/anova/anova_base.cpp +++ b/esphome/components/anova/anova_base.cpp @@ -1,4 +1,6 @@ #include "anova_base.h" +#include +#include namespace esphome { namespace anova { diff --git a/esphome/components/as3935/as3935.h b/esphome/components/as3935/as3935.h index d0e53e7832..76c7697d38 100644 --- a/esphome/components/as3935/as3935.h +++ b/esphome/components/as3935/as3935.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/esphal.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/binary_sensor/binary_sensor.h" diff --git a/esphome/components/climate/climate_traits.cpp b/esphome/components/climate/climate_traits.cpp index c871552360..16c9cd05be 100644 --- a/esphome/components/climate/climate_traits.cpp +++ b/esphome/components/climate/climate_traits.cpp @@ -1,5 +1,5 @@ #include "climate_traits.h" -#include "esphome/core/log.h" +#include namespace esphome { namespace climate { diff --git a/esphome/components/deep_sleep/deep_sleep_component.h b/esphome/components/deep_sleep/deep_sleep_component.h index e163cf7709..5050bd6b4d 100644 --- a/esphome/components/deep_sleep/deep_sleep_component.h +++ b/esphome/components/deep_sleep/deep_sleep_component.h @@ -3,6 +3,7 @@ #include "esphome/core/component.h" #include "esphome/core/helpers.h" #include "esphome/core/automation.h" +#include "esphome/core/esphal.h" namespace esphome { namespace deep_sleep { diff --git a/esphome/components/dht/dht.h b/esphome/components/dht/dht.h index 8826a3d2b7..9b848cb119 100644 --- a/esphome/components/dht/dht.h +++ b/esphome/components/dht/dht.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/esphal.h" #include "esphome/components/sensor/sensor.h" namespace esphome { diff --git a/esphome/components/esp32_ble/ble_uuid.h b/esphome/components/esp32_ble/ble_uuid.h index 0e3c9b0d0a..89082e19fa 100644 --- a/esphome/components/esp32_ble/ble_uuid.h +++ b/esphome/components/esp32_ble/ble_uuid.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/helpers.h" +#include "esphome/core/esphal.h" #ifdef ARDUINO_ARCH_ESP32 diff --git a/esphome/components/gpio/binary_sensor/gpio_binary_sensor.h b/esphome/components/gpio/binary_sensor/gpio_binary_sensor.h index cfe49b3c94..f655207243 100644 --- a/esphome/components/gpio/binary_sensor/gpio_binary_sensor.h +++ b/esphome/components/gpio/binary_sensor/gpio_binary_sensor.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/esphal.h" #include "esphome/components/binary_sensor/binary_sensor.h" namespace esphome { diff --git a/esphome/components/gpio/switch/gpio_switch.h b/esphome/components/gpio/switch/gpio_switch.h index c0036b20e9..b39e070c85 100644 --- a/esphome/components/gpio/switch/gpio_switch.h +++ b/esphome/components/gpio/switch/gpio_switch.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/esphal.h" #include "esphome/components/switch/switch.h" namespace esphome { diff --git a/esphome/components/nfc/nfc.cpp b/esphome/components/nfc/nfc.cpp index 01f63e6ec9..706c09a5aa 100644 --- a/esphome/components/nfc/nfc.cpp +++ b/esphome/components/nfc/nfc.cpp @@ -1,4 +1,5 @@ #include "nfc.h" +#include #include "esphome/core/log.h" namespace esphome { diff --git a/esphome/components/rc522/rc522.h b/esphome/components/rc522/rc522.h index 6880651ac4..e4e3387ff6 100644 --- a/esphome/components/rc522/rc522.h +++ b/esphome/components/rc522/rc522.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/esphal.h" #include "esphome/core/automation.h" #include "esphome/components/binary_sensor/binary_sensor.h" diff --git a/esphome/components/remote_receiver/remote_receiver_esp8266.cpp b/esphome/components/remote_receiver/remote_receiver_esp8266.cpp index 3fb68caca2..d509eea925 100644 --- a/esphome/components/remote_receiver/remote_receiver_esp8266.cpp +++ b/esphome/components/remote_receiver/remote_receiver_esp8266.cpp @@ -1,4 +1,5 @@ #include "remote_receiver.h" +#include "esphome/core/esphal.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" diff --git a/esphome/components/sx1509/sx1509.h b/esphome/components/sx1509/sx1509.h index 55d5e54091..1390059675 100644 --- a/esphome/components/sx1509/sx1509.h +++ b/esphome/components/sx1509/sx1509.h @@ -2,6 +2,7 @@ #include "esphome/components/i2c/i2c.h" #include "esphome/core/component.h" +#include "esphome/core/esphal.h" #include "sx1509_gpio_pin.h" #include "sx1509_registers.h" diff --git a/esphome/components/ttp229_bsf/ttp229_bsf.h b/esphome/components/ttp229_bsf/ttp229_bsf.h index 94dd014d8e..d73e4fb185 100644 --- a/esphome/components/ttp229_bsf/ttp229_bsf.h +++ b/esphome/components/ttp229_bsf/ttp229_bsf.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/esphal.h" #include "esphome/components/binary_sensor/binary_sensor.h" namespace esphome { diff --git a/esphome/components/tx20/tx20.h b/esphome/components/tx20/tx20.h index 8b79deffbc..56cb723fa1 100644 --- a/esphome/components/tx20/tx20.h +++ b/esphome/components/tx20/tx20.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/esphal.h" #include "esphome/components/sensor/sensor.h" namespace esphome { diff --git a/esphome/components/vl53l0x/vl53l0x_sensor.h b/esphome/components/vl53l0x/vl53l0x_sensor.h index 0c37df67a2..83a6322dcd 100644 --- a/esphome/components/vl53l0x/vl53l0x_sensor.h +++ b/esphome/components/vl53l0x/vl53l0x_sensor.h @@ -3,6 +3,7 @@ #include #include "esphome/core/component.h" +#include "esphome/core/esphal.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/i2c/i2c.h" diff --git a/esphome/core/esphal.h b/esphome/core/esphal.h index 809c7d91b5..08fbfb09f5 100644 --- a/esphome/core/esphal.h +++ b/esphome/core/esphal.h @@ -1,9 +1,7 @@ #pragma once #include "Arduino.h" -#ifdef ARDUINO_ARCH_ESP32 -#include -#endif + // Fix some arduino defs #ifdef round #undef round diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 60bc7a9ad3..a63c627e5a 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -6,8 +6,11 @@ #include #include +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-psram.h" +#endif + #include "esphome/core/optional.h" -#include "esphome/core/esphal.h" #ifdef CLANG_TIDY #undef ICACHE_RAM_ATTR diff --git a/esphome/core/log.h b/esphome/core/log.h index fbaaf14408..0b0911c3ac 100644 --- a/esphome/core/log.h +++ b/esphome/core/log.h @@ -3,18 +3,20 @@ #include #include #include + #ifdef USE_STORE_LOG_STR_IN_FLASH #include "WString.h" #endif -#include "esphome/core/macros.h" -// avoid esp-idf redefining our macros -#include "esphome/core/esphal.h" - +// Both the ESP-IDF and Arduino also define ESP_LOG* macros. Include them here, so that they won't +// be reincluded later on and redefine our macros. #ifdef ARDUINO_ARCH_ESP32 -#include "esp_err.h" +#include +#include #endif +#include "esphome/core/macros.h" + namespace esphome { #define ESPHOME_LOG_LEVEL_NONE 0 diff --git a/esphome/core/preferences.h b/esphome/core/preferences.h index 43d0f023e3..699871ca00 100644 --- a/esphome/core/preferences.h +++ b/esphome/core/preferences.h @@ -1,8 +1,8 @@ #pragma once #include +#include -#include "esphome/core/esphal.h" #include "esphome/core/defines.h" namespace esphome { From 0cd24c629a727914711c7e9e257713d079a0245a Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Mon, 13 Sep 2021 09:35:55 +0200 Subject: [PATCH 008/207] Compatibility with clang-tidy v14 (#2272) --- .clang-tidy | 4 ++++ esphome/components/bme280/bme280.cpp | 20 +++++++++---------- esphome/components/bmp280/bmp280.cpp | 10 +++++----- esphome/components/display/display_buffer.cpp | 4 +--- esphome/components/i2c/i2c.cpp | 2 +- esphome/components/mcp3008/mcp3008.cpp | 3 +-- esphome/components/mqtt/mqtt_client.cpp | 2 +- .../components/remote_base/rc5_protocol.cpp | 6 +++--- esphome/components/st7735/st7735.cpp | 17 ++++++++-------- esphome/components/sun/sun.cpp | 6 ++---- esphome/core/component.cpp | 6 ++---- esphome/core/preferences.cpp | 6 +++--- 12 files changed, 41 insertions(+), 45 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 473529a9b1..936100e9e6 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -2,9 +2,11 @@ Checks: >- *, -abseil-*, + -altera-*, -android-*, -boost-*, -bugprone-branch-clone, + -bugprone-easily-swappable-parameters, -bugprone-narrowing-conversions, -bugprone-signed-char-misuse, -bugprone-too-small-loop-variable, @@ -20,6 +22,7 @@ Checks: >- -clang-diagnostic-sign-compare, -clang-diagnostic-unused-variable, -clang-diagnostic-unused-const-variable, + -concurrency-*, -cppcoreguidelines-avoid-c-arrays, -cppcoreguidelines-avoid-goto, -cppcoreguidelines-avoid-magic-numbers, @@ -72,6 +75,7 @@ Checks: >- -readability-const-return-type, -readability-convert-member-functions-to-static, -readability-else-after-return, + -readability-function-cognitive-complexity, -readability-implicit-bool-conversion, -readability-isolate-declaration, -readability-magic-numbers, diff --git a/esphome/components/bme280/bme280.cpp b/esphome/components/bme280/bme280.cpp index 097974da7a..fe11b69945 100644 --- a/esphome/components/bme280/bme280.cpp +++ b/esphome/components/bme280/bme280.cpp @@ -113,14 +113,14 @@ void BME280Component::setup() { this->calibration_.h5 = read_u8_(BME280_REGISTER_DIG_H5 + 1) << 4 | (read_u8_(BME280_REGISTER_DIG_H5) >> 4); this->calibration_.h6 = read_u8_(BME280_REGISTER_DIG_H6); - uint8_t humid_register = 0; - if (!this->read_byte(BME280_REGISTER_CONTROLHUMID, &humid_register)) { + uint8_t humid_control_val = 0; + if (!this->read_byte(BME280_REGISTER_CONTROLHUMID, &humid_control_val)) { this->mark_failed(); return; } - humid_register &= ~0b00000111; - humid_register |= this->humidity_oversampling_ & 0b111; - if (!this->write_byte(BME280_REGISTER_CONTROLHUMID, humid_register)) { + humid_control_val &= ~0b00000111; + humid_control_val |= this->humidity_oversampling_ & 0b111; + if (!this->write_byte(BME280_REGISTER_CONTROLHUMID, humid_control_val)) { this->mark_failed(); return; } @@ -169,11 +169,11 @@ inline uint8_t oversampling_to_time(BME280Oversampling over_sampling) { return ( void BME280Component::update() { // Enable sensor ESP_LOGV(TAG, "Sending conversion request..."); - uint8_t meas_register = 0; - meas_register |= (this->temperature_oversampling_ & 0b111) << 5; - meas_register |= (this->pressure_oversampling_ & 0b111) << 2; - meas_register |= BME280_MODE_FORCED; - if (!this->write_byte(BME280_REGISTER_CONTROL, meas_register)) { + uint8_t meas_value = 0; + meas_value |= (this->temperature_oversampling_ & 0b111) << 5; + meas_value |= (this->pressure_oversampling_ & 0b111) << 2; + meas_value |= BME280_MODE_FORCED; + if (!this->write_byte(BME280_REGISTER_CONTROL, meas_value)) { this->status_set_warning(); return; } diff --git a/esphome/components/bmp280/bmp280.cpp b/esphome/components/bmp280/bmp280.cpp index 4048e40077..d04a357ca2 100644 --- a/esphome/components/bmp280/bmp280.cpp +++ b/esphome/components/bmp280/bmp280.cpp @@ -123,11 +123,11 @@ inline uint8_t oversampling_to_time(BMP280Oversampling over_sampling) { return ( void BMP280Component::update() { // Enable sensor ESP_LOGV(TAG, "Sending conversion request..."); - uint8_t meas_register = 0; - meas_register |= (this->temperature_oversampling_ & 0b111) << 5; - meas_register |= (this->pressure_oversampling_ & 0b111) << 2; - meas_register |= 0b01; // Forced mode - if (!this->write_byte(BMP280_REGISTER_CONTROL, meas_register)) { + uint8_t meas_value = 0; + meas_value |= (this->temperature_oversampling_ & 0b111) << 5; + meas_value |= (this->pressure_oversampling_ & 0b111) << 2; + meas_value |= 0b01; // Forced mode + if (!this->write_byte(BMP280_REGISTER_CONTROL, meas_value)) { this->status_set_warning(); return; } diff --git a/esphome/components/display/display_buffer.cpp b/esphome/components/display/display_buffer.cpp index 29f86dbd5f..c330d00ce5 100644 --- a/esphome/components/display/display_buffer.cpp +++ b/esphome/components/display/display_buffer.cpp @@ -514,9 +514,7 @@ Color Animation::get_grayscale_pixel(int x, int y) const { return Color(gray | gray << 8 | gray << 16 | gray << 24); } Animation::Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, ImageType type) - : Image(data_start, width, height, type), animation_frame_count_(animation_frame_count) { - current_frame_ = 0; -} + : Image(data_start, width, height, type), current_frame_(0), animation_frame_count_(animation_frame_count) {} int Animation::get_animation_frame_count() const { return this->animation_frame_count_; } int Animation::get_current_frame() const { return this->current_frame_; } void Animation::next_frame() { diff --git a/esphome/components/i2c/i2c.cpp b/esphome/components/i2c/i2c.cpp index 77dfcedc04..f1980e1f1d 100644 --- a/esphome/components/i2c/i2c.cpp +++ b/esphome/components/i2c/i2c.cpp @@ -16,7 +16,7 @@ I2CComponent::I2CComponent() { this->wire_ = new TwoWire(next_i2c_bus_num_); next_i2c_bus_num_++; #else - this->wire_ = &Wire; + this->wire_ = &Wire; // NOLINT(cppcoreguidelines-prefer-member-initializer) #endif } diff --git a/esphome/components/mcp3008/mcp3008.cpp b/esphome/components/mcp3008/mcp3008.cpp index 909a6f4708..bea24b5c6a 100644 --- a/esphome/components/mcp3008/mcp3008.cpp +++ b/esphome/components/mcp3008/mcp3008.cpp @@ -38,9 +38,8 @@ float MCP3008::read_data(uint8_t pin) { } MCP3008Sensor::MCP3008Sensor(MCP3008 *parent, const std::string &name, uint8_t pin, float reference_voltage) - : PollingComponent(1000), parent_(parent), pin_(pin) { + : PollingComponent(1000), parent_(parent), pin_(pin), reference_voltage_(reference_voltage) { this->set_name(name); - this->reference_voltage_ = reference_voltage; } float MCP3008Sensor::get_setup_priority() const { return setup_priority::DATA; } diff --git a/esphome/components/mqtt/mqtt_client.cpp b/esphome/components/mqtt/mqtt_client.cpp index afb67d36ed..be2176df09 100644 --- a/esphome/components/mqtt/mqtt_client.cpp +++ b/esphome/components/mqtt/mqtt_client.cpp @@ -25,7 +25,7 @@ MQTTClientComponent::MQTTClientComponent() { // Connection void MQTTClientComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up MQTT..."); - this->mqtt_client_.onMessage([this](char *topic, char *payload, AsyncMqttClientMessageProperties properties, + this->mqtt_client_.onMessage([this](char const *topic, char *payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) { if (index == 0) this->payload_buffer_.reserve(total); diff --git a/esphome/components/remote_base/rc5_protocol.cpp b/esphome/components/remote_base/rc5_protocol.cpp index 80af2f9ad8..a8b608ec9e 100644 --- a/esphome/components/remote_base/rc5_protocol.cpp +++ b/esphome/components/remote_base/rc5_protocol.cpp @@ -41,7 +41,7 @@ optional RC5Protocol::decode(RemoteReceiveData src) { .address = 0, .command = 0, }; - int field_bit = 0; + uint8_t field_bit; if (src.expect_space(BIT_TIME_US) && src.expect_mark(BIT_TIME_US)) { field_bit = 1; @@ -60,7 +60,7 @@ optional RC5Protocol::decode(RemoteReceiveData src) { return {}; } - uint64_t out_data = 0; + uint32_t out_data = 0; for (int bit = NBITS - 4; bit >= 1; bit--) { if ((src.expect_space(BIT_TIME_US) || src.expect_space(2 * BIT_TIME_US)) && (src.expect_mark(BIT_TIME_US) || src.peek_mark(2 * BIT_TIME_US))) { @@ -78,7 +78,7 @@ optional RC5Protocol::decode(RemoteReceiveData src) { out_data |= 1; } - out.command = (out_data & 0x3F) + (1 - field_bit) * 64; + out.command = (uint8_t)(out_data & 0x3F) + (1 - field_bit) * 64u; out.address = (out_data >> 6) & 0x1F; return out; } diff --git a/esphome/components/st7735/st7735.cpp b/esphome/components/st7735/st7735.cpp index 0467ed83db..ac8802739b 100644 --- a/esphome/components/st7735/st7735.cpp +++ b/esphome/components/st7735/st7735.cpp @@ -220,15 +220,14 @@ static const uint8_t PROGMEM // clang-format on static const char *const TAG = "st7735"; -ST7735::ST7735(ST7735Model model, int width, int height, int colstart, int rowstart, bool eightbitcolor, bool usebgr) { - model_ = model; - this->width_ = width; - this->height_ = height; - this->colstart_ = colstart; - this->rowstart_ = rowstart; - this->eightbitcolor_ = eightbitcolor; - this->usebgr_ = usebgr; -} +ST7735::ST7735(ST7735Model model, int width, int height, int colstart, int rowstart, bool eightbitcolor, bool usebgr) + : model_(model), + colstart_(colstart), + rowstart_(rowstart), + eightbitcolor_(eightbitcolor), + usebgr_(usebgr), + width_(width), + height_(height) {} void ST7735::setup() { ESP_LOGCONFIG(TAG, "Setting up ST7735..."); diff --git a/esphome/components/sun/sun.cpp b/esphome/components/sun/sun.cpp index 4fd034ce7d..113c14d431 100644 --- a/esphome/components/sun/sun.cpp +++ b/esphome/components/sun/sun.cpp @@ -79,10 +79,8 @@ struct SunAtTime { num_t jde; num_t t; - SunAtTime(num_t jde) : jde(jde) { - // eq 25.1, p. 163; julian centuries from the epoch J2000.0 - t = (jde - 2451545) / 36525.0; - } + // eq 25.1, p. 163; julian centuries from the epoch J2000.0 + SunAtTime(num_t jde) : jde(jde), t((jde - 2451545) / 36525.0) {} num_t mean_obliquity() const { // eq. 22.2, p. 147; mean obliquity of the ecliptic diff --git a/esphome/core/component.cpp b/esphome/core/component.cpp index cd3081998b..7682c083a5 100644 --- a/esphome/core/component.cpp +++ b/esphome/core/component.cpp @@ -196,10 +196,8 @@ uint32_t Nameable::get_object_id_hash() { return this->object_id_hash_; } bool Nameable::is_disabled_by_default() const { return this->disabled_by_default_; } void Nameable::set_disabled_by_default(bool disabled_by_default) { this->disabled_by_default_ = disabled_by_default; } -WarnIfComponentBlockingGuard::WarnIfComponentBlockingGuard(Component *component) { - component_ = component; - started_ = millis(); -} +WarnIfComponentBlockingGuard::WarnIfComponentBlockingGuard(Component *component) + : started_(millis()), component_(component) {} WarnIfComponentBlockingGuard::~WarnIfComponentBlockingGuard() { uint32_t now = millis(); if (now - started_ > 50) { diff --git a/esphome/core/preferences.cpp b/esphome/core/preferences.cpp index 68030a2d59..bfc5d9f875 100644 --- a/esphome/core/preferences.cpp +++ b/esphome/core/preferences.cpp @@ -20,7 +20,7 @@ static const char *const TAG = "preferences"; ESPPreferenceObject::ESPPreferenceObject() : offset_(0), length_words_(0), type_(0), data_(nullptr) {} ESPPreferenceObject::ESPPreferenceObject(size_t offset, size_t length, uint32_t type) : offset_(offset), length_words_(length), type_(type) { - this->data_ = new uint32_t[this->length_words_ + 1]; + this->data_ = new uint32_t[this->length_words_ + 1]; // NOLINT(cppcoreguidelines-prefer-member-initializer) for (uint32_t i = 0; i < this->length_words_ + 1; i++) this->data_[i] = 0; } @@ -69,7 +69,7 @@ static inline bool esp_rtc_user_mem_read(uint32_t index, uint32_t *dest) { if (index >= ESP_RTC_USER_MEM_SIZE_WORDS) { return false; } - *dest = ESP_RTC_USER_MEM[index]; + *dest = ESP_RTC_USER_MEM[index]; // NOLINT(performance-no-int-to-ptr) return true; } @@ -83,7 +83,7 @@ static inline bool esp_rtc_user_mem_write(uint32_t index, uint32_t value) { return false; } - auto *ptr = &ESP_RTC_USER_MEM[index]; + auto *ptr = &ESP_RTC_USER_MEM[index]; // NOLINT(performance-no-int-to-ptr) *ptr = value; return true; } From 3aa107142bdca8a9d1b54ea9073033dfcfaf99d6 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Mon, 13 Sep 2021 09:37:11 +0200 Subject: [PATCH 009/207] Only try compat parsing after regular parsing fails (#2269) --- esphome/__main__.py | 146 +++++++++++++++++++++++--------------------- 1 file changed, 78 insertions(+), 68 deletions(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index 8d6f2b8f89..97b2988c2e 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -483,75 +483,9 @@ def parse_args(argv): metavar=("key", "value"), ) - # Keep backward compatibility with the old command line format of - # esphome . - # - # Unfortunately this can't be done by adding another configuration argument to the - # main config parser, as argparse is greedy when parsing arguments, so in regular - # usage it'll eat the command as the configuration argument and error out out - # because it can't parse the configuration as a command. - # - # Instead, construct an ad-hoc parser for the old format that doesn't actually - # process the arguments, but parses them enough to let us figure out if the old - # format is used. In that case, swap the command and configuration in the arguments - # and continue on with the normal parser (after raising a deprecation warning). - # - # Disable argparse's built-in help option and add it manually to prevent this - # parser from printing the help messagefor the old format when invoked with -h. - compat_parser = argparse.ArgumentParser(parents=[options_parser], add_help=False) - compat_parser.add_argument("-h", "--help") - compat_parser.add_argument("configuration", nargs="*") - compat_parser.add_argument( - "command", - choices=[ - "config", - "compile", - "upload", - "logs", - "run", - "clean-mqtt", - "wizard", - "mqtt-fingerprint", - "version", - "clean", - "dashboard", - "vscode", - "update-all", - ], - ) - - # on Python 3.9+ we can simply set exit_on_error=False in the constructor - def _raise(x): - raise argparse.ArgumentError(None, x) - - compat_parser.error = _raise - - deprecated_argv_suggestion = None - - if ["dashboard", "config"] == argv[1:3] or ["version"] == argv[1:2]: - # this is most likely meant in new-style arg format. do not try compat parsing - pass - else: - try: - result, unparsed = compat_parser.parse_known_args(argv[1:]) - last_option = len(argv) - len(unparsed) - 1 - len(result.configuration) - unparsed = [ - "--device" if arg in ("--upload-port", "--serial-port") else arg - for arg in unparsed - ] - argv = ( - argv[0:last_option] + [result.command] + result.configuration + unparsed - ) - deprecated_argv_suggestion = argv - except argparse.ArgumentError: - # This is not an old-style command line, so we don't have to do anything. - pass - - # And continue on with regular parsing parser = argparse.ArgumentParser( description=f"ESPHome v{const.__version__}", parents=[options_parser] ) - parser.set_defaults(deprecated_argv_suggestion=deprecated_argv_suggestion) mqtt_options = argparse.ArgumentParser(add_help=False) mqtt_options.add_argument("--topic", help="Manually set the MQTT topic.") @@ -701,7 +635,83 @@ def parse_args(argv): "configuration", help="Your YAML configuration file directories.", nargs="+" ) - return parser.parse_args(argv[1:]) + # Keep backward compatibility with the old command line format of + # esphome . + # + # Unfortunately this can't be done by adding another configuration argument to the + # main config parser, as argparse is greedy when parsing arguments, so in regular + # usage it'll eat the command as the configuration argument and error out out + # because it can't parse the configuration as a command. + # + # Instead, if parsing using the current format fails, construct an ad-hoc parser + # that doesn't actually process the arguments, but parses them enough to let us + # figure out if the old format is used. In that case, swap the command and + # configuration in the arguments and retry with the normal parser (and raise + # a deprecation warning). + arguments = argv[1:] + + # On Python 3.9+ we can simply set exit_on_error=False in the constructor + def _raise(x): + raise argparse.ArgumentError(None, x) + + # First, try new-style parsing, but don't exit in case of failure + try: + # duplicate parser so that we can use the original one to raise errors later on + current_parser = argparse.ArgumentParser(add_help=False, parents=[parser]) + current_parser.set_defaults(deprecated_argv_suggestion=None) + current_parser.error = _raise + return current_parser.parse_args(arguments) + except argparse.ArgumentError: + pass + + # Second, try compat parsing and rearrange the command-line if it succeeds + # Disable argparse's built-in help option and add it manually to prevent this + # parser from printing the help messagefor the old format when invoked with -h. + compat_parser = argparse.ArgumentParser(parents=[options_parser], add_help=False) + compat_parser.add_argument("-h", "--help", action="store_true") + compat_parser.add_argument("configuration", nargs="*") + compat_parser.add_argument( + "command", + choices=[ + "config", + "compile", + "upload", + "logs", + "run", + "clean-mqtt", + "wizard", + "mqtt-fingerprint", + "version", + "clean", + "dashboard", + "vscode", + "update-all", + ], + ) + + try: + compat_parser.error = _raise + result, unparsed = compat_parser.parse_known_args(argv[1:]) + last_option = len(arguments) - len(unparsed) - 1 - len(result.configuration) + unparsed = [ + "--device" if arg in ("--upload-port", "--serial-port") else arg + for arg in unparsed + ] + arguments = ( + arguments[0:last_option] + + [result.command] + + result.configuration + + unparsed + ) + deprecated_argv_suggestion = arguments + except argparse.ArgumentError: + # old-style parsing failed, don't suggest any argument + deprecated_argv_suggestion = None + + # Finally, run the new-style parser again with the possibly swapped arguments, + # and let it error out if the command is unparsable. + parser.set_defaults(deprecated_argv_suggestion=deprecated_argv_suggestion) + return parser.parse_args(arguments) def run_esphome(argv): @@ -715,7 +725,7 @@ def run_esphome(argv): "and will be removed in the future. " ) _LOGGER.warning("Please instead use:") - _LOGGER.warning(" esphome %s", " ".join(args.deprecated_argv_suggestion[1:])) + _LOGGER.warning(" esphome %s", " ".join(args.deprecated_argv_suggestion)) if sys.version_info < (3, 7, 0): _LOGGER.error( From e18dfdd6569a22ef16c5439b8e29404f4e2dc62b Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Mon, 13 Sep 2021 09:39:18 +0200 Subject: [PATCH 010/207] Suppress excessive warnings about deprecated Fan interfaces (#2270) --- esphome/components/api/api_connection.cpp | 5 ++++- esphome/components/fan/fan_helpers.cpp | 7 ++++--- esphome/components/fan/fan_helpers.h | 8 ++++++++ esphome/components/fan/fan_state.cpp | 2 ++ esphome/components/mqtt/mqtt_fan.cpp | 1 + esphome/components/web_server/web_server.cpp | 1 + 6 files changed, 20 insertions(+), 4 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 650f4f6f6e..ea3be42275 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -214,6 +214,9 @@ void APIConnection::cover_command(const CoverCommandRequest &msg) { #endif #ifdef USE_FAN +// Shut-up about usage of deprecated speed_level_to_enum/speed_enum_to_level functions for a bit. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" bool APIConnection::send_fan_state(fan::FanState *fan) { if (!this->state_subscription_) return false; @@ -262,13 +265,13 @@ void APIConnection::fan_command(const FanCommandRequest &msg) { // Prefer level call.set_speed(msg.speed_level); } else if (msg.has_speed) { - // NOLINTNEXTLINE(clang-diagnostic-deprecated-declarations) call.set_speed(fan::speed_enum_to_level(static_cast(msg.speed), traits.supported_speed_count())); } if (msg.has_direction) call.set_direction(static_cast(msg.direction)); call.perform(); } +#pragma GCC diagnostic pop #endif #ifdef USE_LIGHT diff --git a/esphome/components/fan/fan_helpers.cpp b/esphome/components/fan/fan_helpers.cpp index 5d923a1b15..34883617e6 100644 --- a/esphome/components/fan/fan_helpers.cpp +++ b/esphome/components/fan/fan_helpers.cpp @@ -4,14 +4,15 @@ namespace esphome { namespace fan { -// NOLINTNEXTLINE(clang-diagnostic-deprecated-declarations) +// This whole file is deprecated, don't warn about usage of deprecated types in here. +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + FanSpeed speed_level_to_enum(int speed_level, int supported_speed_levels) { const auto speed_ratio = static_cast(speed_level) / (supported_speed_levels + 1); const auto legacy_level = clamp(static_cast(ceilf(speed_ratio * 3)), 1, 3); - return static_cast(legacy_level - 1); // NOLINT(clang-diagnostic-deprecated-declarations) + return static_cast(legacy_level - 1); } -// NOLINTNEXTLINE(clang-diagnostic-deprecated-declarations) int speed_enum_to_level(FanSpeed speed, int supported_speed_levels) { const auto enum_level = static_cast(speed) + 1; const auto speed_level = roundf(enum_level / 3.0f * supported_speed_levels); diff --git a/esphome/components/fan/fan_helpers.h b/esphome/components/fan/fan_helpers.h index 138aa5bca3..009505601e 100644 --- a/esphome/components/fan/fan_helpers.h +++ b/esphome/components/fan/fan_helpers.h @@ -4,8 +4,16 @@ namespace esphome { namespace fan { +// Shut-up about usage of deprecated FanSpeed for a bit. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + +ESPDEPRECATED("FanSpeed and speed_level_to_enum() are deprecated.", "2021.9") FanSpeed speed_level_to_enum(int speed_level, int supported_speed_levels); +ESPDEPRECATED("FanSpeed and speed_enum_to_level() are deprecated.", "2021.9") int speed_enum_to_level(FanSpeed speed, int supported_speed_levels); +#pragma GCC diagnostic pop + } // namespace fan } // namespace esphome diff --git a/esphome/components/fan/fan_state.cpp b/esphome/components/fan/fan_state.cpp index a4883c5e2c..a57115beb4 100644 --- a/esphome/components/fan/fan_state.cpp +++ b/esphome/components/fan/fan_state.cpp @@ -67,6 +67,8 @@ void FanStateCall::perform() const { this->state_->state_callback_.call(); } +// This whole method is deprecated, don't warn about usage of deprecated methods inside of it. +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" FanStateCall &FanStateCall::set_speed(const char *legacy_speed) { const auto supported_speed_count = this->state_->get_traits().supported_speed_count(); if (strcasecmp(legacy_speed, "low") == 0) { diff --git a/esphome/components/mqtt/mqtt_fan.cpp b/esphome/components/mqtt/mqtt_fan.cpp index ba9121bc5d..b8eecf0ff3 100644 --- a/esphome/components/mqtt/mqtt_fan.cpp +++ b/esphome/components/mqtt/mqtt_fan.cpp @@ -100,6 +100,7 @@ bool MQTTFanComponent::publish_state() { auto traits = this->state_->get_traits(); if (traits.supports_speed()) { const char *payload; + // NOLINTNEXTLINE(clang-diagnostic-deprecated-declarations) switch (fan::speed_level_to_enum(this->state_->speed, traits.supported_speed_count())) { case FAN_SPEED_LOW: { // NOLINT(clang-diagnostic-deprecated-declarations) payload = "low"; diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 56c75a1c58..dc97bcd5c2 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -397,6 +397,7 @@ std::string WebServer::fan_json(fan::FanState *obj) { const auto traits = obj->get_traits(); if (traits.supports_speed()) { root["speed_level"] = obj->speed; + // NOLINTNEXTLINE(clang-diagnostic-deprecated-declarations) switch (fan::speed_level_to_enum(obj->speed, traits.supported_speed_count())) { case fan::FAN_SPEED_LOW: // NOLINT(clang-diagnostic-deprecated-declarations) root["speed"] = "low"; From d594a6fcbca53472b58649fcc13d897fb3edd3e4 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Mon, 13 Sep 2021 09:48:52 +0200 Subject: [PATCH 011/207] Store strings only used for logging in flash (#2274) Co-authored-by: Otto winter --- .../components/binary_sensor/binary_sensor.h | 2 +- esphome/components/climate/climate.cpp | 44 +++++---- esphome/components/climate/climate.h | 2 +- esphome/components/climate/climate_mode.cpp | 98 +++++++++---------- esphome/components/climate/climate_mode.h | 11 ++- esphome/components/cover/cover.h | 2 +- .../components/gpio/switch/gpio_switch.cpp | 16 +-- .../hitachi_ac344/hitachi_ac344.cpp | 2 +- .../hitachi_ac424/hitachi_ac424.cpp | 2 +- esphome/components/light/light_call.cpp | 33 ++++--- esphome/components/mqtt/mqtt_client.cpp | 24 ++--- esphome/components/number/number.h | 2 +- esphome/components/select/select.h | 2 +- esphome/components/sensor/sensor.cpp | 8 +- esphome/components/sensor/sensor.h | 7 +- esphome/components/switch/switch.h | 2 +- esphome/components/text_sensor/text_sensor.h | 2 +- esphome/components/uart/uart.cpp | 14 +-- esphome/components/uart/uart.h | 3 +- esphome/components/uart/uart_esp8266.cpp | 2 +- esphome/components/wifi/wifi_component.cpp | 67 ++++++------- .../wifi/wifi_component_esp8266.cpp | 38 +++---- esphome/core/esphal.cpp | 69 +++++-------- esphome/core/esphal.h | 7 +- esphome/core/log.h | 33 ++++--- 25 files changed, 241 insertions(+), 251 deletions(-) diff --git a/esphome/components/binary_sensor/binary_sensor.h b/esphome/components/binary_sensor/binary_sensor.h index 62e8031cb5..195badd798 100644 --- a/esphome/components/binary_sensor/binary_sensor.h +++ b/esphome/components/binary_sensor/binary_sensor.h @@ -10,7 +10,7 @@ namespace binary_sensor { #define LOG_BINARY_SENSOR(prefix, type, obj) \ if ((obj) != nullptr) { \ - ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, (obj)->get_name().c_str()); \ + ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \ if (!(obj)->get_device_class().empty()) { \ ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \ } \ diff --git a/esphome/components/climate/climate.cpp b/esphome/components/climate/climate.cpp index 4861e7b8cb..383103b014 100644 --- a/esphome/components/climate/climate.cpp +++ b/esphome/components/climate/climate.cpp @@ -9,8 +9,8 @@ void ClimateCall::perform() { ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str()); this->validate_(); if (this->mode_.has_value()) { - const char *mode_s = climate_mode_to_string(*this->mode_); - ESP_LOGD(TAG, " Mode: %s", mode_s); + const LogString *mode_s = climate_mode_to_string(*this->mode_); + ESP_LOGD(TAG, " Mode: %s", LOG_STR_ARG(mode_s)); } if (this->custom_fan_mode_.has_value()) { this->fan_mode_.reset(); @@ -18,8 +18,8 @@ void ClimateCall::perform() { } if (this->fan_mode_.has_value()) { this->custom_fan_mode_.reset(); - const char *fan_mode_s = climate_fan_mode_to_string(*this->fan_mode_); - ESP_LOGD(TAG, " Fan: %s", fan_mode_s); + const LogString *fan_mode_s = climate_fan_mode_to_string(*this->fan_mode_); + ESP_LOGD(TAG, " Fan: %s", LOG_STR_ARG(fan_mode_s)); } if (this->custom_preset_.has_value()) { this->preset_.reset(); @@ -27,12 +27,12 @@ void ClimateCall::perform() { } if (this->preset_.has_value()) { this->custom_preset_.reset(); - const char *preset_s = climate_preset_to_string(*this->preset_); - ESP_LOGD(TAG, " Preset: %s", preset_s); + const LogString *preset_s = climate_preset_to_string(*this->preset_); + ESP_LOGD(TAG, " Preset: %s", LOG_STR_ARG(preset_s)); } if (this->swing_mode_.has_value()) { - const char *swing_mode_s = climate_swing_mode_to_string(*this->swing_mode_); - ESP_LOGD(TAG, " Swing: %s", swing_mode_s); + const LogString *swing_mode_s = climate_swing_mode_to_string(*this->swing_mode_); + ESP_LOGD(TAG, " Swing: %s", LOG_STR_ARG(swing_mode_s)); } if (this->target_temperature_.has_value()) { ESP_LOGD(TAG, " Target Temperature: %.2f", *this->target_temperature_); @@ -50,7 +50,7 @@ void ClimateCall::validate_() { if (this->mode_.has_value()) { auto mode = *this->mode_; if (!traits.supports_mode(mode)) { - ESP_LOGW(TAG, " Mode %s is not supported by this device!", climate_mode_to_string(mode)); + ESP_LOGW(TAG, " Mode %s is not supported by this device!", LOG_STR_ARG(climate_mode_to_string(mode))); this->mode_.reset(); } } @@ -63,7 +63,8 @@ void ClimateCall::validate_() { } else if (this->fan_mode_.has_value()) { auto fan_mode = *this->fan_mode_; if (!traits.supports_fan_mode(fan_mode)) { - ESP_LOGW(TAG, " Fan Mode %s is not supported by this device!", climate_fan_mode_to_string(fan_mode)); + ESP_LOGW(TAG, " Fan Mode %s is not supported by this device!", + LOG_STR_ARG(climate_fan_mode_to_string(fan_mode))); this->fan_mode_.reset(); } } @@ -76,14 +77,15 @@ void ClimateCall::validate_() { } else if (this->preset_.has_value()) { auto preset = *this->preset_; if (!traits.supports_preset(preset)) { - ESP_LOGW(TAG, " Preset %s is not supported by this device!", climate_preset_to_string(preset)); + ESP_LOGW(TAG, " Preset %s is not supported by this device!", LOG_STR_ARG(climate_preset_to_string(preset))); this->preset_.reset(); } } if (this->swing_mode_.has_value()) { auto swing_mode = *this->swing_mode_; if (!traits.supports_swing_mode(swing_mode)) { - ESP_LOGW(TAG, " Swing Mode %s is not supported by this device!", climate_swing_mode_to_string(swing_mode)); + ESP_LOGW(TAG, " Swing Mode %s is not supported by this device!", + LOG_STR_ARG(climate_swing_mode_to_string(swing_mode))); this->swing_mode_.reset(); } } @@ -373,24 +375,24 @@ void Climate::publish_state() { ESP_LOGD(TAG, "'%s' - Sending state:", this->name_.c_str()); auto traits = this->get_traits(); - ESP_LOGD(TAG, " Mode: %s", climate_mode_to_string(this->mode)); + ESP_LOGD(TAG, " Mode: %s", LOG_STR_ARG(climate_mode_to_string(this->mode))); if (traits.get_supports_action()) { - ESP_LOGD(TAG, " Action: %s", climate_action_to_string(this->action)); + ESP_LOGD(TAG, " Action: %s", LOG_STR_ARG(climate_action_to_string(this->action))); } if (traits.get_supports_fan_modes() && this->fan_mode.has_value()) { - ESP_LOGD(TAG, " Fan Mode: %s", climate_fan_mode_to_string(this->fan_mode.value())); + ESP_LOGD(TAG, " Fan Mode: %s", LOG_STR_ARG(climate_fan_mode_to_string(this->fan_mode.value()))); } if (!traits.get_supported_custom_fan_modes().empty() && this->custom_fan_mode.has_value()) { ESP_LOGD(TAG, " Custom Fan Mode: %s", this->custom_fan_mode.value().c_str()); } if (traits.get_supports_presets() && this->preset.has_value()) { - ESP_LOGD(TAG, " Preset: %s", climate_preset_to_string(this->preset.value())); + ESP_LOGD(TAG, " Preset: %s", LOG_STR_ARG(climate_preset_to_string(this->preset.value()))); } if (!traits.get_supported_custom_presets().empty() && this->custom_preset.has_value()) { ESP_LOGD(TAG, " Custom Preset: %s", this->custom_preset.value().c_str()); } if (traits.get_supports_swing_modes()) { - ESP_LOGD(TAG, " Swing Mode: %s", climate_swing_mode_to_string(this->swing_mode)); + ESP_LOGD(TAG, " Swing Mode: %s", LOG_STR_ARG(climate_swing_mode_to_string(this->swing_mode))); } if (traits.get_supports_current_temperature()) { ESP_LOGD(TAG, " Current Temperature: %.2f°C", this->current_temperature); @@ -534,12 +536,12 @@ void Climate::dump_traits_(const char *tag) { if (!traits.get_supported_modes().empty()) { ESP_LOGCONFIG(tag, " [x] Supported modes:"); for (ClimateMode m : traits.get_supported_modes()) - ESP_LOGCONFIG(tag, " - %s", climate_mode_to_string(m)); + ESP_LOGCONFIG(tag, " - %s", LOG_STR_ARG(climate_mode_to_string(m))); } if (!traits.get_supported_fan_modes().empty()) { ESP_LOGCONFIG(tag, " [x] Supported fan modes:"); for (ClimateFanMode m : traits.get_supported_fan_modes()) - ESP_LOGCONFIG(tag, " - %s", climate_fan_mode_to_string(m)); + ESP_LOGCONFIG(tag, " - %s", LOG_STR_ARG(climate_fan_mode_to_string(m))); } if (!traits.get_supported_custom_fan_modes().empty()) { ESP_LOGCONFIG(tag, " [x] Supported custom fan modes:"); @@ -549,7 +551,7 @@ void Climate::dump_traits_(const char *tag) { if (!traits.get_supported_presets().empty()) { ESP_LOGCONFIG(tag, " [x] Supported presets:"); for (ClimatePreset p : traits.get_supported_presets()) - ESP_LOGCONFIG(tag, " - %s", climate_preset_to_string(p)); + ESP_LOGCONFIG(tag, " - %s", LOG_STR_ARG(climate_preset_to_string(p))); } if (!traits.get_supported_custom_presets().empty()) { ESP_LOGCONFIG(tag, " [x] Supported custom presets:"); @@ -559,7 +561,7 @@ void Climate::dump_traits_(const char *tag) { if (!traits.get_supported_swing_modes().empty()) { ESP_LOGCONFIG(tag, " [x] Supported swing modes:"); for (ClimateSwingMode m : traits.get_supported_swing_modes()) - ESP_LOGCONFIG(tag, " - %s", climate_swing_mode_to_string(m)); + ESP_LOGCONFIG(tag, " - %s", LOG_STR_ARG(climate_swing_mode_to_string(m))); } } diff --git a/esphome/components/climate/climate.h b/esphome/components/climate/climate.h index 46d0fb1d77..418db02485 100644 --- a/esphome/components/climate/climate.h +++ b/esphome/components/climate/climate.h @@ -12,7 +12,7 @@ namespace climate { #define LOG_CLIMATE(prefix, type, obj) \ if ((obj) != nullptr) { \ - ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, (obj)->get_name().c_str()); \ + ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \ } class Climate; diff --git a/esphome/components/climate/climate_mode.cpp b/esphome/components/climate/climate_mode.cpp index 7a626942eb..e46159a750 100644 --- a/esphome/components/climate/climate_mode.cpp +++ b/esphome/components/climate/climate_mode.cpp @@ -3,105 +3,105 @@ namespace esphome { namespace climate { -const char *climate_mode_to_string(ClimateMode mode) { +const LogString *climate_mode_to_string(ClimateMode mode) { switch (mode) { case CLIMATE_MODE_OFF: - return "OFF"; - case CLIMATE_MODE_AUTO: - return "AUTO"; - case CLIMATE_MODE_COOL: - return "COOL"; - case CLIMATE_MODE_HEAT: - return "HEAT"; - case CLIMATE_MODE_FAN_ONLY: - return "FAN_ONLY"; - case CLIMATE_MODE_DRY: - return "DRY"; + return LOG_STR("OFF"); case CLIMATE_MODE_HEAT_COOL: - return "HEAT_COOL"; + return LOG_STR("HEAT_COOL"); + case CLIMATE_MODE_AUTO: + return LOG_STR("AUTO"); + case CLIMATE_MODE_COOL: + return LOG_STR("COOL"); + case CLIMATE_MODE_HEAT: + return LOG_STR("HEAT"); + case CLIMATE_MODE_FAN_ONLY: + return LOG_STR("FAN_ONLY"); + case CLIMATE_MODE_DRY: + return LOG_STR("DRY"); default: - return "UNKNOWN"; + return LOG_STR("UNKNOWN"); } } -const char *climate_action_to_string(ClimateAction action) { +const LogString *climate_action_to_string(ClimateAction action) { switch (action) { case CLIMATE_ACTION_OFF: - return "OFF"; + return LOG_STR("OFF"); case CLIMATE_ACTION_COOLING: - return "COOLING"; + return LOG_STR("COOLING"); case CLIMATE_ACTION_HEATING: - return "HEATING"; + return LOG_STR("HEATING"); case CLIMATE_ACTION_IDLE: - return "IDLE"; + return LOG_STR("IDLE"); case CLIMATE_ACTION_DRYING: - return "DRYING"; + return LOG_STR("DRYING"); case CLIMATE_ACTION_FAN: - return "FAN"; + return LOG_STR("FAN"); default: - return "UNKNOWN"; + return LOG_STR("UNKNOWN"); } } -const char *climate_fan_mode_to_string(ClimateFanMode fan_mode) { +const LogString *climate_fan_mode_to_string(ClimateFanMode fan_mode) { switch (fan_mode) { case climate::CLIMATE_FAN_ON: - return "ON"; + return LOG_STR("ON"); case climate::CLIMATE_FAN_OFF: - return "OFF"; + return LOG_STR("OFF"); case climate::CLIMATE_FAN_AUTO: - return "AUTO"; + return LOG_STR("AUTO"); case climate::CLIMATE_FAN_LOW: - return "LOW"; + return LOG_STR("LOW"); case climate::CLIMATE_FAN_MEDIUM: - return "MEDIUM"; + return LOG_STR("MEDIUM"); case climate::CLIMATE_FAN_HIGH: - return "HIGH"; + return LOG_STR("HIGH"); case climate::CLIMATE_FAN_MIDDLE: - return "MIDDLE"; + return LOG_STR("MIDDLE"); case climate::CLIMATE_FAN_FOCUS: - return "FOCUS"; + return LOG_STR("FOCUS"); case climate::CLIMATE_FAN_DIFFUSE: - return "DIFFUSE"; + return LOG_STR("DIFFUSE"); default: - return "UNKNOWN"; + return LOG_STR("UNKNOWN"); } } -const char *climate_swing_mode_to_string(ClimateSwingMode swing_mode) { +const LogString *climate_swing_mode_to_string(ClimateSwingMode swing_mode) { switch (swing_mode) { case climate::CLIMATE_SWING_OFF: - return "OFF"; + return LOG_STR("OFF"); case climate::CLIMATE_SWING_BOTH: - return "BOTH"; + return LOG_STR("BOTH"); case climate::CLIMATE_SWING_VERTICAL: - return "VERTICAL"; + return LOG_STR("VERTICAL"); case climate::CLIMATE_SWING_HORIZONTAL: - return "HORIZONTAL"; + return LOG_STR("HORIZONTAL"); default: - return "UNKNOWN"; + return LOG_STR("UNKNOWN"); } } -const char *climate_preset_to_string(ClimatePreset preset) { +const LogString *climate_preset_to_string(ClimatePreset preset) { switch (preset) { case climate::CLIMATE_PRESET_NONE: - return "NONE"; + return LOG_STR("NONE"); case climate::CLIMATE_PRESET_HOME: - return "HOME"; + return LOG_STR("HOME"); case climate::CLIMATE_PRESET_ECO: - return "ECO"; + return LOG_STR("ECO"); case climate::CLIMATE_PRESET_AWAY: - return "AWAY"; + return LOG_STR("AWAY"); case climate::CLIMATE_PRESET_BOOST: - return "BOOST"; + return LOG_STR("BOOST"); case climate::CLIMATE_PRESET_COMFORT: - return "COMFORT"; + return LOG_STR("COMFORT"); case climate::CLIMATE_PRESET_SLEEP: - return "SLEEP"; + return LOG_STR("SLEEP"); case climate::CLIMATE_PRESET_ACTIVITY: - return "ACTIVITY"; + return LOG_STR("ACTIVITY"); default: - return "UNKNOWN"; + return LOG_STR("UNKNOWN"); } } diff --git a/esphome/components/climate/climate_mode.h b/esphome/components/climate/climate_mode.h index 476cf5bd84..3e5626919c 100644 --- a/esphome/components/climate/climate_mode.h +++ b/esphome/components/climate/climate_mode.h @@ -1,6 +1,7 @@ #pragma once #include +#include "esphome/core/log.h" namespace esphome { namespace climate { @@ -96,19 +97,19 @@ enum ClimatePreset : uint8_t { }; /// Convert the given ClimateMode to a human-readable string. -const char *climate_mode_to_string(ClimateMode mode); +const LogString *climate_mode_to_string(ClimateMode mode); /// Convert the given ClimateAction to a human-readable string. -const char *climate_action_to_string(ClimateAction action); +const LogString *climate_action_to_string(ClimateAction action); /// Convert the given ClimateFanMode to a human-readable string. -const char *climate_fan_mode_to_string(ClimateFanMode mode); +const LogString *climate_fan_mode_to_string(ClimateFanMode mode); /// Convert the given ClimateSwingMode to a human-readable string. -const char *climate_swing_mode_to_string(ClimateSwingMode mode); +const LogString *climate_swing_mode_to_string(ClimateSwingMode mode); /// Convert the given ClimateSwingMode to a human-readable string. -const char *climate_preset_to_string(ClimatePreset preset); +const LogString *climate_preset_to_string(ClimatePreset preset); } // namespace climate } // namespace esphome diff --git a/esphome/components/cover/cover.h b/esphome/components/cover/cover.h index 77a53f11c5..72ec15a459 100644 --- a/esphome/components/cover/cover.h +++ b/esphome/components/cover/cover.h @@ -13,7 +13,7 @@ const extern float COVER_CLOSED; #define LOG_COVER(prefix, type, obj) \ if ((obj) != nullptr) { \ - ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, (obj)->get_name().c_str()); \ + ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \ auto traits_ = (obj)->get_traits(); \ if (traits_.get_is_assumed_state()) { \ ESP_LOGCONFIG(TAG, "%s Assumed State: YES", prefix); \ diff --git a/esphome/components/gpio/switch/gpio_switch.cpp b/esphome/components/gpio/switch/gpio_switch.cpp index 410a3818d6..56e0087eae 100644 --- a/esphome/components/gpio/switch/gpio_switch.cpp +++ b/esphome/components/gpio/switch/gpio_switch.cpp @@ -47,28 +47,28 @@ void GPIOSwitch::setup() { void GPIOSwitch::dump_config() { LOG_SWITCH("", "GPIO Switch", this); LOG_PIN(" Pin: ", this->pin_); - const char *restore_mode = ""; + const LogString *restore_mode = LOG_STR(""); switch (this->restore_mode_) { case GPIO_SWITCH_RESTORE_DEFAULT_OFF: - restore_mode = "Restore (Defaults to OFF)"; + restore_mode = LOG_STR("Restore (Defaults to OFF)"); break; case GPIO_SWITCH_RESTORE_DEFAULT_ON: - restore_mode = "Restore (Defaults to ON)"; + restore_mode = LOG_STR("Restore (Defaults to ON)"); break; case GPIO_SWITCH_RESTORE_INVERTED_DEFAULT_ON: - restore_mode = "Restore inverted (Defaults to ON)"; + restore_mode = LOG_STR("Restore inverted (Defaults to ON)"); break; case GPIO_SWITCH_RESTORE_INVERTED_DEFAULT_OFF: - restore_mode = "Restore inverted (Defaults to OFF)"; + restore_mode = LOG_STR("Restore inverted (Defaults to OFF)"); break; case GPIO_SWITCH_ALWAYS_OFF: - restore_mode = "Always OFF"; + restore_mode = LOG_STR("Always OFF"); break; case GPIO_SWITCH_ALWAYS_ON: - restore_mode = "Always ON"; + restore_mode = LOG_STR("Always ON"); break; } - ESP_LOGCONFIG(TAG, " Restore Mode: %s", restore_mode); + ESP_LOGCONFIG(TAG, " Restore Mode: %s", LOG_STR_ARG(restore_mode)); if (!this->interlock_.empty()) { ESP_LOGCONFIG(TAG, " Interlocks:"); for (auto *lock : this->interlock_) { diff --git a/esphome/components/hitachi_ac344/hitachi_ac344.cpp b/esphome/components/hitachi_ac344/hitachi_ac344.cpp index 1e5bca1396..067ea39d07 100644 --- a/esphome/components/hitachi_ac344/hitachi_ac344.cpp +++ b/esphome/components/hitachi_ac344/hitachi_ac344.cpp @@ -165,7 +165,7 @@ void HitachiClimate::transmit_state() { set_power_(false); break; default: - ESP_LOGW(TAG, "Unsupported mode: %s", climate_mode_to_string(this->mode)); + ESP_LOGW(TAG, "Unsupported mode: %s", LOG_STR_ARG(climate_mode_to_string(this->mode))); } set_temp_(static_cast(this->target_temperature)); diff --git a/esphome/components/hitachi_ac424/hitachi_ac424.cpp b/esphome/components/hitachi_ac424/hitachi_ac424.cpp index 10b83cbd58..2e5423a37a 100644 --- a/esphome/components/hitachi_ac424/hitachi_ac424.cpp +++ b/esphome/components/hitachi_ac424/hitachi_ac424.cpp @@ -166,7 +166,7 @@ void HitachiClimate::transmit_state() { set_power_(false); break; default: - ESP_LOGW(TAG, "Unsupported mode: %s", climate_mode_to_string(this->mode)); + ESP_LOGW(TAG, "Unsupported mode: %s", LOG_STR_ARG(climate_mode_to_string(this->mode))); } set_temp_(static_cast(this->target_temperature)); diff --git a/esphome/components/light/light_call.cpp b/esphome/components/light/light_call.cpp index d979b13368..178a78a94c 100644 --- a/esphome/components/light/light_call.cpp +++ b/esphome/components/light/light_call.cpp @@ -7,24 +7,24 @@ namespace light { static const char *const TAG = "light"; -static const char *color_mode_to_human(ColorMode color_mode) { +static const LogString *color_mode_to_human(ColorMode color_mode) { if (color_mode == ColorMode::UNKNOWN) - return "Unknown"; + return LOG_STR("Unknown"); if (color_mode == ColorMode::WHITE) - return "White"; + return LOG_STR("White"); if (color_mode == ColorMode::COLOR_TEMPERATURE) - return "Color temperature"; + return LOG_STR("Color temperature"); if (color_mode == ColorMode::COLD_WARM_WHITE) - return "Cold/warm white"; + return LOG_STR("Cold/warm white"); if (color_mode == ColorMode::RGB) - return "RGB"; + return LOG_STR("RGB"); if (color_mode == ColorMode::RGB_WHITE) - return "RGBW"; + return LOG_STR("RGBW"); if (color_mode == ColorMode::RGB_COLD_WARM_WHITE) - return "RGB + cold/warm white"; + return LOG_STR("RGB + cold/warm white"); if (color_mode == ColorMode::RGB_COLOR_TEMPERATURE) - return "RGB + color temperature"; - return ""; + return LOG_STR("RGB + color temperature"); + return LOG_STR(""); } void LightCall::perform() { @@ -37,7 +37,7 @@ void LightCall::perform() { // Only print color mode when it's being changed ColorMode current_color_mode = this->parent_->remote_values.get_color_mode(); if (this->color_mode_.value_or(current_color_mode) != current_color_mode) { - ESP_LOGD(TAG, " Color mode: %s", color_mode_to_human(v.get_color_mode())); + ESP_LOGD(TAG, " Color mode: %s", LOG_STR_ARG(color_mode_to_human(v.get_color_mode()))); } // Only print state when it's being changed @@ -135,7 +135,7 @@ LightColorValues LightCall::validate_() { // Color mode check if (this->color_mode_.has_value() && !traits.supports_color_mode(this->color_mode_.value())) { ESP_LOGW(TAG, "'%s' - This light does not support color mode %s!", name, - color_mode_to_human(this->color_mode_.value())); + LOG_STR_ARG(color_mode_to_human(this->color_mode_.value()))); this->color_mode_.reset(); } @@ -206,7 +206,8 @@ LightColorValues LightCall::validate_() { if (name_##_.has_value()) { \ auto val = *name_##_; \ if (val < (min) || val > (max)) { \ - ESP_LOGW(TAG, "'%s' - %s value %.2f is out of range [%.1f - %.1f]!", name, upper_name, val, (min), (max)); \ + ESP_LOGW(TAG, "'%s' - %s value %.2f is out of range [%.1f - %.1f]!", name, LOG_STR_LITERAL(upper_name), val, \ + (min), (max)); \ name_##_ = clamp(val, (min), (max)); \ } \ } @@ -387,7 +388,7 @@ ColorMode LightCall::compute_color_mode_() { // Don't change if the current mode is suitable. if (suitable_modes.count(current_mode) > 0) { ESP_LOGI(TAG, "'%s' - Keeping current color mode %s for call without color mode.", - this->parent_->get_name().c_str(), color_mode_to_human(current_mode)); + this->parent_->get_name().c_str(), LOG_STR_ARG(color_mode_to_human(current_mode))); return current_mode; } @@ -397,7 +398,7 @@ ColorMode LightCall::compute_color_mode_() { continue; ESP_LOGI(TAG, "'%s' - Using color mode %s for call without color mode.", this->parent_->get_name().c_str(), - color_mode_to_human(mode)); + LOG_STR_ARG(color_mode_to_human(mode))); return mode; } @@ -405,7 +406,7 @@ ColorMode LightCall::compute_color_mode_() { // out whatever we don't support. auto color_mode = current_mode != ColorMode::UNKNOWN ? current_mode : *supported_modes.begin(); ESP_LOGW(TAG, "'%s' - No color mode suitable for this call supported, defaulting to %s!", - this->parent_->get_name().c_str(), color_mode_to_human(color_mode)); + this->parent_->get_name().c_str(), LOG_STR_ARG(color_mode_to_human(color_mode))); return color_mode; } std::set LightCall::get_suitable_color_modes_() { diff --git a/esphome/components/mqtt/mqtt_client.cpp b/esphome/components/mqtt/mqtt_client.cpp index be2176df09..129597cc00 100644 --- a/esphome/components/mqtt/mqtt_client.cpp +++ b/esphome/components/mqtt/mqtt_client.cpp @@ -221,40 +221,40 @@ void MQTTClientComponent::check_connected() { void MQTTClientComponent::loop() { if (this->disconnect_reason_.has_value()) { - const char *reason_s = nullptr; + const LogString *reason_s; switch (*this->disconnect_reason_) { case AsyncMqttClientDisconnectReason::TCP_DISCONNECTED: - reason_s = "TCP disconnected"; + reason_s = LOG_STR("TCP disconnected"); break; case AsyncMqttClientDisconnectReason::MQTT_UNACCEPTABLE_PROTOCOL_VERSION: - reason_s = "Unacceptable Protocol Version"; + reason_s = LOG_STR("Unacceptable Protocol Version"); break; case AsyncMqttClientDisconnectReason::MQTT_IDENTIFIER_REJECTED: - reason_s = "Identifier Rejected"; + reason_s = LOG_STR("Identifier Rejected"); break; case AsyncMqttClientDisconnectReason::MQTT_SERVER_UNAVAILABLE: - reason_s = "Server Unavailable"; + reason_s = LOG_STR("Server Unavailable"); break; case AsyncMqttClientDisconnectReason::MQTT_MALFORMED_CREDENTIALS: - reason_s = "Malformed Credentials"; + reason_s = LOG_STR("Malformed Credentials"); break; case AsyncMqttClientDisconnectReason::MQTT_NOT_AUTHORIZED: - reason_s = "Not Authorized"; + reason_s = LOG_STR("Not Authorized"); break; case AsyncMqttClientDisconnectReason::ESP8266_NOT_ENOUGH_SPACE: - reason_s = "Not Enough Space"; + reason_s = LOG_STR("Not Enough Space"); break; case AsyncMqttClientDisconnectReason::TLS_BAD_FINGERPRINT: - reason_s = "TLS Bad Fingerprint"; + reason_s = LOG_STR("TLS Bad Fingerprint"); break; default: - reason_s = "Unknown"; + reason_s = LOG_STR("Unknown"); break; } if (!network_is_connected()) { - reason_s = "WiFi disconnected"; + reason_s = LOG_STR("WiFi disconnected"); } - ESP_LOGW(TAG, "MQTT Disconnected: %s.", reason_s); + ESP_LOGW(TAG, "MQTT Disconnected: %s.", LOG_STR_ARG(reason_s)); this->disconnect_reason_.reset(); } diff --git a/esphome/components/number/number.h b/esphome/components/number/number.h index e32b53187b..945f174510 100644 --- a/esphome/components/number/number.h +++ b/esphome/components/number/number.h @@ -8,7 +8,7 @@ namespace number { #define LOG_NUMBER(prefix, type, obj) \ if ((obj) != nullptr) { \ - ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, (obj)->get_name().c_str()); \ + ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \ if (!(obj)->traits.get_icon().empty()) { \ ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->traits.get_icon().c_str()); \ } \ diff --git a/esphome/components/select/select.h b/esphome/components/select/select.h index 414a8daabb..0dec74b627 100644 --- a/esphome/components/select/select.h +++ b/esphome/components/select/select.h @@ -10,7 +10,7 @@ namespace select { #define LOG_SELECT(prefix, type, obj) \ if ((obj) != nullptr) { \ - ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, (obj)->get_name().c_str()); \ + ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \ if (!(obj)->traits.get_icon().empty()) { \ ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->traits.get_icon().c_str()); \ } \ diff --git a/esphome/components/sensor/sensor.cpp b/esphome/components/sensor/sensor.cpp index 1dbc1c901a..34f72a4508 100644 --- a/esphome/components/sensor/sensor.cpp +++ b/esphome/components/sensor/sensor.cpp @@ -6,15 +6,15 @@ namespace sensor { static const char *const TAG = "sensor"; -const char *state_class_to_string(StateClass state_class) { +const LogString *state_class_to_string(StateClass state_class) { switch (state_class) { case STATE_CLASS_MEASUREMENT: - return "measurement"; + return LOG_STR("measurement"); case STATE_CLASS_TOTAL_INCREASING: - return "total_increasing"; + return LOG_STR("total_increasing"); case STATE_CLASS_NONE: default: - return ""; + return LOG_STR(""); } } diff --git a/esphome/components/sensor/sensor.h b/esphome/components/sensor/sensor.h index 34b8b26a54..6322c12027 100644 --- a/esphome/components/sensor/sensor.h +++ b/esphome/components/sensor/sensor.h @@ -1,5 +1,6 @@ #pragma once +#include "esphome/core/log.h" #include "esphome/core/component.h" #include "esphome/core/helpers.h" #include "esphome/components/sensor/filter.h" @@ -9,11 +10,11 @@ namespace sensor { #define LOG_SENSOR(prefix, type, obj) \ if ((obj) != nullptr) { \ - ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, (obj)->get_name().c_str()); \ + ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \ if (!(obj)->get_device_class().empty()) { \ ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \ } \ - ESP_LOGCONFIG(TAG, "%s State Class: '%s'", prefix, state_class_to_string((obj)->state_class)); \ + ESP_LOGCONFIG(TAG, "%s State Class: '%s'", prefix, LOG_STR_ARG(state_class_to_string((obj)->state_class))); \ ESP_LOGCONFIG(TAG, "%s Unit of Measurement: '%s'", prefix, (obj)->get_unit_of_measurement().c_str()); \ ESP_LOGCONFIG(TAG, "%s Accuracy Decimals: %d", prefix, (obj)->get_accuracy_decimals()); \ if (!(obj)->get_icon().empty()) { \ @@ -36,7 +37,7 @@ enum StateClass : uint8_t { STATE_CLASS_TOTAL_INCREASING = 2, }; -const char *state_class_to_string(StateClass state_class); +const LogString *state_class_to_string(StateClass state_class); /** Base-class for all sensors. * diff --git a/esphome/components/switch/switch.h b/esphome/components/switch/switch.h index 966119a29f..8cfae3b6f8 100644 --- a/esphome/components/switch/switch.h +++ b/esphome/components/switch/switch.h @@ -9,7 +9,7 @@ namespace switch_ { #define LOG_SWITCH(prefix, type, obj) \ if ((obj) != nullptr) { \ - ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, (obj)->get_name().c_str()); \ + ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \ if (!(obj)->get_icon().empty()) { \ ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon().c_str()); \ } \ diff --git a/esphome/components/text_sensor/text_sensor.h b/esphome/components/text_sensor/text_sensor.h index 5c6e5be51a..5293f0d216 100644 --- a/esphome/components/text_sensor/text_sensor.h +++ b/esphome/components/text_sensor/text_sensor.h @@ -8,7 +8,7 @@ namespace text_sensor { #define LOG_TEXT_SENSOR(prefix, type, obj) \ if ((obj) != nullptr) { \ - ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, (obj)->get_name().c_str()); \ + ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \ if (!(obj)->get_icon().empty()) { \ ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon().c_str()); \ } \ diff --git a/esphome/components/uart/uart.cpp b/esphome/components/uart/uart.cpp index 08e3395a7a..8cc8a47b14 100644 --- a/esphome/components/uart/uart.cpp +++ b/esphome/components/uart/uart.cpp @@ -58,21 +58,21 @@ void UARTDevice::check_uart_settings(uint32_t baud_rate, uint8_t stop_bits, UART this->parent_->data_bits_); } if (this->parent_->parity_ != parity) { - ESP_LOGE(TAG, " Invalid parity: Integration requested parity %s but you have %s!", parity_to_str(parity), - parity_to_str(this->parent_->parity_)); + ESP_LOGE(TAG, " Invalid parity: Integration requested parity %s but you have %s!", + LOG_STR_ARG(parity_to_str(parity)), LOG_STR_ARG(parity_to_str(this->parent_->parity_))); } } -const char *parity_to_str(UARTParityOptions parity) { +const LogString *parity_to_str(UARTParityOptions parity) { switch (parity) { case UART_CONFIG_PARITY_NONE: - return "NONE"; + return LOG_STR("NONE"); case UART_CONFIG_PARITY_EVEN: - return "EVEN"; + return LOG_STR("EVEN"); case UART_CONFIG_PARITY_ODD: - return "ODD"; + return LOG_STR("ODD"); default: - return "UNKNOWN"; + return LOG_STR("UNKNOWN"); } } diff --git a/esphome/components/uart/uart.h b/esphome/components/uart/uart.h index 8ac00658f4..041cdaecdf 100644 --- a/esphome/components/uart/uart.h +++ b/esphome/components/uart/uart.h @@ -4,6 +4,7 @@ #include #include "esphome/core/esphal.h" #include "esphome/core/component.h" +#include "esphome/core/log.h" namespace esphome { namespace uart { @@ -14,7 +15,7 @@ enum UARTParityOptions { UART_CONFIG_PARITY_ODD, }; -const char *parity_to_str(UARTParityOptions parity); +const LogString *parity_to_str(UARTParityOptions parity); #ifdef ARDUINO_ARCH_ESP8266 class ESP8266SoftwareSerial { diff --git a/esphome/components/uart/uart_esp8266.cpp b/esphome/components/uart/uart_esp8266.cpp index 5cb625f2ff..bc6b0f72d3 100644 --- a/esphome/components/uart/uart_esp8266.cpp +++ b/esphome/components/uart/uart_esp8266.cpp @@ -104,7 +104,7 @@ void UARTComponent::dump_config() { } ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_); ESP_LOGCONFIG(TAG, " Data Bits: %u", this->data_bits_); - ESP_LOGCONFIG(TAG, " Parity: %s", parity_to_str(this->parity_)); + ESP_LOGCONFIG(TAG, " Parity: %s", LOG_STR_ARG(parity_to_str(this->parity_))); ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_); if (this->hw_serial_ != nullptr) { ESP_LOGCONFIG(TAG, " Using hardware serial interface."); diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 50feeb6cad..c5ba9b01af 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -307,7 +307,7 @@ void WiFiComponent::start_connecting(const WiFiAP &ap, bool two) { this->action_started_ = millis(); } -void print_signal_bars(int8_t rssi, char *buf) { +const LogString *get_signal_bars(int8_t rssi) { // LOWER ONE QUARTER BLOCK // Unicode: U+2582, UTF-8: E2 96 82 // LOWER HALF BLOCK @@ -317,36 +317,36 @@ void print_signal_bars(int8_t rssi, char *buf) { // FULL BLOCK // Unicode: U+2588, UTF-8: E2 96 88 if (rssi >= -50) { - sprintf(buf, "\033[0;32m" // green - "\xe2\x96\x82" - "\xe2\x96\x84" - "\xe2\x96\x86" - "\xe2\x96\x88" - "\033[0m"); + return LOG_STR("\033[0;32m" // green + "\xe2\x96\x82" + "\xe2\x96\x84" + "\xe2\x96\x86" + "\xe2\x96\x88" + "\033[0m"); } else if (rssi >= -65) { - sprintf(buf, "\033[0;33m" // yellow - "\xe2\x96\x82" - "\xe2\x96\x84" - "\xe2\x96\x86" - "\033[0;37m" - "\xe2\x96\x88" - "\033[0m"); + return LOG_STR("\033[0;33m" // yellow + "\xe2\x96\x82" + "\xe2\x96\x84" + "\xe2\x96\x86" + "\033[0;37m" + "\xe2\x96\x88" + "\033[0m"); } else if (rssi >= -85) { - sprintf(buf, "\033[0;33m" // yellow - "\xe2\x96\x82" - "\xe2\x96\x84" - "\033[0;37m" - "\xe2\x96\x86" - "\xe2\x96\x88" - "\033[0m"); + return LOG_STR("\033[0;33m" // yellow + "\xe2\x96\x82" + "\xe2\x96\x84" + "\033[0;37m" + "\xe2\x96\x86" + "\xe2\x96\x88" + "\033[0m"); } else { - sprintf(buf, "\033[0;31m" // red - "\xe2\x96\x82" - "\033[0;37m" - "\xe2\x96\x84" - "\xe2\x96\x86" - "\xe2\x96\x88" - "\033[0m"); + return LOG_STR("\033[0;31m" // red + "\xe2\x96\x82" + "\033[0;37m" + "\xe2\x96\x84" + "\xe2\x96\x86" + "\xe2\x96\x88" + "\033[0m"); } } @@ -361,10 +361,8 @@ void WiFiComponent::print_connect_params_() { ESP_LOGCONFIG(TAG, " BSSID: " LOG_SECRET("%02X:%02X:%02X:%02X:%02X:%02X"), bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]); ESP_LOGCONFIG(TAG, " Hostname: '%s'", App.get_name().c_str()); - char signal_bars[50]; int8_t rssi = WiFi.RSSI(); - print_signal_bars(rssi, signal_bars); - ESP_LOGCONFIG(TAG, " Signal strength: %d dB %s", rssi, signal_bars); + ESP_LOGCONFIG(TAG, " Signal strength: %d dB %s", rssi, LOG_STR_ARG(get_signal_bars(rssi))); if (this->selected_ap_.get_bssid().has_value()) { ESP_LOGV(TAG, " Priority: %.1f", this->get_sta_priority(*this->selected_ap_.get_bssid())); } @@ -433,16 +431,15 @@ void WiFiComponent::check_scanning_finished() { char bssid_s[18]; auto bssid = res.get_bssid(); sprintf(bssid_s, "%02X:%02X:%02X:%02X:%02X:%02X", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]); - char signal_bars[50]; - print_signal_bars(res.get_rssi(), signal_bars); if (res.get_matches()) { ESP_LOGI(TAG, "- '%s' %s" LOG_SECRET("(%s) ") "%s", res.get_ssid().c_str(), - res.get_is_hidden() ? "(HIDDEN) " : "", bssid_s, signal_bars); + res.get_is_hidden() ? "(HIDDEN) " : "", bssid_s, LOG_STR_ARG(get_signal_bars(res.get_rssi()))); ESP_LOGD(TAG, " Channel: %u", res.get_channel()); ESP_LOGD(TAG, " RSSI: %d dB", res.get_rssi()); } else { - ESP_LOGD(TAG, "- " LOG_SECRET("'%s'") " " LOG_SECRET("(%s) ") "%s", res.get_ssid().c_str(), bssid_s, signal_bars); + ESP_LOGD(TAG, "- " LOG_SECRET("'%s'") " " LOG_SECRET("(%s) ") "%s", res.get_ssid().c_str(), bssid_s, + LOG_STR_ARG(get_signal_bars(res.get_rssi()))); } } diff --git a/esphome/components/wifi/wifi_component_esp8266.cpp b/esphome/components/wifi/wifi_component_esp8266.cpp index de529ee3aa..842b44b705 100644 --- a/esphome/components/wifi/wifi_component_esp8266.cpp +++ b/esphome/components/wifi/wifi_component_esp8266.cpp @@ -327,20 +327,20 @@ class WiFiMockClass : public ESP8266WiFiGenericClass { static void _event_callback(void *event) { ESP8266WiFiGenericClass::_eventCallback(event); } // NOLINT }; -const char *get_auth_mode_str(uint8_t mode) { +const LogString *get_auth_mode_str(uint8_t mode) { switch (mode) { case AUTH_OPEN: - return "OPEN"; + return LOG_STR("OPEN"); case AUTH_WEP: - return "WEP"; + return LOG_STR("WEP"); case AUTH_WPA_PSK: - return "WPA PSK"; + return LOG_STR("WPA PSK"); case AUTH_WPA2_PSK: - return "WPA2 PSK"; + return LOG_STR("WPA2 PSK"); case AUTH_WPA_WPA2_PSK: - return "WPA/WPA2 PSK"; + return LOG_STR("WPA/WPA2 PSK"); default: - return "UNKNOWN"; + return LOG_STR("UNKNOWN"); } } #ifdef ipv4_addr @@ -358,22 +358,22 @@ std::string format_ip_addr(struct ip_addr ip) { return buf; } #endif -const char *get_op_mode_str(uint8_t mode) { +const LogString *get_op_mode_str(uint8_t mode) { switch (mode) { case WIFI_OFF: - return "OFF"; + return LOG_STR("OFF"); case WIFI_STA: - return "STA"; + return LOG_STR("STA"); case WIFI_AP: - return "AP"; + return LOG_STR("AP"); case WIFI_AP_STA: - return "AP+STA"; + return LOG_STR("AP+STA"); default: - return "UNKNOWN"; + return LOG_STR("UNKNOWN"); } } -// Note that this method returns PROGMEM strings, so use LOG_STR_ARG() to access them. -const char *get_disconnect_reason_str(uint8_t reason) { + +const LogString *get_disconnect_reason_str(uint8_t reason) { /* If this were one big switch statement, GCC would generate a lookup table for it. However, the values of the * REASON_* constants aren't continuous, and GCC will fill in the gap with the default value -- wasting 4 bytes of RAM * per entry. As there's ~175 default entries, this wastes 700 bytes of RAM. @@ -470,8 +470,8 @@ void WiFiComponent::wifi_event_callback(System_Event_t *event) { } case EVENT_STAMODE_AUTHMODE_CHANGE: { auto it = event->event_info.auth_change; - ESP_LOGV(TAG, "Event: Changed AuthMode old=%s new=%s", get_auth_mode_str(it.old_mode), - get_auth_mode_str(it.new_mode)); + ESP_LOGV(TAG, "Event: Changed AuthMode old=%s new=%s", LOG_STR_ARG(get_auth_mode_str(it.old_mode)), + LOG_STR_ARG(get_auth_mode_str(it.new_mode))); // Mitigate CVE-2020-12638 // https://lbsfilm.at/blog/wpa2-authenticationmode-downgrade-in-espressif-microprocessors if (it.old_mode != AUTH_OPEN && it.new_mode == AUTH_OPEN) { @@ -511,8 +511,8 @@ void WiFiComponent::wifi_event_callback(System_Event_t *event) { #if ARDUINO_VERSION_CODE >= VERSION_CODE(2, 4, 0) case EVENT_OPMODE_CHANGED: { auto it = event->event_info.opmode_changed; - ESP_LOGV(TAG, "Event: Changed Mode old=%s new=%s", get_op_mode_str(it.old_opmode), - get_op_mode_str(it.new_opmode)); + ESP_LOGV(TAG, "Event: Changed Mode old=%s new=%s", LOG_STR_ARG(get_op_mode_str(it.old_opmode)), + LOG_STR_ARG(get_op_mode_str(it.new_opmode))); break; } case EVENT_SOFTAPMODE_DISTRIBUTE_STA_IP: { diff --git a/esphome/core/esphal.cpp b/esphome/core/esphal.cpp index 7851ba01b6..8fb1dd86c5 100644 --- a/esphome/core/esphal.cpp +++ b/esphome/core/esphal.cpp @@ -43,81 +43,56 @@ GPIOPin::GPIOPin(uint8_t pin, uint8_t mode, bool inverted) { } -const char *GPIOPin::get_pin_mode_name() const { +const LogString *GPIOPin::get_pin_mode_name() const { const char *mode_s; switch (this->mode_) { case INPUT: - mode_s = "INPUT"; - break; + return LOG_STR("INPUT"); case OUTPUT: - mode_s = "OUTPUT"; - break; + return LOG_STR("OUTPUT"); case INPUT_PULLUP: - mode_s = "INPUT_PULLUP"; - break; + return LOG_STR("INPUT_PULLUP"); case OUTPUT_OPEN_DRAIN: - mode_s = "OUTPUT_OPEN_DRAIN"; - break; + return LOG_STR("OUTPUT_OPEN_DRAIN"); case SPECIAL: - mode_s = "SPECIAL"; - break; + return LOG_STR("SPECIAL"); case FUNCTION_1: - mode_s = "FUNCTION_1"; - break; + return LOG_STR("FUNCTION_1"); case FUNCTION_2: - mode_s = "FUNCTION_2"; - break; + return LOG_STR("FUNCTION_2"); case FUNCTION_3: - mode_s = "FUNCTION_3"; - break; + return LOG_STR("FUNCTION_3"); case FUNCTION_4: - mode_s = "FUNCTION_4"; - break; - + return LOG_STR("FUNCTION_4"); #ifdef ARDUINO_ARCH_ESP32 case PULLUP: - mode_s = "PULLUP"; - break; + return LOG_STR("PULLUP"); case PULLDOWN: - mode_s = "PULLDOWN"; - break; + return LOG_STR("PULLDOWN"); case INPUT_PULLDOWN: - mode_s = "INPUT_PULLDOWN"; - break; + return LOG_STR("INPUT_PULLDOWN"); case OPEN_DRAIN: - mode_s = "OPEN_DRAIN"; - break; + return LOG_STR("OPEN_DRAIN"); case FUNCTION_5: - mode_s = "FUNCTION_5"; - break; + return LOG_STR("FUNCTION_5"); case FUNCTION_6: - mode_s = "FUNCTION_6"; - break; + return LOG_STR("FUNCTION_6"); case ANALOG: - mode_s = "ANALOG"; - break; + return LOG_STR("ANALOG"); #endif #ifdef ARDUINO_ARCH_ESP8266 case FUNCTION_0: - mode_s = "FUNCTION_0"; - break; + return LOG_STR("FUNCTION_0"); case WAKEUP_PULLUP: - mode_s = "WAKEUP_PULLUP"; - break; + return LOG_STR("WAKEUP_PULLUP"); case WAKEUP_PULLDOWN: - mode_s = "WAKEUP_PULLDOWN"; - break; + return LOG_STR("WAKEUP_PULLDOWN"); case INPUT_PULLDOWN_16: - mode_s = "INPUT_PULLDOWN_16"; - break; + return LOG_STR("INPUT_PULLDOWN_16"); #endif - default: - mode_s = "UNKNOWN"; - break; + return LOG_STR("UNKNOWN"); } - - return mode_s; } unsigned char GPIOPin::get_pin() const { return this->pin_; } diff --git a/esphome/core/esphal.h b/esphome/core/esphal.h index 08fbfb09f5..1b92f816b1 100644 --- a/esphome/core/esphal.h +++ b/esphome/core/esphal.h @@ -25,6 +25,8 @@ #undef abs #endif +#include "esphome/core/log.h" + namespace esphome { #define LOG_PIN(prefix, pin) \ @@ -32,7 +34,8 @@ namespace esphome { ESP_LOGCONFIG(TAG, prefix LOG_PIN_PATTERN, LOG_PIN_ARGS(pin)); \ } #define LOG_PIN_PATTERN "GPIO%u (Mode: %s%s)" -#define LOG_PIN_ARGS(pin) (pin)->get_pin(), (pin)->get_pin_mode_name(), ((pin)->is_inverted() ? ", INVERTED" : "") +#define LOG_PIN_ARGS(pin) \ + (pin)->get_pin(), LOG_STR_ARG((pin)->get_pin_mode_name()), ((pin)->is_inverted() ? ", INVERTED" : "") /// Copy of GPIOPin that is safe to use from ISRs (with no virtual functions) class ISRInternalGPIOPin { @@ -85,7 +88,7 @@ class GPIOPin { /// Get the GPIO pin number. uint8_t get_pin() const; - const char *get_pin_mode_name() const; + const LogString *get_pin_mode_name() const; /// Get the pinMode of this pin. uint8_t get_mode() const; /// Return whether this pin shall be treated as inverted. (for example active-low) diff --git a/esphome/core/log.h b/esphome/core/log.h index 0b0911c3ac..3c2f1e6999 100644 --- a/esphome/core/log.h +++ b/esphome/core/log.h @@ -165,28 +165,37 @@ int esp_idf_log_vprintf_(const char *format, va_list args); // NOLINT #define ONOFF(b) ((b) ? "ON" : "OFF") #define TRUEFALSE(b) ((b) ? "TRUE" : "FALSE") -#ifdef USE_STORE_LOG_STR_IN_FLASH -#define LOG_STR(s) PSTR(s) +// Helper class that identifies strings that may be stored in flash storage (similar to Arduino's __FlashStringHelper) +struct LogString; -// From Arduino 2.5 onwards, we can pass a PSTR() to printf(). For previous versions, emulate support -// by copying the message to a local buffer first. String length is limited to 63 characters. +#ifdef USE_STORE_LOG_STR_IN_FLASH + +#include + +#if ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 0) +#define LOG_STR_ARG(s) ((PGM_P)(s)) +#else +// Pre-Arduino 2.5, we can't pass a PSTR() to printf(). Emulate support by copying the message to a +// local buffer first. String length is limited to 63 characters. // https://github.com/esp8266/Arduino/commit/6280e98b0360f85fdac2b8f10707fffb4f6e6e31 -#include -#if defined(ARDUINO_ARCH_ESP8266) && ARDUINO_VERSION_CODE < VERSION_CODE(2, 5, 0) #define LOG_STR_ARG(s) \ ({ \ char __buf[64]; \ __buf[63] = '\0'; \ - strncpy_P(__buf, s, 63); \ + strncpy_P(__buf, (PGM_P)(s), 63); \ __buf; \ }) -#else -#define LOG_STR_ARG(s) (s) #endif -#else -#define LOG_STR(s) (s) -#define LOG_STR_ARG(s) (s) +#define LOG_STR(s) (reinterpret_cast(PSTR(s))) +#define LOG_STR_LITERAL(s) LOG_STR_ARG(LOG_STR(s)) + +#else // !USE_STORE_LOG_STR_IN_FLASH + +#define LOG_STR(s) (reinterpret_cast(s)) +#define LOG_STR_ARG(s) (reinterpret_cast(s)) +#define LOG_STR_LITERAL(s) (s) + #endif } // namespace esphome From 63a186bdf96e3b8db5f7291e2cd8972375a49a4c Mon Sep 17 00:00:00 2001 From: Jas Strong Date: Mon, 13 Sep 2021 00:54:48 -0700 Subject: [PATCH 012/207] t6615: tolerate sensor dropping commands (#2255) The Amphenol T6615 has a built-in calibration system which means that the sensor could go away for a couple of seconds to figure itself out. While this is happening, commands are silently dropped. This caused the previous version of this code to lock up completely, since there was no way for the command_ state machine to tick back to the NONE state. Instead of just breaking the state machine, which might be harmful on a multi-core or multi-threaded device, add a timestamp and only break the lock if it's been more than a second since the command was issued. The command usually doesn't take more than a few milliseconds to complete, so this should not affect things unduly. While we're at it, rewrite the rx side to be more robust against bytes going missing. Instead of reading in the data essentially inline, read into a buffer and process it when enough has been read to make progress. If data stops coming when we expect it to, or the data is malformed, have a timeout that sends a new command. Co-authored-by: jas --- esphome/components/t6615/t6615.cpp | 64 ++++++++++++++++++------------ esphome/components/t6615/t6615.h | 2 + 2 files changed, 41 insertions(+), 25 deletions(-) diff --git a/esphome/components/t6615/t6615.cpp b/esphome/components/t6615/t6615.cpp index 09ff61827c..c139c56ce4 100644 --- a/esphome/components/t6615/t6615.cpp +++ b/esphome/components/t6615/t6615.cpp @@ -6,7 +6,7 @@ namespace t6615 { static const char *const TAG = "t6615"; -static const uint8_t T6615_RESPONSE_BUFFER_LENGTH = 32; +static const uint32_t T6615_TIMEOUT = 1000; static const uint8_t T6615_MAGIC = 0xFF; static const uint8_t T6615_ADDR_HOST = 0xFA; static const uint8_t T6615_ADDR_SENSOR = 0xFE; @@ -19,31 +19,49 @@ static const uint8_t T6615_COMMAND_ENABLE_ABC[] = {0xB7, 0x01}; static const uint8_t T6615_COMMAND_DISABLE_ABC[] = {0xB7, 0x02}; static const uint8_t T6615_COMMAND_SET_ELEVATION[] = {0x03, 0x0F}; -void T6615Component::loop() { - if (!this->available()) - return; +void T6615Component::send_ppm_command_() { + this->command_time_ = millis(); + this->command_ = T6615Command::GET_PPM; + this->write_byte(T6615_MAGIC); + this->write_byte(T6615_ADDR_SENSOR); + this->write_byte(sizeof(T6615_COMMAND_GET_PPM)); + this->write_array(T6615_COMMAND_GET_PPM, sizeof(T6615_COMMAND_GET_PPM)); +} - // Read header - uint8_t header[3]; - this->read_array(header, 3); - if (header[0] != T6615_MAGIC || header[1] != T6615_ADDR_HOST) { - ESP_LOGW(TAG, "Reading data from T6615 failed!"); - while (this->available()) - this->read(); // Clear the incoming buffer - this->status_set_warning(); +void T6615Component::loop() { + if (this->available() < 5) { + if (this->command_ == T6615Command::GET_PPM && millis() - this->command_time_ > T6615_TIMEOUT) { + /* command got eaten, clear the buffer and fire another */ + while (this->available()) + this->read(); + this->send_ppm_command_(); + } return; } - // Read body - uint8_t length = header[2]; - uint8_t response[T6615_RESPONSE_BUFFER_LENGTH]; - this->read_array(response, length); + uint8_t response_buffer[6]; + + /* by the time we get here, we know we have at least five bytes in the buffer */ + this->read_array(response_buffer, 5); + + // Read header + if (response_buffer[0] != T6615_MAGIC || response_buffer[1] != T6615_ADDR_HOST) { + ESP_LOGW(TAG, "Got bad data from T6615! Magic was %02X and address was %02X", response_buffer[0], + response_buffer[1]); + /* make sure the buffer is empty */ + while (this->available()) + this->read(); + /* try again to read the sensor */ + this->send_ppm_command_(); + this->status_set_warning(); + return; + } this->status_clear_warning(); switch (this->command_) { case T6615Command::GET_PPM: { - const uint16_t ppm = encode_uint16(response[0], response[1]); + const uint16_t ppm = encode_uint16(response_buffer[3], response_buffer[4]); ESP_LOGD(TAG, "T6615 Received CO₂=%uppm", ppm); this->co2_sensor_->publish_state(ppm); break; @@ -51,23 +69,19 @@ void T6615Component::loop() { default: break; } - + this->command_time_ = 0; this->command_ = T6615Command::NONE; } void T6615Component::update() { this->query_ppm_(); } void T6615Component::query_ppm_() { - if (this->co2_sensor_ == nullptr || this->command_ != T6615Command::NONE) { + if (this->co2_sensor_ == nullptr || + (this->command_ != T6615Command::NONE && millis() - this->command_time_ < T6615_TIMEOUT)) { return; } - this->command_ = T6615Command::GET_PPM; - - this->write_byte(T6615_MAGIC); - this->write_byte(T6615_ADDR_SENSOR); - this->write_byte(sizeof(T6615_COMMAND_GET_PPM)); - this->write_array(T6615_COMMAND_GET_PPM, sizeof(T6615_COMMAND_GET_PPM)); + this->send_ppm_command_(); } float T6615Component::get_setup_priority() const { return setup_priority::DATA; } diff --git a/esphome/components/t6615/t6615.h b/esphome/components/t6615/t6615.h index a7da3b4cf6..a075685023 100644 --- a/esphome/components/t6615/t6615.h +++ b/esphome/components/t6615/t6615.h @@ -32,8 +32,10 @@ class T6615Component : public PollingComponent, public uart::UARTDevice { protected: void query_ppm_(); + void send_ppm_command_(); T6615Command command_ = T6615Command::NONE; + unsigned long command_time_ = 0; sensor::Sensor *co2_sensor_{nullptr}; }; From 8a2b1d9359decb113e58959b7cbbfa75bc519f29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= Date: Mon, 13 Sep 2021 10:06:28 +0200 Subject: [PATCH 013/207] Expose select on Frontend `web_server:` (#2245) --- esphome/components/web_server/web_server.cpp | 43 +++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index dc97bcd5c2..f82a8893eb 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -25,7 +25,8 @@ namespace web_server { static const char *const TAG = "web_server"; -void write_row(AsyncResponseStream *stream, Nameable *obj, const std::string &klass, const std::string &action) { +void write_row(AsyncResponseStream *stream, Nameable *obj, const std::string &klass, const std::string &action, + const std::function &action_func = nullptr) { if (obj->is_internal()) return; stream->print("print(obj->get_name().c_str()); stream->print(""); stream->print(action.c_str()); + if (action_func) { + action_func(*stream, obj); + } stream->print(""); stream->print(""); } @@ -219,7 +223,17 @@ void WebServer::handle_index_request(AsyncWebServerRequest *request) { #ifdef USE_SELECT for (auto *obj : App.get_selects()) - write_row(stream, obj, "select", ""); + write_row(stream, obj, "select", "", [](AsyncResponseStream &stream, Nameable *obj) { + select::Select *select = (select::Select *) obj; + stream.print(""); + }); #endif stream->print(F("

See ESPHome Web API for " @@ -648,8 +662,27 @@ void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlM continue; if (obj->get_object_id() != match.id) continue; - std::string data = this->select_json(obj, obj->state); - request->send(200, "text/json", data.c_str()); + + if (request->method() == HTTP_GET) { + std::string data = this->select_json(obj, obj->state); + request->send(200, "text/json", data.c_str()); + return; + } + + if (match.method != "set") { + request->send(404); + return; + } + + auto call = obj->make_call(); + + if (request->hasParam("option")) { + String option = request->getParam("option")->value(); + call.set_option(option.c_str()); // NOLINT(clang-diagnostic-deprecated-declarations) + } + + this->defer([call]() mutable { call.perform(); }); + request->send(200); return; } request->send(404); @@ -721,7 +754,7 @@ bool WebServer::canHandle(AsyncWebServerRequest *request) { #endif #ifdef USE_SELECT - if (request->method() == HTTP_GET && match.domain == "select") + if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "select") return true; #endif From 4e308f551cf543d721f0f394a51764fcee35b062 Mon Sep 17 00:00:00 2001 From: Alex <33379584+alexyao2015@users.noreply.github.com> Date: Mon, 13 Sep 2021 03:08:06 -0500 Subject: [PATCH 014/207] Fix devcontainer scripts on Windows (#2239) --- .gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..94f480de94 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf \ No newline at end of file From c6109024aaff83c9a44ccd579f5994fafe4a3078 Mon Sep 17 00:00:00 2001 From: James Braid <251628+jamesbraid@users.noreply.github.com> Date: Mon, 13 Sep 2021 02:23:59 -0700 Subject: [PATCH 015/207] Fix SM300D2 sensor component routines so they correctly read the sensor output (#2159) --- esphome/components/sm300d2/sensor.py | 4 ++-- esphome/components/sm300d2/sm300d2.cpp | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/esphome/components/sm300d2/sensor.py b/esphome/components/sm300d2/sensor.py index 8452ee81f2..0c3c54f200 100644 --- a/esphome/components/sm300d2/sensor.py +++ b/esphome/components/sm300d2/sensor.py @@ -72,13 +72,13 @@ CONFIG_SCHEMA = cv.All( ), cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( unit_of_measurement=UNIT_CELSIUS, - accuracy_decimals=0, + accuracy_decimals=1, device_class=DEVICE_CLASS_TEMPERATURE, state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( unit_of_measurement=UNIT_PERCENT, - accuracy_decimals=0, + accuracy_decimals=1, device_class=DEVICE_CLASS_HUMIDITY, state_class=STATE_CLASS_MEASUREMENT, ), diff --git a/esphome/components/sm300d2/sm300d2.cpp b/esphome/components/sm300d2/sm300d2.cpp index b1787581ae..d542582fcb 100644 --- a/esphome/components/sm300d2/sm300d2.cpp +++ b/esphome/components/sm300d2/sm300d2.cpp @@ -9,10 +9,12 @@ static const uint8_t SM300D2_RESPONSE_LENGTH = 17; void SM300D2Sensor::update() { uint8_t response[SM300D2_RESPONSE_LENGTH]; + uint8_t peeked; + + while (this->available() > 0 && this->peek_byte(&peeked) && peeked != 0x3C) + this->read(); - flush(); bool read_success = read_array(response, SM300D2_RESPONSE_LENGTH); - flush(); if (!read_success) { ESP_LOGW(TAG, "Reading data from SM300D2 failed!"); @@ -63,7 +65,7 @@ void SM300D2Sensor::update() { if (this->pm_2_5_sensor_ != nullptr) this->pm_2_5_sensor_->publish_state(pm_2_5); - ESP_LOGD(TAG, "Received pm_10_0: %u µg/m³", pm_10_0); + ESP_LOGD(TAG, "Received PM10: %u µg/m³", pm_10_0); if (this->pm_10_0_sensor_ != nullptr) this->pm_10_0_sensor_->publish_state(pm_10_0); From e0cff214b215ab2cd4dfb221b37d0f56dbce141b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Sep 2021 11:25:28 +0200 Subject: [PATCH 016/207] Bump tzlocal from 2.1 to 3.0 (#2154) Bumps [tzlocal](https://github.com/regebro/tzlocal) from 2.1 to 3.0. - [Release notes](https://github.com/regebro/tzlocal/releases) - [Changelog](https://github.com/regebro/tzlocal/blob/master/CHANGES.txt) - [Commits](https://github.com/regebro/tzlocal/compare/2.1...3.0) --- updated-dependencies: - dependency-name: tzlocal dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index daaf86e641..7de02d8ccb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ paho-mqtt==1.5.1 colorama==0.4.4 tornado==6.1 protobuf==3.17.3 -tzlocal==2.1 +tzlocal==3.0 pytz==2021.1 pyserial==3.5 ifaddr==0.1.7 From a4867a00ea6d7170d22dda00705d010bfd0ed756 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 13 Sep 2021 11:31:02 +0200 Subject: [PATCH 017/207] Activate owning-memory clang-tidy check (#1891) * Activate owning-memory clang-tidy check * Lint * Lint * Fix issue with new NfcTag constructor * Update pointers for number and select * Add back the NOLINT to display buffer * Fix merge * DSMR fixes * Nextion fixes * Fix pipsolar * Fix lwip socket * Format * Change socket fix Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- .clang-tidy | 1 - esphome/components/api/api_server.cpp | 42 +++++------ esphome/components/api/api_server.h | 2 +- esphome/components/api/custom_api_device.h | 4 +- .../captive_portal/captive_portal.cpp | 2 +- .../captive_portal/captive_portal.h | 5 +- .../components/dallas/dallas_component.cpp | 16 +---- esphome/components/dallas/dallas_component.h | 6 +- esphome/components/dallas/sensor.py | 13 ++-- esphome/components/display/display_buffer.cpp | 4 +- esphome/components/dsmr/dsmr.cpp | 2 +- esphome/components/e131/e131.cpp | 2 +- .../components/fastled_base/fastled_light.cpp | 2 +- .../components/fastled_base/fastled_light.h | 2 +- esphome/components/hm3301/hm3301.cpp | 2 +- esphome/components/hm3301/hm3301.h | 2 +- .../components/http_request/http_request.cpp | 6 +- .../components/http_request/http_request.h | 7 +- esphome/components/json/json_util.cpp | 30 +++----- esphome/components/json/json_util.h | 2 + esphome/components/lcd_base/lcd_display.cpp | 2 +- esphome/components/logger/logger.cpp | 2 +- esphome/components/max7219/max7219.cpp | 2 +- esphome/components/mqtt/mqtt_component.cpp | 3 +- esphome/components/mqtt/mqtt_component.h | 4 +- .../neopixelbus/neopixelbus_light.h | 2 +- .../binary_sensor/nextion_binarysensor.cpp | 4 +- .../binary_sensor/nextion_binarysensor.h | 3 +- esphome/components/nextion/nextion.cpp | 69 ++++++++----------- esphome/components/nextion/nextion.h | 11 +-- esphome/components/nextion/nextion_base.h | 9 +-- .../nextion/nextion_component_base.h | 3 +- esphome/components/nextion/nextion_upload.cpp | 10 +-- .../nextion/sensor/nextion_sensor.cpp | 8 +-- .../nextion/sensor/nextion_sensor.h | 5 +- .../nextion/switch/nextion_switch.cpp | 4 +- .../nextion/switch/nextion_switch.h | 5 +- .../text_sensor/nextion_textsensor.cpp | 4 +- .../nextion/text_sensor/nextion_textsensor.h | 5 +- esphome/components/nfc/ndef_message.cpp | 14 ++-- esphome/components/nfc/ndef_message.h | 16 +++-- esphome/components/nfc/ndef_record.h | 6 +- esphome/components/nfc/nfc_tag.h | 21 ++++-- esphome/components/ota/ota_component.cpp | 2 +- esphome/components/ota/ota_component.h | 4 +- esphome/components/pca9685/output.py | 5 +- esphome/components/pca9685/pca9685_output.cpp | 10 +-- esphome/components/pca9685/pca9685_output.h | 9 +-- esphome/components/pipsolar/pipsolar.cpp | 2 +- esphome/components/pn532/pn532.cpp | 20 +++--- esphome/components/pn532/pn532.h | 8 +-- .../components/pn532/pn532_mifare_classic.cpp | 10 +-- .../pn532/pn532_mifare_ultralight.cpp | 12 ++-- esphome/components/scd30/scd30.cpp | 7 +- esphome/components/sgp30/sgp30.cpp | 7 +- esphome/components/sht3xd/sht3xd.cpp | 7 +- esphome/components/shtcx/shtcx.cpp | 7 +- .../components/socket/lwip_raw_tcp_impl.cpp | 6 +- esphome/components/sps30/sps30.cpp | 7 +- esphome/components/sts3x/sts3x.cpp | 7 +- esphome/components/tlc59208f/output.py | 5 +- .../components/tlc59208f/tlc59208f_output.cpp | 10 +-- .../components/tlc59208f/tlc59208f_output.h | 9 +-- esphome/components/tm1651/tm1651.cpp | 3 +- esphome/components/tm1651/tm1651.h | 4 +- esphome/components/uart/uart_esp8266.cpp | 4 +- .../web_server_base/web_server_base.cpp | 4 +- .../web_server_base/web_server_base.h | 8 +-- esphome/components/wled/wled_light_effect.cpp | 3 +- esphome/core/esphal.cpp | 5 +- esphome/core/helpers.cpp | 15 ---- esphome/core/helpers.h | 2 +- esphome/core/preferences.cpp | 15 ++-- esphome/core/preferences.h | 23 ++++--- tests/dummy_main.cpp | 10 +-- 75 files changed, 293 insertions(+), 321 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 936100e9e6..597065d50b 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -30,7 +30,6 @@ Checks: >- -cppcoreguidelines-macro-usage, -cppcoreguidelines-narrowing-conversions, -cppcoreguidelines-non-private-member-variables-in-classes, - -cppcoreguidelines-owning-memory, -cppcoreguidelines-pro-bounds-array-to-pointer-decay, -cppcoreguidelines-pro-bounds-constant-array-index, -cppcoreguidelines-pro-bounds-pointer-arithmetic, diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index c4c193b389..70f82c59db 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -64,7 +64,7 @@ void APIServer::setup() { #ifdef USE_LOGGER if (logger::global_logger != nullptr) { logger::global_logger->add_on_log_callback([this](int level, const char *tag, const char *message) { - for (auto *c : this->clients_) { + for (auto &c : this->clients_) { if (!c->remove_) c->send_log_message(level, tag, message); } @@ -77,7 +77,7 @@ void APIServer::setup() { #ifdef USE_ESP32_CAMERA if (esp32_camera::global_esp32_camera != nullptr) { esp32_camera::global_esp32_camera->add_image_callback([this](std::shared_ptr image) { - for (auto *c : this->clients_) + for (auto &c : this->clients_) if (!c->remove_) c->send_camera_state(image); }); @@ -95,25 +95,21 @@ void APIServer::loop() { ESP_LOGD(TAG, "Accepted %s", sock->getpeername().c_str()); auto *conn = new APIConnection(std::move(sock), this); - clients_.push_back(conn); + clients_.emplace_back(conn); conn->start(); } // Partition clients into remove and active - auto new_end = - std::partition(this->clients_.begin(), this->clients_.end(), [](APIConnection *conn) { return !conn->remove_; }); + auto new_end = std::partition(this->clients_.begin(), this->clients_.end(), + [](const std::unique_ptr &conn) { return !conn->remove_; }); // print disconnection messages for (auto it = new_end; it != this->clients_.end(); ++it) { ESP_LOGD(TAG, "Disconnecting %s", (*it)->client_info_.c_str()); } - // only then delete the pointers, otherwise log routine - // would access freed memory - for (auto it = new_end; it != this->clients_.end(); ++it) - delete *it; // resize vector this->clients_.erase(new_end, this->clients_.end()); - for (auto *client : this->clients_) { + for (auto &client : this->clients_) { client->loop(); } @@ -169,7 +165,7 @@ void APIServer::handle_disconnect(APIConnection *conn) {} void APIServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) { if (obj->is_internal()) return; - for (auto *c : this->clients_) + for (auto &c : this->clients_) c->send_binary_sensor_state(obj, state); } #endif @@ -178,7 +174,7 @@ void APIServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool s void APIServer::on_cover_update(cover::Cover *obj) { if (obj->is_internal()) return; - for (auto *c : this->clients_) + for (auto &c : this->clients_) c->send_cover_state(obj); } #endif @@ -187,7 +183,7 @@ void APIServer::on_cover_update(cover::Cover *obj) { void APIServer::on_fan_update(fan::FanState *obj) { if (obj->is_internal()) return; - for (auto *c : this->clients_) + for (auto &c : this->clients_) c->send_fan_state(obj); } #endif @@ -196,7 +192,7 @@ void APIServer::on_fan_update(fan::FanState *obj) { void APIServer::on_light_update(light::LightState *obj) { if (obj->is_internal()) return; - for (auto *c : this->clients_) + for (auto &c : this->clients_) c->send_light_state(obj); } #endif @@ -205,7 +201,7 @@ void APIServer::on_light_update(light::LightState *obj) { void APIServer::on_sensor_update(sensor::Sensor *obj, float state) { if (obj->is_internal()) return; - for (auto *c : this->clients_) + for (auto &c : this->clients_) c->send_sensor_state(obj, state); } #endif @@ -214,7 +210,7 @@ void APIServer::on_sensor_update(sensor::Sensor *obj, float state) { void APIServer::on_switch_update(switch_::Switch *obj, bool state) { if (obj->is_internal()) return; - for (auto *c : this->clients_) + for (auto &c : this->clients_) c->send_switch_state(obj, state); } #endif @@ -223,7 +219,7 @@ void APIServer::on_switch_update(switch_::Switch *obj, bool state) { void APIServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) { if (obj->is_internal()) return; - for (auto *c : this->clients_) + for (auto &c : this->clients_) c->send_text_sensor_state(obj, state); } #endif @@ -232,7 +228,7 @@ void APIServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::s void APIServer::on_climate_update(climate::Climate *obj) { if (obj->is_internal()) return; - for (auto *c : this->clients_) + for (auto &c : this->clients_) c->send_climate_state(obj); } #endif @@ -241,7 +237,7 @@ void APIServer::on_climate_update(climate::Climate *obj) { void APIServer::on_number_update(number::Number *obj, float state) { if (obj->is_internal()) return; - for (auto *c : this->clients_) + for (auto &c : this->clients_) c->send_number_state(obj, state); } #endif @@ -250,7 +246,7 @@ void APIServer::on_number_update(number::Number *obj, float state) { void APIServer::on_select_update(select::Select *obj, const std::string &state) { if (obj->is_internal()) return; - for (auto *c : this->clients_) + for (auto &c : this->clients_) c->send_select_state(obj, state); } #endif @@ -261,7 +257,7 @@ APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-c void APIServer::set_password(const std::string &password) { this->password_ = password; } void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) { - for (auto *client : this->clients_) { + for (auto &client : this->clients_) { client->send_homeassistant_service_call(call); } } @@ -281,7 +277,7 @@ uint16_t APIServer::get_port() const { return this->port_; } void APIServer::set_reboot_timeout(uint32_t reboot_timeout) { this->reboot_timeout_ = reboot_timeout; } #ifdef USE_HOMEASSISTANT_TIME void APIServer::request_time() { - for (auto *client : this->clients_) { + for (auto &client : this->clients_) { if (!client->remove_ && client->connection_state_ == APIConnection::ConnectionState::CONNECTED) client->send_time_request(); } @@ -289,7 +285,7 @@ void APIServer::request_time() { #endif bool APIServer::is_connected() const { return !this->clients_.empty(); } void APIServer::on_shutdown() { - for (auto *c : this->clients_) { + for (auto &c : this->clients_) { c->send_disconnect_request(DisconnectRequest()); } delay(10); diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index e3fa6b18c9..d659f24358 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -91,7 +91,7 @@ class APIServer : public Component, public Controller { uint16_t port_{6053}; uint32_t reboot_timeout_{300000}; uint32_t last_connected_{0}; - std::vector clients_; + std::vector> clients_; std::string password_; std::vector state_subs_; std::vector user_services_; diff --git a/esphome/components/api/custom_api_device.h b/esphome/components/api/custom_api_device.h index 74343904eb..9f125a6149 100644 --- a/esphome/components/api/custom_api_device.h +++ b/esphome/components/api/custom_api_device.h @@ -49,7 +49,7 @@ class CustomAPIDevice { template void register_service(void (T::*callback)(Ts...), const std::string &name, const std::array &arg_names) { - auto *service = new CustomAPIDeviceService(name, arg_names, (T *) this, callback); + auto *service = new CustomAPIDeviceService(name, arg_names, (T *) this, callback); // NOLINT global_api_server->register_user_service(service); } @@ -72,7 +72,7 @@ class CustomAPIDevice { * @param name The name of the arguments for the service, must match the arguments of the function. */ template void register_service(void (T::*callback)(), const std::string &name) { - auto *service = new CustomAPIDeviceService(name, {}, (T *) this, callback); + auto *service = new CustomAPIDeviceService(name, {}, (T *) this, callback); // NOLINT global_api_server->register_user_service(service); } diff --git a/esphome/components/captive_portal/captive_portal.cpp b/esphome/components/captive_portal/captive_portal.cpp index 746029e011..3341769956 100644 --- a/esphome/components/captive_portal/captive_portal.cpp +++ b/esphome/components/captive_portal/captive_portal.cpp @@ -76,7 +76,7 @@ void CaptivePortal::start() { this->base_->add_ota_handler(); } - this->dns_server_ = new DNSServer(); + this->dns_server_ = make_unique(); this->dns_server_->setErrorReplyCode(DNSReplyCode::NoError); IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip(); this->dns_server_->start(53, "*", ip); diff --git a/esphome/components/captive_portal/captive_portal.h b/esphome/components/captive_portal/captive_portal.h index 44b1050f45..399ffaabd3 100644 --- a/esphome/components/captive_portal/captive_portal.h +++ b/esphome/components/captive_portal/captive_portal.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include "esphome/core/component.h" #include "esphome/core/helpers.h" @@ -26,7 +27,7 @@ class CaptivePortal : public AsyncWebHandler, public Component { this->active_ = false; this->base_->deinit(); this->dns_server_->stop(); - delete this->dns_server_; + this->dns_server_ = nullptr; } bool canHandle(AsyncWebServerRequest *request) override { @@ -65,7 +66,7 @@ class CaptivePortal : public AsyncWebHandler, public Component { web_server_base::WebServerBase *base_; bool initialized_{false}; bool active_{false}; - DNSServer *dns_server_{nullptr}; + std::unique_ptr dns_server_{nullptr}; }; extern CaptivePortal *global_captive_portal; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) diff --git a/esphome/components/dallas/dallas_component.cpp b/esphome/components/dallas/dallas_component.cpp index 7e34546078..847d630e7c 100644 --- a/esphome/components/dallas/dallas_component.cpp +++ b/esphome/components/dallas/dallas_component.cpp @@ -97,16 +97,7 @@ void DallasComponent::dump_config() { } } -DallasTemperatureSensor *DallasComponent::get_sensor_by_address(uint64_t address, uint8_t resolution) { - auto s = new DallasTemperatureSensor(address, resolution, this); - this->sensors_.push_back(s); - return s; -} -DallasTemperatureSensor *DallasComponent::get_sensor_by_index(uint8_t index, uint8_t resolution) { - auto s = this->get_sensor_by_address(0, resolution); - s->set_index(index); - return s; -} +void DallasComponent::register_sensor(DallasTemperatureSensor *sensor) { this->sensors_.push_back(sensor); } void DallasComponent::update() { this->status_clear_warning(); @@ -157,11 +148,6 @@ void DallasComponent::update() { } DallasComponent::DallasComponent(ESPOneWire *one_wire) : one_wire_(one_wire) {} -DallasTemperatureSensor::DallasTemperatureSensor(uint64_t address, uint8_t resolution, DallasComponent *parent) - : parent_(parent) { - this->set_address(address); - this->set_resolution(resolution); -} void DallasTemperatureSensor::set_address(uint64_t address) { this->address_ = address; } uint8_t DallasTemperatureSensor::get_resolution() const { return this->resolution_; } void DallasTemperatureSensor::set_resolution(uint8_t resolution) { this->resolution_ = resolution; } diff --git a/esphome/components/dallas/dallas_component.h b/esphome/components/dallas/dallas_component.h index d32aec1758..8d405f6eab 100644 --- a/esphome/components/dallas/dallas_component.h +++ b/esphome/components/dallas/dallas_component.h @@ -13,8 +13,7 @@ class DallasComponent : public PollingComponent { public: explicit DallasComponent(ESPOneWire *one_wire); - DallasTemperatureSensor *get_sensor_by_address(uint64_t address, uint8_t resolution); - DallasTemperatureSensor *get_sensor_by_index(uint8_t index, uint8_t resolution); + void register_sensor(DallasTemperatureSensor *sensor); void setup() override; void dump_config() override; @@ -33,8 +32,7 @@ class DallasComponent : public PollingComponent { /// Internal class that helps us create multiple sensors for one Dallas hub. class DallasTemperatureSensor : public sensor::Sensor { public: - DallasTemperatureSensor(uint64_t address, uint8_t resolution, DallasComponent *parent); - + void set_parent(DallasComponent *parent) { parent_ = parent; } /// Helper to get a pointer to the address as uint8_t. uint8_t *get_address8(); /// Helper to create (and cache) the name for this sensor. For example "0xfe0000031f1eaf29". diff --git a/esphome/components/dallas/sensor.py b/esphome/components/dallas/sensor.py index 5e09701bae..05c2c8b90c 100644 --- a/esphome/components/dallas/sensor.py +++ b/esphome/components/dallas/sensor.py @@ -36,10 +36,15 @@ CONFIG_SCHEMA = cv.All( async def to_code(config): hub = await cg.get_variable(config[CONF_DALLAS_ID]) + var = cg.new_Pvariable(config[CONF_ID]) + if CONF_ADDRESS in config: - address = config[CONF_ADDRESS] - rhs = hub.Pget_sensor_by_address(address, config.get(CONF_RESOLUTION)) + cg.add(var.set_address(config[CONF_ADDRESS])) else: - rhs = hub.Pget_sensor_by_index(config[CONF_INDEX], config.get(CONF_RESOLUTION)) - var = cg.Pvariable(config[CONF_ID], rhs) + cg.add(var.set_index(config[CONF_INDEX])) + + if CONF_RESOLUTION in config: + cg.add(var.set_resolution(config[CONF_RESOLUTION])) + + cg.add(hub.register_sensor(var)) await sensor.register_sensor(var, config) diff --git a/esphome/components/display/display_buffer.cpp b/esphome/components/display/display_buffer.cpp index c330d00ce5..9c4ee3189f 100644 --- a/esphome/components/display/display_buffer.cpp +++ b/esphome/components/display/display_buffer.cpp @@ -1,9 +1,9 @@ #include "display_buffer.h" +#include #include "esphome/core/application.h" #include "esphome/core/color.h" #include "esphome/core/log.h" -#include namespace esphome { namespace display { @@ -14,7 +14,7 @@ const Color COLOR_OFF(0, 0, 0, 0); const Color COLOR_ON(255, 255, 255, 255); void DisplayBuffer::init_internal_(uint32_t buffer_length) { - this->buffer_ = new (std::nothrow) uint8_t[buffer_length]; + this->buffer_ = new (std::nothrow) uint8_t[buffer_length]; // NOLINT if (this->buffer_ == nullptr) { ESP_LOGE(TAG, "Could not allocate buffer for display!"); return; diff --git a/esphome/components/dsmr/dsmr.cpp b/esphome/components/dsmr/dsmr.cpp index 9bce7a382f..39f05a28d1 100644 --- a/esphome/components/dsmr/dsmr.cpp +++ b/esphome/components/dsmr/dsmr.cpp @@ -105,7 +105,7 @@ void Dsmr::receive_encrypted_() { &buffer[18], // cipher size buffer_length - 17); - delete gcmaes128; + delete gcmaes128; // NOLINT(cppcoreguidelines-owning-memory) telegram_len_ = strnlen(this->telegram_, sizeof(this->telegram_)); ESP_LOGV(TAG, "Decrypted data length: %d", telegram_len_); diff --git a/esphome/components/e131/e131.cpp b/esphome/components/e131/e131.cpp index bd749683c5..7694d039e5 100644 --- a/esphome/components/e131/e131.cpp +++ b/esphome/components/e131/e131.cpp @@ -26,7 +26,7 @@ E131Component::~E131Component() { } void E131Component::setup() { - udp_.reset(new WiFiUDP()); + udp_ = make_unique(); if (!udp_->begin(PORT)) { ESP_LOGE(TAG, "Cannot bind E131 to %d.", PORT); diff --git a/esphome/components/fastled_base/fastled_light.cpp b/esphome/components/fastled_base/fastled_light.cpp index edfeb401f1..15a5c8984c 100644 --- a/esphome/components/fastled_base/fastled_light.cpp +++ b/esphome/components/fastled_base/fastled_light.cpp @@ -10,7 +10,7 @@ void FastLEDLightOutput::setup() { ESP_LOGCONFIG(TAG, "Setting up FastLED light..."); this->controller_->init(); this->controller_->setLeds(this->leds_, this->num_leds_); - this->effect_data_ = new uint8_t[this->num_leds_]; + this->effect_data_ = new uint8_t[this->num_leds_]; // NOLINT if (!this->max_refresh_rate_.has_value()) { this->set_max_refresh_rate(this->controller_->getMaxRefreshRate()); } diff --git a/esphome/components/fastled_base/fastled_light.h b/esphome/components/fastled_base/fastled_light.h index ee85735dea..d1c470599d 100644 --- a/esphome/components/fastled_base/fastled_light.h +++ b/esphome/components/fastled_base/fastled_light.h @@ -30,7 +30,7 @@ class FastLEDLightOutput : public light::AddressableLight { CLEDController &add_leds(CLEDController *controller, int num_leds) { this->controller_ = controller; this->num_leds_ = num_leds; - this->leds_ = new CRGB[num_leds]; + this->leds_ = new CRGB[num_leds]; // NOLINT for (int i = 0; i < this->num_leds_; i++) this->leds_[i] = CRGB::Black; diff --git a/esphome/components/hm3301/hm3301.cpp b/esphome/components/hm3301/hm3301.cpp index 5612867d1b..96c1ec0ee9 100644 --- a/esphome/components/hm3301/hm3301.cpp +++ b/esphome/components/hm3301/hm3301.cpp @@ -12,7 +12,7 @@ static const uint8_t PM_10_0_VALUE_INDEX = 7; void HM3301Component::setup() { ESP_LOGCONFIG(TAG, "Setting up HM3301..."); - hm3301_ = new HM330X(); + hm3301_ = make_unique(); error_code_ = hm3301_->init(); if (error_code_ != NO_ERROR) { this->mark_failed(); diff --git a/esphome/components/hm3301/hm3301.h b/esphome/components/hm3301/hm3301.h index 5594f1719c..b024f93719 100644 --- a/esphome/components/hm3301/hm3301.h +++ b/esphome/components/hm3301/hm3301.h @@ -27,7 +27,7 @@ class HM3301Component : public PollingComponent, public i2c::I2CDevice { void update() override; protected: - HM330X *hm3301_; + std::unique_ptr hm3301_; HM330XErrorCode error_code_{NO_ERROR}; diff --git a/esphome/components/http_request/http_request.cpp b/esphome/components/http_request/http_request.cpp index 0fafa8cd86..bf5b24c4f2 100644 --- a/esphome/components/http_request/http_request.cpp +++ b/esphome/components/http_request/http_request.cpp @@ -75,10 +75,10 @@ void HttpRequestComponent::send(const std::vector } #ifdef ARDUINO_ARCH_ESP8266 -WiFiClient *HttpRequestComponent::get_wifi_client_() { +std::shared_ptr HttpRequestComponent::get_wifi_client_() { if (this->secure_) { if (this->wifi_client_secure_ == nullptr) { - this->wifi_client_secure_ = new BearSSL::WiFiClientSecure(); + this->wifi_client_secure_ = std::make_shared(); this->wifi_client_secure_->setInsecure(); this->wifi_client_secure_->setBufferSizes(512, 512); } @@ -86,7 +86,7 @@ WiFiClient *HttpRequestComponent::get_wifi_client_() { } if (this->wifi_client_ == nullptr) { - this->wifi_client_ = new WiFiClient(); + this->wifi_client_ = std::make_shared(); } return this->wifi_client_; } diff --git a/esphome/components/http_request/http_request.h b/esphome/components/http_request/http_request.h index 27919fe75d..740a4580b4 100644 --- a/esphome/components/http_request/http_request.h +++ b/esphome/components/http_request/http_request.h @@ -6,6 +6,7 @@ #include #include #include +#include #ifdef ARDUINO_ARCH_ESP32 #include @@ -51,9 +52,9 @@ class HttpRequestComponent : public Component { std::string body_; std::list

headers_; #ifdef ARDUINO_ARCH_ESP8266 - WiFiClient *wifi_client_{nullptr}; - BearSSL::WiFiClientSecure *wifi_client_secure_{nullptr}; - WiFiClient *get_wifi_client_(); + std::shared_ptr wifi_client_; + std::shared_ptr wifi_client_secure_; + std::shared_ptr get_wifi_client_(); #endif }; diff --git a/esphome/components/json/json_util.cpp b/esphome/components/json/json_util.cpp index 4720c8f9c9..aab5b1ce9b 100644 --- a/esphome/components/json/json_util.cpp +++ b/esphome/components/json/json_util.cpp @@ -6,21 +6,7 @@ namespace json { static const char *const TAG = "json"; -static char *global_json_build_buffer = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -static size_t global_json_build_buffer_size = 0; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) - -void reserve_global_json_build_buffer(size_t required_size) { - if (global_json_build_buffer_size == 0 || global_json_build_buffer_size < required_size) { - delete[] global_json_build_buffer; - global_json_build_buffer_size = std::max(required_size, global_json_build_buffer_size * 2); - - size_t remainder = global_json_build_buffer_size % 16U; - if (remainder != 0) - global_json_build_buffer_size += 16 - remainder; - - global_json_build_buffer = new char[global_json_build_buffer_size]; - } -} +static std::vector global_json_build_buffer; // NOLINT const char *build_json(const json_build_t &f, size_t *length) { global_json_buffer.clear(); @@ -35,16 +21,16 @@ const char *build_json(const json_build_t &f, size_t *length) { // Discovery | 372 | 356 | // Discovery | 336 | 311 | // Discovery | 408 | 393 | - reserve_global_json_build_buffer(global_json_buffer.size()); - size_t bytes_written = root.printTo(global_json_build_buffer, global_json_build_buffer_size); + global_json_build_buffer.reserve(global_json_buffer.size() + 1); + size_t bytes_written = root.printTo(global_json_build_buffer.data(), global_json_build_buffer.capacity()); - if (bytes_written >= global_json_build_buffer_size - 1) { - reserve_global_json_build_buffer(root.measureLength() + 1); - bytes_written = root.printTo(global_json_build_buffer, global_json_build_buffer_size); + if (bytes_written >= global_json_build_buffer.capacity() - 1) { + global_json_build_buffer.reserve(root.measureLength() + 1); + bytes_written = root.printTo(global_json_build_buffer.data(), global_json_build_buffer.capacity()); } *length = bytes_written; - return global_json_build_buffer; + return global_json_build_buffer.data(); } void parse_json(const std::string &data, const json_parse_t &f) { global_json_buffer.clear(); @@ -113,7 +99,7 @@ void VectorJsonBuffer::reserve(size_t size) { // NOLINT target_capacity *= 2; char *old_buffer = this->buffer_; - this->buffer_ = new char[target_capacity]; + this->buffer_ = new char[target_capacity]; // NOLINT if (old_buffer != nullptr && this->capacity_ != 0) { this->free_blocks_.push_back(old_buffer); memcpy(this->buffer_, old_buffer, this->capacity_); diff --git a/esphome/components/json/json_util.h b/esphome/components/json/json_util.h index aafd039b9a..65b2496b85 100644 --- a/esphome/components/json/json_util.h +++ b/esphome/components/json/json_util.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "esphome/core/helpers.h" #include diff --git a/esphome/components/lcd_base/lcd_display.cpp b/esphome/components/lcd_base/lcd_display.cpp index 95b349b9c1..4c0d0ab3ea 100644 --- a/esphome/components/lcd_base/lcd_display.cpp +++ b/esphome/components/lcd_base/lcd_display.cpp @@ -29,7 +29,7 @@ static const uint8_t LCD_DISPLAY_FUNCTION_2_LINE = 0x08; static const uint8_t LCD_DISPLAY_FUNCTION_5X10_DOTS = 0x04; void LCDDisplay::setup() { - this->buffer_ = new uint8_t[this->rows_ * this->columns_]; + this->buffer_ = new uint8_t[this->rows_ * this->columns_]; // NOLINT for (uint8_t i = 0; i < this->rows_ * this->columns_; i++) this->buffer_[i] = ' '; diff --git a/esphome/components/logger/logger.cpp b/esphome/components/logger/logger.cpp index 9d79037087..59dfa93429 100644 --- a/esphome/components/logger/logger.cpp +++ b/esphome/components/logger/logger.cpp @@ -123,7 +123,7 @@ void HOT Logger::log_message_(int level, const char *tag, int offset) { Logger::Logger(uint32_t baud_rate, size_t tx_buffer_size, UARTSelection uart) : baud_rate_(baud_rate), tx_buffer_size_(tx_buffer_size), uart_(uart) { // add 1 to buffer size for null terminator - this->tx_buffer_ = new char[this->tx_buffer_size_ + 1]; + this->tx_buffer_ = new char[this->tx_buffer_size_ + 1]; // NOLINT } void Logger::pre_setup() { diff --git a/esphome/components/max7219/max7219.cpp b/esphome/components/max7219/max7219.cpp index 91bea22e46..97886e53fa 100644 --- a/esphome/components/max7219/max7219.cpp +++ b/esphome/components/max7219/max7219.cpp @@ -117,7 +117,7 @@ float MAX7219Component::get_setup_priority() const { return setup_priority::PROC void MAX7219Component::setup() { ESP_LOGCONFIG(TAG, "Setting up MAX7219..."); this->spi_setup(); - this->buffer_ = new uint8_t[this->num_chips_ * 8]; + this->buffer_ = new uint8_t[this->num_chips_ * 8]; // NOLINT for (uint8_t i = 0; i < this->num_chips_ * 8; i++) this->buffer_[i] = 0; diff --git a/esphome/components/mqtt/mqtt_component.cpp b/esphome/components/mqtt/mqtt_component.cpp index 6ddc080b53..6e376d0fef 100644 --- a/esphome/components/mqtt/mqtt_component.cpp +++ b/esphome/components/mqtt/mqtt_component.cpp @@ -139,8 +139,7 @@ void MQTTComponent::set_custom_command_topic(const std::string &custom_command_t void MQTTComponent::set_availability(std::string topic, std::string payload_available, std::string payload_not_available) { - delete this->availability_; - this->availability_ = new Availability(); + this->availability_ = make_unique(); this->availability_->topic = std::move(topic); this->availability_->payload_available = std::move(payload_available); this->availability_->payload_not_available = std::move(payload_not_available); diff --git a/esphome/components/mqtt/mqtt_component.h b/esphome/components/mqtt/mqtt_component.h index 4d7a522d5f..83a0c06644 100644 --- a/esphome/components/mqtt/mqtt_component.h +++ b/esphome/components/mqtt/mqtt_component.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "esphome/core/component.h" #include "mqtt_client.h" @@ -171,7 +173,7 @@ class MQTTComponent : public Component { std::string custom_command_topic_{}; bool retain_{true}; bool discovery_enabled_{true}; - Availability *availability_{nullptr}; + std::unique_ptr availability_; bool resend_state_{false}; }; diff --git a/esphome/components/neopixelbus/neopixelbus_light.h b/esphome/components/neopixelbus/neopixelbus_light.h index 6fa3fb3cd9..5359bac61b 100644 --- a/esphome/components/neopixelbus/neopixelbus_light.h +++ b/esphome/components/neopixelbus/neopixelbus_light.h @@ -79,7 +79,7 @@ class NeoPixelBusLightOutputBase : public light::AddressableLight { (*this)[i] = Color(0, 0, 0, 0); } - this->effect_data_ = new uint8_t[this->size()]; + this->effect_data_ = new uint8_t[this->size()]; // NOLINT this->controller_->Begin(); } diff --git a/esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp b/esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp index bf6e74cb38..c5bfa78efe 100644 --- a/esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp +++ b/esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp @@ -33,7 +33,7 @@ void NextionBinarySensor::update() { if (this->variable_name_.empty()) // This is a touch component return; - this->nextion_->add_to_get_queue(this); + this->nextion_->add_to_get_queue(shared_from_this()); } void NextionBinarySensor::set_state(bool state, bool publish, bool send_to_nextion) { @@ -48,7 +48,7 @@ void NextionBinarySensor::set_state(bool state, bool publish, bool send_to_nexti this->needs_to_send_update_ = true; } else { this->needs_to_send_update_ = false; - this->nextion_->add_no_result_to_queue_with_set(this, (int) state); + this->nextion_->add_no_result_to_queue_with_set(shared_from_this(), (int) state); } } diff --git a/esphome/components/nextion/binary_sensor/nextion_binarysensor.h b/esphome/components/nextion/binary_sensor/nextion_binarysensor.h index b6b23ada85..b86ee74013 100644 --- a/esphome/components/nextion/binary_sensor/nextion_binarysensor.h +++ b/esphome/components/nextion/binary_sensor/nextion_binarysensor.h @@ -10,7 +10,8 @@ class NextionBinarySensor; class NextionBinarySensor : public NextionComponent, public binary_sensor::BinarySensorInitiallyOff, - public PollingComponent { + public PollingComponent, + public std::enable_shared_from_this { public: NextionBinarySensor(NextionBase *nextion) { this->nextion_ = nextion; } diff --git a/esphome/components/nextion/nextion.cpp b/esphome/components/nextion/nextion.cpp index 3f44fe4075..d56c370412 100644 --- a/esphome/components/nextion/nextion.cpp +++ b/esphome/components/nextion/nextion.cpp @@ -196,7 +196,7 @@ void Nextion::print_queue_members_() { ESP_LOGN(TAG, "print_queue_members_ (top 10) size %zu", this->nextion_queue_.size()); ESP_LOGN(TAG, "*******************************************"); int count = 0; - for (auto *i : this->nextion_queue_) { + for (auto &i : this->nextion_queue_) { if (count++ == 10) break; @@ -257,8 +257,9 @@ bool Nextion::remove_from_q_(bool report_empty) { return false; } - NextionQueue *nb = this->nextion_queue_.front(); - NextionComponentBase *component = nb->component; + auto nb = std::move(this->nextion_queue_.front()); + this->nextion_queue_.pop_front(); + auto &component = nb->component; ESP_LOGN(TAG, "Removing %s from the queue", component->get_variable_name().c_str()); @@ -266,10 +267,8 @@ bool Nextion::remove_from_q_(bool report_empty) { if (component->get_variable_name() == "sleep_wake") { this->is_sleeping_ = false; } - delete component; } - delete nb; - this->nextion_queue_.pop_front(); + return true; } @@ -358,7 +357,7 @@ void Nextion::process_nextion_commands_() { int index = 0; int found = -1; for (auto &nb : this->nextion_queue_) { - NextionComponentBase *component = nb->component; + auto &component = nb->component; if (component->get_queue_type() == NextionQueueType::WAVEFORM_SENSOR) { ESP_LOGW(TAG, "Nextion reported invalid Waveform ID %d or Channel # %d was used!", @@ -369,9 +368,6 @@ void Nextion::process_nextion_commands_() { found = index; - delete component; - delete nb; - break; } ++index; @@ -468,8 +464,9 @@ void Nextion::process_nextion_commands_() { break; } - NextionQueue *nb = this->nextion_queue_.front(); - NextionComponentBase *component = nb->component; + auto nb = std::move(this->nextion_queue_.front()); + this->nextion_queue_.pop_front(); + auto &component = nb->component; if (component->get_queue_type() != NextionQueueType::TEXT_SENSOR) { ESP_LOGE(TAG, "ERROR: Received string return but next in queue \"%s\" is not a text sensor", @@ -480,9 +477,6 @@ void Nextion::process_nextion_commands_() { component->set_state_from_string(to_process, true, false); } - delete nb; - this->nextion_queue_.pop_front(); - break; } // 0x71 0x01 0x02 0x03 0x04 0xFF 0xFF 0xFF @@ -511,8 +505,9 @@ void Nextion::process_nextion_commands_() { ++dataindex; } - NextionQueue *nb = this->nextion_queue_.front(); - NextionComponentBase *component = nb->component; + auto nb = std::move(this->nextion_queue_.front()); + this->nextion_queue_.pop_front(); + auto &component = nb->component; if (component->get_queue_type() != NextionQueueType::SENSOR && component->get_queue_type() != NextionQueueType::BINARY_SENSOR && @@ -526,9 +521,6 @@ void Nextion::process_nextion_commands_() { component->set_state_from_int(value, true, false); } - delete nb; - this->nextion_queue_.pop_front(); - break; } @@ -690,7 +682,7 @@ void Nextion::process_nextion_commands_() { int index = 0; int found = -1; for (auto &nb : this->nextion_queue_) { - auto component = nb->component; + auto &component = nb->component; if (component->get_queue_type() == NextionQueueType::WAVEFORM_SENSOR) { size_t buffer_to_send = component->get_wave_buffer().size() < 255 ? component->get_wave_buffer().size() : 255; // ADDT command can only send 255 @@ -707,8 +699,6 @@ void Nextion::process_nextion_commands_() { component->get_wave_buffer().begin() + buffer_to_send); } found = index; - delete component; - delete nb; break; } ++index; @@ -737,7 +727,7 @@ void Nextion::process_nextion_commands_() { if (!this->nextion_queue_.empty() && this->nextion_queue_.front()->queue_time + this->max_q_age_ms_ < ms) { for (int i = 0; i < this->nextion_queue_.size(); i++) { - NextionComponentBase *component = this->nextion_queue_[i]->component; + auto &component = this->nextion_queue_[i]->component; if (this->nextion_queue_[i]->queue_time + this->max_q_age_ms_ < ms) { if (this->nextion_queue_[i]->queue_time == 0) ESP_LOGD(TAG, "Removing old queue type \"%s\" name \"%s\" queue_time 0", @@ -754,12 +744,10 @@ void Nextion::process_nextion_commands_() { if (component->get_variable_name() == "sleep_wake") { this->is_sleeping_ = false; } - delete component; } - delete this->nextion_queue_[i]; - this->nextion_queue_.erase(this->nextion_queue_.begin() + i); + i--; } else { break; @@ -911,16 +899,16 @@ uint16_t Nextion::recv_ret_string_(std::string &response, uint32_t timeout, bool * @param variable_name Name for the queue */ void Nextion::add_no_result_to_queue_(const std::string &variable_name) { - nextion::NextionQueue *nextion_queue = new nextion::NextionQueue; + auto nextion_queue = make_unique(); - nextion_queue->component = new nextion::NextionComponentBase; + nextion_queue->component = make_unique(); nextion_queue->component->set_variable_name(variable_name); nextion_queue->queue_time = millis(); - this->nextion_queue_.push_back(nextion_queue); - ESP_LOGN(TAG, "Add to queue type: NORESULT component %s", nextion_queue->component->get_variable_name().c_str()); + + this->nextion_queue_.push_back(std::move(nextion_queue)); } /** @@ -991,7 +979,7 @@ bool Nextion::add_no_result_to_queue_with_printf_(const std::string &variable_na * @param is_sleep_safe The command is safe to send when the Nextion is sleeping */ -void Nextion::add_no_result_to_queue_with_set(NextionComponentBase *component, int state_value) { +void Nextion::add_no_result_to_queue_with_set(std::shared_ptr component, int state_value) { this->add_no_result_to_queue_with_set(component->get_variable_name(), component->get_variable_name_to_send(), state_value); } @@ -1019,7 +1007,8 @@ void Nextion::add_no_result_to_queue_with_set_internal_(const std::string &varia * @param state_value String value to set * @param is_sleep_safe The command is safe to send when the Nextion is sleeping */ -void Nextion::add_no_result_to_queue_with_set(NextionComponentBase *component, const std::string &state_value) { +void Nextion::add_no_result_to_queue_with_set(std::shared_ptr component, + const std::string &state_value) { this->add_no_result_to_queue_with_set(component->get_variable_name(), component->get_variable_name_to_send(), state_value); } @@ -1039,11 +1028,11 @@ void Nextion::add_no_result_to_queue_with_set_internal_(const std::string &varia state_value.c_str()); } -void Nextion::add_to_get_queue(NextionComponentBase *component) { +void Nextion::add_to_get_queue(std::shared_ptr component) { if ((!this->is_setup() && !this->ignore_is_setup_)) return; - nextion::NextionQueue *nextion_queue = new nextion::NextionQueue; + auto nextion_queue = make_unique(); nextion_queue->component = component; nextion_queue->queue_time = millis(); @@ -1054,7 +1043,7 @@ void Nextion::add_to_get_queue(NextionComponentBase *component) { std::string command = "get " + component->get_variable_name_to_send(); if (this->send_command_(command)) { - this->nextion_queue_.push_back(nextion_queue); + this->nextion_queue_.push_back(std::move(nextion_queue)); } } @@ -1066,13 +1055,13 @@ void Nextion::add_to_get_queue(NextionComponentBase *component) { * @param buffer_to_send The buffer size * @param buffer_size The buffer data */ -void Nextion::add_addt_command_to_queue(NextionComponentBase *component) { +void Nextion::add_addt_command_to_queue(std::shared_ptr component) { if ((!this->is_setup() && !this->ignore_is_setup_) || this->is_sleeping()) return; - nextion::NextionQueue *nextion_queue = new nextion::NextionQueue; + auto nextion_queue = make_unique(); - nextion_queue->component = new nextion::NextionComponentBase; + nextion_queue->component = std::make_shared(); nextion_queue->queue_time = millis(); size_t buffer_to_send = component->get_wave_buffer_size() < 255 ? component->get_wave_buffer_size() @@ -1081,7 +1070,7 @@ void Nextion::add_addt_command_to_queue(NextionComponentBase *component) { std::string command = "addt " + to_string(component->get_component_id()) + "," + to_string(component->get_wave_channel_id()) + "," + to_string(buffer_to_send); if (this->send_command_(command)) { - this->nextion_queue_.push_back(nextion_queue); + this->nextion_queue_.push_back(std::move(nextion_queue)); } } diff --git a/esphome/components/nextion/nextion.h b/esphome/components/nextion/nextion.h index 3a43a51975..45c4a629c4 100644 --- a/esphome/components/nextion/nextion.h +++ b/esphome/components/nextion/nextion.h @@ -707,17 +707,18 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe void set_nextion_sensor_state(NextionQueueType queue_type, const std::string &name, float state); void set_nextion_text_state(const std::string &name, const std::string &state); - void add_no_result_to_queue_with_set(NextionComponentBase *component, int state_value) override; + void add_no_result_to_queue_with_set(std::shared_ptr component, int state_value) override; void add_no_result_to_queue_with_set(const std::string &variable_name, const std::string &variable_name_to_send, int state_value) override; - void add_no_result_to_queue_with_set(NextionComponentBase *component, const std::string &state_value) override; + void add_no_result_to_queue_with_set(std::shared_ptr component, + const std::string &state_value) override; void add_no_result_to_queue_with_set(const std::string &variable_name, const std::string &variable_name_to_send, const std::string &state_value) override; - void add_to_get_queue(NextionComponentBase *component) override; + void add_to_get_queue(std::shared_ptr component) override; - void add_addt_command_to_queue(NextionComponentBase *component) override; + void add_addt_command_to_queue(std::shared_ptr component) override; void update_components_by_prefix(const std::string &prefix); @@ -728,7 +729,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe void set_auto_wake_on_touch_internal(bool auto_wake_on_touch) { this->auto_wake_on_touch_ = auto_wake_on_touch; } protected: - std::deque nextion_queue_; + std::deque> nextion_queue_; uint16_t recv_ret_string_(std::string &response, uint32_t timeout, bool recv_flag); void all_components_send_state_(bool force_update = false); uint64_t comok_sent_ = 0; diff --git a/esphome/components/nextion/nextion_base.h b/esphome/components/nextion/nextion_base.h index a24fd74060..d91c70c960 100644 --- a/esphome/components/nextion/nextion_base.h +++ b/esphome/components/nextion/nextion_base.h @@ -24,18 +24,19 @@ class NextionBase; class NextionBase { public: - virtual void add_no_result_to_queue_with_set(NextionComponentBase *component, int state_value) = 0; + virtual void add_no_result_to_queue_with_set(std::shared_ptr component, int state_value) = 0; virtual void add_no_result_to_queue_with_set(const std::string &variable_name, const std::string &variable_name_to_send, int state_value) = 0; - virtual void add_no_result_to_queue_with_set(NextionComponentBase *component, const std::string &state_value) = 0; + virtual void add_no_result_to_queue_with_set(std::shared_ptr component, + const std::string &state_value) = 0; virtual void add_no_result_to_queue_with_set(const std::string &variable_name, const std::string &variable_name_to_send, const std::string &state_value) = 0; - virtual void add_addt_command_to_queue(NextionComponentBase *component) = 0; + virtual void add_addt_command_to_queue(std::shared_ptr component) = 0; - virtual void add_to_get_queue(NextionComponentBase *component) = 0; + virtual void add_to_get_queue(std::shared_ptr component) = 0; virtual void set_component_background_color(const char *component, Color color) = 0; virtual void set_component_pressed_background_color(const char *component, Color color) = 0; diff --git a/esphome/components/nextion/nextion_component_base.h b/esphome/components/nextion/nextion_component_base.h index 71ad803bc4..2725d5a30c 100644 --- a/esphome/components/nextion/nextion_component_base.h +++ b/esphome/components/nextion/nextion_component_base.h @@ -1,5 +1,6 @@ #pragma once #include +#include #include "esphome/core/defines.h" namespace esphome { @@ -22,7 +23,7 @@ class NextionComponentBase; class NextionQueue { public: virtual ~NextionQueue() = default; - NextionComponentBase *component; + std::shared_ptr component; uint32_t queue_time = 0; }; diff --git a/esphome/components/nextion/nextion_upload.cpp b/esphome/components/nextion/nextion_upload.cpp index 43c8867e56..a83618e888 100644 --- a/esphome/components/nextion/nextion_upload.cpp +++ b/esphome/components/nextion/nextion_upload.cpp @@ -275,12 +275,12 @@ void Nextion::upload_tft() { } else { #endif ESP_LOGD(TAG, "Allocating buffer size %d, Heap size is %u", chunk_size, ESP.getFreeHeap()); - this->transfer_buffer_ = new (std::nothrow) uint8_t[chunk_size]; - if (this->transfer_buffer_ == nullptr) { // Try a smaller size + this->transfer_buffer_ = new (std::nothrow) uint8_t[chunk_size]; // NOLINT(cppcoreguidelines-owning-memory) + if (this->transfer_buffer_ == nullptr) { // Try a smaller size ESP_LOGD(TAG, "Could not allocate buffer size: %d trying 4096 instead", chunk_size); chunk_size = 4096; ESP_LOGD(TAG, "Allocating %d buffer", chunk_size); - this->transfer_buffer_ = new uint8_t[chunk_size]; + this->transfer_buffer_ = new (std::nothrow) uint8_t[chunk_size]; // NOLINT(cppcoreguidelines-owning-memory) if (!this->transfer_buffer_) this->upload_end_(); @@ -322,7 +322,7 @@ void Nextion::upload_end_() { WiFiClient *Nextion::get_wifi_client_() { if (this->tft_url_.compare(0, 6, "https:") == 0) { if (this->wifi_client_secure_ == nullptr) { - this->wifi_client_secure_ = new BearSSL::WiFiClientSecure(); + this->wifi_client_secure_ = new BearSSL::WiFiClientSecure(); // NOLINT(cppcoreguidelines-owning-memory) this->wifi_client_secure_->setInsecure(); this->wifi_client_secure_->setBufferSizes(512, 512); } @@ -330,7 +330,7 @@ WiFiClient *Nextion::get_wifi_client_() { } if (this->wifi_client_ == nullptr) { - this->wifi_client_ = new WiFiClient(); + this->wifi_client_ = new WiFiClient(); // NOLINT(cppcoreguidelines-owning-memory) } return this->wifi_client_; } diff --git a/esphome/components/nextion/sensor/nextion_sensor.cpp b/esphome/components/nextion/sensor/nextion_sensor.cpp index bbcb465d85..1e79164546 100644 --- a/esphome/components/nextion/sensor/nextion_sensor.cpp +++ b/esphome/components/nextion/sensor/nextion_sensor.cpp @@ -34,7 +34,7 @@ void NextionSensor::update() { return; if (this->wave_chan_id_ == UINT8_MAX) { - this->nextion_->add_to_get_queue(this); + this->nextion_->add_to_get_queue(shared_from_this()); } else { if (this->send_last_value_) { this->add_to_wave_buffer(this->last_value_); @@ -62,9 +62,9 @@ void NextionSensor::set_state(float state, bool publish, bool send_to_nextion) { double to_multiply = pow(10, this->precision_); int state_value = (int) (state * to_multiply); - this->nextion_->add_no_result_to_queue_with_set(this, (int) state_value); + this->nextion_->add_no_result_to_queue_with_set(shared_from_this(), (int) state_value); } else { - this->nextion_->add_no_result_to_queue_with_set(this, (int) state); + this->nextion_->add_no_result_to_queue_with_set(shared_from_this(), (int) state); } } } @@ -103,7 +103,7 @@ void NextionSensor::wave_update_() { buffer_to_send, this->wave_buffer_.size(), this->component_id_, this->wave_chan_id_); #endif - this->nextion_->add_addt_command_to_queue(this); + this->nextion_->add_addt_command_to_queue(shared_from_this()); } } // namespace nextion diff --git a/esphome/components/nextion/sensor/nextion_sensor.h b/esphome/components/nextion/sensor/nextion_sensor.h index e4dde9a513..068ff0451b 100644 --- a/esphome/components/nextion/sensor/nextion_sensor.h +++ b/esphome/components/nextion/sensor/nextion_sensor.h @@ -8,7 +8,10 @@ namespace esphome { namespace nextion { class NextionSensor; -class NextionSensor : public NextionComponent, public sensor::Sensor, public PollingComponent { +class NextionSensor : public NextionComponent, + public sensor::Sensor, + public PollingComponent, + public std::enable_shared_from_this { public: NextionSensor(NextionBase *nextion) { this->nextion_ = nextion; } void send_state_to_nextion() override { this->set_state(this->state, false, true); }; diff --git a/esphome/components/nextion/switch/nextion_switch.cpp b/esphome/components/nextion/switch/nextion_switch.cpp index 1f32ad3425..0bd958e0d8 100644 --- a/esphome/components/nextion/switch/nextion_switch.cpp +++ b/esphome/components/nextion/switch/nextion_switch.cpp @@ -20,7 +20,7 @@ void NextionSwitch::process_bool(const std::string &variable_name, bool on) { void NextionSwitch::update() { if (!this->nextion_->is_setup()) return; - this->nextion_->add_to_get_queue(this); + this->nextion_->add_to_get_queue(shared_from_this()); } void NextionSwitch::set_state(bool state, bool publish, bool send_to_nextion) { @@ -32,7 +32,7 @@ void NextionSwitch::set_state(bool state, bool publish, bool send_to_nextion) { this->needs_to_send_update_ = true; } else { this->needs_to_send_update_ = false; - this->nextion_->add_no_result_to_queue_with_set(this, (int) state); + this->nextion_->add_no_result_to_queue_with_set(shared_from_this(), (int) state); } } if (publish) { diff --git a/esphome/components/nextion/switch/nextion_switch.h b/esphome/components/nextion/switch/nextion_switch.h index 1548287473..d7783e5c51 100644 --- a/esphome/components/nextion/switch/nextion_switch.h +++ b/esphome/components/nextion/switch/nextion_switch.h @@ -8,7 +8,10 @@ namespace esphome { namespace nextion { class NextionSwitch; -class NextionSwitch : public NextionComponent, public switch_::Switch, public PollingComponent { +class NextionSwitch : public NextionComponent, + public switch_::Switch, + public PollingComponent, + public std::enable_shared_from_this { public: NextionSwitch(NextionBase *nextion) { this->nextion_ = nextion; } diff --git a/esphome/components/nextion/text_sensor/nextion_textsensor.cpp b/esphome/components/nextion/text_sensor/nextion_textsensor.cpp index 08f032df74..fa7cb35025 100644 --- a/esphome/components/nextion/text_sensor/nextion_textsensor.cpp +++ b/esphome/components/nextion/text_sensor/nextion_textsensor.cpp @@ -18,7 +18,7 @@ void NextionTextSensor::process_text(const std::string &variable_name, const std void NextionTextSensor::update() { if (!this->nextion_->is_setup()) return; - this->nextion_->add_to_get_queue(this); + this->nextion_->add_to_get_queue(shared_from_this()); } void NextionTextSensor::set_state(const std::string &state, bool publish, bool send_to_nextion) { @@ -29,7 +29,7 @@ void NextionTextSensor::set_state(const std::string &state, bool publish, bool s if (this->nextion_->is_sleeping() || !this->visible_) { this->needs_to_send_update_ = true; } else { - this->nextion_->add_no_result_to_queue_with_set(this, state); + this->nextion_->add_no_result_to_queue_with_set(shared_from_this(), state); } } diff --git a/esphome/components/nextion/text_sensor/nextion_textsensor.h b/esphome/components/nextion/text_sensor/nextion_textsensor.h index 5716d0a008..762797727d 100644 --- a/esphome/components/nextion/text_sensor/nextion_textsensor.h +++ b/esphome/components/nextion/text_sensor/nextion_textsensor.h @@ -8,7 +8,10 @@ namespace esphome { namespace nextion { class NextionTextSensor; -class NextionTextSensor : public NextionComponent, public text_sensor::TextSensor, public PollingComponent { +class NextionTextSensor : public NextionComponent, + public text_sensor::TextSensor, + public PollingComponent, + public std::enable_shared_from_this { public: NextionTextSensor(NextionBase *nextion) { this->nextion_ = nextion; } void update() override; diff --git a/esphome/components/nfc/ndef_message.cpp b/esphome/components/nfc/ndef_message.cpp index 4e295d4469..b1554f41ae 100644 --- a/esphome/components/nfc/ndef_message.cpp +++ b/esphome/components/nfc/ndef_message.cpp @@ -17,7 +17,7 @@ NdefMessage::NdefMessage(std::vector &data) { ESP_LOGVV(TAG, "me=%s, sr=%s, il=%s, tnf=%d", YESNO(me), YESNO(sr), YESNO(il), tnf); - auto record = new NdefRecord(); + auto record = make_unique(); record->set_tnf(tnf); uint8_t type_length = data[index++]; @@ -62,20 +62,20 @@ NdefMessage::NdefMessage(std::vector &data) { record->set_payload(payload_str); index += payload_length; - this->add_record(record); ESP_LOGV(TAG, "Adding record type %s = %s", record->get_type().c_str(), record->get_payload().c_str()); + this->add_record(std::move(record)); if (me) break; } } -bool NdefMessage::add_record(NdefRecord *record) { +bool NdefMessage::add_record(std::unique_ptr record) { if (this->records_.size() >= MAX_NDEF_RECORDS) { ESP_LOGE(TAG, "Too many records. Max: %d", MAX_NDEF_RECORDS); return false; } - this->records_.push_back(record); + this->records_.emplace_back(std::move(record)); return true; } @@ -83,13 +83,11 @@ bool NdefMessage::add_text_record(const std::string &text) { return this->add_te bool NdefMessage::add_text_record(const std::string &text, const std::string &encoding) { std::string payload = to_string(text.length()) + encoding + text; - auto r = new NdefRecord(TNF_WELL_KNOWN, "T", payload); - return this->add_record(r); + return this->add_record(make_unique(TNF_WELL_KNOWN, "T", payload)); } bool NdefMessage::add_uri_record(const std::string &uri) { - auto r = new NdefRecord(TNF_WELL_KNOWN, "U", uri); - return this->add_record(r); + return this->add_record(make_unique(TNF_WELL_KNOWN, "U", uri)); } std::vector NdefMessage::encode() { diff --git a/esphome/components/nfc/ndef_message.h b/esphome/components/nfc/ndef_message.h index e47a7b992a..d2ffa9582e 100644 --- a/esphome/components/nfc/ndef_message.h +++ b/esphome/components/nfc/ndef_message.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "esphome/core/helpers.h" #include "esphome/core/log.h" #include "ndef_record.h" @@ -11,12 +13,18 @@ static const uint8_t MAX_NDEF_RECORDS = 4; class NdefMessage { public: - NdefMessage(){}; + NdefMessage() = default; NdefMessage(std::vector &data); + NdefMessage(const NdefMessage &msg) { + records_.reserve(msg.records_.size()); + for (const auto &r : msg.records_) { + records_.emplace_back(make_unique(*r)); + } + } - std::vector get_records() { return this->records_; }; + const std::vector> &get_records() { return this->records_; }; - bool add_record(NdefRecord *record); + bool add_record(std::unique_ptr record); bool add_text_record(const std::string &text); bool add_text_record(const std::string &text, const std::string &encoding); bool add_uri_record(const std::string &uri); @@ -24,7 +32,7 @@ class NdefMessage { std::vector encode(); protected: - std::vector records_; + std::vector> records_; }; } // namespace nfc diff --git a/esphome/components/nfc/ndef_record.h b/esphome/components/nfc/ndef_record.h index 2059444882..680fe20986 100644 --- a/esphome/components/nfc/ndef_record.h +++ b/esphome/components/nfc/ndef_record.h @@ -85,9 +85,9 @@ class NdefRecord { std::vector encode(bool first, bool last); uint8_t get_tnf_byte(bool first, bool last); - const std::string &get_type() { return this->type_; }; - const std::string &get_id() { return this->id_; }; - const std::string &get_payload() { return this->payload_; }; + const std::string &get_type() const { return this->type_; }; + const std::string &get_id() const { return this->id_; }; + const std::string &get_payload() const { return this->payload_; }; protected: uint8_t tnf_; diff --git a/esphome/components/nfc/nfc_tag.h b/esphome/components/nfc/nfc_tag.h index ff0d1c39b4..ab6ca650e4 100644 --- a/esphome/components/nfc/nfc_tag.h +++ b/esphome/components/nfc/nfc_tag.h @@ -1,6 +1,9 @@ #pragma once +#include + #include "esphome/core/log.h" +#include "esphome/core/helpers.h" #include "ndef_message.h" namespace esphome { @@ -20,27 +23,33 @@ class NfcTag { this->uid_ = uid; this->tag_type_ = tag_type; }; - NfcTag(std::vector &uid, const std::string &tag_type, nfc::NdefMessage *ndef_message) { + NfcTag(std::vector &uid, const std::string &tag_type, std::unique_ptr ndef_message) { this->uid_ = uid; this->tag_type_ = tag_type; - this->ndef_message_ = ndef_message; + this->ndef_message_ = std::move(ndef_message); }; NfcTag(std::vector &uid, const std::string &tag_type, std::vector &ndef_data) { this->uid_ = uid; this->tag_type_ = tag_type; - this->ndef_message_ = new NdefMessage(ndef_data); + this->ndef_message_ = make_unique(ndef_data); }; + NfcTag(const NfcTag &rhs) { + uid_ = rhs.uid_; + tag_type_ = rhs.tag_type_; + if (rhs.ndef_message_ != nullptr) + ndef_message_ = make_unique(*rhs.ndef_message_); + } std::vector &get_uid() { return this->uid_; }; const std::string &get_tag_type() { return this->tag_type_; }; bool has_ndef_message() { return this->ndef_message_ != nullptr; }; - NdefMessage *get_ndef_message() { return this->ndef_message_; }; - void set_ndef_message(NdefMessage *ndef_message) { this->ndef_message_ = ndef_message; }; + const std::unique_ptr &get_ndef_message() { return this->ndef_message_; }; + void set_ndef_message(std::unique_ptr ndef_message) { this->ndef_message_ = std::move(ndef_message); }; protected: std::vector uid_; std::string tag_type_; - NdefMessage *ndef_message_{nullptr}; + std::unique_ptr ndef_message_; }; } // namespace nfc diff --git a/esphome/components/ota/ota_component.cpp b/esphome/components/ota/ota_component.cpp index ac72befb9e..e8ac1b9bcd 100644 --- a/esphome/components/ota/ota_component.cpp +++ b/esphome/components/ota/ota_component.cpp @@ -19,7 +19,7 @@ static const char *const TAG = "ota"; static const uint8_t OTA_VERSION_1_0 = 1; void OTAComponent::setup() { - this->server_ = new WiFiServer(this->port_); + this->server_ = make_unique(this->port_); this->server_->begin(); this->dump_config(); diff --git a/esphome/components/ota/ota_component.h b/esphome/components/ota/ota_component.h index 8b5830295e..04cea56ec2 100644 --- a/esphome/components/ota/ota_component.h +++ b/esphome/components/ota/ota_component.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "esphome/core/component.h" #include "esphome/core/preferences.h" #include "esphome/core/helpers.h" @@ -80,7 +82,7 @@ class OTAComponent : public Component { uint16_t port_; - WiFiServer *server_{nullptr}; + std::unique_ptr server_{nullptr}; WiFiClient client_{}; bool has_safe_mode_{false}; ///< stores whether safe mode can be enabled. diff --git a/esphome/components/pca9685/output.py b/esphome/components/pca9685/output.py index 40f7b3cd74..b7681f9ba0 100644 --- a/esphome/components/pca9685/output.py +++ b/esphome/components/pca9685/output.py @@ -20,6 +20,7 @@ CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend( async def to_code(config): paren = await cg.get_variable(config[CONF_PCA9685_ID]) - rhs = paren.create_channel(config[CONF_CHANNEL]) - var = cg.Pvariable(config[CONF_ID], rhs) + var = cg.new_Pvariable(config[CONF_ID]) + cg.add(var.set_channel(config[CONF_CHANNEL])) + cg.add(paren.register_channel(var)) await output.register_output(var, config) diff --git a/esphome/components/pca9685/pca9685_output.cpp b/esphome/components/pca9685/pca9685_output.cpp index c3fd2b60e4..1ad6f4a665 100644 --- a/esphome/components/pca9685/pca9685_output.cpp +++ b/esphome/components/pca9685/pca9685_output.cpp @@ -123,11 +123,11 @@ void PCA9685Output::loop() { this->update_ = false; } -PCA9685Channel *PCA9685Output::create_channel(uint8_t channel) { - this->min_channel_ = std::min(this->min_channel_, channel); - this->max_channel_ = std::max(this->max_channel_, channel); - auto *c = new PCA9685Channel(this, channel); - return c; +void PCA9685Output::register_channel(PCA9685Channel *channel) { + auto c = channel->channel_; + this->min_channel_ = std::min(this->min_channel_, c); + this->max_channel_ = std::max(this->max_channel_, c); + channel->set_parent(this); } void PCA9685Channel::write_state(float state) { diff --git a/esphome/components/pca9685/pca9685_output.h b/esphome/components/pca9685/pca9685_output.h index 55b52fc660..bd3f7f15f9 100644 --- a/esphome/components/pca9685/pca9685_output.h +++ b/esphome/components/pca9685/pca9685_output.h @@ -20,14 +20,15 @@ extern const uint8_t PCA9685_MODE_OUTNE_LOW; class PCA9685Output; -class PCA9685Channel : public output::FloatOutput { +class PCA9685Channel : public output::FloatOutput, public Parented { public: - PCA9685Channel(PCA9685Output *parent, uint8_t channel) : parent_(parent), channel_(channel) {} + void set_channel(uint8_t channel) { channel_ = channel; } protected: + friend class PCA9685Output; + void write_state(float state) override; - PCA9685Output *parent_; uint8_t channel_; }; @@ -37,7 +38,7 @@ class PCA9685Output : public Component, public i2c::I2CDevice { PCA9685Output(float frequency, uint8_t mode = PCA9685_MODE_OUTPUT_ONACK | PCA9685_MODE_OUTPUT_TOTEM_POLE) : frequency_(frequency), mode_(mode) {} - PCA9685Channel *create_channel(uint8_t channel); + void register_channel(PCA9685Channel *channel); void setup() override; void dump_config() override; diff --git a/esphome/components/pipsolar/pipsolar.cpp b/esphome/components/pipsolar/pipsolar.cpp index 9c7adc13c1..8603adfbf4 100644 --- a/esphome/components/pipsolar/pipsolar.cpp +++ b/esphome/components/pipsolar/pipsolar.cpp @@ -881,7 +881,7 @@ void Pipsolar::add_polling_command_(const char *command, ENUMPollingCommand poll size_t length = strlen(command) + 1; const char *beg = command; const char *end = command + length; - used_polling_command.command = new uint8_t[length]; + used_polling_command.command = new uint8_t[length]; // NOLINT(cppcoreguidelines-owning-memory) size_t i = 0; for (; beg != end; ++beg, ++i) { used_polling_command.command[i] = (uint8_t)(*beg); diff --git a/esphome/components/pn532/pn532.cpp b/esphome/components/pn532/pn532.cpp index fcab9872c4..8fda19cba3 100644 --- a/esphome/components/pn532/pn532.cpp +++ b/esphome/components/pn532/pn532.cpp @@ -104,7 +104,7 @@ void PN532::loop() { if (!success) { // Something failed if (!this->current_uid_.empty()) { - auto tag = new nfc::NfcTag(this->current_uid_); + auto tag = make_unique(this->current_uid_); for (auto *trigger : this->triggers_ontagremoved_) trigger->process(tag); } @@ -117,7 +117,7 @@ void PN532::loop() { if (num_targets != 1) { // no tags found or too many if (!this->current_uid_.empty()) { - auto tag = new nfc::NfcTag(this->current_uid_); + auto tag = make_unique(this->current_uid_); for (auto *trigger : this->triggers_ontagremoved_) trigger->process(tag); } @@ -158,10 +158,10 @@ void PN532::loop() { if (report) { ESP_LOGD(TAG, "Found new tag '%s'", nfc::format_uid(nfcid).c_str()); if (tag->has_ndef_message()) { - auto message = tag->get_ndef_message(); - auto records = message->get_records(); + const auto &message = tag->get_ndef_message(); + const auto &records = message->get_records(); ESP_LOGD(TAG, " NDEF formatted records:"); - for (auto &record : records) { + for (const auto &record : records) { ESP_LOGD(TAG, " %s - %s", record->get_type().c_str(), record->get_payload().c_str()); } } @@ -270,7 +270,7 @@ void PN532::turn_off_rf_() { }); } -nfc::NfcTag *PN532::read_tag_(std::vector &uid) { +std::unique_ptr PN532::read_tag_(std::vector &uid) { uint8_t type = nfc::guess_tag_type(uid.size()); if (type == nfc::TAG_TYPE_MIFARE_CLASSIC) { @@ -281,9 +281,9 @@ nfc::NfcTag *PN532::read_tag_(std::vector &uid) { return this->read_mifare_ultralight_tag_(uid); } else if (type == nfc::TAG_TYPE_UNKNOWN) { ESP_LOGV(TAG, "Cannot determine tag type"); - return new nfc::NfcTag(uid); + return make_unique(uid); } else { - return new nfc::NfcTag(uid); + return make_unique(uid); } } @@ -373,7 +373,9 @@ bool PN532BinarySensor::process(std::vector &data) { this->found_ = true; return true; } -void PN532OnTagTrigger::process(nfc::NfcTag *tag) { this->trigger(nfc::format_uid(tag->get_uid()), *tag); } +void PN532OnTagTrigger::process(const std::unique_ptr &tag) { + this->trigger(nfc::format_uid(tag->get_uid()), *tag); +} } // namespace pn532 } // namespace esphome diff --git a/esphome/components/pn532/pn532.h b/esphome/components/pn532/pn532.h index c4854cf7f2..692a5011e6 100644 --- a/esphome/components/pn532/pn532.h +++ b/esphome/components/pn532/pn532.h @@ -54,13 +54,13 @@ class PN532 : public PollingComponent { virtual bool read_data(std::vector &data, uint8_t len) = 0; virtual bool read_response(uint8_t command, std::vector &data) = 0; - nfc::NfcTag *read_tag_(std::vector &uid); + std::unique_ptr read_tag_(std::vector &uid); bool format_tag_(std::vector &uid); bool clean_tag_(std::vector &uid); bool write_tag_(std::vector &uid, nfc::NdefMessage *message); - nfc::NfcTag *read_mifare_classic_tag_(std::vector &uid); + std::unique_ptr read_mifare_classic_tag_(std::vector &uid); bool read_mifare_classic_block_(uint8_t block_num, std::vector &data); bool write_mifare_classic_block_(uint8_t block_num, std::vector &data); bool auth_mifare_classic_block_(std::vector &uid, uint8_t block_num, uint8_t key_num, const uint8_t *key); @@ -68,7 +68,7 @@ class PN532 : public PollingComponent { bool format_mifare_classic_ndef_(std::vector &uid); bool write_mifare_classic_tag_(std::vector &uid, nfc::NdefMessage *message); - nfc::NfcTag *read_mifare_ultralight_tag_(std::vector &uid); + std::unique_ptr read_mifare_ultralight_tag_(std::vector &uid); bool read_mifare_ultralight_page_(uint8_t page_num, std::vector &data); bool is_mifare_ultralight_formatted_(); uint16_t read_mifare_ultralight_capacity_(); @@ -117,7 +117,7 @@ class PN532BinarySensor : public binary_sensor::BinarySensor { class PN532OnTagTrigger : public Trigger { public: - void process(nfc::NfcTag *tag); + void process(const std::unique_ptr &tag); }; class PN532OnFinishedWriteTrigger : public Trigger<> { diff --git a/esphome/components/pn532/pn532_mifare_classic.cpp b/esphome/components/pn532/pn532_mifare_classic.cpp index 119d1ceef6..fa99e5cfab 100644 --- a/esphome/components/pn532/pn532_mifare_classic.cpp +++ b/esphome/components/pn532/pn532_mifare_classic.cpp @@ -6,7 +6,7 @@ namespace pn532 { static const char *const TAG = "pn532.mifare_classic"; -nfc::NfcTag *PN532::read_mifare_classic_tag_(std::vector &uid) { +std::unique_ptr PN532::read_mifare_classic_tag_(std::vector &uid) { uint8_t current_block = 4; uint8_t message_start_index = 0; uint32_t message_length = 0; @@ -15,15 +15,15 @@ nfc::NfcTag *PN532::read_mifare_classic_tag_(std::vector &uid) { std::vector data; if (this->read_mifare_classic_block_(current_block, data)) { if (!nfc::decode_mifare_classic_tlv(data, message_length, message_start_index)) { - return new nfc::NfcTag(uid, nfc::ERROR); + return make_unique(uid, nfc::ERROR); } } else { ESP_LOGE(TAG, "Failed to read block %d", current_block); - return new nfc::NfcTag(uid, nfc::MIFARE_CLASSIC); + return make_unique(uid, nfc::MIFARE_CLASSIC); } } else { ESP_LOGV(TAG, "Tag is not NDEF formatted"); - return new nfc::NfcTag(uid, nfc::MIFARE_CLASSIC); + return make_unique(uid, nfc::MIFARE_CLASSIC); } uint32_t index = 0; @@ -51,7 +51,7 @@ nfc::NfcTag *PN532::read_mifare_classic_tag_(std::vector &uid) { } } buffer.erase(buffer.begin(), buffer.begin() + message_start_index); - return new nfc::NfcTag(uid, nfc::MIFARE_CLASSIC, buffer); + return make_unique(uid, nfc::MIFARE_CLASSIC, buffer); } bool PN532::read_mifare_classic_block_(uint8_t block_num, std::vector &data) { diff --git a/esphome/components/pn532/pn532_mifare_ultralight.cpp b/esphome/components/pn532/pn532_mifare_ultralight.cpp index 1787a3c225..6f118419ba 100644 --- a/esphome/components/pn532/pn532_mifare_ultralight.cpp +++ b/esphome/components/pn532/pn532_mifare_ultralight.cpp @@ -6,28 +6,28 @@ namespace pn532 { static const char *const TAG = "pn532.mifare_ultralight"; -nfc::NfcTag *PN532::read_mifare_ultralight_tag_(std::vector &uid) { +std::unique_ptr PN532::read_mifare_ultralight_tag_(std::vector &uid) { if (!this->is_mifare_ultralight_formatted_()) { ESP_LOGD(TAG, "Not NDEF formatted"); - return new nfc::NfcTag(uid, nfc::NFC_FORUM_TYPE_2); + return make_unique(uid, nfc::NFC_FORUM_TYPE_2); } uint8_t message_length; uint8_t message_start_index; if (!this->find_mifare_ultralight_ndef_(message_length, message_start_index)) { - return new nfc::NfcTag(uid, nfc::NFC_FORUM_TYPE_2); + return make_unique(uid, nfc::NFC_FORUM_TYPE_2); } ESP_LOGVV(TAG, "message length: %d, start: %d", message_length, message_start_index); if (message_length == 0) { - return new nfc::NfcTag(uid, nfc::NFC_FORUM_TYPE_2); + return make_unique(uid, nfc::NFC_FORUM_TYPE_2); } std::vector data; for (uint8_t page = nfc::MIFARE_ULTRALIGHT_DATA_START_PAGE; page < nfc::MIFARE_ULTRALIGHT_MAX_PAGE; page++) { std::vector page_data; if (!this->read_mifare_ultralight_page_(page, page_data)) { ESP_LOGE(TAG, "Error reading page %d", page); - return new nfc::NfcTag(uid, nfc::NFC_FORUM_TYPE_2); + return make_unique(uid, nfc::NFC_FORUM_TYPE_2); } data.insert(data.end(), page_data.begin(), page_data.end()); @@ -38,7 +38,7 @@ nfc::NfcTag *PN532::read_mifare_ultralight_tag_(std::vector &uid) { data.erase(data.begin(), data.begin() + message_start_index); data.erase(data.begin() + message_length, data.end()); - return new nfc::NfcTag(uid, nfc::NFC_FORUM_TYPE_2, data); + return make_unique(uid, nfc::NFC_FORUM_TYPE_2, data); } bool PN532::read_mifare_ultralight_page_(uint8_t page_num, std::vector &data) { diff --git a/esphome/components/scd30/scd30.cpp b/esphome/components/scd30/scd30.cpp index 838c92f04e..8dfd992abe 100644 --- a/esphome/components/scd30/scd30.cpp +++ b/esphome/components/scd30/scd30.cpp @@ -221,10 +221,9 @@ uint8_t SCD30Component::sht_crc_(uint8_t data1, uint8_t data2) { bool SCD30Component::read_data_(uint16_t *data, uint8_t len) { const uint8_t num_bytes = len * 3; - auto *buf = new uint8_t[num_bytes]; + std::vector buf(num_bytes); - if (!this->parent_->raw_receive(this->address_, buf, num_bytes)) { - delete[](buf); + if (!this->parent_->raw_receive(this->address_, buf.data(), num_bytes)) { return false; } @@ -233,13 +232,11 @@ bool SCD30Component::read_data_(uint16_t *data, uint8_t len) { uint8_t crc = sht_crc_(buf[j], buf[j + 1]); if (crc != buf[j + 2]) { ESP_LOGE(TAG, "CRC8 Checksum invalid! 0x%02X != 0x%02X", buf[j + 2], crc); - delete[](buf); return false; } data[i] = (buf[j] << 8) | buf[j + 1]; } - delete[](buf); return true; } diff --git a/esphome/components/sgp30/sgp30.cpp b/esphome/components/sgp30/sgp30.cpp index e30d6d3adc..5a6f438429 100644 --- a/esphome/components/sgp30/sgp30.cpp +++ b/esphome/components/sgp30/sgp30.cpp @@ -332,10 +332,9 @@ uint8_t SGP30Component::sht_crc_(uint8_t data1, uint8_t data2) { bool SGP30Component::read_data_(uint16_t *data, uint8_t len) { const uint8_t num_bytes = len * 3; - auto *buf = new uint8_t[num_bytes]; + std::vector buf(num_bytes); - if (!this->parent_->raw_receive(this->address_, buf, num_bytes)) { - delete[](buf); + if (!this->parent_->raw_receive(this->address_, buf.data(), num_bytes)) { return false; } @@ -344,13 +343,11 @@ bool SGP30Component::read_data_(uint16_t *data, uint8_t len) { uint8_t crc = sht_crc_(buf[j], buf[j + 1]); if (crc != buf[j + 2]) { ESP_LOGE(TAG, "CRC8 Checksum invalid! 0x%02X != 0x%02X", buf[j + 2], crc); - delete[](buf); return false; } data[i] = (buf[j] << 8) | buf[j + 1]; } - delete[](buf); return true; } diff --git a/esphome/components/sht3xd/sht3xd.cpp b/esphome/components/sht3xd/sht3xd.cpp index 0bf2cc1a81..be5b93c124 100644 --- a/esphome/components/sht3xd/sht3xd.cpp +++ b/esphome/components/sht3xd/sht3xd.cpp @@ -101,10 +101,9 @@ uint8_t sht_crc(uint8_t data1, uint8_t data2) { bool SHT3XDComponent::read_data_(uint16_t *data, uint8_t len) { const uint8_t num_bytes = len * 3; - auto *buf = new uint8_t[num_bytes]; + std::vector buf(num_bytes); - if (!this->parent_->raw_receive(this->address_, buf, num_bytes)) { - delete[](buf); + if (!this->parent_->raw_receive(this->address_, buf.data(), num_bytes)) { return false; } @@ -113,13 +112,11 @@ bool SHT3XDComponent::read_data_(uint16_t *data, uint8_t len) { uint8_t crc = sht_crc(buf[j], buf[j + 1]); if (crc != buf[j + 2]) { ESP_LOGE(TAG, "CRC8 Checksum invalid! 0x%02X != 0x%02X", buf[j + 2], crc); - delete[](buf); return false; } data[i] = (buf[j] << 8) | buf[j + 1]; } - delete[](buf); return true; } diff --git a/esphome/components/shtcx/shtcx.cpp b/esphome/components/shtcx/shtcx.cpp index 848da8d7d7..db3a3bf803 100644 --- a/esphome/components/shtcx/shtcx.cpp +++ b/esphome/components/shtcx/shtcx.cpp @@ -130,10 +130,9 @@ uint8_t sht_crc(uint8_t data1, uint8_t data2) { bool SHTCXComponent::read_data_(uint16_t *data, uint8_t len) { const uint8_t num_bytes = len * 3; - auto *buf = new uint8_t[num_bytes]; + std::vector buf(num_bytes); - if (!this->parent_->raw_receive(this->address_, buf, num_bytes)) { - delete[](buf); + if (!this->parent_->raw_receive(this->address_, buf.data(), num_bytes)) { return false; } @@ -142,13 +141,11 @@ bool SHTCXComponent::read_data_(uint16_t *data, uint8_t len) { uint8_t crc = sht_crc(buf[j], buf[j + 1]); if (crc != buf[j + 2]) { ESP_LOGE(TAG, "CRC8 Checksum invalid! 0x%02X != 0x%02X", buf[j + 2], crc); - delete[](buf); return false; } data[i] = (buf[j] << 8) | buf[j + 1]; } - delete[](buf); return true; } diff --git a/esphome/components/socket/lwip_raw_tcp_impl.cpp b/esphome/components/socket/lwip_raw_tcp_impl.cpp index aaeee7268a..29e1b23823 100644 --- a/esphome/components/socket/lwip_raw_tcp_impl.cpp +++ b/esphome/components/socket/lwip_raw_tcp_impl.cpp @@ -423,9 +423,9 @@ class LWIPRawImpl : public Socket { // nothing to do here, we just don't push it to the queue return ERR_OK; } - auto *sock = new LWIPRawImpl(newpcb); + auto sock = std::unique_ptr(new LWIPRawImpl(newpcb)); sock->init(); - accepted_sockets_.emplace(sock); + accepted_sockets_.push(std::move(sock)); return ERR_OK; } void err_fn(err_t err) { @@ -486,7 +486,7 @@ std::unique_ptr socket(int domain, int type, int protocol) { auto *pcb = tcp_new(); if (pcb == nullptr) return nullptr; - auto *sock = new LWIPRawImpl(pcb); + auto *sock = new LWIPRawImpl(pcb); // NOLINT(cppcoreguidelines-owning-memory) sock->init(); return std::unique_ptr{sock}; } diff --git a/esphome/components/sps30/sps30.cpp b/esphome/components/sps30/sps30.cpp index 30bd134626..3a31f4d607 100644 --- a/esphome/components/sps30/sps30.cpp +++ b/esphome/components/sps30/sps30.cpp @@ -242,10 +242,9 @@ bool SPS30Component::start_continuous_measurement_() { bool SPS30Component::read_data_(uint16_t *data, uint8_t len) { const uint8_t num_bytes = len * 3; - auto *buf = new uint8_t[num_bytes]; + std::vector buf(num_bytes); - if (!this->parent_->raw_receive(this->address_, buf, num_bytes)) { - delete[](buf); + if (!this->parent_->raw_receive(this->address_, buf.data(), num_bytes)) { return false; } @@ -254,13 +253,11 @@ bool SPS30Component::read_data_(uint16_t *data, uint8_t len) { uint8_t crc = sht_crc_(buf[j], buf[j + 1]); if (crc != buf[j + 2]) { ESP_LOGE(TAG, "CRC8 Checksum invalid! 0x%02X != 0x%02X", buf[j + 2], crc); - delete[](buf); return false; } data[i] = (buf[j] << 8) | buf[j + 1]; } - delete[](buf); return true; } diff --git a/esphome/components/sts3x/sts3x.cpp b/esphome/components/sts3x/sts3x.cpp index 5d6d725a17..77383c6daa 100644 --- a/esphome/components/sts3x/sts3x.cpp +++ b/esphome/components/sts3x/sts3x.cpp @@ -97,10 +97,9 @@ uint8_t sts3x_crc(uint8_t data1, uint8_t data2) { bool STS3XComponent::read_data_(uint16_t *data, uint8_t len) { const uint8_t num_bytes = len * 3; - auto *buf = new uint8_t[num_bytes]; + std::vector buf(num_bytes); - if (!this->parent_->raw_receive(this->address_, buf, num_bytes)) { - delete[](buf); + if (!this->parent_->raw_receive(this->address_, buf.data(), num_bytes)) { return false; } @@ -109,13 +108,11 @@ bool STS3XComponent::read_data_(uint16_t *data, uint8_t len) { uint8_t crc = sts3x_crc(buf[j], buf[j + 1]); if (crc != buf[j + 2]) { ESP_LOGE(TAG, "CRC8 Checksum invalid! 0x%02X != 0x%02X", buf[j + 2], crc); - delete[](buf); return false; } data[i] = (buf[j] << 8) | buf[j + 1]; } - delete[](buf); return true; } diff --git a/esphome/components/tlc59208f/output.py b/esphome/components/tlc59208f/output.py index f7cc89b252..ac45909673 100644 --- a/esphome/components/tlc59208f/output.py +++ b/esphome/components/tlc59208f/output.py @@ -20,6 +20,7 @@ CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend( async def to_code(config): paren = await cg.get_variable(config[CONF_TLC59208F_ID]) - rhs = paren.create_channel(config[CONF_CHANNEL]) - var = cg.Pvariable(config[CONF_ID], rhs) + var = cg.new_Pvariable(config[CONF_ID]) + cg.add(var.set_channel(config[CONF_CHANNEL])) + cg.add(paren.register_channel(var)) await output.register_output(var, config) diff --git a/esphome/components/tlc59208f/tlc59208f_output.cpp b/esphome/components/tlc59208f/tlc59208f_output.cpp index 784a0947ed..e416a5c62b 100644 --- a/esphome/components/tlc59208f/tlc59208f_output.cpp +++ b/esphome/components/tlc59208f/tlc59208f_output.cpp @@ -137,11 +137,11 @@ void TLC59208FOutput::loop() { this->update_ = false; } -TLC59208FChannel *TLC59208FOutput::create_channel(uint8_t channel) { - this->min_channel_ = std::min(this->min_channel_, channel); - this->max_channel_ = std::max(this->max_channel_, channel); - auto *c = new TLC59208FChannel(this, channel); - return c; +void TLC59208FOutput::register_channel(TLC59208FChannel *channel) { + auto c = channel->channel_; + this->min_channel_ = std::min(this->min_channel_, c); + this->max_channel_ = std::max(this->max_channel_, c); + channel->set_parent(this); } void TLC59208FChannel::write_state(float state) { diff --git a/esphome/components/tlc59208f/tlc59208f_output.h b/esphome/components/tlc59208f/tlc59208f_output.h index 06b7adc882..79fccc97f3 100644 --- a/esphome/components/tlc59208f/tlc59208f_output.h +++ b/esphome/components/tlc59208f/tlc59208f_output.h @@ -21,14 +21,15 @@ extern const uint8_t TLC59208F_MODE2_WDT_35MS; class TLC59208FOutput; -class TLC59208FChannel : public output::FloatOutput { +class TLC59208FChannel : public output::FloatOutput, public Parented { public: - TLC59208FChannel(TLC59208FOutput *parent, uint8_t channel) : parent_(parent), channel_(channel) {} + void set_channel(uint8_t channel) { channel_ = channel; } protected: + friend class TLC59208FOutput; + void write_state(float state) override; - TLC59208FOutput *parent_; uint8_t channel_; }; @@ -37,7 +38,7 @@ class TLC59208FOutput : public Component, public i2c::I2CDevice { public: TLC59208FOutput(uint8_t mode = TLC59208F_MODE2_OCH) : mode_(mode) {} - TLC59208FChannel *create_channel(uint8_t channel); + void register_channel(TLC59208FChannel *channel); void setup() override; void dump_config() override; diff --git a/esphome/components/tm1651/tm1651.cpp b/esphome/components/tm1651/tm1651.cpp index bb37e9f4c9..3689ef5894 100644 --- a/esphome/components/tm1651/tm1651.cpp +++ b/esphome/components/tm1651/tm1651.cpp @@ -1,5 +1,6 @@ #include "tm1651.h" #include "esphome/core/log.h" +#include "esphome/core/helpers.h" namespace esphome { namespace tm1651 { @@ -19,7 +20,7 @@ void TM1651Display::setup() { uint8_t clk = clk_pin_->get_pin(); uint8_t dio = dio_pin_->get_pin(); - battery_display_ = new TM1651(clk, dio); + battery_display_ = make_unique(clk, dio); battery_display_->init(); battery_display_->clearDisplay(); } diff --git a/esphome/components/tm1651/tm1651.h b/esphome/components/tm1651/tm1651.h index 6eab24687c..a18519bf41 100644 --- a/esphome/components/tm1651/tm1651.h +++ b/esphome/components/tm1651/tm1651.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "esphome/core/component.h" #include "esphome/core/esphal.h" #include "esphome/core/automation.h" @@ -25,7 +27,7 @@ class TM1651Display : public Component { void turn_off(); protected: - TM1651 *battery_display_; + std::unique_ptr battery_display_; GPIOPin *clk_pin_; GPIOPin *dio_pin_; bool is_on_ = true; diff --git a/esphome/components/uart/uart_esp8266.cpp b/esphome/components/uart/uart_esp8266.cpp index bc6b0f72d3..6d207fde6c 100644 --- a/esphome/components/uart/uart_esp8266.cpp +++ b/esphome/components/uart/uart_esp8266.cpp @@ -85,7 +85,7 @@ void UARTComponent::setup() { this->hw_serial_->begin(this->baud_rate_, config); this->hw_serial_->setRxBufferSize(this->rx_buffer_size_); } else { - this->sw_serial_ = new ESP8266SoftwareSerial(); + this->sw_serial_ = new ESP8266SoftwareSerial(); // NOLINT int8_t tx = this->tx_pin_.has_value() ? *this->tx_pin_ : -1; int8_t rx = this->rx_pin_.has_value() ? *this->rx_pin_ : -1; this->sw_serial_->setup(tx, rx, this->baud_rate_, this->stop_bits_, this->data_bits_, this->parity_, @@ -227,7 +227,7 @@ void ESP8266SoftwareSerial::setup(int8_t tx_pin, int8_t rx_pin, uint32_t baud_ra pin.setup(); this->gpio_rx_pin_ = &pin; this->rx_pin_ = pin.to_isr(); - this->rx_buffer_ = new uint8_t[this->rx_buffer_size_]; + this->rx_buffer_ = new uint8_t[this->rx_buffer_size_]; // NOLINT pin.attach_interrupt(ESP8266SoftwareSerial::gpio_intr, this, FALLING); } } diff --git a/esphome/components/web_server_base/web_server_base.cpp b/esphome/components/web_server_base/web_server_base.cpp index 85711704b9..9f04c3b7bf 100644 --- a/esphome/components/web_server_base/web_server_base.cpp +++ b/esphome/components/web_server_base/web_server_base.cpp @@ -86,7 +86,9 @@ void OTARequestHandler::handleRequest(AsyncWebServerRequest *request) { request->send(response); } -void WebServerBase::add_ota_handler() { this->add_handler(new OTARequestHandler(this)); } +void WebServerBase::add_ota_handler() { + this->add_handler(new OTARequestHandler(this)); // NOLINT +} float WebServerBase::get_setup_priority() const { // Before WiFi (captive portal) return setup_priority::WIFI + 2.0f; diff --git a/esphome/components/web_server_base/web_server_base.h b/esphome/components/web_server_base/web_server_base.h index b6024ceafa..158006ae40 100644 --- a/esphome/components/web_server_base/web_server_base.h +++ b/esphome/components/web_server_base/web_server_base.h @@ -1,5 +1,6 @@ #pragma once +#include #include "esphome/core/component.h" #include @@ -14,7 +15,7 @@ class WebServerBase : public Component { this->initialized_++; return; } - this->server_ = new AsyncWebServer(this->port_); + this->server_ = std::make_shared(this->port_); this->server_->begin(); for (auto *handler : this->handlers_) @@ -25,11 +26,10 @@ class WebServerBase : public Component { void deinit() { this->initialized_--; if (this->initialized_ == 0) { - delete this->server_; this->server_ = nullptr; } } - AsyncWebServer *get_server() const { return server_; } + std::shared_ptr get_server() const { return server_; } float get_setup_priority() const override; void add_handler(AsyncWebHandler *handler) { @@ -50,7 +50,7 @@ class WebServerBase : public Component { int initialized_{0}; uint16_t port_{80}; - AsyncWebServer *server_{nullptr}; + std::shared_ptr server_{nullptr}; std::vector handlers_; }; diff --git a/esphome/components/wled/wled_light_effect.cpp b/esphome/components/wled/wled_light_effect.cpp index 915d1c6cc2..500b9bbad2 100644 --- a/esphome/components/wled/wled_light_effect.cpp +++ b/esphome/components/wled/wled_light_effect.cpp @@ -1,5 +1,6 @@ #include "wled_light_effect.h" #include "esphome/core/log.h" +#include "esphome/core/helpers.h" #ifdef ARDUINO_ARCH_ESP32 #include @@ -48,7 +49,7 @@ void WLEDLightEffect::blank_all_leds_(light::AddressableLight &it) { void WLEDLightEffect::apply(light::AddressableLight &it, const Color ¤t_color) { // Init UDP lazily if (!udp_) { - udp_.reset(new WiFiUDP()); + udp_ = make_unique(); if (!udp_->begin(port_)) { ESP_LOGW(TAG, "Cannot bind WLEDLightEffect to %d.", port_); diff --git a/esphome/core/esphal.cpp b/esphome/core/esphal.cpp index 8fb1dd86c5..849fdf95ad 100644 --- a/esphome/core/esphal.cpp +++ b/esphome/core/esphal.cpp @@ -230,10 +230,11 @@ void GPIOPin::attach_interrupt_(void (*func)(void *), void *arg, int mode) const } } #ifdef ARDUINO_ARCH_ESP8266 - ArgStructure *as = new ArgStructure; + ArgStructure *as = new ArgStructure; // NOLINT as->interruptInfo = nullptr; as->functionInfo = new ESPHomeInterruptFuncInfo{ + // NOLINT .func = func, .arg = arg, }; @@ -249,7 +250,7 @@ void GPIOPin::attach_interrupt_(void (*func)(void *), void *arg, int mode) const } ISRInternalGPIOPin *GPIOPin::to_isr() const { - return new ISRInternalGPIOPin(this->pin_, + return new ISRInternalGPIOPin(this->pin_, // NOLINT #ifdef ARDUINO_ARCH_ESP32 this->gpio_clear_, this->gpio_set_, #endif diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index c5ff0102c3..53c7d1de43 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -141,21 +141,6 @@ std::string uint32_to_string(uint32_t num) { snprintf(buffer, sizeof(buffer), "%04X%04X", address16[1], address16[0]); return std::string(buffer); } -static char *global_json_build_buffer = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -static size_t global_json_build_buffer_size = 0; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) - -void reserve_global_json_build_buffer(size_t required_size) { - if (global_json_build_buffer_size == 0 || global_json_build_buffer_size < required_size) { - delete[] global_json_build_buffer; - global_json_build_buffer_size = std::max(required_size, global_json_build_buffer_size * 2); - - size_t remainder = global_json_build_buffer_size % 16U; - if (remainder != 0) - global_json_build_buffer_size += 16 - remainder; - - global_json_build_buffer = new char[global_json_build_buffer_size]; - } -} ParseOnOffState parse_on_off(const char *str, const char *on, const char *off) { if (on == nullptr && strcasecmp(str, "on") == 0) diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index a63c627e5a..044de6c8b0 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -338,7 +338,7 @@ template T *new_buffer(size_t length) { buffer = new T[length]; } #else - buffer = new T[length]; + buffer = new T[length]; // NOLINT #endif return buffer; diff --git a/esphome/core/preferences.cpp b/esphome/core/preferences.cpp index bfc5d9f875..f051ce0e8a 100644 --- a/esphome/core/preferences.cpp +++ b/esphome/core/preferences.cpp @@ -17,13 +17,8 @@ namespace esphome { static const char *const TAG = "preferences"; -ESPPreferenceObject::ESPPreferenceObject() : offset_(0), length_words_(0), type_(0), data_(nullptr) {} ESPPreferenceObject::ESPPreferenceObject(size_t offset, size_t length, uint32_t type) - : offset_(offset), length_words_(length), type_(type) { - this->data_ = new uint32_t[this->length_words_ + 1]; // NOLINT(cppcoreguidelines-prefer-member-initializer) - for (uint32_t i = 0; i < this->length_words_ + 1; i++) - this->data_[i] = 0; -} + : offset_(offset), length_words_(length), type_(type), data_(length + 1) {} bool ESPPreferenceObject::load_() { if (!this->is_initialized()) { ESP_LOGV(TAG, "Load Pref Not initialized!"); @@ -173,7 +168,7 @@ ESPPreferences::ESPPreferences() : current_offset_(0) {} void ESPPreferences::begin() { - this->flash_storage_ = new uint32_t[ESP8266_FLASH_STORAGE_SIZE]; + this->flash_storage_ = new uint32_t[ESP8266_FLASH_STORAGE_SIZE]; // NOLINT ESP_LOGVV(TAG, "Loading preferences from flash..."); { @@ -234,7 +229,7 @@ bool ESPPreferenceObject::save_internal_() { char key[32]; sprintf(key, "%u", this->offset_); uint32_t len = (this->length_words_ + 1) * 4; - esp_err_t err = nvs_set_blob(global_preferences.nvs_handle_, key, this->data_, len); + esp_err_t err = nvs_set_blob(global_preferences.nvs_handle_, key, this->data_.data(), len); if (err) { ESP_LOGV(TAG, "nvs_set_blob('%s', len=%u) failed: %s", key, len, esp_err_to_name(err)); return false; @@ -264,7 +259,7 @@ bool ESPPreferenceObject::load_internal_() { ESP_LOGVV(TAG, "NVS length does not match. Assuming key changed (%u!=%u)", actual_len, len); return false; } - err = nvs_get_blob(global_preferences.nvs_handle_, key, this->data_, &len); + err = nvs_get_blob(global_preferences.nvs_handle_, key, this->data_.data(), &len); if (err) { ESP_LOGV(TAG, "nvs_get_blob('%s') failed: %s", key, esp_err_to_name(err)); return false; @@ -301,7 +296,7 @@ uint32_t ESPPreferenceObject::calculate_crc_() const { } return crc; } -bool ESPPreferenceObject::is_initialized() const { return this->data_ != nullptr; } +bool ESPPreferenceObject::is_initialized() const { return !this->data_.empty(); } ESPPreferences global_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) diff --git a/esphome/core/preferences.h b/esphome/core/preferences.h index 699871ca00..7ed9a2c8c5 100644 --- a/esphome/core/preferences.h +++ b/esphome/core/preferences.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include #include #include "esphome/core/defines.h" @@ -9,7 +11,7 @@ namespace esphome { class ESPPreferenceObject { public: - ESPPreferenceObject(); + ESPPreferenceObject() = default; ESPPreferenceObject(size_t offset, size_t length, uint32_t type); template bool save(T *src); @@ -28,12 +30,12 @@ class ESPPreferenceObject { uint32_t calculate_crc_() const; - size_t offset_; - size_t length_words_; - uint32_t type_; - uint32_t *data_; + size_t offset_ = 0; + size_t length_words_ = 0; + uint32_t type_ = 0; + std::vector data_; #ifdef ARDUINO_ARCH_ESP8266 - bool in_flash_{false}; + bool in_flash_ = false; #endif }; @@ -92,17 +94,18 @@ template ESPPreferenceObject ESPPreferences::make_preference(uint32_ template bool ESPPreferenceObject::save(T *src) { if (!this->is_initialized()) return false; - memset(this->data_, 0, this->length_words_ * 4); - memcpy(this->data_, src, sizeof(T)); + // ensure all bytes are 0 (in case sizeof(T) is not multiple of 4) + std::fill_n(data_.begin(), length_words_, 0); + memcpy(data_.data(), src, sizeof(T)); return this->save_(); } template bool ESPPreferenceObject::load(T *dest) { - memset(this->data_, 0, this->length_words_ * 4); + std::fill_n(data_.begin(), length_words_, 0); if (!this->load_()) return false; - memcpy(dest, this->data_, sizeof(T)); + memcpy(dest, data_.data(), sizeof(T)); return true; } diff --git a/tests/dummy_main.cpp b/tests/dummy_main.cpp index c3b192d15f..f9ba868096 100644 --- a/tests/dummy_main.cpp +++ b/tests/dummy_main.cpp @@ -13,23 +13,23 @@ using namespace esphome; void setup() { App.pre_setup("livingroom", __DATE__ ", " __TIME__, false); - auto *log = new logger::Logger(115200, 512, logger::UART_SELECTION_UART0); + auto *log = new logger::Logger(115200, 512, logger::UART_SELECTION_UART0); // NOLINT log->pre_setup(); App.register_component(log); - auto *wifi = new wifi::WiFiComponent(); + auto *wifi = new wifi::WiFiComponent(); // NOLINT App.register_component(wifi); wifi::WiFiAP ap; ap.set_ssid("Test SSID"); ap.set_password("password1"); wifi->add_sta(ap); - auto *ota = new ota::OTAComponent(); + auto *ota = new ota::OTAComponent(); // NOLINT ota->set_port(8266); - auto *gpio = new gpio::GPIOSwitch(); + auto *gpio = new gpio::GPIOSwitch(); // NOLINT gpio->set_name("GPIO Switch"); - gpio->set_pin(new GPIOPin(8, OUTPUT, false)); + gpio->set_pin(new GPIOPin(8, OUTPUT, false)); // NOLINT App.register_component(gpio); App.register_switch(gpio); From aad03f1bf59e4e812109a6311422411cf748affd Mon Sep 17 00:00:00 2001 From: Tercio Filho Date: Mon, 13 Sep 2021 10:36:01 -0300 Subject: [PATCH 018/207] Fix issue #2054. PZEM004T Component doesn't set the module address. (#1784) --- esphome/components/pzem004t/pzem004t.cpp | 8 ++++++++ esphome/components/pzem004t/pzem004t.h | 2 ++ 2 files changed, 10 insertions(+) diff --git a/esphome/components/pzem004t/pzem004t.cpp b/esphome/components/pzem004t/pzem004t.cpp index 969ce8fa10..e5418765bd 100644 --- a/esphome/components/pzem004t/pzem004t.cpp +++ b/esphome/components/pzem004t/pzem004t.cpp @@ -6,6 +6,14 @@ namespace pzem004t { static const char *const TAG = "pzem004t"; +void PZEM004T::setup() { + // Clear UART buffer + while (this->available()) + this->read(); + // Set module address + this->write_state_(SET_ADDRESS); +} + void PZEM004T::loop() { const uint32_t now = millis(); if (now - this->last_read_ > 500 && this->available() < 7) { diff --git a/esphome/components/pzem004t/pzem004t.h b/esphome/components/pzem004t/pzem004t.h index 517b81eb21..f4f9f29b4d 100644 --- a/esphome/components/pzem004t/pzem004t.h +++ b/esphome/components/pzem004t/pzem004t.h @@ -14,6 +14,8 @@ class PZEM004T : public PollingComponent, public uart::UARTDevice { void set_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; } void set_energy_sensor(sensor::Sensor *energy_sensor) { energy_sensor_ = energy_sensor; } + void setup() override; + void loop() override; void update() override; From fe47ddc27a295da15e8c6df9c262ba1fba5d27e0 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Mon, 13 Sep 2021 16:39:35 +0200 Subject: [PATCH 019/207] Convert st7735.h to use LF line endings (#2287) --- esphome/components/st7735/st7735.h | 174 ++++++++++++++--------------- 1 file changed, 87 insertions(+), 87 deletions(-) diff --git a/esphome/components/st7735/st7735.h b/esphome/components/st7735/st7735.h index 737170e99b..0eb4ccadd8 100644 --- a/esphome/components/st7735/st7735.h +++ b/esphome/components/st7735/st7735.h @@ -1,87 +1,87 @@ -#pragma once - -#include "esphome/core/component.h" -#include "esphome/components/spi/spi.h" -#include "esphome/components/display/display_buffer.h" - -namespace esphome { -namespace st7735 { - -static const uint8_t ST7735_TFTWIDTH_128 = 128; // for 1.44 and mini^M -static const uint8_t ST7735_TFTWIDTH_80 = 80; // for mini^M -static const uint8_t ST7735_TFTHEIGHT_128 = 128; // for 1.44" display^M -static const uint8_t ST7735_TFTHEIGHT_160 = 160; // for 1.8" and mini display^M - -// some flags for initR() :( -static const uint8_t INITR_GREENTAB = 0x00; -static const uint8_t INITR_REDTAB = 0x01; -static const uint8_t INITR_BLACKTAB = 0x02; -static const uint8_t INITR_144GREENTAB = 0x01; -static const uint8_t INITR_MINI_160X80 = 0x04; -static const uint8_t INITR_HALLOWING = 0x05; -static const uint8_t INITR_18GREENTAB = INITR_GREENTAB; -static const uint8_t INITR_18REDTAB = INITR_REDTAB; -static const uint8_t INITR_18BLACKTAB = INITR_BLACKTAB; - -enum ST7735Model { - ST7735_INITR_GREENTAB = INITR_GREENTAB, - ST7735_INITR_REDTAB = INITR_REDTAB, - ST7735_INITR_BLACKTAB = INITR_BLACKTAB, - ST7735_INITR_MINI_160X80 = INITR_MINI_160X80, - ST7735_INITR_18BLACKTAB = INITR_18BLACKTAB, - ST7735_INITR_18REDTAB = INITR_18REDTAB -}; - -class ST7735 : public PollingComponent, - public display::DisplayBuffer, - public spi::SPIDevice { - public: - ST7735(ST7735Model model, int width, int height, int colstart, int rowstart, bool eightbitcolor, bool usebgr); - void dump_config() override; - void setup() override; - - void display(); - - void update() override; - - void set_model(ST7735Model model) { this->model_ = model; } - float get_setup_priority() const override { return setup_priority::PROCESSOR; } - - void set_reset_pin(GPIOPin *value) { this->reset_pin_ = value; } - void set_dc_pin(GPIOPin *value) { dc_pin_ = value; } - size_t get_buffer_length(); - - protected: - void sendcommand_(uint8_t cmd, const uint8_t *data_bytes, uint8_t num_data_bytes); - void senddata_(const uint8_t *data_bytes, uint8_t num_data_bytes); - - void writecommand_(uint8_t value); - void writedata_(uint8_t value); - - void write_display_data_(); - - void init_reset_(); - void display_init_(const uint8_t *addr); - void set_addr_window_(uint16_t x, uint16_t y, uint16_t w, uint16_t h); - void draw_absolute_pixel_internal(int x, int y, Color color) override; - void spi_master_write_addr_(uint16_t addr1, uint16_t addr2); - void spi_master_write_color_(uint16_t color, uint16_t size); - - int get_width_internal() override; - int get_height_internal() override; - - const char *model_str_(); - - ST7735Model model_{ST7735_INITR_18BLACKTAB}; - uint8_t colstart_ = 0, rowstart_ = 0; - bool eightbitcolor_ = false; - bool usebgr_ = false; - int16_t width_ = 80, height_ = 80; // Watch heap size - - GPIOPin *reset_pin_{nullptr}; - GPIOPin *dc_pin_{nullptr}; -}; - -} // namespace st7735 -} // namespace esphome +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/spi/spi.h" +#include "esphome/components/display/display_buffer.h" + +namespace esphome { +namespace st7735 { + +static const uint8_t ST7735_TFTWIDTH_128 = 128; // for 1.44 and mini^M +static const uint8_t ST7735_TFTWIDTH_80 = 80; // for mini^M +static const uint8_t ST7735_TFTHEIGHT_128 = 128; // for 1.44" display^M +static const uint8_t ST7735_TFTHEIGHT_160 = 160; // for 1.8" and mini display^M + +// some flags for initR() :( +static const uint8_t INITR_GREENTAB = 0x00; +static const uint8_t INITR_REDTAB = 0x01; +static const uint8_t INITR_BLACKTAB = 0x02; +static const uint8_t INITR_144GREENTAB = 0x01; +static const uint8_t INITR_MINI_160X80 = 0x04; +static const uint8_t INITR_HALLOWING = 0x05; +static const uint8_t INITR_18GREENTAB = INITR_GREENTAB; +static const uint8_t INITR_18REDTAB = INITR_REDTAB; +static const uint8_t INITR_18BLACKTAB = INITR_BLACKTAB; + +enum ST7735Model { + ST7735_INITR_GREENTAB = INITR_GREENTAB, + ST7735_INITR_REDTAB = INITR_REDTAB, + ST7735_INITR_BLACKTAB = INITR_BLACKTAB, + ST7735_INITR_MINI_160X80 = INITR_MINI_160X80, + ST7735_INITR_18BLACKTAB = INITR_18BLACKTAB, + ST7735_INITR_18REDTAB = INITR_18REDTAB +}; + +class ST7735 : public PollingComponent, + public display::DisplayBuffer, + public spi::SPIDevice { + public: + ST7735(ST7735Model model, int width, int height, int colstart, int rowstart, bool eightbitcolor, bool usebgr); + void dump_config() override; + void setup() override; + + void display(); + + void update() override; + + void set_model(ST7735Model model) { this->model_ = model; } + float get_setup_priority() const override { return setup_priority::PROCESSOR; } + + void set_reset_pin(GPIOPin *value) { this->reset_pin_ = value; } + void set_dc_pin(GPIOPin *value) { dc_pin_ = value; } + size_t get_buffer_length(); + + protected: + void sendcommand_(uint8_t cmd, const uint8_t *data_bytes, uint8_t num_data_bytes); + void senddata_(const uint8_t *data_bytes, uint8_t num_data_bytes); + + void writecommand_(uint8_t value); + void writedata_(uint8_t value); + + void write_display_data_(); + + void init_reset_(); + void display_init_(const uint8_t *addr); + void set_addr_window_(uint16_t x, uint16_t y, uint16_t w, uint16_t h); + void draw_absolute_pixel_internal(int x, int y, Color color) override; + void spi_master_write_addr_(uint16_t addr1, uint16_t addr2); + void spi_master_write_color_(uint16_t color, uint16_t size); + + int get_width_internal() override; + int get_height_internal() override; + + const char *model_str_(); + + ST7735Model model_{ST7735_INITR_18BLACKTAB}; + uint8_t colstart_ = 0, rowstart_ = 0; + bool eightbitcolor_ = false; + bool usebgr_ = false; + int16_t width_ = 80, height_ = 80; // Watch heap size + + GPIOPin *reset_pin_{nullptr}; + GPIOPin *dc_pin_{nullptr}; +}; + +} // namespace st7735 +} // namespace esphome From 133a17d6eb3b8b94d6bc0d6d3313d7f7103d3585 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Mon, 13 Sep 2021 16:55:01 +0200 Subject: [PATCH 020/207] Add esphal.h include to inkplate6 component (#2286) --- esphome/components/inkplate6/inkplate.h | 1 + tests/test5.yaml | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/esphome/components/inkplate6/inkplate.h b/esphome/components/inkplate6/inkplate.h index 94d14b4f6e..f2821b22a9 100644 --- a/esphome/components/inkplate6/inkplate.h +++ b/esphome/components/inkplate6/inkplate.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/esphal.h" #include "esphome/components/i2c/i2c.h" #include "esphome/components/display/display_buffer.h" diff --git a/tests/test5.yaml b/tests/test5.yaml index 5d4ff025d9..84d4a5a44e 100644 --- a/tests/test5.yaml +++ b/tests/test5.yaml @@ -28,6 +28,8 @@ uart: rx_pin: 16 baud_rate: 19200 +i2c: + modbus: uart_id: uart1 @@ -149,3 +151,20 @@ sensor: uart_id: uart2 co2: name: CO2 Sensor + +display: + - platform: inkplate6 + id: inkplate_display + greyscale: false + partial_updating: false + update_interval: 60s + + ckv_pin: GPIO1 + sph_pin: GPIO1 + gmod_pin: GPIO1 + gpio0_enable_pin: GPIO1 + oe_pin: GPIO1 + spv_pin: GPIO1 + powerup_pin: GPIO1 + wakeup_pin: GPIO1 + vcom_pin: GPIO1 From a2d2863c72edf6d5f04a8291fdb5a47349149d63 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 13 Sep 2021 17:22:24 +0200 Subject: [PATCH 021/207] Revert "Bump tzlocal from 2.1 to 3.0 (#2154)" (#2289) This reverts commit e0cff214b215ab2cd4dfb221b37d0f56dbce141b. --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 7de02d8ccb..daaf86e641 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ paho-mqtt==1.5.1 colorama==0.4.4 tornado==6.1 protobuf==3.17.3 -tzlocal==3.0 +tzlocal==2.1 pytz==2021.1 pyserial==3.5 ifaddr==0.1.7 From 40c474cd832f736b191361e01a20165fbb0fc36b Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Mon, 13 Sep 2021 18:11:27 +0200 Subject: [PATCH 022/207] Run clang-tidy against ESP32 (#2147) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: Otto winter --- .clang-tidy | 2 +- .github/workflows/ci.yml | 21 ++++++---- esphome/components/ac_dimmer/ac_dimmer.cpp | 2 +- .../airthings_ble/airthings_listener.cpp | 2 +- .../airthings_wave_plus.cpp | 30 ++++++------- .../airthings_wave_plus/airthings_wave_plus.h | 32 +++++++------- esphome/components/am43/am43.cpp | 6 +-- esphome/components/am43/am43.h | 4 +- esphome/components/am43/cover/am43_cover.cpp | 6 +-- esphome/components/am43/cover/am43_cover.h | 4 +- esphome/components/anova/anova.cpp | 6 +-- esphome/components/anova/anova.h | 2 +- esphome/components/api/api_connection.cpp | 2 +- esphome/components/api/api_server.cpp | 11 ++--- .../atc_mithermometer/atc_mithermometer.cpp | 12 ++---- .../atc_mithermometer/atc_mithermometer.h | 1 - esphome/components/ble_client/ble_client.cpp | 16 +++---- esphome/components/esp32_ble/ble.cpp | 18 ++++---- .../components/esp32_ble/ble_advertising.cpp | 1 + esphome/components/esp32_ble/ble_uuid.cpp | 42 +++++++++---------- .../esp32_ble_beacon/esp32_ble_beacon.cpp | 7 ++-- .../esp32_ble_server/ble_characteristic.cpp | 2 +- .../esp32_ble_server/ble_descriptor.cpp | 4 +- .../esp32_ble_server/ble_server.cpp | 4 +- .../esp32_ble_server/ble_service.cpp | 3 +- .../esp32_ble_tracker/esp32_ble_tracker.cpp | 12 +++--- .../components/esp32_camera/esp32_camera.cpp | 4 +- .../esp32_improv/esp32_improv_component.cpp | 10 ++--- .../components/esp8266_pwm/esp8266_pwm.cpp | 4 ++ esphome/components/esp8266_pwm/esp8266_pwm.h | 4 ++ .../ethernet/ethernet_component.cpp | 8 ++-- .../components/ethernet/ethernet_component.h | 2 +- esphome/components/i2c/i2c.cpp | 11 ++--- esphome/components/i2c/i2c.h | 4 -- .../inkbird_ibsth1_mini.cpp | 24 +++++------ esphome/components/inkplate6/inkplate.cpp | 16 ++++--- esphome/components/ledc/ledc_output.cpp | 2 +- esphome/components/mqtt/mqtt_client.cpp | 4 +- esphome/components/nextion/nextion_upload.cpp | 8 +--- .../pulse_counter/pulse_counter_sensor.cpp | 7 +--- .../pulse_counter/pulse_counter_sensor.h | 4 -- .../pvvx_mithermometer/pvvx_mithermometer.cpp | 14 ++----- .../pvvx_mithermometer/pvvx_mithermometer.h | 1 - .../components/remote_base/rc5_protocol.cpp | 6 +-- .../components/socket/bsd_sockets_impl.cpp | 8 ++-- esphome/components/spi/spi.cpp | 2 +- esphome/components/st7735/st7735.cpp | 20 ++++----- esphome/components/st7789v/st7789v.cpp | 20 ++++----- esphome/components/uart/uart.h | 4 -- esphome/components/uart/uart_esp32.cpp | 6 +-- .../components/wifi/wifi_component_esp32.cpp | 36 ++++++++-------- .../wifi/wifi_component_esp8266.cpp | 6 +-- esphome/components/xiaomi_ble/xiaomi_ble.cpp | 16 +++---- .../components/xiaomi_cgd1/xiaomi_cgd1.cpp | 8 +--- .../components/xiaomi_cgdk2/xiaomi_cgdk2.cpp | 8 +--- .../components/xiaomi_cgg1/xiaomi_cgg1.cpp | 8 +--- .../components/xiaomi_cgpr1/xiaomi_cgpr1.cpp | 10 ++--- .../xiaomi_gcls002/xiaomi_gcls002.cpp | 6 +-- .../xiaomi_hhccjcy01/xiaomi_hhccjcy01.cpp | 6 +-- .../xiaomi_hhccpot002/xiaomi_hhccpot002.cpp | 6 +-- .../xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.cpp | 6 +-- .../xiaomi_lywsd02/xiaomi_lywsd02.cpp | 6 +-- .../xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp | 8 +--- .../xiaomi_lywsdcgq/xiaomi_lywsdcgq.cpp | 6 +-- .../xiaomi_mhoc401/xiaomi_mhoc401.cpp | 8 +--- .../xiaomi_miscale2/xiaomi_miscale2.cpp | 10 ++--- .../xiaomi_mjyd02yla/xiaomi_mjyd02yla.cpp | 8 +--- .../xiaomi_mue4094rt/xiaomi_mue4094rt.cpp | 6 +-- .../xiaomi_wx08zm/xiaomi_wx08zm.cpp | 6 +-- esphome/core/application.cpp | 6 +-- esphome/core/defines.h | 5 +-- platformio.ini | 1 - script/clang-tidy | 14 +++++-- script/helpers.py | 10 +++++ 74 files changed, 291 insertions(+), 364 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 597065d50b..8451a2bdd4 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -124,7 +124,7 @@ CheckOptions: - key: readability-identifier-naming.StaticConstantCase value: 'UPPER_CASE' - key: readability-identifier-naming.StaticVariableCase - value: 'UPPER_CASE' + value: 'lower_case' - key: readability-identifier-naming.GlobalConstantCase value: 'UPPER_CASE' - key: readability-identifier-naming.ParameterCase diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d7a841f84c..eb93c36d19 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,17 +19,20 @@ jobs: - id: clang-format name: Run script/clang-format - id: clang-tidy - name: Run script/clang-tidy 1/4 - split: 1 + name: Run script/clang-tidy for ESP8266 + options: --environment esp8266-tidy --grep ARDUINO_ARCH_ESP8266 - id: clang-tidy - name: Run script/clang-tidy 2/4 - split: 2 + name: Run script/clang-tidy for ESP32 1/4 + options: --environment esp32-tidy --split-num 4 --split-at 1 - id: clang-tidy - name: Run script/clang-tidy 3/4 - split: 3 + name: Run script/clang-tidy for ESP32 2/4 + options: --environment esp32-tidy --split-num 4 --split-at 2 - id: clang-tidy - name: Run script/clang-tidy 4/4 - split: 4 + name: Run script/clang-tidy for ESP32 3/4 + options: --environment esp32-tidy --split-num 4 --split-at 3 + - id: clang-tidy + name: Run script/clang-tidy for ESP32 4/4 + options: --environment esp32-tidy --split-num 4 --split-at 4 # cpp lint job runs with esphome-lint docker image so that clang-format-* # doesn't have to be installed @@ -51,7 +54,7 @@ jobs: if: ${{ matrix.id == 'clang-format' }} - name: Run clang-tidy - run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }} + run: script/clang-tidy --all-headers --fix ${{ matrix.options }} if: ${{ matrix.id == 'clang-tidy' }} - name: Suggested changes diff --git a/esphome/components/ac_dimmer/ac_dimmer.cpp b/esphome/components/ac_dimmer/ac_dimmer.cpp index 7e9251ddf3..9a71f0e224 100644 --- a/esphome/components/ac_dimmer/ac_dimmer.cpp +++ b/esphome/components/ac_dimmer/ac_dimmer.cpp @@ -141,7 +141,7 @@ void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store #ifdef ARDUINO_ARCH_ESP32 // ESP32 implementation, uses basically the same code but needs to wrap // timer_interrupt() function to auto-reschedule -static hw_timer_t *dimmer_timer = nullptr; +static hw_timer_t *dimmer_timer = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_timer_intr() { timer_interrupt(); } #endif diff --git a/esphome/components/airthings_ble/airthings_listener.cpp b/esphome/components/airthings_ble/airthings_listener.cpp index 921e42c498..a843bcb145 100644 --- a/esphome/components/airthings_ble/airthings_listener.cpp +++ b/esphome/components/airthings_ble/airthings_listener.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace airthings_ble { -static const char *TAG = "airthings_ble"; +static const char *const TAG = "airthings_ble"; bool AirthingsListener::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { for (auto &it : device.get_manufacturer_datas()) { diff --git a/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp b/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp index 6b2e807e0b..6b4780726d 100644 --- a/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp +++ b/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp @@ -5,6 +5,8 @@ namespace esphome { namespace airthings_wave_plus { +static const char *const TAG = "airthings_wave_plus"; + void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { switch (event) { @@ -21,15 +23,15 @@ void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt } case ESP_GATTC_SEARCH_CMPL_EVT: { - this->handle = 0; - auto chr = this->parent()->get_characteristic(service_uuid, sensors_data_characteristic_uuid); + this->handle_ = 0; + auto chr = this->parent()->get_characteristic(service_uuid_, sensors_data_characteristic_uuid_); if (chr == nullptr) { - ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", service_uuid.to_string().c_str(), - sensors_data_characteristic_uuid.to_string().c_str()); + ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", service_uuid_.to_string().c_str(), + sensors_data_characteristic_uuid_.to_string().c_str()); break; } - this->handle = chr->handle; - this->node_state = espbt::ClientState::Established; + this->handle_ = chr->handle; + this->node_state = esp32_ble_tracker::ClientState::Established; request_read_values_(); break; @@ -42,7 +44,7 @@ void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status); break; } - if (param->read.handle == this->handle) { + if (param->read.handle == this->handle_) { read_sensors_(param->read.value, param->read.value_len); } break; @@ -88,16 +90,16 @@ void AirthingsWavePlus::read_sensors_(uint8_t *raw_value, uint16_t value_len) { } } -bool AirthingsWavePlus::is_valid_radon_value_(short radon) { return 0 <= radon && radon <= 16383; } +bool AirthingsWavePlus::is_valid_radon_value_(uint16_t radon) { return 0 <= radon && radon <= 16383; } -bool AirthingsWavePlus::is_valid_voc_value_(short voc) { return 0 <= voc && voc <= 16383; } +bool AirthingsWavePlus::is_valid_voc_value_(uint16_t voc) { return 0 <= voc && voc <= 16383; } -bool AirthingsWavePlus::is_valid_co2_value_(short co2) { return 0 <= co2 && co2 <= 16383; } +bool AirthingsWavePlus::is_valid_co2_value_(uint16_t co2) { return 0 <= co2 && co2 <= 16383; } void AirthingsWavePlus::loop() {} void AirthingsWavePlus::update() { - if (this->node_state != espbt::ClientState::Established) { + if (this->node_state != esp32_ble_tracker::ClientState::Established) { if (!parent()->enabled) { ESP_LOGW(TAG, "Reconnecting to device"); parent()->set_enabled(true); @@ -110,7 +112,7 @@ void AirthingsWavePlus::update() { void AirthingsWavePlus::request_read_values_() { auto status = - esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle, ESP_GATT_AUTH_REQ_NONE); + esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle_, ESP_GATT_AUTH_REQ_NONE); if (status) { ESP_LOGW(TAG, "Error sending read request for sensor, status=%d", status); } @@ -130,8 +132,8 @@ AirthingsWavePlus::AirthingsWavePlus() : PollingComponent(10000) { auto service_bt = *BLEUUID::fromString(std::string("b42e1c08-ade7-11e4-89d3-123b93f75cba")).getNative(); auto characteristic_bt = *BLEUUID::fromString(std::string("b42e2a68-ade7-11e4-89d3-123b93f75cba")).getNative(); - service_uuid = espbt::ESPBTUUID::from_uuid(service_bt); - sensors_data_characteristic_uuid = espbt::ESPBTUUID::from_uuid(characteristic_bt); + service_uuid_ = esp32_ble_tracker::ESPBTUUID::from_uuid(service_bt); + sensors_data_characteristic_uuid_ = esp32_ble_tracker::ESPBTUUID::from_uuid(characteristic_bt); } void AirthingsWavePlus::setup() {} diff --git a/esphome/components/airthings_wave_plus/airthings_wave_plus.h b/esphome/components/airthings_wave_plus/airthings_wave_plus.h index 18d7fe60d2..88d7a7a01b 100644 --- a/esphome/components/airthings_wave_plus/airthings_wave_plus.h +++ b/esphome/components/airthings_wave_plus/airthings_wave_plus.h @@ -1,25 +1,21 @@ #pragma once +#ifdef ARDUINO_ARCH_ESP32 + +#include +#include +#include +#include #include "esphome/core/component.h" +#include "esphome/core/log.h" #include "esphome/components/ble_client/ble_client.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/sensor/sensor.h" -#include "esphome/core/log.h" -#include -#include - -#ifdef ARDUINO_ARCH_ESP32 -#include -#include - -using namespace esphome::ble_client; namespace esphome { namespace airthings_wave_plus { -static const char *TAG = "airthings_wave_plus"; - -class AirthingsWavePlus : public PollingComponent, public BLEClientNode { +class AirthingsWavePlus : public PollingComponent, public ble_client::BLEClientNode { public: AirthingsWavePlus(); @@ -40,9 +36,9 @@ class AirthingsWavePlus : public PollingComponent, public BLEClientNode { void set_tvoc(sensor::Sensor *tvoc) { tvoc_sensor_ = tvoc; } protected: - bool is_valid_radon_value_(short radon); - bool is_valid_voc_value_(short voc); - bool is_valid_co2_value_(short co2); + bool is_valid_radon_value_(uint16_t radon); + bool is_valid_voc_value_(uint16_t voc); + bool is_valid_co2_value_(uint16_t co2); void read_sensors_(uint8_t *value, uint16_t value_len); void request_read_values_(); @@ -55,9 +51,9 @@ class AirthingsWavePlus : public PollingComponent, public BLEClientNode { sensor::Sensor *co2_sensor_{nullptr}; sensor::Sensor *tvoc_sensor_{nullptr}; - uint16_t handle; - espbt::ESPBTUUID service_uuid; - espbt::ESPBTUUID sensors_data_characteristic_uuid; + uint16_t handle_; + esp32_ble_tracker::ESPBTUUID service_uuid_; + esp32_ble_tracker::ESPBTUUID sensors_data_characteristic_uuid_; struct WavePlusReadings { uint8_t version; diff --git a/esphome/components/am43/am43.cpp b/esphome/components/am43/am43.cpp index e60bb2474c..94be194a1d 100644 --- a/esphome/components/am43/am43.cpp +++ b/esphome/components/am43/am43.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace am43 { -static const char *TAG = "am43"; +static const char *const TAG = "am43"; void Am43::dump_config() { ESP_LOGCONFIG(TAG, "AM43"); @@ -15,8 +15,8 @@ void Am43::dump_config() { } void Am43::setup() { - this->encoder_ = new Am43Encoder(); - this->decoder_ = new Am43Decoder(); + this->encoder_ = make_unique(); + this->decoder_ = make_unique(); this->logged_in_ = false; this->last_battery_update_ = 0; this->current_sensor_ = 0; diff --git a/esphome/components/am43/am43.h b/esphome/components/am43/am43.h index 460bb601b2..025d075b68 100644 --- a/esphome/components/am43/am43.h +++ b/esphome/components/am43/am43.h @@ -28,8 +28,8 @@ class Am43 : public esphome::ble_client::BLEClientNode, public PollingComponent protected: uint16_t char_handle_; - Am43Encoder *encoder_; - Am43Decoder *decoder_; + std::unique_ptr encoder_; + std::unique_ptr decoder_; bool logged_in_; sensor::Sensor *battery_{nullptr}; sensor::Sensor *illuminance_{nullptr}; diff --git a/esphome/components/am43/cover/am43_cover.cpp b/esphome/components/am43/cover/am43_cover.cpp index 3ae7fe8f8c..1067ce9ebb 100644 --- a/esphome/components/am43/cover/am43_cover.cpp +++ b/esphome/components/am43/cover/am43_cover.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace am43 { -static const char *TAG = "am43_cover"; +static const char *const TAG = "am43_cover"; using namespace esphome::cover; @@ -18,8 +18,8 @@ void Am43Component::dump_config() { void Am43Component::setup() { this->position = COVER_OPEN; - this->encoder_ = new Am43Encoder(); - this->decoder_ = new Am43Decoder(); + this->encoder_ = make_unique(); + this->decoder_ = make_unique(); this->logged_in_ = false; } diff --git a/esphome/components/am43/cover/am43_cover.h b/esphome/components/am43/cover/am43_cover.h index 5557da49e7..dba1391b63 100644 --- a/esphome/components/am43/cover/am43_cover.h +++ b/esphome/components/am43/cover/am43_cover.h @@ -32,8 +32,8 @@ class Am43Component : public cover::Cover, public esphome::ble_client::BLEClient uint16_t char_handle_; uint16_t pin_; bool invert_position_; - Am43Encoder *encoder_; - Am43Decoder *decoder_; + std::unique_ptr encoder_; + std::unique_ptr decoder_; bool logged_in_; float position_; diff --git a/esphome/components/anova/anova.cpp b/esphome/components/anova/anova.cpp index f330969c33..d0ea818669 100644 --- a/esphome/components/anova/anova.cpp +++ b/esphome/components/anova/anova.cpp @@ -6,14 +6,14 @@ namespace esphome { namespace anova { -static const char *TAG = "anova"; +static const char *const TAG = "anova"; using namespace esphome::climate; void Anova::dump_config() { LOG_CLIMATE("", "Anova BLE Cooker", this); } void Anova::setup() { - this->codec_ = new AnovaCodec(); + this->codec_ = make_unique(); this->current_request_ = 0; } @@ -135,7 +135,7 @@ void Anova::update() { if (this->current_request_ < 2) { auto pkt = this->codec_->get_read_device_status_request(); if (this->current_request_ == 0) - auto pkt = this->codec_->get_set_unit_request(this->fahrenheit_ ? 'f' : 'c'); + this->codec_->get_set_unit_request(this->fahrenheit_ ? 'f' : 'c'); auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); if (status) diff --git a/esphome/components/anova/anova.h b/esphome/components/anova/anova.h index 42bdbcaed0..e033513013 100644 --- a/esphome/components/anova/anova.h +++ b/esphome/components/anova/anova.h @@ -39,7 +39,7 @@ class Anova : public climate::Climate, public esphome::ble_client::BLEClientNode void set_unit_of_measurement(const char *); protected: - AnovaCodec *codec_; + std::unique_ptr codec_; void control(const climate::ClimateCall &call) override; uint16_t char_handle_; uint8_t current_request_; diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index ea3be42275..a530554155 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -633,7 +633,7 @@ void APIConnection::send_camera_state(std::shared_ptr return; if (this->image_reader_.available()) return; - this->image_reader_.set_image(image); + this->image_reader_.set_image(std::move(image)); } bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) { ListEntitiesCameraResponse msg; diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index 70f82c59db..1ef567ab57 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -76,11 +76,12 @@ void APIServer::setup() { #ifdef USE_ESP32_CAMERA if (esp32_camera::global_esp32_camera != nullptr) { - esp32_camera::global_esp32_camera->add_image_callback([this](std::shared_ptr image) { - for (auto &c : this->clients_) - if (!c->remove_) - c->send_camera_state(image); - }); + esp32_camera::global_esp32_camera->add_image_callback( + [this](const std::shared_ptr &image) { + for (auto &c : this->clients_) + if (!c->remove_) + c->send_camera_state(image); + }); } #endif } diff --git a/esphome/components/atc_mithermometer/atc_mithermometer.cpp b/esphome/components/atc_mithermometer/atc_mithermometer.cpp index 5656cdf430..1dba259143 100644 --- a/esphome/components/atc_mithermometer/atc_mithermometer.cpp +++ b/esphome/components/atc_mithermometer/atc_mithermometer.cpp @@ -26,7 +26,7 @@ bool ATCMiThermometer::parse_device(const esp32_ble_tracker::ESPBTDevice &device bool success = false; for (auto &service_data : device.get_service_datas()) { auto res = parse_header(service_data); - if (res->is_duplicate) { + if (!res.has_value()) { continue; } if (!(parse_message(service_data.data, *res))) { @@ -46,11 +46,7 @@ bool ATCMiThermometer::parse_device(const esp32_ble_tracker::ESPBTDevice &device success = true; } - if (!success) { - return false; - } - - return true; + return success; } optional ATCMiThermometer::parse_header(const esp32_ble_tracker::ServiceData &service_data) { @@ -64,12 +60,10 @@ optional ATCMiThermometer::parse_header(const esp32_ble_tracker::Se static uint8_t last_frame_count = 0; if (last_frame_count == raw[12]) { - ESP_LOGVV(TAG, "parse_header(): duplicate data packet received (%d).", static_cast(last_frame_count)); - result.is_duplicate = true; + ESP_LOGVV(TAG, "parse_header(): duplicate data packet received (%hhu).", last_frame_count); return {}; } last_frame_count = raw[12]; - result.is_duplicate = false; return result; } diff --git a/esphome/components/atc_mithermometer/atc_mithermometer.h b/esphome/components/atc_mithermometer/atc_mithermometer.h index 203dca3200..eff14811be 100644 --- a/esphome/components/atc_mithermometer/atc_mithermometer.h +++ b/esphome/components/atc_mithermometer/atc_mithermometer.h @@ -14,7 +14,6 @@ struct ParseResult { optional humidity; optional battery_level; optional battery_voltage; - bool is_duplicate; int raw_offset; }; diff --git a/esphome/components/ble_client/ble_client.cpp b/esphome/components/ble_client/ble_client.cpp index 956848b73d..0398113f83 100644 --- a/esphome/components/ble_client/ble_client.cpp +++ b/esphome/components/ble_client/ble_client.cpp @@ -130,7 +130,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es break; } ESP_LOGV(TAG, "cfg_mtu status %d, mtu %d", param->cfg_mtu.status, param->cfg_mtu.mtu); - esp_ble_gattc_search_service(esp_gattc_if, param->cfg_mtu.conn_id, NULL); + esp_ble_gattc_search_service(esp_gattc_if, param->cfg_mtu.conn_id, nullptr); break; } case ESP_GATTC_DISCONNECT_EVT: { @@ -139,13 +139,13 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es } ESP_LOGV(TAG, "[%s] ESP_GATTC_DISCONNECT_EVT", this->address_str().c_str()); for (auto &svc : this->services_) - delete svc; + delete svc; // NOLINT(cppcoreguidelines-owning-memory) this->services_.clear(); this->set_states(espbt::ClientState::Idle); break; } case ESP_GATTC_SEARCH_RES_EVT: { - BLEService *ble_service = new BLEService(); + BLEService *ble_service = new BLEService(); // NOLINT(cppcoreguidelines-owning-memory) ble_service->uuid = espbt::ESPBTUUID::from_uuid(param->search_res.srvc_id.uuid); ble_service->start_handle = param->search_res.start_handle; ble_service->end_handle = param->search_res.end_handle; @@ -194,7 +194,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es // Delete characteristics after clients have used them to save RAM. if (!all_established && this->all_nodes_established()) { for (auto &svc : this->services_) - delete svc; + delete svc; // NOLINT(cppcoreguidelines-owning-memory) this->services_.clear(); } } @@ -307,7 +307,7 @@ BLEDescriptor *BLEClient::get_descriptor(uint16_t service, uint16_t chr, uint16_ BLEService::~BLEService() { for (auto &chr : this->characteristics) - delete chr; + delete chr; // NOLINT(cppcoreguidelines-owning-memory) } void BLEService::parse_characteristics() { @@ -329,7 +329,7 @@ void BLEService::parse_characteristics() { break; } - BLECharacteristic *characteristic = new BLECharacteristic(); + BLECharacteristic *characteristic = new BLECharacteristic(); // NOLINT(cppcoreguidelines-owning-memory) characteristic->uuid = espbt::ESPBTUUID::from_uuid(result.uuid); characteristic->properties = result.properties; characteristic->handle = result.char_handle; @@ -344,7 +344,7 @@ void BLEService::parse_characteristics() { BLECharacteristic::~BLECharacteristic() { for (auto &desc : this->descriptors) - delete desc; + delete desc; // NOLINT(cppcoreguidelines-owning-memory) } void BLECharacteristic::parse_descriptors() { @@ -366,7 +366,7 @@ void BLECharacteristic::parse_descriptors() { break; } - BLEDescriptor *desc = new BLEDescriptor(); + BLEDescriptor *desc = new BLEDescriptor(); // NOLINT(cppcoreguidelines-owning-memory) desc->uuid = espbt::ESPBTUUID::from_uuid(result.uuid); desc->handle = result.handle; desc->characteristic = this; diff --git a/esphome/components/esp32_ble/ble.cpp b/esphome/components/esp32_ble/ble.cpp index a54ee48b30..2d710a6ef7 100644 --- a/esphome/components/esp32_ble/ble.cpp +++ b/esphome/components/esp32_ble/ble.cpp @@ -27,7 +27,7 @@ void ESP32BLE::setup() { return; } - this->advertising_ = new BLEAdvertising(); + this->advertising_ = new BLEAdvertising(); // NOLINT(cppcoreguidelines-owning-memory) this->advertising_->set_scan_response(true); this->advertising_->set_min_preferred_interval(0x06); @@ -123,25 +123,25 @@ void ESP32BLE::loop() { BLEEvent *ble_event = this->ble_events_.pop(); while (ble_event != nullptr) { switch (ble_event->type_) { - case ble_event->GATTS: + case BLEEvent::GATTS: this->real_gatts_event_handler_(ble_event->event_.gatts.gatts_event, ble_event->event_.gatts.gatts_if, &ble_event->event_.gatts.gatts_param); break; - case ble_event->GAP: + case BLEEvent::GAP: this->real_gap_event_handler_(ble_event->event_.gap.gap_event, &ble_event->event_.gap.gap_param); break; default: break; } - delete ble_event; + delete ble_event; // NOLINT(cppcoreguidelines-owning-memory) ble_event = this->ble_events_.pop(); } } void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { - BLEEvent *new_event = new BLEEvent(event, param); + BLEEvent *new_event = new BLEEvent(event, param); // NOLINT(cppcoreguidelines-owning-memory) global_ble->ble_events_.push(new_event); -} +} // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) void ESP32BLE::real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { ESP_LOGV(TAG, "(BLE) gap_event_handler - %d", event); @@ -153,9 +153,9 @@ void ESP32BLE::real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap void ESP32BLE::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { - BLEEvent *new_event = new BLEEvent(event, gatts_if, param); + BLEEvent *new_event = new BLEEvent(event, gatts_if, param); // NOLINT(cppcoreguidelines-owning-memory) global_ble->ble_events_.push(new_event); -} +} // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) void ESP32BLE::real_gatts_event_handler_(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { @@ -174,7 +174,7 @@ float ESP32BLE::get_setup_priority() const { return setup_priority::BLUETOOTH; } void ESP32BLE::dump_config() { ESP_LOGCONFIG(TAG, "ESP32 BLE:"); } -ESP32BLE *global_ble = nullptr; +ESP32BLE *global_ble = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) } // namespace esp32_ble } // namespace esphome diff --git a/esphome/components/esp32_ble/ble_advertising.cpp b/esphome/components/esp32_ble/ble_advertising.cpp index 92270124dd..bcfb0d1a1b 100644 --- a/esphome/components/esp32_ble/ble_advertising.cpp +++ b/esphome/components/esp32_ble/ble_advertising.cpp @@ -45,6 +45,7 @@ void BLEAdvertising::start() { this->advertising_data_.service_uuid_len = 0; } else { this->advertising_data_.service_uuid_len = 16 * num_services; + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) this->advertising_data_.p_service_uuid = new uint8_t[this->advertising_data_.service_uuid_len]; uint8_t *p = this->advertising_data_.p_service_uuid; for (int i = 0; i < num_services; i++) { diff --git a/esphome/components/esp32_ble/ble_uuid.cpp b/esphome/components/esp32_ble/ble_uuid.cpp index 0de938d9a8..b33275110d 100644 --- a/esphome/components/esp32_ble/ble_uuid.cpp +++ b/esphome/components/esp32_ble/ble_uuid.cpp @@ -33,28 +33,28 @@ ESPBTUUID ESPBTUUID::from_raw(const std::string &data) { ret.uuid_.len = ESP_UUID_LEN_16; ret.uuid_.uuid.uuid16 = 0; for (int i = 0; i < data.length();) { - uint8_t MSB = data.c_str()[i]; - uint8_t LSB = data.c_str()[i + 1]; + uint8_t msb = data.c_str()[i]; + uint8_t lsb = data.c_str()[i + 1]; - if (MSB > '9') - MSB -= 7; - if (LSB > '9') - LSB -= 7; - ret.uuid_.uuid.uuid16 += (((MSB & 0x0F) << 4) | (LSB & 0x0F)) << (2 - i) * 4; + if (msb > '9') + msb -= 7; + if (lsb > '9') + lsb -= 7; + ret.uuid_.uuid.uuid16 += (((msb & 0x0F) << 4) | (lsb & 0x0F)) << (2 - i) * 4; i += 2; } } else if (data.length() == 8) { ret.uuid_.len = ESP_UUID_LEN_32; ret.uuid_.uuid.uuid32 = 0; for (int i = 0; i < data.length();) { - uint8_t MSB = data.c_str()[i]; - uint8_t LSB = data.c_str()[i + 1]; + uint8_t msb = data.c_str()[i]; + uint8_t lsb = data.c_str()[i + 1]; - if (MSB > '9') - MSB -= 7; - if (LSB > '9') - LSB -= 7; - ret.uuid_.uuid.uuid32 += (((MSB & 0x0F) << 4) | (LSB & 0x0F)) << (6 - i) * 4; + if (msb > '9') + msb -= 7; + if (lsb > '9') + lsb -= 7; + ret.uuid_.uuid.uuid32 += (((msb & 0x0F) << 4) | (lsb & 0x0F)) << (6 - i) * 4; i += 2; } } else if (data.length() == 16) { // how we can have 16 byte length string reprezenting 128 bit uuid??? needs to be @@ -69,14 +69,14 @@ ESPBTUUID ESPBTUUID::from_raw(const std::string &data) { for (int i = 0; i < data.length();) { if (data.c_str()[i] == '-') i++; - uint8_t MSB = data.c_str()[i]; - uint8_t LSB = data.c_str()[i + 1]; + uint8_t msb = data.c_str()[i]; + uint8_t lsb = data.c_str()[i + 1]; - if (MSB > '9') - MSB -= 7; - if (LSB > '9') - LSB -= 7; - ret.uuid_.uuid.uuid128[15 - n++] = ((MSB & 0x0F) << 4) | (LSB & 0x0F); + if (msb > '9') + msb -= 7; + if (lsb > '9') + lsb -= 7; + ret.uuid_.uuid.uuid128[15 - n++] = ((msb & 0x0F) << 4) | (lsb & 0x0F); i += 2; } } else { diff --git a/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp b/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp index 1b06fd787e..c9e00a1093 100644 --- a/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp +++ b/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp @@ -15,6 +15,7 @@ namespace esp32_ble_beacon { static const char *const TAG = "esp32_ble_beacon"; +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) static esp_ble_adv_params_t ble_adv_params = { .adv_int_min = 0x20, .adv_int_max = 0x40, @@ -28,7 +29,7 @@ static esp_ble_adv_params_t ble_adv_params = { #define ENDIAN_CHANGE_U16(x) ((((x) &0xFF00) >> 8) + (((x) &0xFF) << 8)) -static esp_ble_ibeacon_head_t ibeacon_common_head = { +static const esp_ble_ibeacon_head_t IBEACON_COMMON_HEAD = { .flags = {0x02, 0x01, 0x06}, .length = 0x1A, .type = 0xFF, .company_id = 0x004C, .beacon_type = 0x1502}; void ESP32BLEBeacon::dump_config() { @@ -90,7 +91,7 @@ void ESP32BLEBeacon::ble_setup() { } esp_ble_ibeacon_t ibeacon_adv_data; - memcpy(&ibeacon_adv_data.ibeacon_head, &ibeacon_common_head, sizeof(esp_ble_ibeacon_head_t)); + memcpy(&ibeacon_adv_data.ibeacon_head, &IBEACON_COMMON_HEAD, sizeof(esp_ble_ibeacon_head_t)); memcpy(&ibeacon_adv_data.ibeacon_vendor.proximity_uuid, global_esp32_ble_beacon->uuid_.data(), sizeof(ibeacon_adv_data.ibeacon_vendor.proximity_uuid)); ibeacon_adv_data.ibeacon_vendor.minor = ENDIAN_CHANGE_U16(global_esp32_ble_beacon->minor_); @@ -131,7 +132,7 @@ void ESP32BLEBeacon::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap } } -ESP32BLEBeacon *global_esp32_ble_beacon = nullptr; +ESP32BLEBeacon *global_esp32_ble_beacon = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) } // namespace esp32_ble_beacon } // namespace esphome diff --git a/esphome/components/esp32_ble_server/ble_characteristic.cpp b/esphome/components/esp32_ble_server/ble_characteristic.cpp index 62775ab05f..d8ff39cc94 100644 --- a/esphome/components/esp32_ble_server/ble_characteristic.cpp +++ b/esphome/components/esp32_ble_server/ble_characteristic.cpp @@ -27,7 +27,7 @@ BLECharacteristic::BLECharacteristic(const ESPBTUUID uuid, uint32_t properties) void BLECharacteristic::set_value(std::vector value) { xSemaphoreTake(this->set_value_lock_, 0L); - this->value_ = value; + this->value_ = std::move(value); xSemaphoreGive(this->set_value_lock_); } void BLECharacteristic::set_value(const std::string &value) { diff --git a/esphome/components/esp32_ble_server/ble_descriptor.cpp b/esphome/components/esp32_ble_server/ble_descriptor.cpp index a04343da3a..5262087ad1 100644 --- a/esphome/components/esp32_ble_server/ble_descriptor.cpp +++ b/esphome/components/esp32_ble_server/ble_descriptor.cpp @@ -15,10 +15,10 @@ BLEDescriptor::BLEDescriptor(ESPBTUUID uuid, uint16_t max_len) { this->uuid_ = uuid; this->value_.attr_len = 0; this->value_.attr_max_len = max_len; - this->value_.attr_value = (uint8_t *) malloc(max_len); + this->value_.attr_value = (uint8_t *) malloc(max_len); // NOLINT } -BLEDescriptor::~BLEDescriptor() { free(this->value_.attr_value); } +BLEDescriptor::~BLEDescriptor() { free(this->value_.attr_value); } // NOLINT void BLEDescriptor::do_create(BLECharacteristic *characteristic) { this->characteristic_ = characteristic; diff --git a/esphome/components/esp32_ble_server/ble_server.cpp b/esphome/components/esp32_ble_server/ble_server.cpp index f1eebf3c8a..5777e99e23 100644 --- a/esphome/components/esp32_ble_server/ble_server.cpp +++ b/esphome/components/esp32_ble_server/ble_server.cpp @@ -109,7 +109,7 @@ BLEService *BLEServer::create_service(const std::string &uuid, bool advertise) { } BLEService *BLEServer::create_service(ESPBTUUID uuid, bool advertise, uint16_t num_handles, uint8_t inst_id) { ESP_LOGV(TAG, "Creating service - %s", uuid.to_string().c_str()); - BLEService *service = new BLEService(uuid, num_handles, inst_id); + BLEService *service = new BLEService(uuid, num_handles, inst_id); // NOLINT(cppcoreguidelines-owning-memory) this->services_.push_back(service); if (advertise) { esp32_ble::global_ble->get_advertising()->add_service_uuid(uuid); @@ -158,7 +158,7 @@ float BLEServer::get_setup_priority() const { return setup_priority::BLUETOOTH - void BLEServer::dump_config() { ESP_LOGCONFIG(TAG, "ESP32 BLE Server:"); } -BLEServer *global_ble_server = nullptr; +BLEServer *global_ble_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) } // namespace esp32_ble_server } // namespace esphome diff --git a/esphome/components/esp32_ble_server/ble_service.cpp b/esphome/components/esp32_ble_server/ble_service.cpp index b7c88a436c..5cfc53a397 100644 --- a/esphome/components/esp32_ble_server/ble_service.cpp +++ b/esphome/components/esp32_ble_server/ble_service.cpp @@ -14,7 +14,7 @@ BLEService::BLEService(ESPBTUUID uuid, uint16_t num_handles, uint8_t inst_id) BLEService::~BLEService() { for (auto &chr : this->characteristics_) - delete chr; + delete chr; // NOLINT(cppcoreguidelines-owning-memory) } BLECharacteristic *BLEService::get_characteristic(ESPBTUUID uuid) { @@ -34,6 +34,7 @@ BLECharacteristic *BLEService::create_characteristic(const std::string &uuid, es return create_characteristic(ESPBTUUID::from_raw(uuid), properties); } BLECharacteristic *BLEService::create_characteristic(ESPBTUUID uuid, esp_gatt_char_prop_t properties) { + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) BLECharacteristic *characteristic = new BLECharacteristic(uuid, properties); this->characteristics_.push_back(characteristic); return characteristic; diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index e1cd3975e8..358fe50605 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -21,7 +21,7 @@ namespace esp32_ble_tracker { static const char *const TAG = "esp32_ble_tracker"; -ESP32BLETracker *global_esp32_ble_tracker = nullptr; +ESP32BLETracker *global_esp32_ble_tracker = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) uint64_t ble_addr_to_uint64(const esp_bd_addr_t address) { uint64_t u = 0; @@ -55,7 +55,7 @@ void ESP32BLETracker::loop() { &ble_event->event_.gattc.gattc_param); else this->real_gap_event_handler(ble_event->event_.gap.gap_event, &ble_event->event_.gap.gap_param); - delete ble_event; + delete ble_event; // NOLINT(cppcoreguidelines-owning-memory) ble_event = this->ble_events_.pop(); } @@ -204,9 +204,9 @@ void ESP32BLETracker::register_client(ESPBTClient *client) { } void ESP32BLETracker::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { - BLEEvent *gap_event = new BLEEvent(event, param); + BLEEvent *gap_event = new BLEEvent(event, param); // NOLINT(cppcoreguidelines-owning-memory) global_esp32_ble_tracker->ble_events_.push(gap_event); -} +} // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) void ESP32BLETracker::real_gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { switch (event) { @@ -254,9 +254,9 @@ void ESP32BLETracker::gap_scan_result(const esp_ble_gap_cb_param_t::ble_scan_res void ESP32BLETracker::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { - BLEEvent *gattc_event = new BLEEvent(event, gattc_if, param); + BLEEvent *gattc_event = new BLEEvent(event, gattc_if, param); // NOLINT(cppcoreguidelines-owning-memory) global_esp32_ble_tracker->ble_events_.push(gattc_event); -} +} // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) void ESP32BLETracker::real_gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { diff --git a/esphome/components/esp32_camera/esp32_camera.cpp b/esphome/components/esp32_camera/esp32_camera.cpp index 500ddd67c9..072cf41460 100644 --- a/esphome/components/esp32_camera/esp32_camera.cpp +++ b/esphome/components/esp32_camera/esp32_camera.cpp @@ -275,10 +275,10 @@ void ESP32Camera::set_idle_update_interval(uint32_t idle_update_interval) { } void ESP32Camera::set_test_pattern(bool test_pattern) { this->test_pattern_ = test_pattern; } -ESP32Camera *global_esp32_camera; +ESP32Camera *global_esp32_camera; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) void CameraImageReader::set_image(std::shared_ptr image) { - this->image_ = image; + this->image_ = std::move(image); this->offset_ = 0; } size_t CameraImageReader::available() const { diff --git a/esphome/components/esp32_improv/esp32_improv_component.cpp b/esphome/components/esp32_improv/esp32_improv_component.cpp index 65d253078c..b584d2e8b9 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.cpp +++ b/esphome/components/esp32_improv/esp32_improv_component.cpp @@ -32,7 +32,7 @@ void ESP32ImprovComponent::setup_characteristics() { this->rpc_ = this->service_->create_characteristic(improv::RPC_COMMAND_UUID, BLECharacteristic::PROPERTY_WRITE); this->rpc_->on_write([this](const std::vector &data) { - if (data.size() > 0) { + if (!data.empty()) { this->incoming_data_.insert(this->incoming_data_.end(), data.begin(), data.end()); } }); @@ -56,7 +56,7 @@ void ESP32ImprovComponent::setup_characteristics() { } void ESP32ImprovComponent::loop() { - if (this->incoming_data_.size() > 0) + if (!this->incoming_data_.empty()) this->process_incoming_data_(); uint32_t now = millis(); @@ -162,7 +162,7 @@ bool ESP32ImprovComponent::check_identify_() { void ESP32ImprovComponent::set_state_(improv::State state) { ESP_LOGV(TAG, "Setting state: %d", state); this->state_ = state; - if (this->status_->get_value().size() == 0 || this->status_->get_value()[0] != state) { + if (this->status_->get_value().empty() || this->status_->get_value()[0] != state) { uint8_t data[1]{state}; this->status_->set_value(data, 1); if (state != improv::STATE_STOPPED) @@ -173,7 +173,7 @@ void ESP32ImprovComponent::set_state_(improv::State state) { void ESP32ImprovComponent::set_error_(improv::Error error) { if (error != improv::ERROR_NONE) ESP_LOGE(TAG, "Error: %d", error); - if (this->error_->get_value().size() == 0 || this->error_->get_value()[0] != error) { + if (this->error_->get_value().empty() || this->error_->get_value()[0] != error) { uint8_t data[1]{error}; this->error_->set_value(data, 1); if (this->state_ != improv::STATE_STOPPED) @@ -274,7 +274,7 @@ void ESP32ImprovComponent::on_wifi_connect_timeout_() { void ESP32ImprovComponent::on_client_disconnect() { this->set_error_(improv::ERROR_NONE); }; -ESP32ImprovComponent *global_improv_component = nullptr; +ESP32ImprovComponent *global_improv_component = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) } // namespace esp32_improv } // namespace esphome diff --git a/esphome/components/esp8266_pwm/esp8266_pwm.cpp b/esphome/components/esp8266_pwm/esp8266_pwm.cpp index d725e90edc..186653acd1 100644 --- a/esphome/components/esp8266_pwm/esp8266_pwm.cpp +++ b/esphome/components/esp8266_pwm/esp8266_pwm.cpp @@ -1,3 +1,5 @@ +#ifdef ARDUINO_ARCH_ESP8266 + #include "esp8266_pwm.h" #include "esphome/core/macros.h" #include "esphome/core/log.h" @@ -55,3 +57,5 @@ void HOT ESP8266PWM::write_state(float state) { } // namespace esp8266_pwm } // namespace esphome + +#endif diff --git a/esphome/components/esp8266_pwm/esp8266_pwm.h b/esphome/components/esp8266_pwm/esp8266_pwm.h index 661db6611f..1bf875dc43 100644 --- a/esphome/components/esp8266_pwm/esp8266_pwm.h +++ b/esphome/components/esp8266_pwm/esp8266_pwm.h @@ -1,5 +1,7 @@ #pragma once +#ifdef ARDUINO_ARCH_ESP8266 + #include "esphome/core/component.h" #include "esphome/core/esphal.h" #include "esphome/core/automation.h" @@ -48,3 +50,5 @@ template class SetFrequencyAction : public Action { } // namespace esp8266_pwm } // namespace esphome + +#endif diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index b22f8c97d1..cad76ae476 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -16,17 +16,17 @@ // Defined in WiFiGeneric.cpp, sets global initialized flag, starts network event task queue and calls // tcpip_adapter_init() -extern void tcpipInit(); +extern void tcpipInit(); // NOLINT(readability-identifier-naming) namespace esphome { namespace ethernet { static const char *const TAG = "ethernet"; -EthernetComponent *global_eth_component; +EthernetComponent *global_eth_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) #define ESPHL_ERROR_CHECK(err, message) \ - if (err != ESP_OK) { \ + if ((err) != ESP_OK) { \ ESP_LOGE(TAG, message ": (%d) %s", err, esp_err_to_name(err)); \ this->mark_failed(); \ return; \ @@ -258,7 +258,7 @@ void EthernetComponent::set_mdc_pin(uint8_t mdc_pin) { this->mdc_pin_ = mdc_pin; void EthernetComponent::set_mdio_pin(uint8_t mdio_pin) { this->mdio_pin_ = mdio_pin; } void EthernetComponent::set_type(EthernetType type) { this->type_ = type; } void EthernetComponent::set_clk_mode(eth_clock_mode_t clk_mode) { this->clk_mode_ = clk_mode; } -void EthernetComponent::set_manual_ip(ManualIP manual_ip) { this->manual_ip_ = manual_ip; } +void EthernetComponent::set_manual_ip(const ManualIP &manual_ip) { this->manual_ip_ = manual_ip; } std::string EthernetComponent::get_use_address() const { if (this->use_address_.empty()) { return App.get_name() + ".local"; diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index 326cd1edea..dc8816ef86 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -48,7 +48,7 @@ class EthernetComponent : public Component { void set_mdio_pin(uint8_t mdio_pin); void set_type(EthernetType type); void set_clk_mode(eth_clock_mode_t clk_mode); - void set_manual_ip(ManualIP manual_ip); + void set_manual_ip(const ManualIP &manual_ip); IPAddress get_ip_address(); std::string get_use_address() const; diff --git a/esphome/components/i2c/i2c.cpp b/esphome/components/i2c/i2c.cpp index f1980e1f1d..a3a3f7f82a 100644 --- a/esphome/components/i2c/i2c.cpp +++ b/esphome/components/i2c/i2c.cpp @@ -10,11 +10,12 @@ static const char *const TAG = "i2c"; I2CComponent::I2CComponent() { #ifdef ARDUINO_ARCH_ESP32 - if (next_i2c_bus_num_ == 0) + static uint8_t next_i2c_bus_num = 0; + if (next_i2c_bus_num == 0) this->wire_ = &Wire; else - this->wire_ = new TwoWire(next_i2c_bus_num_); - next_i2c_bus_num_++; + this->wire_ = new TwoWire(next_i2c_bus_num); // NOLINT(cppcoreguidelines-owning-memory) + next_i2c_bus_num++; #else this->wire_ = &Wire; // NOLINT(cppcoreguidelines-prefer-member-initializer) #endif @@ -273,10 +274,6 @@ bool I2CDevice::write_byte_16(uint8_t a_register, uint16_t data) { // NOLINT } void I2CDevice::set_i2c_parent(I2CComponent *parent) { this->parent_ = parent; } -#ifdef ARDUINO_ARCH_ESP32 -uint8_t next_i2c_bus_num_ = 0; -#endif - I2CRegister &I2CRegister::operator=(uint8_t value) { this->parent_->write_byte(this->register_, value); return *this; diff --git a/esphome/components/i2c/i2c.h b/esphome/components/i2c/i2c.h index da791ec633..6f93d4c0e4 100644 --- a/esphome/components/i2c/i2c.h +++ b/esphome/components/i2c/i2c.h @@ -131,10 +131,6 @@ class I2CComponent : public Component { bool scan_; }; -#ifdef ARDUINO_ARCH_ESP32 -extern uint8_t next_i2c_bus_num_; -#endif - class I2CDevice; class I2CMultiplexer; class I2CRegister { diff --git a/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp b/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp index a940a77148..81693c6d96 100644 --- a/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp +++ b/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp @@ -37,25 +37,25 @@ bool InkbirdIBSTH1_MINI::parse_device(const esp32_ble_tracker::ESPBTDevice &devi ESP_LOGVV(TAG, "parse_device(): address is not public"); return false; } - if (device.get_service_datas().size() != 0) { + if (!device.get_service_datas().empty()) { ESP_LOGVV(TAG, "parse_device(): service_data is expected to be empty"); return false; } - auto mnfDatas = device.get_manufacturer_datas(); - if (mnfDatas.size() != 1) { + auto mnf_datas = device.get_manufacturer_datas(); + if (mnf_datas.size() != 1) { ESP_LOGVV(TAG, "parse_device(): manufacturer_datas is expected to have a single element"); return false; } - auto mnfData = mnfDatas[0]; - if (mnfData.uuid.get_uuid().len != ESP_UUID_LEN_16) { + auto mnf_data = mnf_datas[0]; + if (mnf_data.uuid.get_uuid().len != ESP_UUID_LEN_16) { ESP_LOGVV(TAG, "parse_device(): manufacturer data element is expected to have uuid of length 16"); return false; } - if (mnfData.data.size() != 7) { + if (mnf_data.data.size() != 7) { ESP_LOGVV(TAG, "parse_device(): manufacturer data element length is expected to be of length 7"); return false; } - if (mnfData.data[6] != 8) { + if (mnf_data.data[6] != 8) { ESP_LOGVV(TAG, "parse_device(): unexpected data"); return false; } @@ -72,20 +72,20 @@ bool InkbirdIBSTH1_MINI::parse_device(const esp32_ble_tracker::ESPBTDevice &devi auto external_temperature = NAN; // Read bluetooth data into variable - auto measured_temperature = mnfData.uuid.get_uuid().uuid.uuid16 / 100.0f; + auto measured_temperature = mnf_data.uuid.get_uuid().uuid.uuid16 / 100.0f; // Set temperature or external_temperature based on which sensor is in use - if (mnfData.data[2] == 0) { + if (mnf_data.data[2] == 0) { temperature = measured_temperature; - } else if (mnfData.data[2] == 1) { + } else if (mnf_data.data[2] == 1) { external_temperature = measured_temperature; } else { ESP_LOGVV(TAG, "parse_device(): unknown sensor type"); return false; } - auto battery_level = mnfData.data[5]; - auto humidity = ((mnfData.data[1] << 8) + mnfData.data[0]) / 100.0f; + auto battery_level = mnf_data.data[5]; + auto humidity = ((mnf_data.data[1] << 8) + mnf_data.data[0]) / 100.0f; // Send temperature only if the value is set if (!isnan(temperature) && this->temperature_ != nullptr) { diff --git a/esphome/components/inkplate6/inkplate.cpp b/esphome/components/inkplate6/inkplate.cpp index 089b4791a6..b5cec6cf9e 100644 --- a/esphome/components/inkplate6/inkplate.cpp +++ b/esphome/components/inkplate6/inkplate.cpp @@ -150,7 +150,6 @@ void Inkplate6::dump_config() { } void Inkplate6::eink_off_() { ESP_LOGV(TAG, "Eink off called"); - unsigned long start_time = millis(); if (panel_on_ == 0) return; panel_on_ = 0; @@ -170,7 +169,6 @@ void Inkplate6::eink_off_() { } void Inkplate6::eink_on_() { ESP_LOGV(TAG, "Eink on called"); - unsigned long start_time = millis(); if (panel_on_ == 1) return; panel_on_ = 1; @@ -200,7 +198,7 @@ void Inkplate6::eink_on_() { } void Inkplate6::fill(Color color) { ESP_LOGV(TAG, "Fill called"); - unsigned long start_time = millis(); + uint32_t start_time = millis(); if (this->greyscale_) { uint8_t fill = ((color.red * 2126 / 10000) + (color.green * 7152 / 10000) + (color.blue * 722 / 10000)) >> 5; @@ -214,7 +212,7 @@ void Inkplate6::fill(Color color) { } void Inkplate6::display() { ESP_LOGV(TAG, "Display called"); - unsigned long start_time = millis(); + uint32_t start_time = millis(); if (this->greyscale_) { this->display3b_(); @@ -229,7 +227,7 @@ void Inkplate6::display() { } void Inkplate6::display1b_() { ESP_LOGV(TAG, "Display1b called"); - unsigned long start_time = millis(); + uint32_t start_time = millis(); memcpy(this->buffer_, this->partial_buffer_, this->get_buffer_length_()); @@ -348,7 +346,7 @@ void Inkplate6::display1b_() { } void Inkplate6::display3b_() { ESP_LOGV(TAG, "Display3b called"); - unsigned long start_time = millis(); + uint32_t start_time = millis(); eink_on_(); clean_fast_(0, 1); @@ -424,7 +422,7 @@ void Inkplate6::display3b_() { } bool Inkplate6::partial_update_() { ESP_LOGV(TAG, "Partial update called"); - unsigned long start_time = millis(); + uint32_t start_time = millis(); if (this->greyscale_) return false; if (this->block_partial_) @@ -531,7 +529,7 @@ void Inkplate6::vscan_end_() { } void Inkplate6::clean() { ESP_LOGV(TAG, "Clean called"); - unsigned long start_time = millis(); + uint32_t start_time = millis(); eink_on_(); clean_fast_(0, 1); // White @@ -544,7 +542,7 @@ void Inkplate6::clean() { } void Inkplate6::clean_fast_(uint8_t c, uint8_t rep) { ESP_LOGV(TAG, "Clean fast called with: (%d, %d)", c, rep); - unsigned long start_time = millis(); + uint32_t start_time = millis(); eink_on_(); uint8_t data = 0; diff --git a/esphome/components/ledc/ledc_output.cpp b/esphome/components/ledc/ledc_output.cpp index 0575dbee6a..45bee5b871 100644 --- a/esphome/components/ledc/ledc_output.cpp +++ b/esphome/components/ledc/ledc_output.cpp @@ -63,7 +63,7 @@ void LEDCOutput::update_frequency(float frequency) { this->write_state(this->duty_); } -uint8_t next_ledc_channel = 0; +uint8_t next_ledc_channel = 0; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) } // namespace ledc } // namespace esphome diff --git a/esphome/components/mqtt/mqtt_client.cpp b/esphome/components/mqtt/mqtt_client.cpp index 129597cc00..237a8146e6 100644 --- a/esphome/components/mqtt/mqtt_client.cpp +++ b/esphome/components/mqtt/mqtt_client.cpp @@ -88,8 +88,8 @@ void MQTTClientComponent::start_dnslookup_() { this->dns_resolved_ = false; ip_addr_t addr; #ifdef ARDUINO_ARCH_ESP32 - err_t err = dns_gethostbyname_addrtype(this->credentials_.address.c_str(), &addr, this->dns_found_callback, this, - LWIP_DNS_ADDRTYPE_IPV4); + err_t err = dns_gethostbyname_addrtype(this->credentials_.address.c_str(), &addr, + MQTTClientComponent::dns_found_callback, this, LWIP_DNS_ADDRTYPE_IPV4); #endif #ifdef ARDUINO_ARCH_ESP8266 err_t err = dns_gethostbyname(this->credentials_.address.c_str(), &addr, diff --git a/esphome/components/nextion/nextion_upload.cpp b/esphome/components/nextion/nextion_upload.cpp index a83618e888..35b904357b 100644 --- a/esphome/components/nextion/nextion_upload.cpp +++ b/esphome/components/nextion/nextion_upload.cpp @@ -25,15 +25,10 @@ int Nextion::upload_by_chunks_(HTTPClient *http, int range_start) { if (range_end > this->tft_size_) range_end = this->tft_size_; - bool begin_status = false; -#ifdef ARDUINO_ARCH_ESP32 - begin_status = http->begin(this->tft_url_.c_str()); -#endif #ifdef ARDUINO_ARCH_ESP8266 #ifndef CLANG_TIDY http->setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); http->setRedirectLimit(3); - begin_status = http->begin(*this->get_wifi_client_(), this->tft_url_.c_str()); #endif #endif @@ -44,6 +39,7 @@ int Nextion::upload_by_chunks_(HTTPClient *http, int range_start) { int tries = 1; int code = 0; + bool begin_status = false; while (tries <= 5) { #ifdef ARDUINO_ARCH_ESP32 begin_status = http->begin(this->tft_url_.c_str()); @@ -156,7 +152,7 @@ void Nextion::upload_tft() { ESP_LOGD(TAG, "connection failed"); #ifdef ARDUINO_ARCH_ESP32 if (psramFound()) - free(this->transfer_buffer_); + free(this->transfer_buffer_); // NOLINT else #endif delete this->transfer_buffer_; diff --git a/esphome/components/pulse_counter/pulse_counter_sensor.cpp b/esphome/components/pulse_counter/pulse_counter_sensor.cpp index 9a0cf568a9..602c0bdd67 100644 --- a/esphome/components/pulse_counter/pulse_counter_sensor.cpp +++ b/esphome/components/pulse_counter/pulse_counter_sensor.cpp @@ -45,10 +45,11 @@ pulse_counter_t PulseCounterStorage::read_raw_value() { #ifdef ARDUINO_ARCH_ESP32 bool PulseCounterStorage::pulse_counter_setup(GPIOPin *pin) { + static pcnt_unit_t next_pcnt_unit = PCNT_UNIT_0; this->pin = pin; this->pin->setup(); this->pcnt_unit = next_pcnt_unit; - next_pcnt_unit = pcnt_unit_t(int(next_pcnt_unit) + 1); // NOLINT + next_pcnt_unit = pcnt_unit_t(int(next_pcnt_unit) + 1); ESP_LOGCONFIG(TAG, " PCNT Unit Number: %u", this->pcnt_unit); @@ -166,9 +167,5 @@ void PulseCounterSensor::update() { } } -#ifdef ARDUINO_ARCH_ESP32 -pcnt_unit_t next_pcnt_unit = PCNT_UNIT_0; -#endif - } // namespace pulse_counter } // namespace esphome diff --git a/esphome/components/pulse_counter/pulse_counter_sensor.h b/esphome/components/pulse_counter/pulse_counter_sensor.h index b3e3f42c01..3203ab81fd 100644 --- a/esphome/components/pulse_counter/pulse_counter_sensor.h +++ b/esphome/components/pulse_counter/pulse_counter_sensor.h @@ -69,9 +69,5 @@ class PulseCounterSensor : public sensor::Sensor, public PollingComponent { sensor::Sensor *total_sensor_; }; -#ifdef ARDUINO_ARCH_ESP32 -extern pcnt_unit_t next_pcnt_unit; -#endif - } // namespace pulse_counter } // namespace esphome diff --git a/esphome/components/pvvx_mithermometer/pvvx_mithermometer.cpp b/esphome/components/pvvx_mithermometer/pvvx_mithermometer.cpp index 34f190e45e..9edcd9166c 100644 --- a/esphome/components/pvvx_mithermometer/pvvx_mithermometer.cpp +++ b/esphome/components/pvvx_mithermometer/pvvx_mithermometer.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace pvvx_mithermometer { -static const char *TAG = "pvvx_mithermometer"; +static const char *const TAG = "pvvx_mithermometer"; void PVVXMiThermometer::dump_config() { ESP_LOGCONFIG(TAG, "PVVX MiThermometer"); @@ -26,7 +26,7 @@ bool PVVXMiThermometer::parse_device(const esp32_ble_tracker::ESPBTDevice &devic bool success = false; for (auto &service_data : device.get_service_datas()) { auto res = parse_header(service_data); - if (res->is_duplicate) { + if (!res.has_value()) { continue; } if (!(parse_message(service_data.data, *res))) { @@ -46,11 +46,7 @@ bool PVVXMiThermometer::parse_device(const esp32_ble_tracker::ESPBTDevice &devic success = true; } - if (!success) { - return false; - } - - return true; + return success; } optional PVVXMiThermometer::parse_header(const esp32_ble_tracker::ServiceData &service_data) { @@ -64,12 +60,10 @@ optional PVVXMiThermometer::parse_header(const esp32_ble_tracker::S static uint8_t last_frame_count = 0; if (last_frame_count == raw[13]) { - ESP_LOGVV(TAG, "parse_header(): duplicate data packet received (%d).", static_cast(last_frame_count)); - result.is_duplicate = true; + ESP_LOGVV(TAG, "parse_header(): duplicate data packet received (%hhu).", last_frame_count); return {}; } last_frame_count = raw[13]; - result.is_duplicate = false; return result; } diff --git a/esphome/components/pvvx_mithermometer/pvvx_mithermometer.h b/esphome/components/pvvx_mithermometer/pvvx_mithermometer.h index 42a40d4200..4132954983 100644 --- a/esphome/components/pvvx_mithermometer/pvvx_mithermometer.h +++ b/esphome/components/pvvx_mithermometer/pvvx_mithermometer.h @@ -14,7 +14,6 @@ struct ParseResult { optional humidity; optional battery_level; optional battery_voltage; - bool is_duplicate; int raw_offset; }; diff --git a/esphome/components/remote_base/rc5_protocol.cpp b/esphome/components/remote_base/rc5_protocol.cpp index a8b608ec9e..47a85cda57 100644 --- a/esphome/components/remote_base/rc5_protocol.cpp +++ b/esphome/components/remote_base/rc5_protocol.cpp @@ -10,7 +10,7 @@ static const uint32_t BIT_TIME_US = 889; static const uint8_t NBITS = 14; void RC5Protocol::encode(RemoteTransmitData *dst, const RC5Data &data) { - static bool TOGGLE = false; + static bool toggle = false; dst->set_carrier_frequency(36000); uint64_t out_data = 0; @@ -21,7 +21,7 @@ void RC5Protocol::encode(RemoteTransmitData *dst, const RC5Data &data) { } else { out_data |= 0b11 << 12; } - out_data |= TOGGLE << 11; + out_data |= toggle << 11; out_data |= data.address << 6; out_data |= command; @@ -34,7 +34,7 @@ void RC5Protocol::encode(RemoteTransmitData *dst, const RC5Data &data) { dst->space(BIT_TIME_US); } } - TOGGLE = !TOGGLE; + toggle = !toggle; } optional RC5Protocol::decode(RemoteReceiveData src) { RC5Data out{ diff --git a/esphome/components/socket/bsd_sockets_impl.cpp b/esphome/components/socket/bsd_sockets_impl.cpp index a0cdb6ec42..1ad520a099 100644 --- a/esphome/components/socket/bsd_sockets_impl.cpp +++ b/esphome/components/socket/bsd_sockets_impl.cpp @@ -3,7 +3,7 @@ #ifdef USE_SOCKET_IMPL_BSD_SOCKETS -#include +#include namespace esphome { namespace socket { @@ -13,14 +13,14 @@ std::string format_sockaddr(const struct sockaddr_storage &storage) { const struct sockaddr_in *addr = reinterpret_cast(&storage); char buf[INET_ADDRSTRLEN]; const char *ret = inet_ntop(AF_INET, &addr->sin_addr, buf, sizeof(buf)); - if (ret == NULL) + if (ret == nullptr) return {}; return std::string{buf}; } else if (storage.ss_family == AF_INET6) { const struct sockaddr_in6 *addr = reinterpret_cast(&storage); char buf[INET6_ADDRSTRLEN]; const char *ret = inet_ntop(AF_INET6, &addr->sin6_addr, buf, sizeof(buf)); - if (ret == NULL) + if (ret == nullptr) return {}; return std::string{buf}; } @@ -32,7 +32,7 @@ class BSDSocketImpl : public Socket { BSDSocketImpl(int fd) : Socket(), fd_(fd) {} ~BSDSocketImpl() override { if (!closed_) { - close(); + close(); // NOLINT(clang-analyzer-optin.cplusplus.VirtualCall) } } std::unique_ptr accept(struct sockaddr *addr, socklen_t *addrlen) override { diff --git a/esphome/components/spi/spi.cpp b/esphome/components/spi/spi.cpp index a5d7fba30a..609b4df456 100644 --- a/esphome/components/spi/spi.cpp +++ b/esphome/components/spi/spi.cpp @@ -61,7 +61,7 @@ void SPIComponent::setup() { if (spi_bus_num == 0) { this->hw_spi_ = &SPI; } else { - this->hw_spi_ = new SPIClass(VSPI); + this->hw_spi_ = new SPIClass(VSPI); // NOLINT(cppcoreguidelines-owning-memory) } spi_bus_num++; this->hw_spi_->begin(clk_pin, miso_pin, mosi_pin); diff --git a/esphome/components/st7735/st7735.cpp b/esphome/components/st7735/st7735.cpp index ac8802739b..b44f76a9e6 100644 --- a/esphome/components/st7735/st7735.cpp +++ b/esphome/components/st7735/st7735.cpp @@ -458,26 +458,26 @@ void HOT ST7735::write_display_data_() { } void ST7735::spi_master_write_addr_(uint16_t addr1, uint16_t addr2) { - static uint8_t BYTE[4]; - BYTE[0] = (addr1 >> 8) & 0xFF; - BYTE[1] = addr1 & 0xFF; - BYTE[2] = (addr2 >> 8) & 0xFF; - BYTE[3] = addr2 & 0xFF; + static uint8_t byte[4]; + byte[0] = (addr1 >> 8) & 0xFF; + byte[1] = addr1 & 0xFF; + byte[2] = (addr2 >> 8) & 0xFF; + byte[3] = addr2 & 0xFF; this->dc_pin_->digital_write(true); - this->write_array(BYTE, 4); + this->write_array(byte, 4); } void ST7735::spi_master_write_color_(uint16_t color, uint16_t size) { - static uint8_t BYTE[1024]; + static uint8_t byte[1024]; int index = 0; for (int i = 0; i < size; i++) { - BYTE[index++] = (color >> 8) & 0xFF; - BYTE[index++] = color & 0xFF; + byte[index++] = (color >> 8) & 0xFF; + byte[index++] = color & 0xFF; } this->dc_pin_->digital_write(true); - return write_array(BYTE, size * 2); + return write_array(byte, size * 2); } } // namespace st7735 diff --git a/esphome/components/st7789v/st7789v.cpp b/esphome/components/st7789v/st7789v.cpp index c84153970d..471ad6664c 100644 --- a/esphome/components/st7789v/st7789v.cpp +++ b/esphome/components/st7789v/st7789v.cpp @@ -197,26 +197,26 @@ void ST7789V::write_data_(uint8_t value) { } void ST7789V::write_addr_(uint16_t addr1, uint16_t addr2) { - static uint8_t BYTE[4]; - BYTE[0] = (addr1 >> 8) & 0xFF; - BYTE[1] = addr1 & 0xFF; - BYTE[2] = (addr2 >> 8) & 0xFF; - BYTE[3] = addr2 & 0xFF; + static uint8_t byte[4]; + byte[0] = (addr1 >> 8) & 0xFF; + byte[1] = addr1 & 0xFF; + byte[2] = (addr2 >> 8) & 0xFF; + byte[3] = addr2 & 0xFF; this->dc_pin_->digital_write(true); - this->write_array(BYTE, 4); + this->write_array(byte, 4); } void ST7789V::write_color_(uint16_t color, uint16_t size) { - static uint8_t BYTE[1024]; + static uint8_t byte[1024]; int index = 0; for (int i = 0; i < size; i++) { - BYTE[index++] = (color >> 8) & 0xFF; - BYTE[index++] = color & 0xFF; + byte[index++] = (color >> 8) & 0xFF; + byte[index++] = color & 0xFF; } this->dc_pin_->digital_write(true); - return write_array(BYTE, size * 2); + return write_array(byte, size * 2); } int ST7789V::get_height_internal() { diff --git a/esphome/components/uart/uart.h b/esphome/components/uart/uart.h index 041cdaecdf..a79b7b841e 100644 --- a/esphome/components/uart/uart.h +++ b/esphome/components/uart/uart.h @@ -126,10 +126,6 @@ class UARTComponent : public Component, public Stream { #endif }; -#ifdef ARDUINO_ARCH_ESP32 -extern uint8_t next_uart_num; -#endif - class UARTDevice : public Stream { public: UARTDevice() = default; diff --git a/esphome/components/uart/uart_esp32.cpp b/esphome/components/uart/uart_esp32.cpp index c672a34c36..db2757780e 100644 --- a/esphome/components/uart/uart_esp32.cpp +++ b/esphome/components/uart/uart_esp32.cpp @@ -8,7 +8,6 @@ namespace esphome { namespace uart { static const char *const TAG = "uart_esp32"; -uint8_t next_uart_num = 1; static const uint32_t UART_PARITY_EVEN = 0 << 0; static const uint32_t UART_PARITY_ODD = 1 << 0; @@ -80,7 +79,8 @@ void UARTComponent::setup() { #endif this->hw_serial_ = &Serial; } else { - this->hw_serial_ = new HardwareSerial(next_uart_num++); + static uint8_t next_uart_num = 1; + this->hw_serial_ = new HardwareSerial(next_uart_num++); // NOLINT(cppcoreguidelines-owning-memory) } int8_t tx = this->tx_pin_.has_value() ? *this->tx_pin_ : -1; int8_t rx = this->rx_pin_.has_value() ? *this->rx_pin_ : -1; @@ -99,7 +99,7 @@ void UARTComponent::dump_config() { } ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_); ESP_LOGCONFIG(TAG, " Data Bits: %u", this->data_bits_); - ESP_LOGCONFIG(TAG, " Parity: %s", parity_to_str(this->parity_)); + ESP_LOGCONFIG(TAG, " Parity: %s", LOG_STR_ARG(parity_to_str(this->parity_))); ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_); this->check_logger_conflict_(); } diff --git a/esphome/components/wifi/wifi_component_esp32.cpp b/esphome/components/wifi/wifi_component_esp32.cpp index b56030db56..38c9e12bec 100644 --- a/esphome/components/wifi/wifi_component_esp32.cpp +++ b/esphome/components/wifi/wifi_component_esp32.cpp @@ -25,31 +25,31 @@ namespace wifi { static const char *const TAG = "wifi_esp32"; bool WiFiComponent::wifi_mode_(optional sta, optional ap) { - uint8_t current_mode = WiFi.getMode(); + uint8_t current_mode = WiFiClass::getMode(); bool current_sta = current_mode & 0b01; bool current_ap = current_mode & 0b10; - bool sta_ = sta.value_or(current_sta); - bool ap_ = ap.value_or(current_ap); - if (current_sta == sta_ && current_ap == ap_) + bool enable_sta = sta.value_or(current_sta); + bool enable_ap = ap.value_or(current_ap); + if (current_sta == enable_sta && current_ap == enable_ap) return true; - if (sta_ && !current_sta) { + if (enable_sta && !current_sta) { ESP_LOGV(TAG, "Enabling STA."); - } else if (!sta_ && current_sta) { + } else if (!enable_sta && current_sta) { ESP_LOGV(TAG, "Disabling STA."); } - if (ap_ && !current_ap) { + if (enable_ap && !current_ap) { ESP_LOGV(TAG, "Enabling AP."); - } else if (!ap_ && current_ap) { + } else if (!enable_ap && current_ap) { ESP_LOGV(TAG, "Disabling AP."); } uint8_t mode = 0; - if (sta_) + if (enable_sta) mode |= 0b01; - if (ap_) + if (enable_ap) mode |= 0b10; - bool ret = WiFi.mode(static_cast(mode)); + bool ret = WiFiClass::mode(static_cast(mode)); if (!ret) { ESP_LOGW(TAG, "Setting WiFi mode failed!"); @@ -159,8 +159,8 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv417wifi_sta_config_t wifi_config_t conf; memset(&conf, 0, sizeof(conf)); - strcpy(reinterpret_cast(conf.sta.ssid), ap.get_ssid().c_str()); - strcpy(reinterpret_cast(conf.sta.password), ap.get_password().c_str()); + strlcpy(reinterpret_cast(conf.sta.ssid), ap.get_ssid().c_str(), sizeof(conf.sta.ssid)); + strlcpy(reinterpret_cast(conf.sta.password), ap.get_password().c_str(), sizeof(conf.sta.password)); // The weakest authmode to accept in the fast scan mode if (ap.get_password().empty()) { @@ -176,10 +176,10 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { #endif if (ap.get_bssid().has_value()) { - conf.sta.bssid_set = 1; + conf.sta.bssid_set = true; memcpy(conf.sta.bssid, ap.get_bssid()->data(), 6); } else { - conf.sta.bssid_set = 0; + conf.sta.bssid_set = false; } if (ap.get_channel().has_value()) { conf.sta.channel = *ap.get_channel(); @@ -559,7 +559,7 @@ void WiFiComponent::wifi_pre_setup_() { // Make sure WiFi is in clean state before anything starts this->wifi_mode_(false, false); } -wl_status_t WiFiComponent::wifi_sta_status_() { return WiFi.status(); } +wl_status_t WiFiComponent::wifi_sta_status_() { return WiFiClass::status(); } bool WiFiComponent::wifi_scan_start_() { // enable STA if (!this->wifi_mode_(true, {})) @@ -660,7 +660,7 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { wifi_config_t conf; memset(&conf, 0, sizeof(conf)); - strcpy(reinterpret_cast(conf.ap.ssid), ap.get_ssid().c_str()); + strlcpy(reinterpret_cast(conf.ap.ssid), ap.get_ssid().c_str(), sizeof(conf.ap.ssid)); conf.ap.channel = ap.get_channel().value_or(1); conf.ap.ssid_hidden = ap.get_ssid().size(); conf.ap.max_connection = 5; @@ -671,7 +671,7 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { *conf.ap.password = 0; } else { conf.ap.authmode = WIFI_AUTH_WPA2_PSK; - strcpy(reinterpret_cast(conf.ap.password), ap.get_password().c_str()); + strlcpy(reinterpret_cast(conf.ap.password), ap.get_password().c_str(), sizeof(conf.ap.ssid)); } #if ESP_IDF_VERSION_MAJOR >= 4 diff --git a/esphome/components/wifi/wifi_component_esp8266.cpp b/esphome/components/wifi/wifi_component_esp8266.cpp index 842b44b705..5a0ba92b09 100644 --- a/esphome/components/wifi/wifi_component_esp8266.cpp +++ b/esphome/components/wifi/wifi_component_esp8266.cpp @@ -581,7 +581,7 @@ wl_status_t WiFiComponent::wifi_sta_status_() { } } bool WiFiComponent::wifi_scan_start_() { - static bool FIRST_SCAN = false; + static bool first_scan = false; // enable STA if (!this->wifi_mode_(true, {})) @@ -595,7 +595,7 @@ bool WiFiComponent::wifi_scan_start_() { config.show_hidden = 1; #if ARDUINO_VERSION_CODE >= VERSION_CODE(2, 4, 0) config.scan_type = WIFI_SCAN_TYPE_ACTIVE; - if (FIRST_SCAN) { + if (first_scan) { config.scan_time.active.min = 100; config.scan_time.active.max = 200; } else { @@ -603,7 +603,7 @@ bool WiFiComponent::wifi_scan_start_() { config.scan_time.active.max = 500; } #endif - FIRST_SCAN = false; + first_scan = false; bool ret = wifi_station_scan(&config, &WiFiComponent::s_wifi_scan_done_callback); if (!ret) { ESP_LOGV(TAG, "wifi_station_scan failed!"); diff --git a/esphome/components/xiaomi_ble/xiaomi_ble.cpp b/esphome/components/xiaomi_ble/xiaomi_ble.cpp index f86dc44eeb..3e7e591f9a 100644 --- a/esphome/components/xiaomi_ble/xiaomi_ble.cpp +++ b/esphome/components/xiaomi_ble/xiaomi_ble.cpp @@ -15,7 +15,7 @@ static const char *const TAG = "xiaomi_ble"; bool parse_xiaomi_value(uint8_t value_type, const uint8_t *data, uint8_t value_length, XiaomiParseResult &result) { // motion detection, 1 byte, 8-bit unsigned integer if ((value_type == 0x03) && (value_length == 1)) { - result.has_motion = (data[0]) ? true : false; + result.has_motion = data[0]; } // temperature, 2 bytes, 16-bit signed integer (LE), 0.1 °C else if ((value_type == 0x04) && (value_length == 2)) { @@ -31,7 +31,7 @@ bool parse_xiaomi_value(uint8_t value_type, const uint8_t *data, uint8_t value_l else if (((value_type == 0x07) || (value_type == 0x0F)) && (value_length == 3)) { const uint32_t illuminance = uint32_t(data[0]) | (uint32_t(data[1]) << 8) | (uint32_t(data[2]) << 16); result.illuminance = illuminance; - result.is_light = (illuminance == 100) ? true : false; + result.is_light = illuminance == 100; if (value_type == 0x0F) result.has_motion = true; } @@ -62,7 +62,7 @@ bool parse_xiaomi_value(uint8_t value_type, const uint8_t *data, uint8_t value_l } // on/off state, 1 byte, 8-bit unsigned integer else if ((value_type == 0x12) && (value_length == 1)) { - result.is_active = (data[0]) ? true : false; + result.is_active = data[0]; } // mosquito tablet, 1 byte, 8-bit unsigned integer, 1 % else if ((value_type == 0x13) && (value_length == 1)) { @@ -72,7 +72,7 @@ bool parse_xiaomi_value(uint8_t value_type, const uint8_t *data, uint8_t value_l else if ((value_type == 0x17) && (value_length == 4)) { const uint32_t idle_time = encode_uint32(data[3], data[2], data[1], data[0]); result.idle_time = idle_time / 60.0f; - result.has_motion = (idle_time) ? false : true; + result.has_motion = !idle_time; } else { return false; } @@ -81,7 +81,7 @@ bool parse_xiaomi_value(uint8_t value_type, const uint8_t *data, uint8_t value_l } bool parse_xiaomi_message(const std::vector &message, XiaomiParseResult &result) { - result.has_encryption = (message[0] & 0x08) ? true : false; // update encryption status + result.has_encryption = message[0] & 0x08; // update encryption status if (result.has_encryption) { ESP_LOGVV(TAG, "parse_xiaomi_message(): payload is encrypted, stop reading message."); return false; @@ -136,9 +136,9 @@ optional parse_xiaomi_header(const esp32_ble_tracker::Service } auto raw = service_data.data; - result.has_data = (raw[0] & 0x40) ? true : false; - result.has_capability = (raw[0] & 0x20) ? true : false; - result.has_encryption = (raw[0] & 0x08) ? true : false; + result.has_data = raw[0] & 0x40; + result.has_capability = raw[0] & 0x20; + result.has_encryption = raw[0] & 0x08; if (!result.has_data) { ESP_LOGVV(TAG, "parse_xiaomi_header(): service data has no DATA flag."); diff --git a/esphome/components/xiaomi_cgd1/xiaomi_cgd1.cpp b/esphome/components/xiaomi_cgd1/xiaomi_cgd1.cpp index c6e7a3f962..cfbe986fff 100644 --- a/esphome/components/xiaomi_cgd1/xiaomi_cgd1.cpp +++ b/esphome/components/xiaomi_cgd1/xiaomi_cgd1.cpp @@ -52,11 +52,7 @@ bool XiaomiCGD1::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { success = true; } - if (!success) { - return false; - } - - return true; + return success; } void XiaomiCGD1::set_bindkey(const std::string &bindkey) { @@ -67,7 +63,7 @@ void XiaomiCGD1::set_bindkey(const std::string &bindkey) { char temp[3] = {0}; for (int i = 0; i < 16; i++) { strncpy(temp, &(bindkey.c_str()[i * 2]), 2); - bindkey_[i] = std::strtoul(temp, NULL, 16); + bindkey_[i] = std::strtoul(temp, nullptr, 16); } } diff --git a/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.cpp b/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.cpp index 2ba2ac4c0a..8f42eaecaf 100644 --- a/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.cpp +++ b/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.cpp @@ -52,11 +52,7 @@ bool XiaomiCGDK2::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { success = true; } - if (!success) { - return false; - } - - return true; + return success; } void XiaomiCGDK2::set_bindkey(const std::string &bindkey) { @@ -67,7 +63,7 @@ void XiaomiCGDK2::set_bindkey(const std::string &bindkey) { char temp[3] = {0}; for (int i = 0; i < 16; i++) { strncpy(temp, &(bindkey.c_str()[i * 2]), 2); - bindkey_[i] = std::strtoul(temp, NULL, 16); + bindkey_[i] = std::strtoul(temp, nullptr, 16); } } diff --git a/esphome/components/xiaomi_cgg1/xiaomi_cgg1.cpp b/esphome/components/xiaomi_cgg1/xiaomi_cgg1.cpp index 86192fb028..d0a91d23f0 100644 --- a/esphome/components/xiaomi_cgg1/xiaomi_cgg1.cpp +++ b/esphome/components/xiaomi_cgg1/xiaomi_cgg1.cpp @@ -52,11 +52,7 @@ bool XiaomiCGG1::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { success = true; } - if (!success) { - return false; - } - - return true; + return success; } void XiaomiCGG1::set_bindkey(const std::string &bindkey) { @@ -67,7 +63,7 @@ void XiaomiCGG1::set_bindkey(const std::string &bindkey) { char temp[3] = {0}; for (int i = 0; i < 16; i++) { strncpy(temp, &(bindkey.c_str()[i * 2]), 2); - bindkey_[i] = std::strtoul(temp, NULL, 16); + bindkey_[i] = std::strtoul(temp, nullptr, 16); } } diff --git a/esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.cpp b/esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.cpp index 2ed1024076..75ed947c25 100644 --- a/esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.cpp +++ b/esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace xiaomi_cgpr1 { -static const char *TAG = "xiaomi_cgpr1"; +static const char *const TAG = "xiaomi_cgpr1"; void XiaomiCGPR1::dump_config() { ESP_LOGCONFIG(TAG, "Xiaomi CGPR1"); @@ -54,11 +54,7 @@ bool XiaomiCGPR1::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { success = true; } - if (!success) { - return false; - } - - return true; + return success; } void XiaomiCGPR1::set_bindkey(const std::string &bindkey) { @@ -69,7 +65,7 @@ void XiaomiCGPR1::set_bindkey(const std::string &bindkey) { char temp[3] = {0}; for (int i = 0; i < 16; i++) { strncpy(temp, &(bindkey.c_str()[i * 2]), 2); - bindkey_[i] = std::strtoul(temp, NULL, 16); + bindkey_[i] = std::strtoul(temp, nullptr, 16); } } diff --git a/esphome/components/xiaomi_gcls002/xiaomi_gcls002.cpp b/esphome/components/xiaomi_gcls002/xiaomi_gcls002.cpp index 5f7d67b85a..f626daad88 100644 --- a/esphome/components/xiaomi_gcls002/xiaomi_gcls002.cpp +++ b/esphome/components/xiaomi_gcls002/xiaomi_gcls002.cpp @@ -53,11 +53,7 @@ bool XiaomiGCLS002::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { success = true; } - if (!success) { - return false; - } - - return true; + return success; } } // namespace xiaomi_gcls002 diff --git a/esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.cpp b/esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.cpp index 103201d511..3f2cbf963a 100644 --- a/esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.cpp +++ b/esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.cpp @@ -56,11 +56,7 @@ bool XiaomiHHCCJCY01::parse_device(const esp32_ble_tracker::ESPBTDevice &device) success = true; } - if (!success) { - return false; - } - - return true; + return success; } } // namespace xiaomi_hhccjcy01 diff --git a/esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.cpp b/esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.cpp index efc83cb6cc..92b7171004 100644 --- a/esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.cpp +++ b/esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.cpp @@ -47,11 +47,7 @@ bool XiaomiHHCCPOT002::parse_device(const esp32_ble_tracker::ESPBTDevice &device success = true; } - if (!success) { - return false; - } - - return true; + return success; } } // namespace xiaomi_hhccpot002 diff --git a/esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.cpp b/esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.cpp index c88a3c3b61..646796525e 100644 --- a/esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.cpp +++ b/esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.cpp @@ -53,11 +53,7 @@ bool XiaomiJQJCY01YM::parse_device(const esp32_ble_tracker::ESPBTDevice &device) success = true; } - if (!success) { - return false; - } - - return true; + return success; } } // namespace xiaomi_jqjcy01ym diff --git a/esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.cpp b/esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.cpp index 6b8ecdeaff..1ecf0606a2 100644 --- a/esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.cpp +++ b/esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.cpp @@ -50,11 +50,7 @@ bool XiaomiLYWSD02::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { success = true; } - if (!success) { - return false; - } - - return true; + return success; } } // namespace xiaomi_lywsd02 diff --git a/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp b/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp index f0a2cee8d4..3eb0b68318 100644 --- a/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp +++ b/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp @@ -56,11 +56,7 @@ bool XiaomiLYWSD03MMC::parse_device(const esp32_ble_tracker::ESPBTDevice &device success = true; } - if (!success) { - return false; - } - - return true; + return success; } void XiaomiLYWSD03MMC::set_bindkey(const std::string &bindkey) { @@ -71,7 +67,7 @@ void XiaomiLYWSD03MMC::set_bindkey(const std::string &bindkey) { char temp[3] = {0}; for (int i = 0; i < 16; i++) { strncpy(temp, &(bindkey.c_str()[i * 2]), 2); - bindkey_[i] = std::strtoul(temp, NULL, 16); + bindkey_[i] = std::strtoul(temp, nullptr, 16); } } diff --git a/esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.cpp b/esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.cpp index 39bcd9df03..f16fce296b 100644 --- a/esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.cpp +++ b/esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.cpp @@ -50,11 +50,7 @@ bool XiaomiLYWSDCGQ::parse_device(const esp32_ble_tracker::ESPBTDevice &device) success = true; } - if (!success) { - return false; - } - - return true; + return success; } } // namespace xiaomi_lywsdcgq diff --git a/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp b/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp index e93a4b91ae..fdbb799ea6 100644 --- a/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp +++ b/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp @@ -56,11 +56,7 @@ bool XiaomiMHOC401::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { success = true; } - if (!success) { - return false; - } - - return true; + return success; } void XiaomiMHOC401::set_bindkey(const std::string &bindkey) { @@ -71,7 +67,7 @@ void XiaomiMHOC401::set_bindkey(const std::string &bindkey) { char temp[3] = {0}; for (int i = 0; i < 16; i++) { strncpy(temp, &(bindkey.c_str()[i * 2]), 2); - bindkey_[i] = std::strtoul(temp, NULL, 16); + bindkey_[i] = std::strtoul(temp, nullptr, 16); } } diff --git a/esphome/components/xiaomi_miscale2/xiaomi_miscale2.cpp b/esphome/components/xiaomi_miscale2/xiaomi_miscale2.cpp index cc63e46573..34c5a975c7 100644 --- a/esphome/components/xiaomi_miscale2/xiaomi_miscale2.cpp +++ b/esphome/components/xiaomi_miscale2/xiaomi_miscale2.cpp @@ -71,8 +71,8 @@ bool XiaomiMiscale2::parse_message(const std::vector &message, ParseRes return false; } - bool is_Stabilized = ((data[1] & (1 << 5)) != 0) ? true : false; - bool loadRemoved = ((data[1] & (1 << 7)) != 0) ? true : false; + bool is_stabilized = ((data[1] & (1 << 5)) != 0); + bool load_removed = ((data[1] & (1 << 7)) != 0); // weight, 2 bytes, 16-bit unsigned integer, 1 kg const int16_t weight = uint16_t(data[11]) | (uint16_t(data[12]) << 8); @@ -85,11 +85,7 @@ bool XiaomiMiscale2::parse_message(const std::vector &message, ParseRes const int16_t impedance = uint16_t(data[9]) | (uint16_t(data[10]) << 8); result.impedance = impedance; - if (!is_Stabilized || loadRemoved || impedance == 0 || impedance >= 3000) { - return false; - } - - return true; + return is_stabilized && !load_removed && impedance != 0 && impedance < 3000; } bool XiaomiMiscale2::report_results(const optional &result, const std::string &address) { diff --git a/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.cpp b/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.cpp index 739543ddb6..df1cede068 100644 --- a/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.cpp +++ b/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.cpp @@ -57,11 +57,7 @@ bool XiaomiMJYD02YLA::parse_device(const esp32_ble_tracker::ESPBTDevice &device) success = true; } - if (!success) { - return false; - } - - return true; + return success; } void XiaomiMJYD02YLA::set_bindkey(const std::string &bindkey) { @@ -72,7 +68,7 @@ void XiaomiMJYD02YLA::set_bindkey(const std::string &bindkey) { char temp[3] = {0}; for (int i = 0; i < 16; i++) { strncpy(temp, &(bindkey.c_str()[i * 2]), 2); - bindkey_[i] = std::strtoul(temp, NULL, 16); + bindkey_[i] = std::strtoul(temp, nullptr, 16); } } diff --git a/esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.cpp b/esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.cpp index c1eff2e066..53429c1806 100644 --- a/esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.cpp +++ b/esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.cpp @@ -46,11 +46,7 @@ bool XiaomiMUE4094RT::parse_device(const esp32_ble_tracker::ESPBTDevice &device) success = true; } - if (!success) { - return false; - } - - return true; + return success; } } // namespace xiaomi_mue4094rt diff --git a/esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.cpp b/esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.cpp index d6e2ebe5b2..7529bb7431 100644 --- a/esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.cpp +++ b/esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.cpp @@ -51,11 +51,7 @@ bool XiaomiWX08ZM::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { success = true; } - if (!success) { - return false; - } - - return true; + return success; } } // namespace xiaomi_wx08zm diff --git a/esphome/core/application.cpp b/esphome/core/application.cpp index fac17a8271..c0f68d80d6 100644 --- a/esphome/core/application.cpp +++ b/esphome/core/application.cpp @@ -111,11 +111,11 @@ void Application::loop() { } void ICACHE_RAM_ATTR HOT Application::feed_wdt() { - static uint32_t LAST_FEED = 0; + static uint32_t last_feed = 0; uint32_t now = millis(); - if (now - LAST_FEED > 3) { + if (now - last_feed > 3) { this->feed_wdt_arch_(); - LAST_FEED = now; + last_feed = now; #ifdef USE_STATUS_LED if (status_led::global_status_led != nullptr) { status_led::global_status_led->call(); diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 3cca6445b5..45bb3b82a5 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -11,7 +11,6 @@ #define ESPHOME_PROJECT_VERSION "v2" // Feature flags -#define USE_ADC_SENSOR_VCC #define USE_API #define USE_BINARY_SENSOR #define USE_CAPTIVE_PORTAL @@ -46,12 +45,12 @@ #define USE_ESP32_CAMERA #define USE_ETHERNET #define USE_IMPROV +#define USE_SOCKET_IMPL_BSD_SOCKETS #endif #ifdef ARDUINO_ARCH_ESP8266 +#define USE_ADC_SENSOR_VCC #define USE_SOCKET_IMPL_LWIP_TCP -#else -#define USE_SOCKET_IMPL_BSD_SOCKETS #endif #define USE_API_PLAINTEXT diff --git a/platformio.ini b/platformio.ini index f4dea3fcb9..a35e0c10fe 100644 --- a/platformio.ini +++ b/platformio.ini @@ -69,7 +69,6 @@ lib_deps = Update build_flags = ${common.build_flags} - -DUSE_ETHERNET src_filter = ${common.src_filter} - diff --git a/script/clang-tidy b/script/clang-tidy index c2f47bad11..a63ebc674a 100755 --- a/script/clang-tidy +++ b/script/clang-tidy @@ -16,7 +16,7 @@ import click import pexpect sys.path.append(os.path.dirname(__file__)) -from helpers import shlex_quote, get_output, \ +from helpers import shlex_quote, get_output, filter_grep, \ build_all_include, temp_header_file, git_ls_files, filter_changed, load_idedata @@ -27,13 +27,15 @@ def clang_options(idedata): # disable built-in include directories from the host '-nostdinc', '-nostdinc++', + # pretend we're an Xtensa compiler, which gates some features in the headers + '-D__XTENSA__', # allow to condition code on the presence of clang-tidy '-DCLANG_TIDY' ] # copy compiler flags, except those clang doesn't understand. cmd.extend(flag for flag in idedata['cxx_flags'].split(' ') - if flag not in ('-free', '-fipa-pta', '-mlongcalls', '-mtext-section-literals')) + if flag not in ('-free', '-fipa-pta', '-fstrict-volatile-bitfields', '-mlongcalls', '-mtext-section-literals')) # defines cmd.extend(f'-D{define}' for define in idedata['defines']) @@ -97,6 +99,8 @@ def main(): parser.add_argument('-j', '--jobs', type=int, default=multiprocessing.cpu_count(), help='number of tidy instances to be run in parallel.') + parser.add_argument('-e', '--environment', default='esp8266-tidy', + help='the PlatformIO environment to run against (esp8266-tidy or esp32-tidy)') parser.add_argument('files', nargs='*', default=[], help='files to be processed (regex on path)') parser.add_argument('--fix', action='store_true', help='apply fix-its') @@ -104,6 +108,7 @@ def main(): help='run clang-tidy in quiet mode') parser.add_argument('-c', '--changed', action='store_true', help='only run on changed files') + parser.add_argument('-g', '--grep', help='only run on files containing value') parser.add_argument('--split-num', type=int, help='split the files into X jobs.', default=None) parser.add_argument('--split-at', type=int, help='which split is this? starts at 1', @@ -126,7 +131,7 @@ def main(): """) return 1 - idedata = load_idedata("esp8266-tidy") + idedata = load_idedata(args.environment) options = clang_options(idedata) files = [] @@ -141,6 +146,9 @@ def main(): if args.changed: files = filter_changed(files) + if args.grep: + files = filter_grep(files, args.grep) + files.sort() if args.split_num: diff --git a/script/helpers.py b/script/helpers.py index 7200078822..d32e0eafbe 100644 --- a/script/helpers.py +++ b/script/helpers.py @@ -92,6 +92,16 @@ def filter_changed(files): return files +def filter_grep(files, value): + matched = [] + for file in files: + with open(file, "r") as handle: + contents = handle.read() + if value in contents: + matched.append(file) + return matched + + def git_ls_files(patterns=None): command = ["git", "ls-files", "-s"] if patterns is not None: From ed7983af41e16e8af56b39913bd62918dfb6bc9f Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 13 Sep 2021 18:52:53 +0200 Subject: [PATCH 023/207] Fix API socket issues (#2288) * Fix API socket issues * Fix compile error against beta * Format --- esphome/components/api/api_connection.cpp | 74 ++++++++++++------- esphome/components/api/api_connection.h | 16 +--- esphome/components/api/api_frame_helper.cpp | 70 +++++++++++++----- esphome/components/api/api_frame_helper.h | 3 +- esphome/components/api/api_server.cpp | 2 +- .../components/socket/lwip_raw_tcp_impl.cpp | 54 +++++++++----- 6 files changed, 142 insertions(+), 77 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index a530554155..58a5662055 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -36,19 +36,14 @@ void APIConnection::start() { APIError err = helper_->init(); if (err != APIError::OK) { - ESP_LOGW(TAG, "Helper init failed: %d errno=%d", (int) err, errno); - remove_ = true; + on_fatal_error(); + ESP_LOGW(TAG, "%s: Helper init failed: %s errno=%d", client_info_.c_str(), api_error_to_str(err), errno); return; } client_info_ = helper_->getpeername(); helper_->set_log_info(client_info_); } -void APIConnection::force_disconnect_client() { - this->helper_->close(); - this->remove_ = true; -} - void APIConnection::loop() { if (this->remove_) return; @@ -57,9 +52,11 @@ void APIConnection::loop() { // when network is disconnected force disconnect immediately // don't wait for timeout this->on_fatal_error(); + ESP_LOGW(TAG, "%s: Network unavailable, disconnecting", client_info_.c_str()); return; } if (this->next_close_) { + // requested a disconnect this->helper_->close(); this->remove_ = true; return; @@ -68,7 +65,7 @@ void APIConnection::loop() { APIError err = helper_->loop(); if (err != APIError::OK) { on_fatal_error(); - ESP_LOGW(TAG, "%s: Socket operation failed: %d", client_info_.c_str(), (int) err); + ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", client_info_.c_str(), api_error_to_str(err), errno); return; } ReadPacketBuffer buffer; @@ -77,7 +74,11 @@ void APIConnection::loop() { // pass } else if (err != APIError::OK) { on_fatal_error(); - ESP_LOGW(TAG, "%s: Reading failed: %d", client_info_.c_str(), (int) err); + if (err == APIError::SOCKET_READ_FAILED && errno == ECONNRESET) { + ESP_LOGW(TAG, "%s: Connection reset", client_info_.c_str()); + } else { + ESP_LOGW(TAG, "%s: Reading failed: %s errno=%d", client_info_.c_str(), api_error_to_str(err), errno); + } return; } else { this->last_traffic_ = millis(); @@ -95,8 +96,8 @@ void APIConnection::loop() { if (this->sent_ping_) { // Disconnect if not responded within 2.5*keepalive if (now - this->last_traffic_ > (keepalive * 5) / 2) { - this->force_disconnect_client(); - ESP_LOGW(TAG, "'%s' didn't respond to ping request in time. Disconnecting...", this->client_info_.c_str()); + on_fatal_error(); + ESP_LOGW(TAG, "%s didn't respond to ping request in time. Disconnecting...", this->client_info_.c_str()); } } else if (now - this->last_traffic_ > keepalive) { this->sent_ping_ = true; @@ -124,12 +125,40 @@ void APIConnection::loop() { } } #endif + + if (state_subs_at_ != -1) { + const auto &subs = this->parent_->get_state_subs(); + if (state_subs_at_ >= subs.size()) { + state_subs_at_ = -1; + } else { + auto &it = subs[state_subs_at_]; + SubscribeHomeAssistantStateResponse resp; + resp.entity_id = it.entity_id; + resp.attribute = it.attribute.value(); + if (this->send_subscribe_home_assistant_state_response(resp)) { + state_subs_at_++; + } + } + } } std::string get_default_unique_id(const std::string &component_type, Nameable *nameable) { return App.get_name() + component_type + nameable->get_object_id(); } +DisconnectResponse APIConnection::disconnect(const DisconnectRequest &msg) { + // remote initiated disconnect_client + // don't close yet, we still need to send the disconnect response + // close will happen on next loop + ESP_LOGD(TAG, "%s requested disconnected", client_info_.c_str()); + this->next_close_ = true; + DisconnectResponse resp; + return resp; +} +void APIConnection::on_disconnect_response(const DisconnectResponse &value) { + // pass +} + #ifdef USE_BINARY_SENSOR bool APIConnection::send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor, bool state) { if (!this->state_subscription_) @@ -703,7 +732,7 @@ ConnectResponse APIConnection::connect(const ConnectRequest &msg) { // bool invalid_password = 1; resp.invalid_password = !correct; if (correct) { - ESP_LOGD(TAG, "Client '%s' connected successfully!", this->client_info_.c_str()); + ESP_LOGD(TAG, "%s: Connected successfully", this->client_info_.c_str()); this->connection_state_ = ConnectionState::AUTHENTICATED; #ifdef USE_HOMEASSISTANT_TIME @@ -749,15 +778,7 @@ void APIConnection::execute_service(const ExecuteServiceRequest &msg) { } } void APIConnection::subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) { - for (auto &it : this->parent_->get_state_subs()) { - SubscribeHomeAssistantStateResponse resp; - resp.entity_id = it.entity_id; - resp.attribute = it.attribute.value(); - if (!this->send_subscribe_home_assistant_state_response(resp)) { - this->on_fatal_error(); - return; - } - } + state_subs_at_ = 0; } bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) { if (this->remove_) @@ -770,7 +791,11 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) return false; if (err != APIError::OK) { on_fatal_error(); - ESP_LOGW(TAG, "%s: Packet write failed %d errno=%d", client_info_.c_str(), (int) err, errno); + if (err == APIError::SOCKET_WRITE_FAILED && errno == ECONNRESET) { + ESP_LOGW(TAG, "%s: Connection reset", client_info_.c_str()); + } else { + ESP_LOGW(TAG, "%s: Packet write failed %s errno=%d", client_info_.c_str(), api_error_to_str(err), errno); + } return false; } this->last_traffic_ = millis(); @@ -778,14 +803,13 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) } void APIConnection::on_unauthenticated_access() { this->on_fatal_error(); - ESP_LOGD(TAG, "'%s' tried to access without authentication.", this->client_info_.c_str()); + ESP_LOGD(TAG, "%s: tried to access without authentication.", this->client_info_.c_str()); } void APIConnection::on_no_setup_connection() { this->on_fatal_error(); - ESP_LOGD(TAG, "'%s' tried to access without full connection.", this->client_info_.c_str()); + ESP_LOGD(TAG, "%s: tried to access without full connection.", this->client_info_.c_str()); } void APIConnection::on_fatal_error() { - ESP_LOGV(TAG, "Error: Disconnecting %s", this->client_info_.c_str()); this->helper_->close(); this->remove_ = true; } diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index a1788bbede..a1f1769a19 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -16,7 +16,6 @@ class APIConnection : public APIServerConnection { virtual ~APIConnection() = default; void start(); - void force_disconnect_client(); void loop(); bool send_list_info_done() { @@ -88,10 +87,7 @@ class APIConnection : public APIServerConnection { } #endif - void on_disconnect_response(const DisconnectResponse &value) override { - this->helper_->close(); - this->remove_ = true; - } + void on_disconnect_response(const DisconnectResponse &value) override; void on_ping_response(const PingResponse &value) override { // we initiated ping this->sent_ping_ = false; @@ -102,14 +98,7 @@ class APIConnection : public APIServerConnection { #endif HelloResponse hello(const HelloRequest &msg) override; ConnectResponse connect(const ConnectRequest &msg) override; - DisconnectResponse disconnect(const DisconnectRequest &msg) override { - // remote initiated disconnect_client - // don't close yet, we still need to send the disconnect response - // close will happen on next loop - this->next_close_ = true; - DisconnectResponse resp; - return resp; - } + DisconnectResponse disconnect(const DisconnectRequest &msg) override; PingResponse ping(const PingRequest &msg) override { return {}; } DeviceInfoResponse device_info(const DeviceInfoRequest &msg) override; void list_entities(const ListEntitiesRequest &msg) override { this->list_entities_iterator_.begin(); } @@ -177,6 +166,7 @@ class APIConnection : public APIServerConnection { APIServer *parent_; InitialStateIterator initial_state_iterator_; ListEntitiesIterator list_entities_iterator_; + int state_subs_at_ = -1; }; } // namespace api diff --git a/esphome/components/api/api_frame_helper.cpp b/esphome/components/api/api_frame_helper.cpp index 520a5c2caf..c064c7278f 100644 --- a/esphome/components/api/api_frame_helper.cpp +++ b/esphome/components/api/api_frame_helper.cpp @@ -17,6 +17,54 @@ bool is_would_block(ssize_t ret) { return ret == 0; } +const char *api_error_to_str(APIError err) { + // not using switch to ensure compiler doesn't try to build a big table out of it + if (err == APIError::OK) { + return "OK"; + } else if (err == APIError::WOULD_BLOCK) { + return "WOULD_BLOCK"; + } else if (err == APIError::BAD_HANDSHAKE_PACKET_LEN) { + return "BAD_HANDSHAKE_PACKET_LEN"; + } else if (err == APIError::BAD_INDICATOR) { + return "BAD_INDICATOR"; + } else if (err == APIError::BAD_DATA_PACKET) { + return "BAD_DATA_PACKET"; + } else if (err == APIError::TCP_NODELAY_FAILED) { + return "TCP_NODELAY_FAILED"; + } else if (err == APIError::TCP_NONBLOCKING_FAILED) { + return "TCP_NONBLOCKING_FAILED"; + } else if (err == APIError::CLOSE_FAILED) { + return "CLOSE_FAILED"; + } else if (err == APIError::SHUTDOWN_FAILED) { + return "SHUTDOWN_FAILED"; + } else if (err == APIError::BAD_STATE) { + return "BAD_STATE"; + } else if (err == APIError::BAD_ARG) { + return "BAD_ARG"; + } else if (err == APIError::SOCKET_READ_FAILED) { + return "SOCKET_READ_FAILED"; + } else if (err == APIError::SOCKET_WRITE_FAILED) { + return "SOCKET_WRITE_FAILED"; + } else if (err == APIError::HANDSHAKESTATE_READ_FAILED) { + return "HANDSHAKESTATE_READ_FAILED"; + } else if (err == APIError::HANDSHAKESTATE_WRITE_FAILED) { + return "HANDSHAKESTATE_WRITE_FAILED"; + } else if (err == APIError::HANDSHAKESTATE_BAD_STATE) { + return "HANDSHAKESTATE_BAD_STATE"; + } else if (err == APIError::CIPHERSTATE_DECRYPT_FAILED) { + return "CIPHERSTATE_DECRYPT_FAILED"; + } else if (err == APIError::CIPHERSTATE_ENCRYPT_FAILED) { + return "CIPHERSTATE_ENCRYPT_FAILED"; + } else if (err == APIError::OUT_OF_MEMORY) { + return "OUT_OF_MEMORY"; + } else if (err == APIError::HANDSHAKESTATE_SETUP_FAILED) { + return "HANDSHAKESTATE_SETUP_FAILED"; + } else if (err == APIError::HANDSHAKESTATE_SPLIT_FAILED) { + return "HANDSHAKESTATE_SPLIT_FAILED"; + } + return "UNKNOWN"; +} + #define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s: " msg, info_.c_str(), ##__VA_ARGS__) #ifdef USE_API_NOISE @@ -808,14 +856,12 @@ APIError APIPlaintextFrameHelper::try_send_tx_buf_() { // try send from tx_buf while (state_ != State::CLOSED && !tx_buf_.empty()) { ssize_t sent = socket_->write(tx_buf_.data(), tx_buf_.size()); - if (sent == -1) { - if (errno == EWOULDBLOCK || errno == EAGAIN) - break; + if (is_would_block(sent)) { + break; + } else if (sent == -1) { state_ = State::FAILED; HELPER_LOG("Socket write failed with errno %d", errno); return APIError::SOCKET_WRITE_FAILED; - } else if (sent == 0) { - break; } // TODO: inefficient if multiple packets in txbuf // replace with deque of buffers @@ -869,20 +915,6 @@ APIError APIPlaintextFrameHelper::write_raw_(const uint8_t *data, size_t len) { // fully sent return APIError::OK; } -APIError APIPlaintextFrameHelper::write_frame_(const uint8_t *data, size_t len) { - APIError aerr; - - uint8_t header[3]; - header[0] = 0x01; // indicator - header[1] = (uint8_t)(len >> 8); - header[2] = (uint8_t) len; - - aerr = write_raw_(header, 3); - if (aerr != APIError::OK) - return aerr; - aerr = write_raw_(data, len); - return aerr; -} APIError APIPlaintextFrameHelper::close() { state_ = State::CLOSED; diff --git a/esphome/components/api/api_frame_helper.h b/esphome/components/api/api_frame_helper.h index 7189bc4b4b..a8974cd25f 100644 --- a/esphome/components/api/api_frame_helper.h +++ b/esphome/components/api/api_frame_helper.h @@ -53,6 +53,8 @@ enum class APIError : int { HANDSHAKESTATE_SPLIT_FAILED = 1020, }; +const char *api_error_to_str(APIError err); + class APIFrameHelper { public: virtual APIError init() = 0; @@ -150,7 +152,6 @@ class APIPlaintextFrameHelper : public APIFrameHelper { APIError try_read_frame_(ParsedFrame *frame); APIError try_send_tx_buf_(); - APIError write_frame_(const uint8_t *data, size_t len); APIError write_raw_(const uint8_t *data, size_t len); std::unique_ptr socket_; diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index 1ef567ab57..d84a35747c 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -105,7 +105,7 @@ void APIServer::loop() { [](const std::unique_ptr &conn) { return !conn->remove_; }); // print disconnection messages for (auto it = new_end; it != this->clients_.end(); ++it) { - ESP_LOGD(TAG, "Disconnecting %s", (*it)->client_info_.c_str()); + ESP_LOGV(TAG, "Removing connection to %s", (*it)->client_info_.c_str()); } // resize vector this->clients_.erase(new_end, this->clients_.end()); diff --git a/esphome/components/socket/lwip_raw_tcp_impl.cpp b/esphome/components/socket/lwip_raw_tcp_impl.cpp index 29e1b23823..2147e36632 100644 --- a/esphome/components/socket/lwip_raw_tcp_impl.cpp +++ b/esphome/components/socket/lwip_raw_tcp_impl.cpp @@ -109,14 +109,17 @@ class LWIPRawImpl : public Socket { LWIP_LOG("tcp_bind(%p ip=%u port=%u)", pcb_, ip.addr, port); err_t err = tcp_bind(pcb_, &ip, port); if (err == ERR_USE) { + LWIP_LOG(" -> err ERR_USE"); errno = EADDRINUSE; return -1; } if (err == ERR_VAL) { + LWIP_LOG(" -> err ERR_VAL"); errno = EINVAL; return -1; } if (err != ERR_OK) { + LWIP_LOG(" -> err %d", err); errno = EIO; return -1; } @@ -124,12 +127,13 @@ class LWIPRawImpl : public Socket { } int close() override { if (pcb_ == nullptr) { - errno = EBADF; + errno = ECONNRESET; return -1; } LWIP_LOG("tcp_close(%p)", pcb_); err_t err = tcp_close(pcb_); if (err != ERR_OK) { + LWIP_LOG(" -> err %d", err); tcp_abort(pcb_); pcb_ = nullptr; errno = err == ERR_MEM ? ENOMEM : EIO; @@ -140,7 +144,7 @@ class LWIPRawImpl : public Socket { } int shutdown(int how) override { if (pcb_ == nullptr) { - errno = EBADF; + errno = ECONNRESET; return -1; } bool shut_rx = false, shut_tx = false; @@ -157,6 +161,7 @@ class LWIPRawImpl : public Socket { LWIP_LOG("tcp_shutdown(%p shut_rx=%d shut_tx=%d)", pcb_, shut_rx ? 1 : 0, shut_tx ? 1 : 0); err_t err = tcp_shutdown(pcb_, shut_rx, shut_tx); if (err != ERR_OK) { + LWIP_LOG(" -> err %d", err); errno = err == ERR_MEM ? ENOMEM : EIO; return -1; } @@ -165,7 +170,7 @@ class LWIPRawImpl : public Socket { int getpeername(struct sockaddr *name, socklen_t *addrlen) override { if (pcb_ == nullptr) { - errno = EBADF; + errno = ECONNRESET; return -1; } if (name == nullptr || addrlen == nullptr) { @@ -185,7 +190,7 @@ class LWIPRawImpl : public Socket { } std::string getpeername() override { if (pcb_ == nullptr) { - errno = EBADF; + errno = ECONNRESET; return ""; } char buffer[24]; @@ -196,7 +201,7 @@ class LWIPRawImpl : public Socket { } int getsockname(struct sockaddr *name, socklen_t *addrlen) override { if (pcb_ == nullptr) { - errno = EBADF; + errno = ECONNRESET; return -1; } if (name == nullptr || addrlen == nullptr) { @@ -216,7 +221,7 @@ class LWIPRawImpl : public Socket { } std::string getsockname() override { if (pcb_ == nullptr) { - errno = EBADF; + errno = ECONNRESET; return ""; } char buffer[24]; @@ -227,7 +232,7 @@ class LWIPRawImpl : public Socket { } int getsockopt(int level, int optname, void *optval, socklen_t *optlen) override { if (pcb_ == nullptr) { - errno = EBADF; + errno = ECONNRESET; return -1; } if (optlen == nullptr || optval == nullptr) { @@ -261,7 +266,7 @@ class LWIPRawImpl : public Socket { } int setsockopt(int level, int optname, const void *optval, socklen_t optlen) override { if (pcb_ == nullptr) { - errno = EBADF; + errno = ECONNRESET; return -1; } if (level == SOL_SOCKET && optname == SO_REUSEADDR) { @@ -314,7 +319,7 @@ class LWIPRawImpl : public Socket { } ssize_t read(void *buf, size_t len) override { if (pcb_ == nullptr) { - errno = EBADF; + errno = ECONNRESET; return -1; } if (rx_closed_ && rx_buf_ == nullptr) { @@ -368,7 +373,7 @@ class LWIPRawImpl : public Socket { } ssize_t write(const void *buf, size_t len) override { if (pcb_ == nullptr) { - errno = EBADF; + errno = ECONNRESET; return -1; } if (len == 0) @@ -386,24 +391,37 @@ class LWIPRawImpl : public Socket { LWIP_LOG("tcp_write(%p buf=%p %u)", pcb_, buf, to_send); err_t err = tcp_write(pcb_, buf, to_send, TCP_WRITE_FLAG_COPY); if (err == ERR_MEM) { + LWIP_LOG(" -> err ERR_MEM"); errno = EWOULDBLOCK; return -1; } if (err != ERR_OK) { - errno = EIO; + LWIP_LOG(" -> err %d", err); + errno = ECONNRESET; return -1; } - LWIP_LOG("tcp_output(%p)", pcb_); - err = tcp_output(pcb_); - if (err != ERR_OK) { - errno = EIO; - return -1; + if (tcp_nagle_disabled(pcb_)) { + LWIP_LOG("tcp_output(%p)", pcb_); + err = tcp_output(pcb_); + if (err == ERR_ABRT) { + LWIP_LOG(" -> err ERR_ABRT"); + // sometimes lwip returns ERR_ABRT for no apparent reason + // the connection works fine afterwards, and back with ESPAsyncTCP we + // indirectly also ignored this error + // FIXME: figure out where this is returned and what it means in this context + return to_send; + } + if (err != ERR_OK) { + LWIP_LOG(" -> err %d", err); + errno = ECONNRESET; + return -1; + } } return to_send; } int setblocking(bool blocking) override { if (pcb_ == nullptr) { - errno = EBADF; + errno = ECONNRESET; return -1; } if (blocking) { @@ -466,7 +484,7 @@ class LWIPRawImpl : public Socket { static void s_err_fn(void *arg, err_t err) { LWIPRawImpl *arg_this = reinterpret_cast(arg); - return arg_this->err_fn(err); + arg_this->err_fn(err); } static err_t s_recv_fn(void *arg, struct tcp_pcb *pcb, struct pbuf *pb, err_t err) { From 924df1e7de1a6f11d5ab75ed1e94ed3dcd739134 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Mon, 13 Sep 2021 18:55:04 +0200 Subject: [PATCH 024/207] Run clang-tidy against Arduino 3 (#2146) * Add macros header with more usable Arduino version defines * Change Arduino version checking to use our version defines * Add missing ESP8266 check * Rename Arduino version macro to ARDUINO_VERSION_CODE * Upgrade clang-tidy to use Arduino 3 * Fix clang-tidy warnings * Upgrade NeoPixelBus to upstream 2.6.7 * Use Arduino-version-appropriate API to set redirect flags * Remove now unnecessary CLANG_TIDY ifdefs * Add preprocessor hackery to avoid including pgmspace.h * Bump base image to 4.1.1 and update lint * Fix nfctag * Fix make_unique ambiguous * Fix ignore name * Fix ambiguous v2 * Remove unused begin * Cast time_t to prevent issues on platforms where time_t is 32bit Co-authored-by: Otto winter --- .clang-tidy | 5 +++- .github/workflows/ci.yml | 2 +- .github/workflows/docker-lint-build.yml | 4 +-- docker/Dockerfile.dev | 2 +- docker/build.py | 2 +- esphome/components/adc/adc_sensor.cpp | 2 +- esphome/components/debug/debug_component.cpp | 11 ++++---- .../deep_sleep/deep_sleep_component.cpp | 2 +- .../components/http_request/http_request.cpp | 11 +++++--- esphome/components/light/light_color_values.h | 1 + esphome/components/neopixelbus/light.py | 2 +- esphome/components/nextion/nextion_upload.cpp | 25 +++++++++++++------ esphome/components/nfc/ndef_message.cpp | 4 +-- esphome/components/nfc/nfc_tag.h | 4 +-- esphome/components/pn532/pn532.cpp | 8 +++--- .../components/pn532/pn532_mifare_classic.cpp | 8 +++--- .../pn532/pn532_mifare_ultralight.cpp | 10 ++++---- esphome/components/sgp40/sgp40.cpp | 1 + .../components/shutdown/shutdown_switch.cpp | 2 +- esphome/components/spi/spi.cpp | 6 ++--- esphome/components/time/automation.cpp | 4 +-- esphome/components/uart/uart_esp8266.cpp | 6 ++--- .../web_server_base/web_server_base.cpp | 1 + esphome/core/application.cpp | 4 +-- esphome/core/application_esp8266.cpp | 4 ++- esphome/core/helpers.h | 7 ------ esphome/core/util.cpp | 4 +-- esphome/core/util.h | 2 +- platformio.ini | 5 ++-- script/clang-tidy | 15 +++++++++++ 30 files changed, 98 insertions(+), 66 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 8451a2bdd4..890eb18608 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -63,11 +63,15 @@ Checks: >- -misc-no-recursion, -misc-unused-parameters, -modernize-avoid-c-arrays, + -modernize-avoid-bind, + -modernize-concat-nested-namespaces, -modernize-return-braced-init-list, -modernize-use-auto, -modernize-use-default-member-init, -modernize-use-equals-default, -modernize-use-trailing-return-type, + -modernize-make-unique, + -modernize-use-nodiscard, -mpi-*, -objc-*, -readability-braces-around-statements, @@ -86,7 +90,6 @@ Checks: >- -readability-redundant-string-init, -readability-uppercase-literal-suffix, -readability-use-anyofallof, - -warnings-as-errors WarningsAsErrors: '*' AnalyzeTemporaryDtors: false FormatStyle: google diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eb93c36d19..b00e1b3835 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,7 @@ jobs: # cpp lint job runs with esphome-lint docker image so that clang-format-* # doesn't have to be installed - container: ghcr.io/esphome/esphome-lint:1.1 + container: ghcr.io/esphome/esphome-lint:1.2 steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/docker-lint-build.yml b/.github/workflows/docker-lint-build.yml index 32aec87cdd..8350d4c719 100644 --- a/.github/workflows/docker-lint-build.yml +++ b/.github/workflows/docker-lint-build.yml @@ -29,7 +29,7 @@ jobs: python-version: '3.9' - name: Set TAG run: | - echo "TAG=1.1" >> $GITHUB_ENV + echo "TAG=1.2" >> $GITHUB_ENV - name: Run build run: | @@ -74,7 +74,7 @@ jobs: python-version: '3.9' - name: Set TAG run: | - echo "TAG=1.1" >> $GITHUB_ENV + echo "TAG=1.2" >> $GITHUB_ENV - name: Enable experimental manifest support run: | mkdir -p ~/.docker diff --git a/docker/Dockerfile.dev b/docker/Dockerfile.dev index 27eaa18da9..c646ce989b 100644 --- a/docker/Dockerfile.dev +++ b/docker/Dockerfile.dev @@ -1 +1 @@ -FROM esphome/esphome-lint:1.1 +FROM esphome/esphome-lint:1.2 diff --git a/docker/build.py b/docker/build.py index 54a279f845..039d7dc15b 100755 --- a/docker/build.py +++ b/docker/build.py @@ -24,7 +24,7 @@ TYPE_LINT = 'lint' TYPES = [TYPE_DOCKER, TYPE_HA_ADDON, TYPE_LINT] -BASE_VERSION = "3.6.0" +BASE_VERSION = "4.1.1" parser = argparse.ArgumentParser() diff --git a/esphome/components/adc/adc_sensor.cpp b/esphome/components/adc/adc_sensor.cpp index 78f12a6b9e..4106a0c49b 100644 --- a/esphome/components/adc/adc_sensor.cpp +++ b/esphome/components/adc/adc_sensor.cpp @@ -148,7 +148,7 @@ float ADCSensor::sample() { #ifdef ARDUINO_ARCH_ESP8266 #ifdef USE_ADC_SENSOR_VCC - return ESP.getVcc() / 1024.0f; + return ESP.getVcc() / 1024.0f; // NOLINT(readability-static-accessed-through-instance) #else return analogRead(this->pin_) / 1024.0f; // NOLINT #endif diff --git a/esphome/components/debug/debug_component.cpp b/esphome/components/debug/debug_component.cpp index 668792c7b1..11590a0978 100644 --- a/esphome/components/debug/debug_component.cpp +++ b/esphome/components/debug/debug_component.cpp @@ -21,11 +21,11 @@ void DebugComponent::dump_config() { #endif ESP_LOGD(TAG, "ESPHome version %s", ESPHOME_VERSION); - this->free_heap_ = ESP.getFreeHeap(); + this->free_heap_ = ESP.getFreeHeap(); // NOLINT(readability-static-accessed-through-instance) ESP_LOGD(TAG, "Free Heap Size: %u bytes", this->free_heap_); const char *flash_mode; - switch (ESP.getFlashChipMode()) { + switch (ESP.getFlashChipMode()) { // NOLINT(readability-static-accessed-through-instance) case FM_QIO: flash_mode = "QIO"; break; @@ -49,6 +49,7 @@ void DebugComponent::dump_config() { default: flash_mode = "UNKNOWN"; } + // NOLINTNEXTLINE(readability-static-accessed-through-instance) ESP_LOGD(TAG, "Flash Chip: Size=%ukB Speed=%uMHz Mode=%s", ESP.getFlashChipSize() / 1024, ESP.getFlashChipSpeed() / 1000000, flash_mode); @@ -87,7 +88,7 @@ void DebugComponent::dump_config() { ESP_LOGD(TAG, "ESP-IDF Version: %s", esp_get_idf_version()); - std::string mac = uint64_to_string(ESP.getEfuseMac()); + std::string mac = uint64_to_string(ESP.getEfuseMac()); // NOLINT(readability-static-accessed-through-instance) ESP_LOGD(TAG, "EFuse MAC: %s", mac.c_str()); const char *reset_reason; @@ -186,7 +187,7 @@ void DebugComponent::dump_config() { ESP_LOGD(TAG, "Wakeup Reason: %s", wakeup_reason); #endif -#ifdef ARDUINO_ARCH_ESP8266 +#if defined(ARDUINO_ARCH_ESP8266) && !defined(CLANG_TIDY) ESP_LOGD(TAG, "Chip ID: 0x%08X", ESP.getChipId()); ESP_LOGD(TAG, "SDK Version: %s", ESP.getSdkVersion()); ESP_LOGD(TAG, "Core Version: %s", ESP.getCoreVersion().c_str()); @@ -198,7 +199,7 @@ void DebugComponent::dump_config() { #endif } void DebugComponent::loop() { - uint32_t new_free_heap = ESP.getFreeHeap(); + uint32_t new_free_heap = ESP.getFreeHeap(); // NOLINT(readability-static-accessed-through-instance) if (new_free_heap < this->free_heap_ / 2) { this->free_heap_ = new_free_heap; ESP_LOGD(TAG, "Free Heap Size: %u bytes", this->free_heap_); diff --git a/esphome/components/deep_sleep/deep_sleep_component.cpp b/esphome/components/deep_sleep/deep_sleep_component.cpp index de5672759a..00f660f41a 100644 --- a/esphome/components/deep_sleep/deep_sleep_component.cpp +++ b/esphome/components/deep_sleep/deep_sleep_component.cpp @@ -84,7 +84,7 @@ void DeepSleepComponent::begin_sleep(bool manual) { #endif #ifdef ARDUINO_ARCH_ESP8266 - ESP.deepSleep(*this->sleep_duration_); + ESP.deepSleep(*this->sleep_duration_); // NOLINT(readability-static-accessed-through-instance) #endif } float DeepSleepComponent::get_setup_priority() const { return setup_priority::LATE; } diff --git a/esphome/components/http_request/http_request.cpp b/esphome/components/http_request/http_request.cpp index bf5b24c4f2..0d4e425147 100644 --- a/esphome/components/http_request/http_request.cpp +++ b/esphome/components/http_request/http_request.cpp @@ -1,4 +1,5 @@ #include "http_request.h" +#include "esphome/core/macros.h" #include "esphome/core/log.h" namespace esphome { @@ -31,11 +32,15 @@ void HttpRequestComponent::send(const std::vector begin_status = this->client_.begin(url); #endif #ifdef ARDUINO_ARCH_ESP8266 -#ifndef CLANG_TIDY +#if ARDUINO_VERSION_CODE >= VERSION_CODE(2, 7, 0) + this->client_.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); +#elif ARDUINO_VERSION_CODE >= VERSION_CODE(2, 6, 0) this->client_.setFollowRedirects(true); - this->client_.setRedirectLimit(3); - begin_status = this->client_.begin(*this->get_wifi_client_(), url); #endif +#if ARDUINO_VERSION_CODE >= VERSION_CODE(2, 6, 0) + this->client_.setRedirectLimit(3); +#endif + begin_status = this->client_.begin(*this->get_wifi_client_(), url); #endif if (!begin_status) { diff --git a/esphome/components/light/light_color_values.h b/esphome/components/light/light_color_values.h index dd74c396c6..ffbe378ee3 100644 --- a/esphome/components/light/light_color_values.h +++ b/esphome/components/light/light_color_values.h @@ -2,6 +2,7 @@ #include "esphome/core/helpers.h" #include "color_mode.h" +#include namespace esphome { namespace light { diff --git a/esphome/components/neopixelbus/light.py b/esphome/components/neopixelbus/light.py index 59d784a614..575f69b731 100644 --- a/esphome/components/neopixelbus/light.py +++ b/esphome/components/neopixelbus/light.py @@ -205,4 +205,4 @@ async def to_code(config): cg.add(var.set_pixel_order(getattr(ESPNeoPixelOrder, config[CONF_TYPE]))) # https://github.com/Makuna/NeoPixelBus/blob/master/library.json - cg.add_library("NeoPixelBus-esphome", "2.6.2") + cg.add_library("NeoPixelBus", "2.6.7") diff --git a/esphome/components/nextion/nextion_upload.cpp b/esphome/components/nextion/nextion_upload.cpp index 35b904357b..f864a397cc 100644 --- a/esphome/components/nextion/nextion_upload.cpp +++ b/esphome/components/nextion/nextion_upload.cpp @@ -1,6 +1,7 @@ #include "nextion.h" #include "esphome/core/application.h" +#include "esphome/core/macros.h" #include "esphome/core/util.h" #include "esphome/core/log.h" @@ -26,8 +27,12 @@ int Nextion::upload_by_chunks_(HTTPClient *http, int range_start) { range_end = this->tft_size_; #ifdef ARDUINO_ARCH_ESP8266 -#ifndef CLANG_TIDY +#if ARDUINO_VERSION_CODE >= VERSION_CODE(2, 7, 0) http->setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); +#elif ARDUINO_VERSION_CODE >= VERSION_CODE(2, 6, 0) + http->setFollowRedirects(true); +#endif +#if ARDUINO_VERSION_CODE >= VERSION_CODE(2, 6, 0) http->setRedirectLimit(3); #endif #endif @@ -44,10 +49,8 @@ int Nextion::upload_by_chunks_(HTTPClient *http, int range_start) { #ifdef ARDUINO_ARCH_ESP32 begin_status = http->begin(this->tft_url_.c_str()); #endif -#ifndef CLANG_TIDY #ifdef ARDUINO_ARCH_ESP8266 begin_status = http->begin(*this->get_wifi_client_(), this->tft_url_.c_str()); -#endif #endif ++tries; @@ -140,11 +143,15 @@ void Nextion::upload_tft() { begin_status = http.begin(this->tft_url_.c_str()); #endif #ifdef ARDUINO_ARCH_ESP8266 -#ifndef CLANG_TIDY +#if ARDUINO_VERSION_CODE >= VERSION_CODE(2, 7, 0) http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); - http.setRedirectLimit(3); - begin_status = http.begin(*this->get_wifi_client_(), this->tft_url_.c_str()); +#elif ARDUINO_VERSION_CODE >= VERSION_CODE(2, 6, 0) + http.setFollowRedirects(true); #endif +#if ARDUINO_VERSION_CODE >= VERSION_CODE(2, 6, 0) + http.setRedirectLimit(3); +#endif + begin_status = http.begin(*this->get_wifi_client_(), this->tft_url_.c_str()); #endif if (!begin_status) { @@ -256,6 +263,7 @@ void Nextion::upload_tft() { } } #else + // NOLINTNEXTLINE(readability-static-accessed-through-instance) uint32_t chunk_size = ESP.getFreeHeap() < 10240 ? 4096 : 8192; #endif @@ -270,6 +278,7 @@ void Nextion::upload_tft() { } } else { #endif + // NOLINTNEXTLINE(readability-static-accessed-through-instance) ESP_LOGD(TAG, "Allocating buffer size %d, Heap size is %u", chunk_size, ESP.getFreeHeap()); this->transfer_buffer_ = new (std::nothrow) uint8_t[chunk_size]; // NOLINT(cppcoreguidelines-owning-memory) if (this->transfer_buffer_ == nullptr) { // Try a smaller size @@ -288,6 +297,7 @@ void Nextion::upload_tft() { this->transfer_buffer_size_ = chunk_size; } + // NOLINTNEXTLINE(readability-static-accessed-through-instance) ESP_LOGD(TAG, "Updating tft from \"%s\" with a file size of %d using %zu chunksize, Heap Size %d", this->tft_url_.c_str(), this->content_length_, this->transfer_buffer_size_, ESP.getFreeHeap()); @@ -299,6 +309,7 @@ void Nextion::upload_tft() { this->upload_end_(); } App.feed_wdt(); + // NOLINTNEXTLINE(readability-static-accessed-through-instance) ESP_LOGD(TAG, "Heap Size %d, Bytes left %d", ESP.getFreeHeap(), this->content_length_); } ESP_LOGD(TAG, "Successfully updated Nextion!"); @@ -311,7 +322,7 @@ void Nextion::upload_end_() { this->soft_reset(); delay(1500); // NOLINT ESP_LOGD(TAG, "Restarting esphome"); - ESP.restart(); + ESP.restart(); // NOLINT(readability-static-accessed-through-instance) } #ifdef ARDUINO_ARCH_ESP8266 diff --git a/esphome/components/nfc/ndef_message.cpp b/esphome/components/nfc/ndef_message.cpp index b1554f41ae..147392940c 100644 --- a/esphome/components/nfc/ndef_message.cpp +++ b/esphome/components/nfc/ndef_message.cpp @@ -83,11 +83,11 @@ bool NdefMessage::add_text_record(const std::string &text) { return this->add_te bool NdefMessage::add_text_record(const std::string &text, const std::string &encoding) { std::string payload = to_string(text.length()) + encoding + text; - return this->add_record(make_unique(TNF_WELL_KNOWN, "T", payload)); + return this->add_record(std::unique_ptr{new NdefRecord(TNF_WELL_KNOWN, "T", payload)}); } bool NdefMessage::add_uri_record(const std::string &uri) { - return this->add_record(make_unique(TNF_WELL_KNOWN, "U", uri)); + return this->add_record(std::unique_ptr{new NdefRecord(TNF_WELL_KNOWN, "U", uri)}); } std::vector NdefMessage::encode() { diff --git a/esphome/components/nfc/nfc_tag.h b/esphome/components/nfc/nfc_tag.h index ab6ca650e4..2c8b0a5f21 100644 --- a/esphome/components/nfc/nfc_tag.h +++ b/esphome/components/nfc/nfc_tag.h @@ -31,13 +31,13 @@ class NfcTag { NfcTag(std::vector &uid, const std::string &tag_type, std::vector &ndef_data) { this->uid_ = uid; this->tag_type_ = tag_type; - this->ndef_message_ = make_unique(ndef_data); + this->ndef_message_ = std::unique_ptr(new NdefMessage(ndef_data)); }; NfcTag(const NfcTag &rhs) { uid_ = rhs.uid_; tag_type_ = rhs.tag_type_; if (rhs.ndef_message_ != nullptr) - ndef_message_ = make_unique(*rhs.ndef_message_); + ndef_message_ = std::unique_ptr(new NdefMessage(*rhs.ndef_message_)); } std::vector &get_uid() { return this->uid_; }; diff --git a/esphome/components/pn532/pn532.cpp b/esphome/components/pn532/pn532.cpp index 8fda19cba3..c28ce8d503 100644 --- a/esphome/components/pn532/pn532.cpp +++ b/esphome/components/pn532/pn532.cpp @@ -104,7 +104,7 @@ void PN532::loop() { if (!success) { // Something failed if (!this->current_uid_.empty()) { - auto tag = make_unique(this->current_uid_); + auto tag = std::unique_ptr{new nfc::NfcTag(this->current_uid_)}; for (auto *trigger : this->triggers_ontagremoved_) trigger->process(tag); } @@ -117,7 +117,7 @@ void PN532::loop() { if (num_targets != 1) { // no tags found or too many if (!this->current_uid_.empty()) { - auto tag = make_unique(this->current_uid_); + auto tag = std::unique_ptr{new nfc::NfcTag(this->current_uid_)}; for (auto *trigger : this->triggers_ontagremoved_) trigger->process(tag); } @@ -281,9 +281,9 @@ std::unique_ptr PN532::read_tag_(std::vector &uid) { return this->read_mifare_ultralight_tag_(uid); } else if (type == nfc::TAG_TYPE_UNKNOWN) { ESP_LOGV(TAG, "Cannot determine tag type"); - return make_unique(uid); + return std::unique_ptr{new nfc::NfcTag(uid)}; } else { - return make_unique(uid); + return std::unique_ptr{new nfc::NfcTag(uid)}; } } diff --git a/esphome/components/pn532/pn532_mifare_classic.cpp b/esphome/components/pn532/pn532_mifare_classic.cpp index fa99e5cfab..f4bd11d49f 100644 --- a/esphome/components/pn532/pn532_mifare_classic.cpp +++ b/esphome/components/pn532/pn532_mifare_classic.cpp @@ -15,15 +15,15 @@ std::unique_ptr PN532::read_mifare_classic_tag_(std::vector data; if (this->read_mifare_classic_block_(current_block, data)) { if (!nfc::decode_mifare_classic_tlv(data, message_length, message_start_index)) { - return make_unique(uid, nfc::ERROR); + return std::unique_ptr{new nfc::NfcTag(uid, nfc::ERROR)}; } } else { ESP_LOGE(TAG, "Failed to read block %d", current_block); - return make_unique(uid, nfc::MIFARE_CLASSIC); + return std::unique_ptr{new nfc::NfcTag(uid, nfc::MIFARE_CLASSIC)}; } } else { ESP_LOGV(TAG, "Tag is not NDEF formatted"); - return make_unique(uid, nfc::MIFARE_CLASSIC); + return std::unique_ptr{new nfc::NfcTag(uid, nfc::MIFARE_CLASSIC)}; } uint32_t index = 0; @@ -51,7 +51,7 @@ std::unique_ptr PN532::read_mifare_classic_tag_(std::vector(uid, nfc::MIFARE_CLASSIC, buffer); + return std::unique_ptr{new nfc::NfcTag(uid, nfc::MIFARE_CLASSIC, buffer)}; } bool PN532::read_mifare_classic_block_(uint8_t block_num, std::vector &data) { diff --git a/esphome/components/pn532/pn532_mifare_ultralight.cpp b/esphome/components/pn532/pn532_mifare_ultralight.cpp index 6f118419ba..24e97ab95c 100644 --- a/esphome/components/pn532/pn532_mifare_ultralight.cpp +++ b/esphome/components/pn532/pn532_mifare_ultralight.cpp @@ -9,25 +9,25 @@ static const char *const TAG = "pn532.mifare_ultralight"; std::unique_ptr PN532::read_mifare_ultralight_tag_(std::vector &uid) { if (!this->is_mifare_ultralight_formatted_()) { ESP_LOGD(TAG, "Not NDEF formatted"); - return make_unique(uid, nfc::NFC_FORUM_TYPE_2); + return std::unique_ptr{new nfc::NfcTag(uid, nfc::NFC_FORUM_TYPE_2)}; } uint8_t message_length; uint8_t message_start_index; if (!this->find_mifare_ultralight_ndef_(message_length, message_start_index)) { - return make_unique(uid, nfc::NFC_FORUM_TYPE_2); + return std::unique_ptr{new nfc::NfcTag(uid, nfc::NFC_FORUM_TYPE_2)}; } ESP_LOGVV(TAG, "message length: %d, start: %d", message_length, message_start_index); if (message_length == 0) { - return make_unique(uid, nfc::NFC_FORUM_TYPE_2); + return std::unique_ptr{new nfc::NfcTag(uid, nfc::NFC_FORUM_TYPE_2)}; } std::vector data; for (uint8_t page = nfc::MIFARE_ULTRALIGHT_DATA_START_PAGE; page < nfc::MIFARE_ULTRALIGHT_MAX_PAGE; page++) { std::vector page_data; if (!this->read_mifare_ultralight_page_(page, page_data)) { ESP_LOGE(TAG, "Error reading page %d", page); - return make_unique(uid, nfc::NFC_FORUM_TYPE_2); + return std::unique_ptr{new nfc::NfcTag(uid, nfc::NFC_FORUM_TYPE_2)}; } data.insert(data.end(), page_data.begin(), page_data.end()); @@ -38,7 +38,7 @@ std::unique_ptr PN532::read_mifare_ultralight_tag_(std::vector(uid, nfc::NFC_FORUM_TYPE_2, data); + return std::unique_ptr{new nfc::NfcTag(uid, nfc::NFC_FORUM_TYPE_2, data)}; } bool PN532::read_mifare_ultralight_page_(uint8_t page_num, std::vector &data) { diff --git a/esphome/components/sgp40/sgp40.cpp b/esphome/components/sgp40/sgp40.cpp index a911c107b9..1a1909eeb0 100644 --- a/esphome/components/sgp40/sgp40.cpp +++ b/esphome/components/sgp40/sgp40.cpp @@ -1,5 +1,6 @@ #include "esphome/core/log.h" #include "sgp40.h" +#include namespace esphome { namespace sgp40 { diff --git a/esphome/components/shutdown/shutdown_switch.cpp b/esphome/components/shutdown/shutdown_switch.cpp index 3cc8ba9e1b..87b755cab6 100644 --- a/esphome/components/shutdown/shutdown_switch.cpp +++ b/esphome/components/shutdown/shutdown_switch.cpp @@ -18,7 +18,7 @@ void ShutdownSwitch::write_state(bool state) { App.run_safe_shutdown_hooks(); #ifdef ARDUINO_ARCH_ESP8266 - ESP.deepSleep(0); + ESP.deepSleep(0); // NOLINT(readability-static-accessed-through-instance) #endif #ifdef ARDUINO_ARCH_ESP32 esp_deep_sleep_start(); diff --git a/esphome/components/spi/spi.cpp b/esphome/components/spi/spi.cpp index 609b4df456..3180447711 100644 --- a/esphome/components/spi/spi.cpp +++ b/esphome/components/spi/spi.cpp @@ -95,12 +95,12 @@ void SPIComponent::debug_rx(uint8_t value) { void SPIComponent::debug_enable(uint8_t pin) { ESP_LOGVV(TAG, "Enabling SPI Chip on pin %u...", pin); } void SPIComponent::cycle_clock_(bool value) { - uint32_t start = ESP.getCycleCount(); - while (start - ESP.getCycleCount() < this->wait_cycle_) + uint32_t start = ESP.getCycleCount(); // NOLINT(readability-static-accessed-through-instance) + while (start - ESP.getCycleCount() < this->wait_cycle_) // NOLINT(readability-static-accessed-through-instance) ; this->clk_->digital_write(value); start += this->wait_cycle_; - while (start - ESP.getCycleCount() < this->wait_cycle_) + while (start - ESP.getCycleCount() < this->wait_cycle_) // NOLINT(readability-static-accessed-through-instance) ; } diff --git a/esphome/components/time/automation.cpp b/esphome/components/time/automation.cpp index 84cc76a762..f133ab021c 100644 --- a/esphome/components/time/automation.cpp +++ b/esphome/components/time/automation.cpp @@ -43,9 +43,9 @@ void CronTrigger::loop() { this->last_check_ = time; if (!time.fields_in_range()) { ESP_LOGW(TAG, "Time is out of range!"); - ESP_LOGD(TAG, "Second=%02u Minute=%02u Hour=%02u DayOfWeek=%u DayOfMonth=%u DayOfYear=%u Month=%u time=%ld", + ESP_LOGD(TAG, "Second=%02u Minute=%02u Hour=%02u DayOfWeek=%u DayOfMonth=%u DayOfYear=%u Month=%u time=%" PRId64, time.second, time.minute, time.hour, time.day_of_week, time.day_of_month, time.day_of_year, time.month, - time.timestamp); + (int64_t) time.timestamp); } if (this->matches(time)) diff --git a/esphome/components/uart/uart_esp8266.cpp b/esphome/components/uart/uart_esp8266.cpp index 6d207fde6c..a74f2601e4 100644 --- a/esphome/components/uart/uart_esp8266.cpp +++ b/esphome/components/uart/uart_esp8266.cpp @@ -233,7 +233,7 @@ void ESP8266SoftwareSerial::setup(int8_t tx_pin, int8_t rx_pin, uint32_t baud_ra } void ICACHE_RAM_ATTR ESP8266SoftwareSerial::gpio_intr(ESP8266SoftwareSerial *arg) { uint32_t wait = arg->bit_time_ + arg->bit_time_ / 3 - 500; - const uint32_t start = ESP.getCycleCount(); + const uint32_t start = ESP.getCycleCount(); // NOLINT(readability-static-accessed-through-instance) uint8_t rec = 0; // Manually unroll the loop for (int i = 0; i < arg->data_bits_; i++) @@ -273,7 +273,7 @@ void ICACHE_RAM_ATTR HOT ESP8266SoftwareSerial::write_byte(uint8_t data) { { InterruptLock lock; uint32_t wait = this->bit_time_; - const uint32_t start = ESP.getCycleCount(); + const uint32_t start = ESP.getCycleCount(); // NOLINT(readability-static-accessed-through-instance) // Start bit this->write_bit_(false, &wait, start); for (int i = 0; i < this->data_bits_; i++) { @@ -291,7 +291,7 @@ void ICACHE_RAM_ATTR HOT ESP8266SoftwareSerial::write_byte(uint8_t data) { } } void ICACHE_RAM_ATTR ESP8266SoftwareSerial::wait_(uint32_t *wait, const uint32_t &start) { - while (ESP.getCycleCount() - start < *wait) + while (ESP.getCycleCount() - start < *wait) // NOLINT(readability-static-accessed-through-instance) ; *wait += this->bit_time_; } diff --git a/esphome/components/web_server_base/web_server_base.cpp b/esphome/components/web_server_base/web_server_base.cpp index 9f04c3b7bf..6351941aee 100644 --- a/esphome/components/web_server_base/web_server_base.cpp +++ b/esphome/components/web_server_base/web_server_base.cpp @@ -29,6 +29,7 @@ void OTARequestHandler::handleUpload(AsyncWebServerRequest *request, const Strin this->ota_read_length_ = 0; #ifdef ARDUINO_ARCH_ESP8266 Update.runAsync(true); + // NOLINTNEXTLINE(readability-static-accessed-through-instance) success = Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000); #endif #ifdef ARDUINO_ARCH_ESP32 diff --git a/esphome/core/application.cpp b/esphome/core/application.cpp index c0f68d80d6..5ab1d973f4 100644 --- a/esphome/core/application.cpp +++ b/esphome/core/application.cpp @@ -127,7 +127,7 @@ void Application::reboot() { ESP_LOGI(TAG, "Forcing a reboot..."); for (auto *comp : this->components_) comp->on_shutdown(); - ESP.restart(); + ESP.restart(); // NOLINT(readability-static-accessed-through-instance) // restart() doesn't always end execution while (true) { yield(); @@ -139,7 +139,7 @@ void Application::safe_reboot() { comp->on_safe_shutdown(); for (auto *comp : this->components_) comp->on_shutdown(); - ESP.restart(); + ESP.restart(); // NOLINT(readability-static-accessed-through-instance) // restart() doesn't always end execution while (true) { yield(); diff --git a/esphome/core/application_esp8266.cpp b/esphome/core/application_esp8266.cpp index 95139ca112..ee32417634 100644 --- a/esphome/core/application_esp8266.cpp +++ b/esphome/core/application_esp8266.cpp @@ -6,7 +6,9 @@ namespace esphome { static const char *const TAG = "app_esp8266"; -void ICACHE_RAM_ATTR HOT Application::feed_wdt_arch_() { ESP.wdtFeed(); } +void ICACHE_RAM_ATTR HOT Application::feed_wdt_arch_() { + ESP.wdtFeed(); // NOLINT(readability-static-accessed-through-instance) +} } // namespace esphome #endif diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 044de6c8b0..86c70088d4 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -12,13 +12,6 @@ #include "esphome/core/optional.h" -#ifdef CLANG_TIDY -#undef ICACHE_RAM_ATTR -#define ICACHE_RAM_ATTR -#undef ICACHE_RODATA_ATTR -#define ICACHE_RODATA_ATTR -#endif - #define HOT __attribute__((hot)) #define ESPDEPRECATED(msg, when) __attribute__((deprecated(msg))) #define ALWAYS_INLINE __attribute__((always_inline)) diff --git a/esphome/core/util.cpp b/esphome/core/util.cpp index 67e575e1c5..e0132e2e4a 100644 --- a/esphome/core/util.cpp +++ b/esphome/core/util.cpp @@ -75,12 +75,12 @@ static const uint8_t WEBSERVER_PORT = 80; #ifdef USE_MDNS #ifdef ARDUINO_ARCH_ESP8266 -void network_setup_mdns(IPAddress address, int interface) { +void network_setup_mdns(const IPAddress &address, int interface) { // Latest arduino framework breaks mDNS for AP interface // see https://github.com/esp8266/Arduino/issues/6114 if (interface == 1) return; - MDNS.begin(App.get_name().c_str(), std::move(address)); + MDNS.begin(App.get_name().c_str(), address); mdns_setup = true; #endif #ifdef ARDUINO_ARCH_ESP32 diff --git a/esphome/core/util.h b/esphome/core/util.h index 2d58eff893..764c6aaf03 100644 --- a/esphome/core/util.h +++ b/esphome/core/util.h @@ -21,7 +21,7 @@ bool remote_is_connected(); /// Manually set up the network stack (outside of the App.setup() loop, for example in OTA safe mode) #ifdef ARDUINO_ARCH_ESP8266 -void network_setup_mdns(IPAddress address, int interface); +void network_setup_mdns(const IPAddress &address, int interface); #endif #ifdef ARDUINO_ARCH_ESP32 void network_setup_mdns(); diff --git a/platformio.ini b/platformio.ini index a35e0c10fe..3b1241f282 100644 --- a/platformio.ini +++ b/platformio.ini @@ -30,7 +30,7 @@ lib_deps = ArduinoJson-esphomelib@5.13.3 esphome/ESPAsyncWebServer-esphome@1.3.0 FastLED@3.3.2 - NeoPixelBus-esphome@2.6.2 + NeoPixelBus@2.6.7 1655@1.0.2 ; TinyGPSPlus (has name conflict) 6865@1.0.0 ; TM1651 Battery Display 6306@1.0.3 ; HM3301 @@ -47,8 +47,7 @@ src_filter = +<.temp/all-include.cpp> [common:esp8266] -; use Arduino framework v2.4.2 for clang-tidy (latest 2.5.2 breaks static code analysis, see #760) -platform = platformio/espressif8266@1.8.0 +platform = platformio/espressif8266@3.1.0 framework = arduino board = nodemcuv2 lib_deps = diff --git a/script/clang-tidy b/script/clang-tidy index a63ebc674a..463959fa5d 100755 --- a/script/clang-tidy +++ b/script/clang-tidy @@ -27,6 +27,21 @@ def clang_options(idedata): # disable built-in include directories from the host '-nostdinc', '-nostdinc++', + # replace pgmspace.h, as it uses GNU extensions clang doesn't support + # https://github.com/earlephilhower/newlib-xtensa/pull/18 + '-D_PGMSPACE_H_', + '-Dpgm_read_byte(s)=(*(const uint8_t *)(s))', + '-Dpgm_read_byte_near(s)=(*(const uint8_t *)(s))', + '-Dpgm_read_dword(s)=(*(const uint32_t *)(s))', + '-DPROGMEM=', + '-DPGM_P=const char *', + '-DPSTR(s)=(s)', + # this next one is also needed with upstream pgmspace.h + # suppress warning about identifier naming in expansion of this macro + '-DPSTRN(s, n)=(s)', + # suppress warning about attribute cannot be applied to type + # https://github.com/esp8266/Arduino/pull/8258 + '-Ddeprecated(x)=', # pretend we're an Xtensa compiler, which gates some features in the headers '-D__XTENSA__', # allow to condition code on the presence of clang-tidy From e6b0a0ca2b9fbf329d8233c322257078d2f5a7b6 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Mon, 13 Sep 2021 18:58:49 +0200 Subject: [PATCH 025/207] Clean-up sensor integration (#2275) --- esphome/components/api/api_connection.cpp | 2 +- esphome/components/demo/demo_sensor.h | 2 +- esphome/components/mqtt/mqtt_sensor.cpp | 15 +-- esphome/components/sensor/filter.cpp | 28 ----- esphome/components/sensor/filter.h | 19 --- esphome/components/sensor/sensor.cpp | 106 +++++++--------- esphome/components/sensor/sensor.h | 140 ++++++++-------------- 7 files changed, 102 insertions(+), 210 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 58a5662055..31a530c04d 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -422,7 +422,7 @@ bool APIConnection::send_sensor_info(sensor::Sensor *sensor) { msg.accuracy_decimals = sensor->get_accuracy_decimals(); msg.force_update = sensor->get_force_update(); msg.device_class = sensor->get_device_class(); - msg.state_class = static_cast(sensor->state_class); + msg.state_class = static_cast(sensor->get_state_class()); msg.disabled_by_default = sensor->is_disabled_by_default(); return this->send_list_entities_sensor_response(msg); diff --git a/esphome/components/demo/demo_sensor.h b/esphome/components/demo/demo_sensor.h index 344aaf26f8..9a35674124 100644 --- a/esphome/components/demo/demo_sensor.h +++ b/esphome/components/demo/demo_sensor.h @@ -11,7 +11,7 @@ class DemoSensor : public sensor::Sensor, public PollingComponent { public: void update() override { float val = random_float(); - bool increasing = this->state_class == sensor::STATE_CLASS_TOTAL_INCREASING; + bool increasing = this->get_state_class() == sensor::STATE_CLASS_TOTAL_INCREASING; if (increasing) { float base = isnan(this->state) ? 0.0f : this->state; this->publish_state(base + val * 10); diff --git a/esphome/components/mqtt/mqtt_sensor.cpp b/esphome/components/mqtt/mqtt_sensor.cpp index d440e30fc4..e921056167 100644 --- a/esphome/components/mqtt/mqtt_sensor.cpp +++ b/esphome/components/mqtt/mqtt_sensor.cpp @@ -31,16 +31,9 @@ void MQTTSensorComponent::dump_config() { std::string MQTTSensorComponent::component_type() const { return "sensor"; } uint32_t MQTTSensorComponent::get_expire_after() const { - if (this->expire_after_.has_value()) { + if (this->expire_after_.has_value()) return *this->expire_after_; - } else { -#ifdef USE_DEEP_SLEEP - if (deep_sleep::global_has_deep_sleep) { - return 0; - } -#endif - return this->sensor_->calculate_expected_filter_update_interval() * 5; - } + return 0; } void MQTTSensorComponent::set_expire_after(uint32_t expire_after) { this->expire_after_ = expire_after; } void MQTTSensorComponent::disable_expire_after() { this->expire_after_ = 0; } @@ -61,8 +54,8 @@ void MQTTSensorComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryCo if (this->sensor_->get_force_update()) root["force_update"] = true; - if (this->sensor_->state_class != STATE_CLASS_NONE) - root["state_class"] = state_class_to_string(this->sensor_->state_class); + if (this->sensor_->get_state_class() != STATE_CLASS_NONE) + root["state_class"] = state_class_to_string(this->sensor_->get_state_class()); config.command_topic = false; } diff --git a/esphome/components/sensor/filter.cpp b/esphome/components/sensor/filter.cpp index bbe47b43ec..f048189959 100644 --- a/esphome/components/sensor/filter.cpp +++ b/esphome/components/sensor/filter.cpp @@ -8,7 +8,6 @@ namespace sensor { static const char *const TAG = "sensor.filter"; // Filter -uint32_t Filter::expected_interval(uint32_t input) { return input; } void Filter::input(float value) { ESP_LOGVV(TAG, "Filter(%p)::input(%f)", this, value); optional out = this->new_value(value); @@ -29,15 +28,6 @@ void Filter::initialize(Sensor *parent, Filter *next) { this->parent_ = parent; this->next_ = next; } -uint32_t Filter::calculate_remaining_interval(uint32_t input) { - uint32_t this_interval = this->expected_interval(input); - ESP_LOGVV(TAG, "Filter(%p)::calculate_remaining_interval(%u) -> %u", this, input, this_interval); - if (this->next_ == nullptr) { - return this_interval; - } else { - return this->next_->calculate_remaining_interval(this_interval); - } -} // MedianFilter MedianFilter::MedianFilter(size_t window_size, size_t send_every, size_t send_first_at) @@ -75,8 +65,6 @@ optional MedianFilter::new_value(float value) { return {}; } -uint32_t MedianFilter::expected_interval(uint32_t input) { return input * this->send_every_; } - // MinFilter MinFilter::MinFilter(size_t window_size, size_t send_every, size_t send_first_at) : send_every_(send_every), send_at_(send_every - send_first_at), window_size_(window_size) {} @@ -106,8 +94,6 @@ optional MinFilter::new_value(float value) { return {}; } -uint32_t MinFilter::expected_interval(uint32_t input) { return input * this->send_every_; } - // MaxFilter MaxFilter::MaxFilter(size_t window_size, size_t send_every, size_t send_first_at) : send_every_(send_every), send_at_(send_every - send_first_at), window_size_(window_size) {} @@ -137,8 +123,6 @@ optional MaxFilter::new_value(float value) { return {}; } -uint32_t MaxFilter::expected_interval(uint32_t input) { return input * this->send_every_; } - // SlidingWindowMovingAverageFilter SlidingWindowMovingAverageFilter::SlidingWindowMovingAverageFilter(size_t window_size, size_t send_every, size_t send_first_at) @@ -177,8 +161,6 @@ optional SlidingWindowMovingAverageFilter::new_value(float value) { return {}; } -uint32_t SlidingWindowMovingAverageFilter::expected_interval(uint32_t input) { return input * this->send_every_; } - // ExponentialMovingAverageFilter ExponentialMovingAverageFilter::ExponentialMovingAverageFilter(float alpha, size_t send_every) : send_every_(send_every), send_at_(send_every - 1), alpha_(alpha) {} @@ -203,7 +185,6 @@ optional ExponentialMovingAverageFilter::new_value(float value) { } void ExponentialMovingAverageFilter::set_send_every(size_t send_every) { this->send_every_ = send_every; } void ExponentialMovingAverageFilter::set_alpha(float alpha) { this->alpha_ = alpha; } -uint32_t ExponentialMovingAverageFilter::expected_interval(uint32_t input) { return input * this->send_every_; } // LambdaFilter LambdaFilter::LambdaFilter(lambda_filter_t lambda_filter) : lambda_filter_(std::move(lambda_filter)) {} @@ -296,14 +277,6 @@ void OrFilter::initialize(Sensor *parent, Filter *next) { this->phi_.initialize(parent, nullptr); } -uint32_t OrFilter::expected_interval(uint32_t input) { - uint32_t min_interval = UINT32_MAX; - for (Filter *filter : this->filters_) { - min_interval = std::min(min_interval, filter->calculate_remaining_interval(input)); - } - - return min_interval; -} // DebounceFilter optional DebounceFilter::new_value(float value) { this->set_timeout("debounce", this->time_period_, [this, value]() { this->output(value); }); @@ -324,7 +297,6 @@ optional HeartbeatFilter::new_value(float value) { return {}; } -uint32_t HeartbeatFilter::expected_interval(uint32_t input) { return this->time_period_; } void HeartbeatFilter::setup() { this->set_interval("heartbeat", this->time_period_, [this]() { ESP_LOGVV(TAG, "HeartbeatFilter(%p)::interval(has_value=%s, last_input=%f)", this, YESNO(this->has_value_), diff --git a/esphome/components/sensor/filter.h b/esphome/components/sensor/filter.h index 270a91ccff..29a6813ea9 100644 --- a/esphome/components/sensor/filter.h +++ b/esphome/components/sensor/filter.h @@ -33,11 +33,6 @@ class Filter { void input(float value); - /// Return the amount of time that this filter is expected to take based on the input time interval. - virtual uint32_t expected_interval(uint32_t input); - - uint32_t calculate_remaining_interval(uint32_t input); - void output(float value); protected: @@ -68,8 +63,6 @@ class MedianFilter : public Filter { void set_send_every(size_t send_every); void set_window_size(size_t window_size); - uint32_t expected_interval(uint32_t input) override; - protected: std::deque queue_; size_t send_every_; @@ -98,8 +91,6 @@ class MinFilter : public Filter { void set_send_every(size_t send_every); void set_window_size(size_t window_size); - uint32_t expected_interval(uint32_t input) override; - protected: std::deque queue_; size_t send_every_; @@ -128,8 +119,6 @@ class MaxFilter : public Filter { void set_send_every(size_t send_every); void set_window_size(size_t window_size); - uint32_t expected_interval(uint32_t input) override; - protected: std::deque queue_; size_t send_every_; @@ -159,8 +148,6 @@ class SlidingWindowMovingAverageFilter : public Filter { void set_send_every(size_t send_every); void set_window_size(size_t window_size); - uint32_t expected_interval(uint32_t input) override; - protected: float sum_{0.0}; std::deque queue_; @@ -183,8 +170,6 @@ class ExponentialMovingAverageFilter : public Filter { void set_send_every(size_t send_every); void set_alpha(float alpha); - uint32_t expected_interval(uint32_t input) override; - protected: bool first_value_{true}; float accumulator_{0.0f}; @@ -279,8 +264,6 @@ class HeartbeatFilter : public Filter, public Component { optional new_value(float value) override; - uint32_t expected_interval(uint32_t input) override; - float get_setup_priority() const override; protected: @@ -306,8 +289,6 @@ class OrFilter : public Filter { void initialize(Sensor *parent, Filter *next) override; - uint32_t expected_interval(uint32_t input) override; - optional new_value(float value) override; protected: diff --git a/esphome/components/sensor/sensor.cpp b/esphome/components/sensor/sensor.cpp index 34f72a4508..128f36fc93 100644 --- a/esphome/components/sensor/sensor.cpp +++ b/esphome/components/sensor/sensor.cpp @@ -18,6 +18,51 @@ const LogString *state_class_to_string(StateClass state_class) { } } +Sensor::Sensor(const std::string &name) : Nameable(name), state(NAN), raw_state(NAN) {} +Sensor::Sensor() : Sensor("") {} + +std::string Sensor::get_unit_of_measurement() { + if (this->unit_of_measurement_.has_value()) + return *this->unit_of_measurement_; + return this->unit_of_measurement(); +} +void Sensor::set_unit_of_measurement(const std::string &unit_of_measurement) { + this->unit_of_measurement_ = unit_of_measurement; +} +std::string Sensor::unit_of_measurement() { return ""; } + +std::string Sensor::get_icon() { + if (this->icon_.has_value()) + return *this->icon_; + return this->icon(); +} +void Sensor::set_icon(const std::string &icon) { this->icon_ = icon; } +std::string Sensor::icon() { return ""; } + +int8_t Sensor::get_accuracy_decimals() { + if (this->accuracy_decimals_.has_value()) + return *this->accuracy_decimals_; + return this->accuracy_decimals(); +} +void Sensor::set_accuracy_decimals(int8_t accuracy_decimals) { this->accuracy_decimals_ = accuracy_decimals; } +int8_t Sensor::accuracy_decimals() { return 0; } + +std::string Sensor::get_device_class() { + if (this->device_class_.has_value()) + return *this->device_class_; + return this->device_class(); +} +void Sensor::set_device_class(const std::string &device_class) { this->device_class_ = device_class; } +std::string Sensor::device_class() { return ""; } + +void Sensor::set_state_class(StateClass state_class) { this->state_class_ = state_class; } +StateClass Sensor::get_state_class() { + if (this->state_class_.has_value()) + return *this->state_class_; + return this->state_class(); +} +StateClass Sensor::state_class() { return StateClass::STATE_CLASS_NONE; } + void Sensor::publish_state(float state) { this->raw_state = state; this->raw_callback_.call(state); @@ -30,54 +75,12 @@ void Sensor::publish_state(float state) { this->filter_list_->input(state); } } -std::string Sensor::unit_of_measurement() { return ""; } -std::string Sensor::icon() { return ""; } -uint32_t Sensor::update_interval() { return 0; } -int8_t Sensor::accuracy_decimals() { return 0; } -Sensor::Sensor(const std::string &name) : Nameable(name), state(NAN), raw_state(NAN) {} -Sensor::Sensor() : Sensor("") {} -void Sensor::set_unit_of_measurement(const std::string &unit_of_measurement) { - this->unit_of_measurement_ = unit_of_measurement; -} -void Sensor::set_icon(const std::string &icon) { this->icon_ = icon; } -void Sensor::set_accuracy_decimals(int8_t accuracy_decimals) { this->accuracy_decimals_ = accuracy_decimals; } void Sensor::add_on_state_callback(std::function &&callback) { this->callback_.add(std::move(callback)); } void Sensor::add_on_raw_state_callback(std::function &&callback) { this->raw_callback_.add(std::move(callback)); } -std::string Sensor::get_icon() { - if (this->icon_.has_value()) - return *this->icon_; - return this->icon(); -} -void Sensor::set_device_class(const std::string &device_class) { this->device_class_ = device_class; } -std::string Sensor::get_device_class() { - if (this->device_class_.has_value()) - return *this->device_class_; - return this->device_class(); -} -std::string Sensor::device_class() { return ""; } -void Sensor::set_state_class(StateClass state_class) { this->state_class = state_class; } -void Sensor::set_state_class(const std::string &state_class) { - if (str_equals_case_insensitive(state_class, "measurement")) { - this->state_class = STATE_CLASS_MEASUREMENT; - } else if (str_equals_case_insensitive(state_class, "total_increasing")) { - this->state_class = STATE_CLASS_TOTAL_INCREASING; - } else { - ESP_LOGW(TAG, "'%s' - Unrecognized state class %s", this->get_name().c_str(), state_class.c_str()); - } -} -std::string Sensor::get_unit_of_measurement() { - if (this->unit_of_measurement_.has_value()) - return *this->unit_of_measurement_; - return this->unit_of_measurement(); -} -int8_t Sensor::get_accuracy_decimals() { - if (this->accuracy_decimals_.has_value()) - return *this->accuracy_decimals_; - return this->accuracy_decimals(); -} + void Sensor::add_filter(Filter *filter) { // inefficient, but only happens once on every sensor setup and nobody's going to have massive amounts of // filters @@ -119,24 +122,7 @@ void Sensor::internal_send_state_to_frontend(float state) { this->callback_.call(state); } bool Sensor::has_state() const { return this->has_state_; } -uint32_t Sensor::calculate_expected_filter_update_interval() { - uint32_t interval = this->update_interval(); - if (interval == 4294967295UL) - // update_interval: never - return 0; - - if (this->filter_list_ == nullptr) { - return interval; - } - - return this->filter_list_->calculate_remaining_interval(interval); -} uint32_t Sensor::hash_base() { return 2455723294UL; } -PollingSensorComponent::PollingSensorComponent(const std::string &name, uint32_t update_interval) - : PollingComponent(update_interval), Sensor(name) {} - -uint32_t PollingSensorComponent::update_interval() { return this->get_update_interval(); } - } // namespace sensor } // namespace esphome diff --git a/esphome/components/sensor/sensor.h b/esphome/components/sensor/sensor.h index 6322c12027..3fc993cb2c 100644 --- a/esphome/components/sensor/sensor.h +++ b/esphome/components/sensor/sensor.h @@ -14,7 +14,7 @@ namespace sensor { if (!(obj)->get_device_class().empty()) { \ ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \ } \ - ESP_LOGCONFIG(TAG, "%s State Class: '%s'", prefix, LOG_STR_ARG(state_class_to_string((obj)->state_class))); \ + ESP_LOGCONFIG(TAG, "%s State Class: '%s'", prefix, LOG_STR_ARG(state_class_to_string((obj)->get_state_class()))); \ ESP_LOGCONFIG(TAG, "%s Unit of Measurement: '%s'", prefix, (obj)->get_unit_of_measurement().c_str()); \ ESP_LOGCONFIG(TAG, "%s Accuracy Decimals: %d", prefix, (obj)->get_accuracy_decimals()); \ if (!(obj)->get_icon().empty()) { \ @@ -48,26 +48,42 @@ class Sensor : public Nameable { explicit Sensor(); explicit Sensor(const std::string &name); - /** Manually set the unit of measurement of this sensor. By default the sensor's default defined by - * unit_of_measurement() is used. - * - * @param unit_of_measurement The unit of measurement, "" to disable. - */ + /// Get the unit of measurement, using the manual override if set. + std::string get_unit_of_measurement(); + /// Manually set the unit of measurement. void set_unit_of_measurement(const std::string &unit_of_measurement); - /** Manually set the icon of this sensor. By default the sensor's default defined by icon() is used. - * - * @param icon The icon, for example "mdi:flash". "" to disable. - */ + /// Get the icon. Uses the manual override if specified or the default value instead. + std::string get_icon(); + /// Manually set the icon, for example "mdi:flash". void set_icon(const std::string &icon); - /** Manually set the accuracy in decimals for this sensor. By default, the sensor's default defined by - * accuracy_decimals() is used. - * - * @param accuracy_decimals The accuracy decimal that should be used. - */ + /// Get the accuracy in decimals, using the manual override if set. + int8_t get_accuracy_decimals(); + /// Manually set the accuracy in decimals. void set_accuracy_decimals(int8_t accuracy_decimals); + /// Get the device class, using the manual override if set. + std::string get_device_class(); + /// Manually set the device class. + void set_device_class(const std::string &device_class); + + /// Get the state class, using the manual override if set. + StateClass get_state_class(); + /// Manually set the state class. + void set_state_class(StateClass state_class); + + /** + * Get whether force update mode is enabled. + * + * If the sensor is in force_update mode, the frontend is required to save all + * state changes to the database when they are published, even if the state is the + * same as before. + */ + bool get_force_update() const { return force_update_; } + /// Set force update mode. + void set_force_update(bool force_update) { force_update_ = force_update; } + /// Add a filter to the filter chain. Will be appended to the back. void add_filter(Filter *filter); @@ -94,15 +110,6 @@ class Sensor : public Nameable { /// Getter-syntax for .raw_state float get_raw_state() const; - /// Get the accuracy in decimals. Uses the manual override if specified or the default value instead. - int8_t get_accuracy_decimals(); - - /// Get the unit of measurement. Uses the manual override if specified or the default value instead. - std::string get_unit_of_measurement(); - - /// Get the Home Assistant Icon. Uses the manual override if specified or the default value instead. - std::string get_icon(); - /** Publish a new state to the front-end. * * First, the new state will be assigned to the raw_value. Then it's passed through all filters @@ -128,35 +135,15 @@ class Sensor : public Nameable { */ float state; - /// Manually set the Home Assistant device class (see sensor::device_class) - void set_device_class(const std::string &device_class); - - /// Get the device class for this sensor, using the manual override if specified. - std::string get_device_class(); - - /** This member variable stores the current raw state of the sensor. Unlike .state, - * this will be updated immediately when publish_state is called. + /** This member variable stores the current raw state of the sensor, without any filters applied. + * + * Unlike .state,this will be updated immediately when publish_state is called. */ float raw_state; /// Return whether this sensor has gotten a full state (that passed through all filters) yet. bool has_state() const; - // The state class of this sensor state - StateClass state_class{STATE_CLASS_NONE}; - - /// Manually set the Home Assistant state class (see sensor::state_class) - void set_state_class(StateClass state_class); - void set_state_class(const std::string &state_class); - - /** Override this to set the Home Assistant device class for this sensor. - * - * Return "" to disable this feature. - * - * @return The device class of this sensor, for example "temperature". - */ - virtual std::string device_class(); - /** A unique ID for this sensor, empty for no unique id. See unique ID requirements: * https://developers.home-assistant.io/docs/en/entity_registry_index.html#unique-id-requirements * @@ -164,65 +151,38 @@ class Sensor : public Nameable { */ virtual std::string unique_id(); - /// Return with which interval the sensor is polled. Return 0 for non-polling mode. - virtual uint32_t update_interval(); - - /// Calculate the expected update interval for values that pass through all filters. - uint32_t calculate_expected_filter_update_interval(); - void internal_send_state_to_frontend(float state); - bool get_force_update() const { return force_update_; } - /** Set this sensor's force_update mode. - * - * If the sensor is in force_update mode, the frontend is required to save all - * state changes to the database when they are published, even if the state is the - * same as before. - */ - void set_force_update(bool force_update) { force_update_ = force_update; } - protected: - /** Override this to set the Home Assistant unit of measurement for this sensor. - * - * Return "" to disable this feature. - * - * @return The icon of this sensor, for example "°C". - */ + /// Override this to set the default unit of measurement. virtual std::string unit_of_measurement(); // NOLINT - /** Override this to set the Home Assistant icon for this sensor. - * - * Return "" to disable this feature. - * - * @return The icon of this sensor, for example "mdi:battery". - */ + /// Override this to set the default icon. virtual std::string icon(); // NOLINT - /// Return the accuracy in decimals for this sensor. + /// Override this to set the default accuracy in decimals. virtual int8_t accuracy_decimals(); // NOLINT - optional device_class_{}; ///< Stores the override of the device class + /// Override this to set the default device class. + virtual std::string device_class(); // NOLINT + + /// Override this to set the default state class. + virtual StateClass state_class(); // NOLINT uint32_t hash_base() override; CallbackManager raw_callback_; ///< Storage for raw state callbacks. CallbackManager callback_; ///< Storage for filtered state callbacks. - /// Override the unit of measurement - optional unit_of_measurement_; - /// Override the icon advertised to Home Assistant, otherwise sensor's icon will be used. - optional icon_; - /// Override the accuracy in decimals, otherwise the sensor's values will be used. - optional accuracy_decimals_; - Filter *filter_list_{nullptr}; ///< Store all active filters. + bool has_state_{false}; - bool force_update_{false}; -}; + Filter *filter_list_{nullptr}; ///< Store all active filters. -class PollingSensorComponent : public PollingComponent, public Sensor { - public: - explicit PollingSensorComponent(const std::string &name, uint32_t update_interval); - - uint32_t update_interval() override; + optional unit_of_measurement_; ///< Unit of measurement override + optional icon_; ///< Icon override + optional accuracy_decimals_; ///< Accuracy in decimals override + optional device_class_; ///< Device class override + optional state_class_{STATE_CLASS_NONE}; ///< State class override + bool force_update_{false}; ///< Force update mode }; } // namespace sensor From b9767bdcbcb00aca39ea8f0cf63f3fbcbe142649 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 13 Sep 2021 21:16:13 +0200 Subject: [PATCH 026/207] Bump platformio to 5.2.0 (#2291) --- docker/build.py | 2 +- esphome/zeroconf.py | 7 ++++--- requirements.txt | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docker/build.py b/docker/build.py index 039d7dc15b..c926b3653b 100755 --- a/docker/build.py +++ b/docker/build.py @@ -24,7 +24,7 @@ TYPE_LINT = 'lint' TYPES = [TYPE_DOCKER, TYPE_HA_ADDON, TYPE_LINT] -BASE_VERSION = "4.1.1" +BASE_VERSION = "4.2.0" parser = argparse.ArgumentParser() diff --git a/esphome/zeroconf.py b/esphome/zeroconf.py index e94b59d3ae..443ed6a33a 100644 --- a/esphome/zeroconf.py +++ b/esphome/zeroconf.py @@ -4,9 +4,6 @@ import time from typing import Dict, Optional from zeroconf import ( - _CLASS_IN, - _FLAGS_QR_QUERY, - _TYPE_A, DNSAddress, DNSOutgoing, DNSRecord, @@ -15,6 +12,10 @@ from zeroconf import ( Zeroconf, ) +_CLASS_IN = 1 +_FLAGS_QR_QUERY = 0x0000 # query +_TYPE_A = 1 + class HostResolver(RecordUpdateListener): def __init__(self, name: str): diff --git a/requirements.txt b/requirements.txt index daaf86e641..2d354d5f04 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ tzlocal==2.1 pytz==2021.1 pyserial==3.5 ifaddr==0.1.7 -platformio==5.1.1 +platformio==5.2.0 esptool==3.1 click==7.1.2 esphome-dashboard==20210908.0 From 855112dfc3b3005b55f28c2e79859b072e86c3c8 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Tue, 14 Sep 2021 09:53:37 +0200 Subject: [PATCH 027/207] API Noise logging (#2298) --- esphome/__main__.py | 2 +- esphome/api/__init__.py | 0 esphome/api/api_pb2.py | 3997 ------------------------------ esphome/api/client.py | 518 ---- esphome/components/api/client.py | 73 + requirements.txt | 3 +- 6 files changed, 75 insertions(+), 4518 deletions(-) delete mode 100644 esphome/api/__init__.py delete mode 100644 esphome/api/api_pb2.py delete mode 100644 esphome/api/client.py create mode 100644 esphome/components/api/client.py diff --git a/esphome/__main__.py b/esphome/__main__.py index 97b2988c2e..121fa7cc9e 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -256,7 +256,7 @@ def show_logs(config, args, port): run_miniterm(config, port) return 0 if get_port_type(port) == "NETWORK" and "api" in config: - from esphome.api.client import run_logs + from esphome.components.api.client import run_logs return run_logs(config, port) if get_port_type(port) == "MQTT" and "mqtt" in config: diff --git a/esphome/api/__init__.py b/esphome/api/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/esphome/api/api_pb2.py b/esphome/api/api_pb2.py deleted file mode 100644 index 6262b752c6..0000000000 --- a/esphome/api/api_pb2.py +++ /dev/null @@ -1,3997 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: api.proto - -import sys - -_b = sys.version_info[0] < 3 and (lambda x: x) or (lambda x: x.encode("latin1")) -from google.protobuf.internal import enum_type_wrapper -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database - -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -DESCRIPTOR = _descriptor.FileDescriptor( - name="api.proto", - package="", - syntax="proto3", - serialized_options=None, - serialized_pb=_b( - '\n\tapi.proto"#\n\x0cHelloRequest\x12\x13\n\x0b\x63lient_info\x18\x01 \x01(\t"Z\n\rHelloResponse\x12\x19\n\x11\x61pi_version_major\x18\x01 \x01(\r\x12\x19\n\x11\x61pi_version_minor\x18\x02 \x01(\r\x12\x13\n\x0bserver_info\x18\x03 \x01(\t""\n\x0e\x43onnectRequest\x12\x10\n\x08password\x18\x01 \x01(\t"+\n\x0f\x43onnectResponse\x12\x18\n\x10invalid_password\x18\x01 \x01(\x08"\x13\n\x11\x44isconnectRequest"\x14\n\x12\x44isconnectResponse"\r\n\x0bPingRequest"\x0e\n\x0cPingResponse"\x13\n\x11\x44\x65viceInfoRequest"\xad\x01\n\x12\x44\x65viceInfoResponse\x12\x15\n\ruses_password\x18\x01 \x01(\x08\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x13\n\x0bmac_address\x18\x03 \x01(\t\x12\x1c\n\x14\x65sphome_core_version\x18\x04 \x01(\t\x12\x18\n\x10\x63ompilation_time\x18\x05 \x01(\t\x12\r\n\x05model\x18\x06 \x01(\t\x12\x16\n\x0ehas_deep_sleep\x18\x07 \x01(\x08"\x15\n\x13ListEntitiesRequest"\x9a\x01\n ListEntitiesBinarySensorResponse\x12\x11\n\tobject_id\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\x07\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x11\n\tunique_id\x18\x04 \x01(\t\x12\x14\n\x0c\x64\x65vice_class\x18\x05 \x01(\t\x12\x1f\n\x17is_status_binary_sensor\x18\x06 \x01(\x08"s\n\x19ListEntitiesCoverResponse\x12\x11\n\tobject_id\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\x07\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x11\n\tunique_id\x18\x04 \x01(\t\x12\x15\n\ris_optimistic\x18\x05 \x01(\x08"\x90\x01\n\x17ListEntitiesFanResponse\x12\x11\n\tobject_id\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\x07\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x11\n\tunique_id\x18\x04 \x01(\t\x12\x1c\n\x14supports_oscillation\x18\x05 \x01(\x08\x12\x16\n\x0esupports_speed\x18\x06 \x01(\x08"\x8a\x02\n\x19ListEntitiesLightResponse\x12\x11\n\tobject_id\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\x07\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x11\n\tunique_id\x18\x04 \x01(\t\x12\x1b\n\x13supports_brightness\x18\x05 \x01(\x08\x12\x14\n\x0csupports_rgb\x18\x06 \x01(\x08\x12\x1c\n\x14supports_white_value\x18\x07 \x01(\x08\x12"\n\x1asupports_color_temperature\x18\x08 \x01(\x08\x12\x12\n\nmin_mireds\x18\t \x01(\x02\x12\x12\n\nmax_mireds\x18\n \x01(\x02\x12\x0f\n\x07\x65\x66\x66\x65\x63ts\x18\x0b \x03(\t"\xa3\x01\n\x1aListEntitiesSensorResponse\x12\x11\n\tobject_id\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\x07\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x11\n\tunique_id\x18\x04 \x01(\t\x12\x0c\n\x04icon\x18\x05 \x01(\t\x12\x1b\n\x13unit_of_measurement\x18\x06 \x01(\t\x12\x19\n\x11\x61\x63\x63uracy_decimals\x18\x07 \x01(\x05"\x7f\n\x1aListEntitiesSwitchResponse\x12\x11\n\tobject_id\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\x07\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x11\n\tunique_id\x18\x04 \x01(\t\x12\x0c\n\x04icon\x18\x05 \x01(\t\x12\x12\n\noptimistic\x18\x06 \x01(\x08"o\n\x1eListEntitiesTextSensorResponse\x12\x11\n\tobject_id\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\x07\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x11\n\tunique_id\x18\x04 \x01(\t\x12\x0c\n\x04icon\x18\x05 \x01(\t"\x1a\n\x18ListEntitiesDoneResponse"\x18\n\x16SubscribeStatesRequest"7\n\x19\x42inarySensorStateResponse\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\r\n\x05state\x18\x02 \x01(\x08"t\n\x12\x43overStateResponse\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12-\n\x05state\x18\x02 \x01(\x0e\x32\x1e.CoverStateResponse.CoverState""\n\nCoverState\x12\x08\n\x04OPEN\x10\x00\x12\n\n\x06\x43LOSED\x10\x01"]\n\x10\x46\x61nStateResponse\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\r\n\x05state\x18\x02 \x01(\x08\x12\x13\n\x0boscillating\x18\x03 \x01(\x08\x12\x18\n\x05speed\x18\x04 \x01(\x0e\x32\t.FanSpeed"\xa8\x01\n\x12LightStateResponse\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\r\n\x05state\x18\x02 \x01(\x08\x12\x12\n\nbrightness\x18\x03 \x01(\x02\x12\x0b\n\x03red\x18\x04 \x01(\x02\x12\r\n\x05green\x18\x05 \x01(\x02\x12\x0c\n\x04\x62lue\x18\x06 \x01(\x02\x12\r\n\x05white\x18\x07 \x01(\x02\x12\x19\n\x11\x63olor_temperature\x18\x08 \x01(\x02\x12\x0e\n\x06\x65\x66\x66\x65\x63t\x18\t \x01(\t"1\n\x13SensorStateResponse\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\r\n\x05state\x18\x02 \x01(\x02"1\n\x13SwitchStateResponse\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\r\n\x05state\x18\x02 \x01(\x08"5\n\x17TextSensorStateResponse\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\r\n\x05state\x18\x02 \x01(\t"\x98\x01\n\x13\x43overCommandRequest\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\x11\n\thas_state\x18\x02 \x01(\x08\x12\x32\n\x07\x63ommand\x18\x03 \x01(\x0e\x32!.CoverCommandRequest.CoverCommand"-\n\x0c\x43overCommand\x12\x08\n\x04OPEN\x10\x00\x12\t\n\x05\x43LOSE\x10\x01\x12\x08\n\x04STOP\x10\x02"\x9d\x01\n\x11\x46\x61nCommandRequest\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\x11\n\thas_state\x18\x02 \x01(\x08\x12\r\n\x05state\x18\x03 \x01(\x08\x12\x11\n\thas_speed\x18\x04 \x01(\x08\x12\x18\n\x05speed\x18\x05 \x01(\x0e\x32\t.FanSpeed\x12\x17\n\x0fhas_oscillating\x18\x06 \x01(\x08\x12\x13\n\x0boscillating\x18\x07 \x01(\x08"\x95\x03\n\x13LightCommandRequest\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\x11\n\thas_state\x18\x02 \x01(\x08\x12\r\n\x05state\x18\x03 \x01(\x08\x12\x16\n\x0ehas_brightness\x18\x04 \x01(\x08\x12\x12\n\nbrightness\x18\x05 \x01(\x02\x12\x0f\n\x07has_rgb\x18\x06 \x01(\x08\x12\x0b\n\x03red\x18\x07 \x01(\x02\x12\r\n\x05green\x18\x08 \x01(\x02\x12\x0c\n\x04\x62lue\x18\t \x01(\x02\x12\x11\n\thas_white\x18\n \x01(\x08\x12\r\n\x05white\x18\x0b \x01(\x02\x12\x1d\n\x15has_color_temperature\x18\x0c \x01(\x08\x12\x19\n\x11\x63olor_temperature\x18\r \x01(\x02\x12\x1d\n\x15has_transition_length\x18\x0e \x01(\x08\x12\x19\n\x11transition_length\x18\x0f \x01(\r\x12\x18\n\x10has_flash_length\x18\x10 \x01(\x08\x12\x14\n\x0c\x66lash_length\x18\x11 \x01(\r\x12\x12\n\nhas_effect\x18\x12 \x01(\x08\x12\x0e\n\x06\x65\x66\x66\x65\x63t\x18\x13 \x01(\t"2\n\x14SwitchCommandRequest\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\r\n\x05state\x18\x02 \x01(\x08"E\n\x14SubscribeLogsRequest\x12\x18\n\x05level\x18\x01 \x01(\x0e\x32\t.LogLevel\x12\x13\n\x0b\x64ump_config\x18\x02 \x01(\x08"d\n\x15SubscribeLogsResponse\x12\x18\n\x05level\x18\x01 \x01(\x0e\x32\t.LogLevel\x12\x0b\n\x03tag\x18\x02 \x01(\t\x12\x0f\n\x07message\x18\x03 \x01(\t\x12\x13\n\x0bsend_failed\x18\x04 \x01(\x08"\x1e\n\x1cSubscribeServiceCallsRequest"\xdf\x02\n\x13ServiceCallResponse\x12\x0f\n\x07service\x18\x01 \x01(\t\x12,\n\x04\x64\x61ta\x18\x02 \x03(\x0b\x32\x1e.ServiceCallResponse.DataEntry\x12=\n\rdata_template\x18\x03 \x03(\x0b\x32&.ServiceCallResponse.DataTemplateEntry\x12\x36\n\tvariables\x18\x04 \x03(\x0b\x32#.ServiceCallResponse.VariablesEntry\x1a+\n\tDataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x33\n\x11\x44\x61taTemplateEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x30\n\x0eVariablesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01"%\n#SubscribeHomeAssistantStatesRequest"8\n#SubscribeHomeAssistantStateResponse\x12\x11\n\tentity_id\x18\x01 \x01(\t">\n\x1aHomeAssistantStateResponse\x12\x11\n\tentity_id\x18\x01 \x01(\t\x12\r\n\x05state\x18\x02 \x01(\t"\x10\n\x0eGetTimeRequest"(\n\x0fGetTimeResponse\x12\x15\n\repoch_seconds\x18\x01 \x01(\x07*)\n\x08\x46\x61nSpeed\x12\x07\n\x03LOW\x10\x00\x12\n\n\x06MEDIUM\x10\x01\x12\x08\n\x04HIGH\x10\x02*]\n\x08LogLevel\x12\x08\n\x04NONE\x10\x00\x12\t\n\x05\x45RROR\x10\x01\x12\x08\n\x04WARN\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\x0b\n\x07VERBOSE\x10\x05\x12\x10\n\x0cVERY_VERBOSE\x10\x06\x62\x06proto3' - ), -) - -_FANSPEED = _descriptor.EnumDescriptor( - name="FanSpeed", - full_name="FanSpeed", - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name="LOW", index=0, number=0, serialized_options=None, type=None - ), - _descriptor.EnumValueDescriptor( - name="MEDIUM", index=1, number=1, serialized_options=None, type=None - ), - _descriptor.EnumValueDescriptor( - name="HIGH", index=2, number=2, serialized_options=None, type=None - ), - ], - containing_type=None, - serialized_options=None, - serialized_start=3822, - serialized_end=3863, -) -_sym_db.RegisterEnumDescriptor(_FANSPEED) - -FanSpeed = enum_type_wrapper.EnumTypeWrapper(_FANSPEED) -_LOGLEVEL = _descriptor.EnumDescriptor( - name="LogLevel", - full_name="LogLevel", - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name="NONE", index=0, number=0, serialized_options=None, type=None - ), - _descriptor.EnumValueDescriptor( - name="ERROR", index=1, number=1, serialized_options=None, type=None - ), - _descriptor.EnumValueDescriptor( - name="WARN", index=2, number=2, serialized_options=None, type=None - ), - _descriptor.EnumValueDescriptor( - name="INFO", index=3, number=3, serialized_options=None, type=None - ), - _descriptor.EnumValueDescriptor( - name="DEBUG", index=4, number=4, serialized_options=None, type=None - ), - _descriptor.EnumValueDescriptor( - name="VERBOSE", index=5, number=5, serialized_options=None, type=None - ), - _descriptor.EnumValueDescriptor( - name="VERY_VERBOSE", index=6, number=6, serialized_options=None, type=None - ), - ], - containing_type=None, - serialized_options=None, - serialized_start=3865, - serialized_end=3958, -) -_sym_db.RegisterEnumDescriptor(_LOGLEVEL) - -LogLevel = enum_type_wrapper.EnumTypeWrapper(_LOGLEVEL) -LOW = 0 -MEDIUM = 1 -HIGH = 2 -NONE = 0 -ERROR = 1 -WARN = 2 -INFO = 3 -DEBUG = 4 -VERBOSE = 5 -VERY_VERBOSE = 6 - - -_COVERSTATERESPONSE_COVERSTATE = _descriptor.EnumDescriptor( - name="CoverState", - full_name="CoverStateResponse.CoverState", - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name="OPEN", index=0, number=0, serialized_options=None, type=None - ), - _descriptor.EnumValueDescriptor( - name="CLOSED", index=1, number=1, serialized_options=None, type=None - ), - ], - containing_type=None, - serialized_options=None, - serialized_start=1808, - serialized_end=1842, -) -_sym_db.RegisterEnumDescriptor(_COVERSTATERESPONSE_COVERSTATE) - -_COVERCOMMANDREQUEST_COVERCOMMAND = _descriptor.EnumDescriptor( - name="CoverCommand", - full_name="CoverCommandRequest.CoverCommand", - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name="OPEN", index=0, number=0, serialized_options=None, type=None - ), - _descriptor.EnumValueDescriptor( - name="CLOSE", index=1, number=1, serialized_options=None, type=None - ), - _descriptor.EnumValueDescriptor( - name="STOP", index=2, number=2, serialized_options=None, type=None - ), - ], - containing_type=None, - serialized_options=None, - serialized_start=2375, - serialized_end=2420, -) -_sym_db.RegisterEnumDescriptor(_COVERCOMMANDREQUEST_COVERCOMMAND) - - -_HELLOREQUEST = _descriptor.Descriptor( - name="HelloRequest", - full_name="HelloRequest", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="client_info", - full_name="HelloRequest.client_info", - index=0, - number=1, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=13, - serialized_end=48, -) - - -_HELLORESPONSE = _descriptor.Descriptor( - name="HelloResponse", - full_name="HelloResponse", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="api_version_major", - full_name="HelloResponse.api_version_major", - index=0, - number=1, - type=13, - cpp_type=3, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="api_version_minor", - full_name="HelloResponse.api_version_minor", - index=1, - number=2, - type=13, - cpp_type=3, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="server_info", - full_name="HelloResponse.server_info", - index=2, - number=3, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=50, - serialized_end=140, -) - - -_CONNECTREQUEST = _descriptor.Descriptor( - name="ConnectRequest", - full_name="ConnectRequest", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="password", - full_name="ConnectRequest.password", - index=0, - number=1, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=142, - serialized_end=176, -) - - -_CONNECTRESPONSE = _descriptor.Descriptor( - name="ConnectResponse", - full_name="ConnectResponse", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="invalid_password", - full_name="ConnectResponse.invalid_password", - index=0, - number=1, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=178, - serialized_end=221, -) - - -_DISCONNECTREQUEST = _descriptor.Descriptor( - name="DisconnectRequest", - full_name="DisconnectRequest", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=223, - serialized_end=242, -) - - -_DISCONNECTRESPONSE = _descriptor.Descriptor( - name="DisconnectResponse", - full_name="DisconnectResponse", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=244, - serialized_end=264, -) - - -_PINGREQUEST = _descriptor.Descriptor( - name="PingRequest", - full_name="PingRequest", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=266, - serialized_end=279, -) - - -_PINGRESPONSE = _descriptor.Descriptor( - name="PingResponse", - full_name="PingResponse", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=281, - serialized_end=295, -) - - -_DEVICEINFOREQUEST = _descriptor.Descriptor( - name="DeviceInfoRequest", - full_name="DeviceInfoRequest", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=297, - serialized_end=316, -) - - -_DEVICEINFORESPONSE = _descriptor.Descriptor( - name="DeviceInfoResponse", - full_name="DeviceInfoResponse", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="uses_password", - full_name="DeviceInfoResponse.uses_password", - index=0, - number=1, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="name", - full_name="DeviceInfoResponse.name", - index=1, - number=2, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="mac_address", - full_name="DeviceInfoResponse.mac_address", - index=2, - number=3, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="esphome_core_version", - full_name="DeviceInfoResponse.esphome_core_version", - index=3, - number=4, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="compilation_time", - full_name="DeviceInfoResponse.compilation_time", - index=4, - number=5, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="model", - full_name="DeviceInfoResponse.model", - index=5, - number=6, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="has_deep_sleep", - full_name="DeviceInfoResponse.has_deep_sleep", - index=6, - number=7, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=319, - serialized_end=492, -) - - -_LISTENTITIESREQUEST = _descriptor.Descriptor( - name="ListEntitiesRequest", - full_name="ListEntitiesRequest", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=494, - serialized_end=515, -) - - -_LISTENTITIESBINARYSENSORRESPONSE = _descriptor.Descriptor( - name="ListEntitiesBinarySensorResponse", - full_name="ListEntitiesBinarySensorResponse", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="object_id", - full_name="ListEntitiesBinarySensorResponse.object_id", - index=0, - number=1, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="key", - full_name="ListEntitiesBinarySensorResponse.key", - index=1, - number=2, - type=7, - cpp_type=3, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="name", - full_name="ListEntitiesBinarySensorResponse.name", - index=2, - number=3, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="unique_id", - full_name="ListEntitiesBinarySensorResponse.unique_id", - index=3, - number=4, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="device_class", - full_name="ListEntitiesBinarySensorResponse.device_class", - index=4, - number=5, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="is_status_binary_sensor", - full_name="ListEntitiesBinarySensorResponse.is_status_binary_sensor", - index=5, - number=6, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=518, - serialized_end=672, -) - - -_LISTENTITIESCOVERRESPONSE = _descriptor.Descriptor( - name="ListEntitiesCoverResponse", - full_name="ListEntitiesCoverResponse", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="object_id", - full_name="ListEntitiesCoverResponse.object_id", - index=0, - number=1, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="key", - full_name="ListEntitiesCoverResponse.key", - index=1, - number=2, - type=7, - cpp_type=3, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="name", - full_name="ListEntitiesCoverResponse.name", - index=2, - number=3, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="unique_id", - full_name="ListEntitiesCoverResponse.unique_id", - index=3, - number=4, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="is_optimistic", - full_name="ListEntitiesCoverResponse.is_optimistic", - index=4, - number=5, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=674, - serialized_end=789, -) - - -_LISTENTITIESFANRESPONSE = _descriptor.Descriptor( - name="ListEntitiesFanResponse", - full_name="ListEntitiesFanResponse", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="object_id", - full_name="ListEntitiesFanResponse.object_id", - index=0, - number=1, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="key", - full_name="ListEntitiesFanResponse.key", - index=1, - number=2, - type=7, - cpp_type=3, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="name", - full_name="ListEntitiesFanResponse.name", - index=2, - number=3, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="unique_id", - full_name="ListEntitiesFanResponse.unique_id", - index=3, - number=4, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="supports_oscillation", - full_name="ListEntitiesFanResponse.supports_oscillation", - index=4, - number=5, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="supports_speed", - full_name="ListEntitiesFanResponse.supports_speed", - index=5, - number=6, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=792, - serialized_end=936, -) - - -_LISTENTITIESLIGHTRESPONSE = _descriptor.Descriptor( - name="ListEntitiesLightResponse", - full_name="ListEntitiesLightResponse", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="object_id", - full_name="ListEntitiesLightResponse.object_id", - index=0, - number=1, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="key", - full_name="ListEntitiesLightResponse.key", - index=1, - number=2, - type=7, - cpp_type=3, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="name", - full_name="ListEntitiesLightResponse.name", - index=2, - number=3, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="unique_id", - full_name="ListEntitiesLightResponse.unique_id", - index=3, - number=4, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="supports_brightness", - full_name="ListEntitiesLightResponse.supports_brightness", - index=4, - number=5, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="supports_rgb", - full_name="ListEntitiesLightResponse.supports_rgb", - index=5, - number=6, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="supports_white_value", - full_name="ListEntitiesLightResponse.supports_white_value", - index=6, - number=7, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="supports_color_temperature", - full_name="ListEntitiesLightResponse.supports_color_temperature", - index=7, - number=8, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="min_mireds", - full_name="ListEntitiesLightResponse.min_mireds", - index=8, - number=9, - type=2, - cpp_type=6, - label=1, - has_default_value=False, - default_value=float(0), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="max_mireds", - full_name="ListEntitiesLightResponse.max_mireds", - index=9, - number=10, - type=2, - cpp_type=6, - label=1, - has_default_value=False, - default_value=float(0), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="effects", - full_name="ListEntitiesLightResponse.effects", - index=10, - number=11, - type=9, - cpp_type=9, - label=3, - has_default_value=False, - default_value=[], - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=939, - serialized_end=1205, -) - - -_LISTENTITIESSENSORRESPONSE = _descriptor.Descriptor( - name="ListEntitiesSensorResponse", - full_name="ListEntitiesSensorResponse", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="object_id", - full_name="ListEntitiesSensorResponse.object_id", - index=0, - number=1, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="key", - full_name="ListEntitiesSensorResponse.key", - index=1, - number=2, - type=7, - cpp_type=3, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="name", - full_name="ListEntitiesSensorResponse.name", - index=2, - number=3, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="unique_id", - full_name="ListEntitiesSensorResponse.unique_id", - index=3, - number=4, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="icon", - full_name="ListEntitiesSensorResponse.icon", - index=4, - number=5, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="unit_of_measurement", - full_name="ListEntitiesSensorResponse.unit_of_measurement", - index=5, - number=6, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="accuracy_decimals", - full_name="ListEntitiesSensorResponse.accuracy_decimals", - index=6, - number=7, - type=5, - cpp_type=1, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=1208, - serialized_end=1371, -) - - -_LISTENTITIESSWITCHRESPONSE = _descriptor.Descriptor( - name="ListEntitiesSwitchResponse", - full_name="ListEntitiesSwitchResponse", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="object_id", - full_name="ListEntitiesSwitchResponse.object_id", - index=0, - number=1, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="key", - full_name="ListEntitiesSwitchResponse.key", - index=1, - number=2, - type=7, - cpp_type=3, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="name", - full_name="ListEntitiesSwitchResponse.name", - index=2, - number=3, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="unique_id", - full_name="ListEntitiesSwitchResponse.unique_id", - index=3, - number=4, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="icon", - full_name="ListEntitiesSwitchResponse.icon", - index=4, - number=5, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="optimistic", - full_name="ListEntitiesSwitchResponse.optimistic", - index=5, - number=6, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=1373, - serialized_end=1500, -) - - -_LISTENTITIESTEXTSENSORRESPONSE = _descriptor.Descriptor( - name="ListEntitiesTextSensorResponse", - full_name="ListEntitiesTextSensorResponse", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="object_id", - full_name="ListEntitiesTextSensorResponse.object_id", - index=0, - number=1, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="key", - full_name="ListEntitiesTextSensorResponse.key", - index=1, - number=2, - type=7, - cpp_type=3, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="name", - full_name="ListEntitiesTextSensorResponse.name", - index=2, - number=3, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="unique_id", - full_name="ListEntitiesTextSensorResponse.unique_id", - index=3, - number=4, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="icon", - full_name="ListEntitiesTextSensorResponse.icon", - index=4, - number=5, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=1502, - serialized_end=1613, -) - - -_LISTENTITIESDONERESPONSE = _descriptor.Descriptor( - name="ListEntitiesDoneResponse", - full_name="ListEntitiesDoneResponse", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=1615, - serialized_end=1641, -) - - -_SUBSCRIBESTATESREQUEST = _descriptor.Descriptor( - name="SubscribeStatesRequest", - full_name="SubscribeStatesRequest", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=1643, - serialized_end=1667, -) - - -_BINARYSENSORSTATERESPONSE = _descriptor.Descriptor( - name="BinarySensorStateResponse", - full_name="BinarySensorStateResponse", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="key", - full_name="BinarySensorStateResponse.key", - index=0, - number=1, - type=7, - cpp_type=3, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="state", - full_name="BinarySensorStateResponse.state", - index=1, - number=2, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=1669, - serialized_end=1724, -) - - -_COVERSTATERESPONSE = _descriptor.Descriptor( - name="CoverStateResponse", - full_name="CoverStateResponse", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="key", - full_name="CoverStateResponse.key", - index=0, - number=1, - type=7, - cpp_type=3, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="state", - full_name="CoverStateResponse.state", - index=1, - number=2, - type=14, - cpp_type=8, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[ - _COVERSTATERESPONSE_COVERSTATE, - ], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=1726, - serialized_end=1842, -) - - -_FANSTATERESPONSE = _descriptor.Descriptor( - name="FanStateResponse", - full_name="FanStateResponse", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="key", - full_name="FanStateResponse.key", - index=0, - number=1, - type=7, - cpp_type=3, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="state", - full_name="FanStateResponse.state", - index=1, - number=2, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="oscillating", - full_name="FanStateResponse.oscillating", - index=2, - number=3, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="speed", - full_name="FanStateResponse.speed", - index=3, - number=4, - type=14, - cpp_type=8, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=1844, - serialized_end=1937, -) - - -_LIGHTSTATERESPONSE = _descriptor.Descriptor( - name="LightStateResponse", - full_name="LightStateResponse", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="key", - full_name="LightStateResponse.key", - index=0, - number=1, - type=7, - cpp_type=3, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="state", - full_name="LightStateResponse.state", - index=1, - number=2, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="brightness", - full_name="LightStateResponse.brightness", - index=2, - number=3, - type=2, - cpp_type=6, - label=1, - has_default_value=False, - default_value=float(0), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="red", - full_name="LightStateResponse.red", - index=3, - number=4, - type=2, - cpp_type=6, - label=1, - has_default_value=False, - default_value=float(0), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="green", - full_name="LightStateResponse.green", - index=4, - number=5, - type=2, - cpp_type=6, - label=1, - has_default_value=False, - default_value=float(0), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="blue", - full_name="LightStateResponse.blue", - index=5, - number=6, - type=2, - cpp_type=6, - label=1, - has_default_value=False, - default_value=float(0), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="white", - full_name="LightStateResponse.white", - index=6, - number=7, - type=2, - cpp_type=6, - label=1, - has_default_value=False, - default_value=float(0), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="color_temperature", - full_name="LightStateResponse.color_temperature", - index=7, - number=8, - type=2, - cpp_type=6, - label=1, - has_default_value=False, - default_value=float(0), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="effect", - full_name="LightStateResponse.effect", - index=8, - number=9, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=1940, - serialized_end=2108, -) - - -_SENSORSTATERESPONSE = _descriptor.Descriptor( - name="SensorStateResponse", - full_name="SensorStateResponse", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="key", - full_name="SensorStateResponse.key", - index=0, - number=1, - type=7, - cpp_type=3, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="state", - full_name="SensorStateResponse.state", - index=1, - number=2, - type=2, - cpp_type=6, - label=1, - has_default_value=False, - default_value=float(0), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=2110, - serialized_end=2159, -) - - -_SWITCHSTATERESPONSE = _descriptor.Descriptor( - name="SwitchStateResponse", - full_name="SwitchStateResponse", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="key", - full_name="SwitchStateResponse.key", - index=0, - number=1, - type=7, - cpp_type=3, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="state", - full_name="SwitchStateResponse.state", - index=1, - number=2, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=2161, - serialized_end=2210, -) - - -_TEXTSENSORSTATERESPONSE = _descriptor.Descriptor( - name="TextSensorStateResponse", - full_name="TextSensorStateResponse", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="key", - full_name="TextSensorStateResponse.key", - index=0, - number=1, - type=7, - cpp_type=3, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="state", - full_name="TextSensorStateResponse.state", - index=1, - number=2, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=2212, - serialized_end=2265, -) - - -_COVERCOMMANDREQUEST = _descriptor.Descriptor( - name="CoverCommandRequest", - full_name="CoverCommandRequest", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="key", - full_name="CoverCommandRequest.key", - index=0, - number=1, - type=7, - cpp_type=3, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="has_state", - full_name="CoverCommandRequest.has_state", - index=1, - number=2, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="command", - full_name="CoverCommandRequest.command", - index=2, - number=3, - type=14, - cpp_type=8, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[ - _COVERCOMMANDREQUEST_COVERCOMMAND, - ], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=2268, - serialized_end=2420, -) - - -_FANCOMMANDREQUEST = _descriptor.Descriptor( - name="FanCommandRequest", - full_name="FanCommandRequest", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="key", - full_name="FanCommandRequest.key", - index=0, - number=1, - type=7, - cpp_type=3, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="has_state", - full_name="FanCommandRequest.has_state", - index=1, - number=2, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="state", - full_name="FanCommandRequest.state", - index=2, - number=3, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="has_speed", - full_name="FanCommandRequest.has_speed", - index=3, - number=4, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="speed", - full_name="FanCommandRequest.speed", - index=4, - number=5, - type=14, - cpp_type=8, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="has_oscillating", - full_name="FanCommandRequest.has_oscillating", - index=5, - number=6, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="oscillating", - full_name="FanCommandRequest.oscillating", - index=6, - number=7, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=2423, - serialized_end=2580, -) - - -_LIGHTCOMMANDREQUEST = _descriptor.Descriptor( - name="LightCommandRequest", - full_name="LightCommandRequest", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="key", - full_name="LightCommandRequest.key", - index=0, - number=1, - type=7, - cpp_type=3, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="has_state", - full_name="LightCommandRequest.has_state", - index=1, - number=2, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="state", - full_name="LightCommandRequest.state", - index=2, - number=3, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="has_brightness", - full_name="LightCommandRequest.has_brightness", - index=3, - number=4, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="brightness", - full_name="LightCommandRequest.brightness", - index=4, - number=5, - type=2, - cpp_type=6, - label=1, - has_default_value=False, - default_value=float(0), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="has_rgb", - full_name="LightCommandRequest.has_rgb", - index=5, - number=6, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="red", - full_name="LightCommandRequest.red", - index=6, - number=7, - type=2, - cpp_type=6, - label=1, - has_default_value=False, - default_value=float(0), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="green", - full_name="LightCommandRequest.green", - index=7, - number=8, - type=2, - cpp_type=6, - label=1, - has_default_value=False, - default_value=float(0), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="blue", - full_name="LightCommandRequest.blue", - index=8, - number=9, - type=2, - cpp_type=6, - label=1, - has_default_value=False, - default_value=float(0), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="has_white", - full_name="LightCommandRequest.has_white", - index=9, - number=10, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="white", - full_name="LightCommandRequest.white", - index=10, - number=11, - type=2, - cpp_type=6, - label=1, - has_default_value=False, - default_value=float(0), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="has_color_temperature", - full_name="LightCommandRequest.has_color_temperature", - index=11, - number=12, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="color_temperature", - full_name="LightCommandRequest.color_temperature", - index=12, - number=13, - type=2, - cpp_type=6, - label=1, - has_default_value=False, - default_value=float(0), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="has_transition_length", - full_name="LightCommandRequest.has_transition_length", - index=13, - number=14, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="transition_length", - full_name="LightCommandRequest.transition_length", - index=14, - number=15, - type=13, - cpp_type=3, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="has_flash_length", - full_name="LightCommandRequest.has_flash_length", - index=15, - number=16, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="flash_length", - full_name="LightCommandRequest.flash_length", - index=16, - number=17, - type=13, - cpp_type=3, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="has_effect", - full_name="LightCommandRequest.has_effect", - index=17, - number=18, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="effect", - full_name="LightCommandRequest.effect", - index=18, - number=19, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=2583, - serialized_end=2988, -) - - -_SWITCHCOMMANDREQUEST = _descriptor.Descriptor( - name="SwitchCommandRequest", - full_name="SwitchCommandRequest", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="key", - full_name="SwitchCommandRequest.key", - index=0, - number=1, - type=7, - cpp_type=3, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="state", - full_name="SwitchCommandRequest.state", - index=1, - number=2, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=2990, - serialized_end=3040, -) - - -_SUBSCRIBELOGSREQUEST = _descriptor.Descriptor( - name="SubscribeLogsRequest", - full_name="SubscribeLogsRequest", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="level", - full_name="SubscribeLogsRequest.level", - index=0, - number=1, - type=14, - cpp_type=8, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="dump_config", - full_name="SubscribeLogsRequest.dump_config", - index=1, - number=2, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=3042, - serialized_end=3111, -) - - -_SUBSCRIBELOGSRESPONSE = _descriptor.Descriptor( - name="SubscribeLogsResponse", - full_name="SubscribeLogsResponse", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="level", - full_name="SubscribeLogsResponse.level", - index=0, - number=1, - type=14, - cpp_type=8, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="tag", - full_name="SubscribeLogsResponse.tag", - index=1, - number=2, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="message", - full_name="SubscribeLogsResponse.message", - index=2, - number=3, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="send_failed", - full_name="SubscribeLogsResponse.send_failed", - index=3, - number=4, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=3113, - serialized_end=3213, -) - - -_SUBSCRIBESERVICECALLSREQUEST = _descriptor.Descriptor( - name="SubscribeServiceCallsRequest", - full_name="SubscribeServiceCallsRequest", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=3215, - serialized_end=3245, -) - - -_SERVICECALLRESPONSE_DATAENTRY = _descriptor.Descriptor( - name="DataEntry", - full_name="ServiceCallResponse.DataEntry", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="key", - full_name="ServiceCallResponse.DataEntry.key", - index=0, - number=1, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="value", - full_name="ServiceCallResponse.DataEntry.value", - index=1, - number=2, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=_b("8\001"), - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=3453, - serialized_end=3496, -) - -_SERVICECALLRESPONSE_DATATEMPLATEENTRY = _descriptor.Descriptor( - name="DataTemplateEntry", - full_name="ServiceCallResponse.DataTemplateEntry", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="key", - full_name="ServiceCallResponse.DataTemplateEntry.key", - index=0, - number=1, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="value", - full_name="ServiceCallResponse.DataTemplateEntry.value", - index=1, - number=2, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=_b("8\001"), - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=3498, - serialized_end=3549, -) - -_SERVICECALLRESPONSE_VARIABLESENTRY = _descriptor.Descriptor( - name="VariablesEntry", - full_name="ServiceCallResponse.VariablesEntry", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="key", - full_name="ServiceCallResponse.VariablesEntry.key", - index=0, - number=1, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="value", - full_name="ServiceCallResponse.VariablesEntry.value", - index=1, - number=2, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=_b("8\001"), - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=3551, - serialized_end=3599, -) - -_SERVICECALLRESPONSE = _descriptor.Descriptor( - name="ServiceCallResponse", - full_name="ServiceCallResponse", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="service", - full_name="ServiceCallResponse.service", - index=0, - number=1, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="data", - full_name="ServiceCallResponse.data", - index=1, - number=2, - type=11, - cpp_type=10, - label=3, - has_default_value=False, - default_value=[], - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="data_template", - full_name="ServiceCallResponse.data_template", - index=2, - number=3, - type=11, - cpp_type=10, - label=3, - has_default_value=False, - default_value=[], - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="variables", - full_name="ServiceCallResponse.variables", - index=3, - number=4, - type=11, - cpp_type=10, - label=3, - has_default_value=False, - default_value=[], - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[ - _SERVICECALLRESPONSE_DATAENTRY, - _SERVICECALLRESPONSE_DATATEMPLATEENTRY, - _SERVICECALLRESPONSE_VARIABLESENTRY, - ], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=3248, - serialized_end=3599, -) - - -_SUBSCRIBEHOMEASSISTANTSTATESREQUEST = _descriptor.Descriptor( - name="SubscribeHomeAssistantStatesRequest", - full_name="SubscribeHomeAssistantStatesRequest", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=3601, - serialized_end=3638, -) - - -_SUBSCRIBEHOMEASSISTANTSTATERESPONSE = _descriptor.Descriptor( - name="SubscribeHomeAssistantStateResponse", - full_name="SubscribeHomeAssistantStateResponse", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="entity_id", - full_name="SubscribeHomeAssistantStateResponse.entity_id", - index=0, - number=1, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=3640, - serialized_end=3696, -) - - -_HOMEASSISTANTSTATERESPONSE = _descriptor.Descriptor( - name="HomeAssistantStateResponse", - full_name="HomeAssistantStateResponse", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="entity_id", - full_name="HomeAssistantStateResponse.entity_id", - index=0, - number=1, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - _descriptor.FieldDescriptor( - name="state", - full_name="HomeAssistantStateResponse.state", - index=1, - number=2, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=_b("").decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=3698, - serialized_end=3760, -) - - -_GETTIMEREQUEST = _descriptor.Descriptor( - name="GetTimeRequest", - full_name="GetTimeRequest", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=3762, - serialized_end=3778, -) - - -_GETTIMERESPONSE = _descriptor.Descriptor( - name="GetTimeResponse", - full_name="GetTimeResponse", - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name="epoch_seconds", - full_name="GetTimeResponse.epoch_seconds", - index=0, - number=1, - type=7, - cpp_type=3, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=3780, - serialized_end=3820, -) - -_COVERSTATERESPONSE.fields_by_name["state"].enum_type = _COVERSTATERESPONSE_COVERSTATE -_COVERSTATERESPONSE_COVERSTATE.containing_type = _COVERSTATERESPONSE -_FANSTATERESPONSE.fields_by_name["speed"].enum_type = _FANSPEED -_COVERCOMMANDREQUEST.fields_by_name[ - "command" -].enum_type = _COVERCOMMANDREQUEST_COVERCOMMAND -_COVERCOMMANDREQUEST_COVERCOMMAND.containing_type = _COVERCOMMANDREQUEST -_FANCOMMANDREQUEST.fields_by_name["speed"].enum_type = _FANSPEED -_SUBSCRIBELOGSREQUEST.fields_by_name["level"].enum_type = _LOGLEVEL -_SUBSCRIBELOGSRESPONSE.fields_by_name["level"].enum_type = _LOGLEVEL -_SERVICECALLRESPONSE_DATAENTRY.containing_type = _SERVICECALLRESPONSE -_SERVICECALLRESPONSE_DATATEMPLATEENTRY.containing_type = _SERVICECALLRESPONSE -_SERVICECALLRESPONSE_VARIABLESENTRY.containing_type = _SERVICECALLRESPONSE -_SERVICECALLRESPONSE.fields_by_name[ - "data" -].message_type = _SERVICECALLRESPONSE_DATAENTRY -_SERVICECALLRESPONSE.fields_by_name[ - "data_template" -].message_type = _SERVICECALLRESPONSE_DATATEMPLATEENTRY -_SERVICECALLRESPONSE.fields_by_name[ - "variables" -].message_type = _SERVICECALLRESPONSE_VARIABLESENTRY -DESCRIPTOR.message_types_by_name["HelloRequest"] = _HELLOREQUEST -DESCRIPTOR.message_types_by_name["HelloResponse"] = _HELLORESPONSE -DESCRIPTOR.message_types_by_name["ConnectRequest"] = _CONNECTREQUEST -DESCRIPTOR.message_types_by_name["ConnectResponse"] = _CONNECTRESPONSE -DESCRIPTOR.message_types_by_name["DisconnectRequest"] = _DISCONNECTREQUEST -DESCRIPTOR.message_types_by_name["DisconnectResponse"] = _DISCONNECTRESPONSE -DESCRIPTOR.message_types_by_name["PingRequest"] = _PINGREQUEST -DESCRIPTOR.message_types_by_name["PingResponse"] = _PINGRESPONSE -DESCRIPTOR.message_types_by_name["DeviceInfoRequest"] = _DEVICEINFOREQUEST -DESCRIPTOR.message_types_by_name["DeviceInfoResponse"] = _DEVICEINFORESPONSE -DESCRIPTOR.message_types_by_name["ListEntitiesRequest"] = _LISTENTITIESREQUEST -DESCRIPTOR.message_types_by_name[ - "ListEntitiesBinarySensorResponse" -] = _LISTENTITIESBINARYSENSORRESPONSE -DESCRIPTOR.message_types_by_name[ - "ListEntitiesCoverResponse" -] = _LISTENTITIESCOVERRESPONSE -DESCRIPTOR.message_types_by_name["ListEntitiesFanResponse"] = _LISTENTITIESFANRESPONSE -DESCRIPTOR.message_types_by_name[ - "ListEntitiesLightResponse" -] = _LISTENTITIESLIGHTRESPONSE -DESCRIPTOR.message_types_by_name[ - "ListEntitiesSensorResponse" -] = _LISTENTITIESSENSORRESPONSE -DESCRIPTOR.message_types_by_name[ - "ListEntitiesSwitchResponse" -] = _LISTENTITIESSWITCHRESPONSE -DESCRIPTOR.message_types_by_name[ - "ListEntitiesTextSensorResponse" -] = _LISTENTITIESTEXTSENSORRESPONSE -DESCRIPTOR.message_types_by_name["ListEntitiesDoneResponse"] = _LISTENTITIESDONERESPONSE -DESCRIPTOR.message_types_by_name["SubscribeStatesRequest"] = _SUBSCRIBESTATESREQUEST -DESCRIPTOR.message_types_by_name[ - "BinarySensorStateResponse" -] = _BINARYSENSORSTATERESPONSE -DESCRIPTOR.message_types_by_name["CoverStateResponse"] = _COVERSTATERESPONSE -DESCRIPTOR.message_types_by_name["FanStateResponse"] = _FANSTATERESPONSE -DESCRIPTOR.message_types_by_name["LightStateResponse"] = _LIGHTSTATERESPONSE -DESCRIPTOR.message_types_by_name["SensorStateResponse"] = _SENSORSTATERESPONSE -DESCRIPTOR.message_types_by_name["SwitchStateResponse"] = _SWITCHSTATERESPONSE -DESCRIPTOR.message_types_by_name["TextSensorStateResponse"] = _TEXTSENSORSTATERESPONSE -DESCRIPTOR.message_types_by_name["CoverCommandRequest"] = _COVERCOMMANDREQUEST -DESCRIPTOR.message_types_by_name["FanCommandRequest"] = _FANCOMMANDREQUEST -DESCRIPTOR.message_types_by_name["LightCommandRequest"] = _LIGHTCOMMANDREQUEST -DESCRIPTOR.message_types_by_name["SwitchCommandRequest"] = _SWITCHCOMMANDREQUEST -DESCRIPTOR.message_types_by_name["SubscribeLogsRequest"] = _SUBSCRIBELOGSREQUEST -DESCRIPTOR.message_types_by_name["SubscribeLogsResponse"] = _SUBSCRIBELOGSRESPONSE -DESCRIPTOR.message_types_by_name[ - "SubscribeServiceCallsRequest" -] = _SUBSCRIBESERVICECALLSREQUEST -DESCRIPTOR.message_types_by_name["ServiceCallResponse"] = _SERVICECALLRESPONSE -DESCRIPTOR.message_types_by_name[ - "SubscribeHomeAssistantStatesRequest" -] = _SUBSCRIBEHOMEASSISTANTSTATESREQUEST -DESCRIPTOR.message_types_by_name[ - "SubscribeHomeAssistantStateResponse" -] = _SUBSCRIBEHOMEASSISTANTSTATERESPONSE -DESCRIPTOR.message_types_by_name[ - "HomeAssistantStateResponse" -] = _HOMEASSISTANTSTATERESPONSE -DESCRIPTOR.message_types_by_name["GetTimeRequest"] = _GETTIMEREQUEST -DESCRIPTOR.message_types_by_name["GetTimeResponse"] = _GETTIMERESPONSE -DESCRIPTOR.enum_types_by_name["FanSpeed"] = _FANSPEED -DESCRIPTOR.enum_types_by_name["LogLevel"] = _LOGLEVEL -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -HelloRequest = _reflection.GeneratedProtocolMessageType( - "HelloRequest", - (_message.Message,), - dict( - DESCRIPTOR=_HELLOREQUEST, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:HelloRequest) - ), -) -_sym_db.RegisterMessage(HelloRequest) - -HelloResponse = _reflection.GeneratedProtocolMessageType( - "HelloResponse", - (_message.Message,), - dict( - DESCRIPTOR=_HELLORESPONSE, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:HelloResponse) - ), -) -_sym_db.RegisterMessage(HelloResponse) - -ConnectRequest = _reflection.GeneratedProtocolMessageType( - "ConnectRequest", - (_message.Message,), - dict( - DESCRIPTOR=_CONNECTREQUEST, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:ConnectRequest) - ), -) -_sym_db.RegisterMessage(ConnectRequest) - -ConnectResponse = _reflection.GeneratedProtocolMessageType( - "ConnectResponse", - (_message.Message,), - dict( - DESCRIPTOR=_CONNECTRESPONSE, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:ConnectResponse) - ), -) -_sym_db.RegisterMessage(ConnectResponse) - -DisconnectRequest = _reflection.GeneratedProtocolMessageType( - "DisconnectRequest", - (_message.Message,), - dict( - DESCRIPTOR=_DISCONNECTREQUEST, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:DisconnectRequest) - ), -) -_sym_db.RegisterMessage(DisconnectRequest) - -DisconnectResponse = _reflection.GeneratedProtocolMessageType( - "DisconnectResponse", - (_message.Message,), - dict( - DESCRIPTOR=_DISCONNECTRESPONSE, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:DisconnectResponse) - ), -) -_sym_db.RegisterMessage(DisconnectResponse) - -PingRequest = _reflection.GeneratedProtocolMessageType( - "PingRequest", - (_message.Message,), - dict( - DESCRIPTOR=_PINGREQUEST, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:PingRequest) - ), -) -_sym_db.RegisterMessage(PingRequest) - -PingResponse = _reflection.GeneratedProtocolMessageType( - "PingResponse", - (_message.Message,), - dict( - DESCRIPTOR=_PINGRESPONSE, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:PingResponse) - ), -) -_sym_db.RegisterMessage(PingResponse) - -DeviceInfoRequest = _reflection.GeneratedProtocolMessageType( - "DeviceInfoRequest", - (_message.Message,), - dict( - DESCRIPTOR=_DEVICEINFOREQUEST, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:DeviceInfoRequest) - ), -) -_sym_db.RegisterMessage(DeviceInfoRequest) - -DeviceInfoResponse = _reflection.GeneratedProtocolMessageType( - "DeviceInfoResponse", - (_message.Message,), - dict( - DESCRIPTOR=_DEVICEINFORESPONSE, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:DeviceInfoResponse) - ), -) -_sym_db.RegisterMessage(DeviceInfoResponse) - -ListEntitiesRequest = _reflection.GeneratedProtocolMessageType( - "ListEntitiesRequest", - (_message.Message,), - dict( - DESCRIPTOR=_LISTENTITIESREQUEST, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:ListEntitiesRequest) - ), -) -_sym_db.RegisterMessage(ListEntitiesRequest) - -ListEntitiesBinarySensorResponse = _reflection.GeneratedProtocolMessageType( - "ListEntitiesBinarySensorResponse", - (_message.Message,), - dict( - DESCRIPTOR=_LISTENTITIESBINARYSENSORRESPONSE, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:ListEntitiesBinarySensorResponse) - ), -) -_sym_db.RegisterMessage(ListEntitiesBinarySensorResponse) - -ListEntitiesCoverResponse = _reflection.GeneratedProtocolMessageType( - "ListEntitiesCoverResponse", - (_message.Message,), - dict( - DESCRIPTOR=_LISTENTITIESCOVERRESPONSE, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:ListEntitiesCoverResponse) - ), -) -_sym_db.RegisterMessage(ListEntitiesCoverResponse) - -ListEntitiesFanResponse = _reflection.GeneratedProtocolMessageType( - "ListEntitiesFanResponse", - (_message.Message,), - dict( - DESCRIPTOR=_LISTENTITIESFANRESPONSE, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:ListEntitiesFanResponse) - ), -) -_sym_db.RegisterMessage(ListEntitiesFanResponse) - -ListEntitiesLightResponse = _reflection.GeneratedProtocolMessageType( - "ListEntitiesLightResponse", - (_message.Message,), - dict( - DESCRIPTOR=_LISTENTITIESLIGHTRESPONSE, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:ListEntitiesLightResponse) - ), -) -_sym_db.RegisterMessage(ListEntitiesLightResponse) - -ListEntitiesSensorResponse = _reflection.GeneratedProtocolMessageType( - "ListEntitiesSensorResponse", - (_message.Message,), - dict( - DESCRIPTOR=_LISTENTITIESSENSORRESPONSE, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:ListEntitiesSensorResponse) - ), -) -_sym_db.RegisterMessage(ListEntitiesSensorResponse) - -ListEntitiesSwitchResponse = _reflection.GeneratedProtocolMessageType( - "ListEntitiesSwitchResponse", - (_message.Message,), - dict( - DESCRIPTOR=_LISTENTITIESSWITCHRESPONSE, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:ListEntitiesSwitchResponse) - ), -) -_sym_db.RegisterMessage(ListEntitiesSwitchResponse) - -ListEntitiesTextSensorResponse = _reflection.GeneratedProtocolMessageType( - "ListEntitiesTextSensorResponse", - (_message.Message,), - dict( - DESCRIPTOR=_LISTENTITIESTEXTSENSORRESPONSE, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:ListEntitiesTextSensorResponse) - ), -) -_sym_db.RegisterMessage(ListEntitiesTextSensorResponse) - -ListEntitiesDoneResponse = _reflection.GeneratedProtocolMessageType( - "ListEntitiesDoneResponse", - (_message.Message,), - dict( - DESCRIPTOR=_LISTENTITIESDONERESPONSE, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:ListEntitiesDoneResponse) - ), -) -_sym_db.RegisterMessage(ListEntitiesDoneResponse) - -SubscribeStatesRequest = _reflection.GeneratedProtocolMessageType( - "SubscribeStatesRequest", - (_message.Message,), - dict( - DESCRIPTOR=_SUBSCRIBESTATESREQUEST, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:SubscribeStatesRequest) - ), -) -_sym_db.RegisterMessage(SubscribeStatesRequest) - -BinarySensorStateResponse = _reflection.GeneratedProtocolMessageType( - "BinarySensorStateResponse", - (_message.Message,), - dict( - DESCRIPTOR=_BINARYSENSORSTATERESPONSE, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:BinarySensorStateResponse) - ), -) -_sym_db.RegisterMessage(BinarySensorStateResponse) - -CoverStateResponse = _reflection.GeneratedProtocolMessageType( - "CoverStateResponse", - (_message.Message,), - dict( - DESCRIPTOR=_COVERSTATERESPONSE, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:CoverStateResponse) - ), -) -_sym_db.RegisterMessage(CoverStateResponse) - -FanStateResponse = _reflection.GeneratedProtocolMessageType( - "FanStateResponse", - (_message.Message,), - dict( - DESCRIPTOR=_FANSTATERESPONSE, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:FanStateResponse) - ), -) -_sym_db.RegisterMessage(FanStateResponse) - -LightStateResponse = _reflection.GeneratedProtocolMessageType( - "LightStateResponse", - (_message.Message,), - dict( - DESCRIPTOR=_LIGHTSTATERESPONSE, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:LightStateResponse) - ), -) -_sym_db.RegisterMessage(LightStateResponse) - -SensorStateResponse = _reflection.GeneratedProtocolMessageType( - "SensorStateResponse", - (_message.Message,), - dict( - DESCRIPTOR=_SENSORSTATERESPONSE, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:SensorStateResponse) - ), -) -_sym_db.RegisterMessage(SensorStateResponse) - -SwitchStateResponse = _reflection.GeneratedProtocolMessageType( - "SwitchStateResponse", - (_message.Message,), - dict( - DESCRIPTOR=_SWITCHSTATERESPONSE, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:SwitchStateResponse) - ), -) -_sym_db.RegisterMessage(SwitchStateResponse) - -TextSensorStateResponse = _reflection.GeneratedProtocolMessageType( - "TextSensorStateResponse", - (_message.Message,), - dict( - DESCRIPTOR=_TEXTSENSORSTATERESPONSE, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:TextSensorStateResponse) - ), -) -_sym_db.RegisterMessage(TextSensorStateResponse) - -CoverCommandRequest = _reflection.GeneratedProtocolMessageType( - "CoverCommandRequest", - (_message.Message,), - dict( - DESCRIPTOR=_COVERCOMMANDREQUEST, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:CoverCommandRequest) - ), -) -_sym_db.RegisterMessage(CoverCommandRequest) - -FanCommandRequest = _reflection.GeneratedProtocolMessageType( - "FanCommandRequest", - (_message.Message,), - dict( - DESCRIPTOR=_FANCOMMANDREQUEST, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:FanCommandRequest) - ), -) -_sym_db.RegisterMessage(FanCommandRequest) - -LightCommandRequest = _reflection.GeneratedProtocolMessageType( - "LightCommandRequest", - (_message.Message,), - dict( - DESCRIPTOR=_LIGHTCOMMANDREQUEST, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:LightCommandRequest) - ), -) -_sym_db.RegisterMessage(LightCommandRequest) - -SwitchCommandRequest = _reflection.GeneratedProtocolMessageType( - "SwitchCommandRequest", - (_message.Message,), - dict( - DESCRIPTOR=_SWITCHCOMMANDREQUEST, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:SwitchCommandRequest) - ), -) -_sym_db.RegisterMessage(SwitchCommandRequest) - -SubscribeLogsRequest = _reflection.GeneratedProtocolMessageType( - "SubscribeLogsRequest", - (_message.Message,), - dict( - DESCRIPTOR=_SUBSCRIBELOGSREQUEST, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:SubscribeLogsRequest) - ), -) -_sym_db.RegisterMessage(SubscribeLogsRequest) - -SubscribeLogsResponse = _reflection.GeneratedProtocolMessageType( - "SubscribeLogsResponse", - (_message.Message,), - dict( - DESCRIPTOR=_SUBSCRIBELOGSRESPONSE, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:SubscribeLogsResponse) - ), -) -_sym_db.RegisterMessage(SubscribeLogsResponse) - -SubscribeServiceCallsRequest = _reflection.GeneratedProtocolMessageType( - "SubscribeServiceCallsRequest", - (_message.Message,), - dict( - DESCRIPTOR=_SUBSCRIBESERVICECALLSREQUEST, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:SubscribeServiceCallsRequest) - ), -) -_sym_db.RegisterMessage(SubscribeServiceCallsRequest) - -ServiceCallResponse = _reflection.GeneratedProtocolMessageType( - "ServiceCallResponse", - (_message.Message,), - dict( - DataEntry=_reflection.GeneratedProtocolMessageType( - "DataEntry", - (_message.Message,), - dict( - DESCRIPTOR=_SERVICECALLRESPONSE_DATAENTRY, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:ServiceCallResponse.DataEntry) - ), - ), - DataTemplateEntry=_reflection.GeneratedProtocolMessageType( - "DataTemplateEntry", - (_message.Message,), - dict( - DESCRIPTOR=_SERVICECALLRESPONSE_DATATEMPLATEENTRY, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:ServiceCallResponse.DataTemplateEntry) - ), - ), - VariablesEntry=_reflection.GeneratedProtocolMessageType( - "VariablesEntry", - (_message.Message,), - dict( - DESCRIPTOR=_SERVICECALLRESPONSE_VARIABLESENTRY, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:ServiceCallResponse.VariablesEntry) - ), - ), - DESCRIPTOR=_SERVICECALLRESPONSE, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:ServiceCallResponse) - ), -) -_sym_db.RegisterMessage(ServiceCallResponse) -_sym_db.RegisterMessage(ServiceCallResponse.DataEntry) -_sym_db.RegisterMessage(ServiceCallResponse.DataTemplateEntry) -_sym_db.RegisterMessage(ServiceCallResponse.VariablesEntry) - -SubscribeHomeAssistantStatesRequest = _reflection.GeneratedProtocolMessageType( - "SubscribeHomeAssistantStatesRequest", - (_message.Message,), - dict( - DESCRIPTOR=_SUBSCRIBEHOMEASSISTANTSTATESREQUEST, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:SubscribeHomeAssistantStatesRequest) - ), -) -_sym_db.RegisterMessage(SubscribeHomeAssistantStatesRequest) - -SubscribeHomeAssistantStateResponse = _reflection.GeneratedProtocolMessageType( - "SubscribeHomeAssistantStateResponse", - (_message.Message,), - dict( - DESCRIPTOR=_SUBSCRIBEHOMEASSISTANTSTATERESPONSE, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:SubscribeHomeAssistantStateResponse) - ), -) -_sym_db.RegisterMessage(SubscribeHomeAssistantStateResponse) - -HomeAssistantStateResponse = _reflection.GeneratedProtocolMessageType( - "HomeAssistantStateResponse", - (_message.Message,), - dict( - DESCRIPTOR=_HOMEASSISTANTSTATERESPONSE, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:HomeAssistantStateResponse) - ), -) -_sym_db.RegisterMessage(HomeAssistantStateResponse) - -GetTimeRequest = _reflection.GeneratedProtocolMessageType( - "GetTimeRequest", - (_message.Message,), - dict( - DESCRIPTOR=_GETTIMEREQUEST, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:GetTimeRequest) - ), -) -_sym_db.RegisterMessage(GetTimeRequest) - -GetTimeResponse = _reflection.GeneratedProtocolMessageType( - "GetTimeResponse", - (_message.Message,), - dict( - DESCRIPTOR=_GETTIMERESPONSE, - __module__="api_pb2" - # @@protoc_insertion_point(class_scope:GetTimeResponse) - ), -) -_sym_db.RegisterMessage(GetTimeResponse) - - -_SERVICECALLRESPONSE_DATAENTRY._options = None -_SERVICECALLRESPONSE_DATATEMPLATEENTRY._options = None -_SERVICECALLRESPONSE_VARIABLESENTRY._options = None -# @@protoc_insertion_point(module_scope) diff --git a/esphome/api/client.py b/esphome/api/client.py deleted file mode 100644 index dd11f79922..0000000000 --- a/esphome/api/client.py +++ /dev/null @@ -1,518 +0,0 @@ -from datetime import datetime -import functools -import logging -import socket -import threading -import time - -# pylint: disable=unused-import -from typing import Optional # noqa -from google.protobuf import message # noqa - -from esphome import const -import esphome.api.api_pb2 as pb -from esphome.const import CONF_PASSWORD, CONF_PORT -from esphome.core import EsphomeError -from esphome.helpers import resolve_ip_address, indent -from esphome.log import color, Fore -from esphome.util import safe_print - -_LOGGER = logging.getLogger(__name__) - - -class APIConnectionError(EsphomeError): - pass - - -MESSAGE_TYPE_TO_PROTO = { - 1: pb.HelloRequest, - 2: pb.HelloResponse, - 3: pb.ConnectRequest, - 4: pb.ConnectResponse, - 5: pb.DisconnectRequest, - 6: pb.DisconnectResponse, - 7: pb.PingRequest, - 8: pb.PingResponse, - 9: pb.DeviceInfoRequest, - 10: pb.DeviceInfoResponse, - 11: pb.ListEntitiesRequest, - 12: pb.ListEntitiesBinarySensorResponse, - 13: pb.ListEntitiesCoverResponse, - 14: pb.ListEntitiesFanResponse, - 15: pb.ListEntitiesLightResponse, - 16: pb.ListEntitiesSensorResponse, - 17: pb.ListEntitiesSwitchResponse, - 18: pb.ListEntitiesTextSensorResponse, - 19: pb.ListEntitiesDoneResponse, - 20: pb.SubscribeStatesRequest, - 21: pb.BinarySensorStateResponse, - 22: pb.CoverStateResponse, - 23: pb.FanStateResponse, - 24: pb.LightStateResponse, - 25: pb.SensorStateResponse, - 26: pb.SwitchStateResponse, - 27: pb.TextSensorStateResponse, - 28: pb.SubscribeLogsRequest, - 29: pb.SubscribeLogsResponse, - 30: pb.CoverCommandRequest, - 31: pb.FanCommandRequest, - 32: pb.LightCommandRequest, - 33: pb.SwitchCommandRequest, - 34: pb.SubscribeServiceCallsRequest, - 35: pb.ServiceCallResponse, - 36: pb.GetTimeRequest, - 37: pb.GetTimeResponse, -} - - -def _varuint_to_bytes(value): - if value <= 0x7F: - return bytes([value]) - - ret = bytes() - while value: - temp = value & 0x7F - value >>= 7 - if value: - ret += bytes([temp | 0x80]) - else: - ret += bytes([temp]) - - return ret - - -def _bytes_to_varuint(value): - result = 0 - bitpos = 0 - for val in value: - result |= (val & 0x7F) << bitpos - bitpos += 7 - if (val & 0x80) == 0: - return result - return None - - -# pylint: disable=too-many-instance-attributes,not-callable -class APIClient(threading.Thread): - def __init__(self, address, port, password): - threading.Thread.__init__(self) - self._address = address # type: str - self._port = port # type: int - self._password = password # type: Optional[str] - self._socket = None # type: Optional[socket.socket] - self._socket_open_event = threading.Event() - self._socket_write_lock = threading.Lock() - self._connected = False - self._authenticated = False - self._message_handlers = [] - self._keepalive = 5 - self._ping_timer = None - - self.on_disconnect = None - self.on_connect = None - self.on_login = None - self.auto_reconnect = False - self._running_event = threading.Event() - self._stop_event = threading.Event() - - @property - def stopped(self): - return self._stop_event.is_set() - - def _refresh_ping(self): - if self._ping_timer is not None: - self._ping_timer.cancel() - self._ping_timer = None - - def func(): - self._ping_timer = None - - if self._connected: - try: - self.ping() - except APIConnectionError as err: - self._fatal_error(err) - else: - self._refresh_ping() - - self._ping_timer = threading.Timer(self._keepalive, func) - self._ping_timer.start() - - def _cancel_ping(self): - if self._ping_timer is not None: - self._ping_timer.cancel() - self._ping_timer = None - - def _close_socket(self): - self._cancel_ping() - if self._socket is not None: - self._socket.close() - self._socket = None - self._socket_open_event.clear() - self._connected = False - self._authenticated = False - self._message_handlers = [] - - def stop(self, force=False): - if self.stopped: - raise ValueError - - if self._connected and not force: - try: - self.disconnect() - except APIConnectionError: - pass - self._close_socket() - - self._stop_event.set() - if not force: - self.join() - - def connect(self): - if not self._running_event.wait(0.1): - raise APIConnectionError("You need to call start() first!") - - if self._connected: - self.disconnect(on_disconnect=False) - - try: - ip = resolve_ip_address(self._address) - except EsphomeError as err: - _LOGGER.warning( - "Error resolving IP address of %s. Is it connected to WiFi?", - self._address, - ) - _LOGGER.warning( - "(If this error persists, please set a static IP address: " - "https://esphome.io/components/wifi.html#manual-ips)" - ) - raise APIConnectionError(err) from err - - _LOGGER.info("Connecting to %s:%s (%s)", self._address, self._port, ip) - self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self._socket.settimeout(10.0) - self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) - try: - self._socket.connect((ip, self._port)) - except OSError as err: - err = APIConnectionError(f"Error connecting to {ip}: {err}") - self._fatal_error(err) - raise err - self._socket.settimeout(0.1) - - self._socket_open_event.set() - - hello = pb.HelloRequest() - hello.client_info = f"ESPHome v{const.__version__}" - try: - resp = self._send_message_await_response(hello, pb.HelloResponse) - except APIConnectionError as err: - self._fatal_error(err) - raise err - _LOGGER.debug( - "Successfully connected to %s ('%s' API=%s.%s)", - self._address, - resp.server_info, - resp.api_version_major, - resp.api_version_minor, - ) - self._connected = True - self._refresh_ping() - if self.on_connect is not None: - self.on_connect() - - def _check_connected(self): - if not self._connected: - err = APIConnectionError("Must be connected!") - self._fatal_error(err) - raise err - - def login(self): - self._check_connected() - if self._authenticated: - raise APIConnectionError("Already logged in!") - - connect = pb.ConnectRequest() - if self._password is not None: - connect.password = self._password - resp = self._send_message_await_response(connect, pb.ConnectResponse) - if resp.invalid_password: - raise APIConnectionError("Invalid password!") - - self._authenticated = True - if self.on_login is not None: - self.on_login() - - def _fatal_error(self, err): - was_connected = self._connected - - self._close_socket() - - if was_connected and self.on_disconnect is not None: - self.on_disconnect(err) - - def _write(self, data): # type: (bytes) -> None - if self._socket is None: - raise APIConnectionError("Socket closed") - - # _LOGGER.debug("Write: %s", format_bytes(data)) - with self._socket_write_lock: - try: - self._socket.sendall(data) - except OSError as err: - err = APIConnectionError(f"Error while writing data: {err}") - self._fatal_error(err) - raise err - - def _send_message(self, msg): - # type: (message.Message) -> None - for message_type, klass in MESSAGE_TYPE_TO_PROTO.items(): - if isinstance(msg, klass): - break - else: - raise ValueError - - encoded = msg.SerializeToString() - _LOGGER.debug("Sending %s:\n%s", type(msg), indent(str(msg))) - req = bytes([0]) - req += _varuint_to_bytes(len(encoded)) - req += _varuint_to_bytes(message_type) - req += encoded - self._write(req) - - def _send_message_await_response_complex( - self, send_msg, do_append, do_stop, timeout=5 - ): - event = threading.Event() - responses = [] - - def on_message(resp): - if do_append(resp): - responses.append(resp) - if do_stop(resp): - event.set() - - self._message_handlers.append(on_message) - self._send_message(send_msg) - ret = event.wait(timeout) - try: - self._message_handlers.remove(on_message) - except ValueError: - pass - if not ret: - raise APIConnectionError("Timeout while waiting for message response!") - return responses - - def _send_message_await_response(self, send_msg, response_type, timeout=5): - def is_response(msg): - return isinstance(msg, response_type) - - return self._send_message_await_response_complex( - send_msg, is_response, is_response, timeout - )[0] - - def device_info(self): - self._check_connected() - return self._send_message_await_response( - pb.DeviceInfoRequest(), pb.DeviceInfoResponse - ) - - def ping(self): - self._check_connected() - return self._send_message_await_response(pb.PingRequest(), pb.PingResponse) - - def disconnect(self, on_disconnect=True): - self._check_connected() - - try: - self._send_message_await_response( - pb.DisconnectRequest(), pb.DisconnectResponse - ) - except APIConnectionError: - pass - self._close_socket() - - if self.on_disconnect is not None and on_disconnect: - self.on_disconnect(None) - - def _check_authenticated(self): - if not self._authenticated: - raise APIConnectionError("Must login first!") - - def subscribe_logs(self, on_log, log_level=7, dump_config=False): - self._check_authenticated() - - def on_msg(msg): - if isinstance(msg, pb.SubscribeLogsResponse): - on_log(msg) - - self._message_handlers.append(on_msg) - req = pb.SubscribeLogsRequest(dump_config=dump_config) - req.level = log_level - self._send_message(req) - - def _recv(self, amount): - ret = bytes() - if amount == 0: - return ret - - while len(ret) < amount: - if self.stopped: - raise APIConnectionError("Stopped!") - if not self._socket_open_event.is_set(): - raise APIConnectionError("No socket!") - try: - val = self._socket.recv(amount - len(ret)) - except AttributeError as err: - raise APIConnectionError("Socket was closed") from err - except socket.timeout: - continue - except OSError as err: - raise APIConnectionError(f"Error while receiving data: {err}") from err - ret += val - return ret - - def _recv_varint(self): - raw = bytes() - while not raw or raw[-1] & 0x80: - raw += self._recv(1) - return _bytes_to_varuint(raw) - - def _run_once(self): - if not self._socket_open_event.wait(0.1): - return - - # Preamble - if self._recv(1)[0] != 0x00: - raise APIConnectionError("Invalid preamble") - - length = self._recv_varint() - msg_type = self._recv_varint() - - raw_msg = self._recv(length) - if msg_type not in MESSAGE_TYPE_TO_PROTO: - _LOGGER.debug("Skipping message type %s", msg_type) - return - - msg = MESSAGE_TYPE_TO_PROTO[msg_type]() - msg.ParseFromString(raw_msg) - _LOGGER.debug("Got message: %s:\n%s", type(msg), indent(str(msg))) - for msg_handler in self._message_handlers[:]: - msg_handler(msg) - self._handle_internal_messages(msg) - - def run(self): - self._running_event.set() - while not self.stopped: - try: - self._run_once() - except APIConnectionError as err: - if self.stopped: - break - if self._connected: - _LOGGER.error("Error while reading incoming messages: %s", err) - self._fatal_error(err) - self._running_event.clear() - - def _handle_internal_messages(self, msg): - if isinstance(msg, pb.DisconnectRequest): - self._send_message(pb.DisconnectResponse()) - if self._socket is not None: - self._socket.close() - self._socket = None - self._connected = False - if self.on_disconnect is not None: - self.on_disconnect(None) - elif isinstance(msg, pb.PingRequest): - self._send_message(pb.PingResponse()) - elif isinstance(msg, pb.GetTimeRequest): - resp = pb.GetTimeResponse() - resp.epoch_seconds = int(time.time()) - self._send_message(resp) - - -def run_logs(config, address): - conf = config["api"] - port = conf[CONF_PORT] - password = conf[CONF_PASSWORD] - _LOGGER.info("Starting log output from %s using esphome API", address) - - cli = APIClient(address, port, password) - stopping = False - retry_timer = [] - - has_connects = [] - - def try_connect(err, tries=0): - if stopping: - return - - if err: - _LOGGER.warning("Disconnected from API: %s", err) - - while retry_timer: - retry_timer.pop(0).cancel() - - error = None - try: - cli.connect() - cli.login() - except APIConnectionError as err2: # noqa - error = err2 - - if error is None: - _LOGGER.info("Successfully connected to %s", address) - return - - wait_time = int(min(1.5 ** min(tries, 100), 30)) - if not has_connects: - _LOGGER.warning( - "Initial connection failed. The ESP might not be connected " - "to WiFi yet (%s). Re-Trying in %s seconds", - error, - wait_time, - ) - else: - _LOGGER.warning( - "Couldn't connect to API (%s). Trying to reconnect in %s seconds", - error, - wait_time, - ) - timer = threading.Timer( - wait_time, functools.partial(try_connect, None, tries + 1) - ) - timer.start() - retry_timer.append(timer) - - def on_log(msg): - time_ = datetime.now().time().strftime("[%H:%M:%S]") - text = msg.message - if msg.send_failed: - text = color( - Fore.WHITE, - "(Message skipped because it was too big to fit in " - "TCP buffer - This is only cosmetic)", - ) - safe_print(time_ + text) - - def on_login(): - try: - cli.subscribe_logs(on_log, dump_config=not has_connects) - has_connects.append(True) - except APIConnectionError: - cli.disconnect() - - cli.on_disconnect = try_connect - cli.on_login = on_login - cli.start() - - try: - try_connect(None) - while True: - time.sleep(1) - except KeyboardInterrupt: - stopping = True - cli.stop(True) - while retry_timer: - retry_timer.pop(0).cancel() - return 0 diff --git a/esphome/components/api/client.py b/esphome/components/api/client.py new file mode 100644 index 0000000000..d8192eb88f --- /dev/null +++ b/esphome/components/api/client.py @@ -0,0 +1,73 @@ +import asyncio +import logging +from datetime import datetime +from typing import Optional + +from aioesphomeapi import APIClient, ReconnectLogic, APIConnectionError, LogLevel +import zeroconf + +from esphome.const import CONF_KEY, CONF_PORT, CONF_PASSWORD, __version__ +from esphome.util import safe_print +from . import CONF_ENCRYPTION + +_LOGGER = logging.getLogger(__name__) + + +async def async_run_logs(config, address): + conf = config["api"] + port: int = conf[CONF_PORT] + password: str = conf[CONF_PASSWORD] + noise_psk: Optional[str] = None + if CONF_ENCRYPTION in conf: + noise_psk = conf[CONF_ENCRYPTION][CONF_KEY] + _LOGGER.info("Starting log output from %s using esphome API", address) + zc = zeroconf.Zeroconf() + cli = APIClient( + asyncio.get_event_loop(), + address, + port, + password, + client_info=f"ESPHome Logs {__version__}", + noise_psk=noise_psk, + ) + first_connect = True + + def on_log(msg): + time_ = datetime.now().time().strftime("[%H:%M:%S]") + text = msg.message.decode("utf8", "backslashreplace") + safe_print(time_ + text) + + async def on_connect(): + nonlocal first_connect + try: + await cli.subscribe_logs( + on_log, + log_level=LogLevel.LOG_LEVEL_VERY_VERBOSE, + dump_config=first_connect, + ) + first_connect = False + except APIConnectionError: + cli.disconnect() + + async def on_disconnect(): + _LOGGER.warning("Disconnected from API") + + zc = zeroconf.Zeroconf() + reconnect = ReconnectLogic( + client=cli, + on_connect=on_connect, + on_disconnect=on_disconnect, + zeroconf_instance=zc, + ) + await reconnect.start() + + try: + while True: + await asyncio.sleep(60) + except KeyboardInterrupt: + await reconnect.stop() + zc.close() + + +def run_logs(config, address): + asyncio.run(async_run_logs(config, address)) diff --git a/requirements.txt b/requirements.txt index 2d354d5f04..18752e16a3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,12 +3,11 @@ PyYAML==5.4.1 paho-mqtt==1.5.1 colorama==0.4.4 tornado==6.1 -protobuf==3.17.3 tzlocal==2.1 pytz==2021.1 pyserial==3.5 -ifaddr==0.1.7 platformio==5.2.0 esptool==3.1 click==7.1.2 esphome-dashboard==20210908.0 +aioesphomeapi==9.0.0 From c5d26a5b4acdb61cf36de228f0e5f480aac42ab3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Sep 2021 10:26:49 +0200 Subject: [PATCH 028/207] Bump click from 7.1.2 to 8.0.1 (#1824) Bumps [click](https://github.com/pallets/click) from 7.1.2 to 8.0.1. - [Release notes](https://github.com/pallets/click/releases) - [Changelog](https://github.com/pallets/click/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets/click/compare/7.1.2...8.0.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 18752e16a3..5ab68beb2e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,6 +8,6 @@ pytz==2021.1 pyserial==3.5 platformio==5.2.0 esptool==3.1 -click==7.1.2 +click==8.0.1 esphome-dashboard==20210908.0 aioesphomeapi==9.0.0 From 5f61897becd2ec2c5d495431df6ffc5c519eeb45 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Tue, 14 Sep 2021 10:35:37 +0200 Subject: [PATCH 029/207] Add stale/lock bots (#2299) --- .github/workflows/lock.yml | 21 +++++++++++++++++++++ .github/workflows/stale.yml | 30 ++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 .github/workflows/lock.yml create mode 100644 .github/workflows/stale.yml diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml new file mode 100644 index 0000000000..aedb9dd9b3 --- /dev/null +++ b/.github/workflows/lock.yml @@ -0,0 +1,21 @@ +name: Lock + +on: + schedule: + - cron: "0 * * * *" + workflow_dispatch: + +permissions: + issues: write + pull-requests: write + +jobs: + lock: + runs-on: ubuntu-latest + steps: + - uses: dessant/lock-threads@v2 + with: + github-token: ${{ github.token }} + pr-lock-inactive-days: "1" + pr-lock-reason: "" + process-only: prs diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000000..f26dd32929 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,30 @@ +name: Stale + +on: + schedule: + - cron: "0 * * * *" + workflow_dispatch: + +permissions: + issues: write + pull-requests: write + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v4 + with: + repo-token: ${{ github.token }} + days-before-pr-stale: 90 + days-before-pr-close: 7 + days-before-issue-stale: -1 + days-before-issue-close: -1 + remove-stale-when-updated: true + stale-pr-label: "stale" + exempt-pr-labels: "no-stale" + stale-pr-message: > + There hasn't been any activity on this pull request recently. This + pull request has been automatically marked as stale because of that + and will be closed if no further activity occurs within 7 days. + Thank you for your contributions. From dd3f2f6c7ebca5eb1bc2561d651b989154596ef9 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Tue, 14 Sep 2021 11:53:49 +0200 Subject: [PATCH 030/207] Fix api noise explicit reject (#2297) --- esphome/components/api/api_frame_helper.cpp | 41 ++++++++++++++++----- esphome/components/api/api_frame_helper.h | 2 + 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/esphome/components/api/api_frame_helper.cpp b/esphome/components/api/api_frame_helper.cpp index c064c7278f..e68831e594 100644 --- a/esphome/components/api/api_frame_helper.cpp +++ b/esphome/components/api/api_frame_helper.cpp @@ -61,11 +61,15 @@ const char *api_error_to_str(APIError err) { return "HANDSHAKESTATE_SETUP_FAILED"; } else if (err == APIError::HANDSHAKESTATE_SPLIT_FAILED) { return "HANDSHAKESTATE_SPLIT_FAILED"; + } else if (err == APIError::BAD_HANDSHAKE_ERROR_BYTE) { + return "BAD_HANDSHAKE_ERROR_BYTE"; } return "UNKNOWN"; } #define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s: " msg, info_.c_str(), ##__VA_ARGS__) +// uncomment to log raw packets +//#define HELPER_LOG_PACKETS #ifdef USE_API_NOISE static const char *const PROLOGUE_INIT = "NoiseAPIInit"; @@ -236,7 +240,9 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) { } // uncomment for even more debugging - // ESP_LOGVV(TAG, "Received frame: %s", hexencode(rx_buf_).c_str()); +#ifdef HELPER_LOG_PACKETS + ESP_LOGVV(TAG, "Received frame: %s", hexencode(rx_buf_).c_str()); +#endif frame->msg = std::move(rx_buf_); // consume msg rx_buf_ = {}; @@ -265,6 +271,14 @@ APIError APINoiseFrameHelper::state_action_() { // waiting for client hello ParsedFrame frame; aerr = try_read_frame_(&frame); + if (aerr == APIError::BAD_INDICATOR) { + send_explicit_handshake_reject_("Bad indicator byte"); + return aerr; + } + if (aerr == APIError::BAD_HANDSHAKE_PACKET_LEN) { + send_explicit_handshake_reject_("Bad handshake packet len"); + return aerr; + } if (aerr != APIError::OK) return aerr; // ignore contents, may be used in future for flags @@ -308,11 +322,11 @@ APIError APINoiseFrameHelper::state_action_() { if (frame.msg.empty()) { send_explicit_handshake_reject_("Empty handshake message"); - return APIError::BAD_HANDSHAKE_PACKET_LEN; + return APIError::BAD_HANDSHAKE_ERROR_BYTE; } else if (frame.msg[0] != 0x00) { HELPER_LOG("Bad handshake error byte: %u", frame.msg[0]); send_explicit_handshake_reject_("Bad handshake error byte"); - return APIError::BAD_HANDSHAKE_PACKET_LEN; + return APIError::BAD_HANDSHAKE_ERROR_BYTE; } NoiseBuffer mbuf; @@ -320,7 +334,6 @@ APIError APINoiseFrameHelper::state_action_() { noise_buffer_set_input(mbuf, frame.msg.data() + 1, frame.msg.size() - 1); err = noise_handshakestate_read_message(handshake_, &mbuf, nullptr); if (err != 0) { - // TODO: explicit rejection state_ = State::FAILED; HELPER_LOG("noise_handshakestate_read_message failed: %s", noise_err_to_str(err).c_str()); if (err == NOISE_ERROR_MAC_FAILURE) { @@ -368,12 +381,16 @@ APIError APINoiseFrameHelper::state_action_() { } void APINoiseFrameHelper::send_explicit_handshake_reject_(const std::string &reason) { std::vector data; - data.reserve(reason.size() + 1); + data.resize(reason.length() + 1); data[0] = 0x01; // failure - for (size_t i = 0; i < reason.size(); i++) { + for (size_t i = 0; i < reason.length(); i++) { data[i + 1] = (uint8_t) reason[i]; } + // temporarily remove failed state + auto orig_state = state_; + state_ = State::EXPLICIT_REJECT; write_frame_(data.data(), data.size()); + state_ = orig_state; } APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) { @@ -516,7 +533,9 @@ APIError APINoiseFrameHelper::write_raw_(const uint8_t *data, size_t len) { APIError aerr; // uncomment for even more debugging - // ESP_LOGVV(TAG, "Sending raw: %s", hexencode(data, len).c_str()); +#ifdef HELPER_LOG_PACKETS + ESP_LOGVV(TAG, "Sending raw: %s", hexencode(data, len).c_str()); +#endif if (!tx_buf_.empty()) { // try to empty tx_buf_ first @@ -799,7 +818,9 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) { } // uncomment for even more debugging - // ESP_LOGVV(TAG, "Received frame: %s", hexencode(rx_buf_).c_str()); +#ifdef HELPER_LOG_PACKETS + ESP_LOGVV(TAG, "Received frame: %s", hexencode(rx_buf_).c_str()); +#endif frame->msg = std::move(rx_buf_); // consume msg rx_buf_ = {}; @@ -882,7 +903,9 @@ APIError APIPlaintextFrameHelper::write_raw_(const uint8_t *data, size_t len) { APIError aerr; // uncomment for even more debugging - // ESP_LOGVV(TAG, "Sending raw: %s", hexencode(data, len).c_str()); +#ifdef HELPER_LOG_PACKETS + ESP_LOGVV(TAG, "Sending raw: %s", hexencode(data, len).c_str()); +#endif if (!tx_buf_.empty()) { // try to empty tx_buf_ first diff --git a/esphome/components/api/api_frame_helper.h b/esphome/components/api/api_frame_helper.h index a8974cd25f..a9a653cf4f 100644 --- a/esphome/components/api/api_frame_helper.h +++ b/esphome/components/api/api_frame_helper.h @@ -51,6 +51,7 @@ enum class APIError : int { OUT_OF_MEMORY = 1018, HANDSHAKESTATE_SETUP_FAILED = 1019, HANDSHAKESTATE_SPLIT_FAILED = 1020, + BAD_HANDSHAKE_ERROR_BYTE = 1021, }; const char *api_error_to_str(APIError err); @@ -125,6 +126,7 @@ class APINoiseFrameHelper : public APIFrameHelper { DATA = 5, CLOSED = 6, FAILED = 7, + EXPLICIT_REJECT = 8, } state_ = State::INITIALIZE; }; #endif // USE_API_NOISE From d437cc915c9fc10644299b3c189f1aedd826ceb3 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 14 Sep 2021 22:40:45 +1200 Subject: [PATCH 031/207] Allow simple hostname for sntp servers (#2300) --- esphome/components/sntp/time.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/sntp/time.py b/esphome/components/sntp/time.py index 5475dc0a1f..b1362f5421 100644 --- a/esphome/components/sntp/time.py +++ b/esphome/components/sntp/time.py @@ -16,7 +16,7 @@ CONFIG_SCHEMA = time_.TIME_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(SNTPComponent), cv.Optional(CONF_SERVERS, default=DEFAULT_SERVERS): cv.All( - cv.ensure_list(cv.domain), cv.Length(min=1, max=3) + cv.ensure_list(cv.Any(cv.domain, cv.hostname)), cv.Length(min=1, max=3) ), } ).extend(cv.COMPONENT_SCHEMA) From 4cc2817fcd42d0d208a00561dcca0dc9b7377a4d Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 14 Sep 2021 22:59:15 +1200 Subject: [PATCH 032/207] Fix binary strobe (#2301) --- esphome/components/light/base_light_effects.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/light/base_light_effects.h b/esphome/components/light/base_light_effects.h index 7826b2eecb..5ab9f66ce4 100644 --- a/esphome/components/light/base_light_effects.h +++ b/esphome/components/light/base_light_effects.h @@ -156,7 +156,7 @@ class StrobeLightEffect : public LightEffect { if (!color.is_on()) { // Don't turn the light off, otherwise the light effect will be stopped - call.set_brightness_if_supported(0.0f); + call.set_brightness(0.0f); call.set_state(true); } call.set_publish(false); From 716039e452b435ea15fcfc5b94c59cb45e277b99 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Tue, 14 Sep 2021 14:27:35 +0200 Subject: [PATCH 033/207] Use standard version of make_unique when available (#2292) --- .clang-tidy | 5 ++++- esphome/components/nfc/ndef_message.cpp | 4 ++-- esphome/components/nfc/nfc_tag.h | 4 ++-- esphome/components/pn532/pn532.cpp | 10 ++++++---- esphome/components/pn532/pn532_mifare_classic.cpp | 10 ++++++---- esphome/components/pn532/pn532_mifare_ultralight.cpp | 12 +++++++----- esphome/components/socket/bsd_sockets_impl.cpp | 3 ++- esphome/core/helpers.h | 7 ++++++- 8 files changed, 35 insertions(+), 20 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 890eb18608..98a1568e5a 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -70,7 +70,6 @@ Checks: >- -modernize-use-default-member-init, -modernize-use-equals-default, -modernize-use-trailing-return-type, - -modernize-make-unique, -modernize-use-nodiscard, -mpi-*, -objc-*, @@ -114,6 +113,10 @@ CheckOptions: value: llvm - key: modernize-use-nullptr.NullMacros value: 'NULL' + - key: modernize-make-unique.MakeSmartPtrFunction + value: 'make_unique' + - key: modernize-make-unique.MakeSmartPtrFunctionHeader + value: 'esphome/core/helpers.h' - key: readability-identifier-naming.LocalVariableCase value: 'lower_case' - key: readability-identifier-naming.ClassCase diff --git a/esphome/components/nfc/ndef_message.cpp b/esphome/components/nfc/ndef_message.cpp index 147392940c..b1554f41ae 100644 --- a/esphome/components/nfc/ndef_message.cpp +++ b/esphome/components/nfc/ndef_message.cpp @@ -83,11 +83,11 @@ bool NdefMessage::add_text_record(const std::string &text) { return this->add_te bool NdefMessage::add_text_record(const std::string &text, const std::string &encoding) { std::string payload = to_string(text.length()) + encoding + text; - return this->add_record(std::unique_ptr{new NdefRecord(TNF_WELL_KNOWN, "T", payload)}); + return this->add_record(make_unique(TNF_WELL_KNOWN, "T", payload)); } bool NdefMessage::add_uri_record(const std::string &uri) { - return this->add_record(std::unique_ptr{new NdefRecord(TNF_WELL_KNOWN, "U", uri)}); + return this->add_record(make_unique(TNF_WELL_KNOWN, "U", uri)); } std::vector NdefMessage::encode() { diff --git a/esphome/components/nfc/nfc_tag.h b/esphome/components/nfc/nfc_tag.h index 2c8b0a5f21..ab6ca650e4 100644 --- a/esphome/components/nfc/nfc_tag.h +++ b/esphome/components/nfc/nfc_tag.h @@ -31,13 +31,13 @@ class NfcTag { NfcTag(std::vector &uid, const std::string &tag_type, std::vector &ndef_data) { this->uid_ = uid; this->tag_type_ = tag_type; - this->ndef_message_ = std::unique_ptr(new NdefMessage(ndef_data)); + this->ndef_message_ = make_unique(ndef_data); }; NfcTag(const NfcTag &rhs) { uid_ = rhs.uid_; tag_type_ = rhs.tag_type_; if (rhs.ndef_message_ != nullptr) - ndef_message_ = std::unique_ptr(new NdefMessage(*rhs.ndef_message_)); + ndef_message_ = make_unique(*rhs.ndef_message_); } std::vector &get_uid() { return this->uid_; }; diff --git a/esphome/components/pn532/pn532.cpp b/esphome/components/pn532/pn532.cpp index c28ce8d503..1c4160539a 100644 --- a/esphome/components/pn532/pn532.cpp +++ b/esphome/components/pn532/pn532.cpp @@ -1,4 +1,6 @@ #include "pn532.h" + +#include #include "esphome/core/log.h" // Based on: @@ -104,7 +106,7 @@ void PN532::loop() { if (!success) { // Something failed if (!this->current_uid_.empty()) { - auto tag = std::unique_ptr{new nfc::NfcTag(this->current_uid_)}; + auto tag = make_unique(this->current_uid_); for (auto *trigger : this->triggers_ontagremoved_) trigger->process(tag); } @@ -117,7 +119,7 @@ void PN532::loop() { if (num_targets != 1) { // no tags found or too many if (!this->current_uid_.empty()) { - auto tag = std::unique_ptr{new nfc::NfcTag(this->current_uid_)}; + auto tag = make_unique(this->current_uid_); for (auto *trigger : this->triggers_ontagremoved_) trigger->process(tag); } @@ -281,9 +283,9 @@ std::unique_ptr PN532::read_tag_(std::vector &uid) { return this->read_mifare_ultralight_tag_(uid); } else if (type == nfc::TAG_TYPE_UNKNOWN) { ESP_LOGV(TAG, "Cannot determine tag type"); - return std::unique_ptr{new nfc::NfcTag(uid)}; + return make_unique(uid); } else { - return std::unique_ptr{new nfc::NfcTag(uid)}; + return make_unique(uid); } } diff --git a/esphome/components/pn532/pn532_mifare_classic.cpp b/esphome/components/pn532/pn532_mifare_classic.cpp index f4bd11d49f..81d135d8e6 100644 --- a/esphome/components/pn532/pn532_mifare_classic.cpp +++ b/esphome/components/pn532/pn532_mifare_classic.cpp @@ -1,3 +1,5 @@ +#include + #include "pn532.h" #include "esphome/core/log.h" @@ -15,15 +17,15 @@ std::unique_ptr PN532::read_mifare_classic_tag_(std::vector data; if (this->read_mifare_classic_block_(current_block, data)) { if (!nfc::decode_mifare_classic_tlv(data, message_length, message_start_index)) { - return std::unique_ptr{new nfc::NfcTag(uid, nfc::ERROR)}; + return make_unique(uid, nfc::ERROR); } } else { ESP_LOGE(TAG, "Failed to read block %d", current_block); - return std::unique_ptr{new nfc::NfcTag(uid, nfc::MIFARE_CLASSIC)}; + return make_unique(uid, nfc::MIFARE_CLASSIC); } } else { ESP_LOGV(TAG, "Tag is not NDEF formatted"); - return std::unique_ptr{new nfc::NfcTag(uid, nfc::MIFARE_CLASSIC)}; + return make_unique(uid, nfc::MIFARE_CLASSIC); } uint32_t index = 0; @@ -51,7 +53,7 @@ std::unique_ptr PN532::read_mifare_classic_tag_(std::vector{new nfc::NfcTag(uid, nfc::MIFARE_CLASSIC, buffer)}; + return make_unique(uid, nfc::MIFARE_CLASSIC, buffer); } bool PN532::read_mifare_classic_block_(uint8_t block_num, std::vector &data) { diff --git a/esphome/components/pn532/pn532_mifare_ultralight.cpp b/esphome/components/pn532/pn532_mifare_ultralight.cpp index 24e97ab95c..1b91ae919e 100644 --- a/esphome/components/pn532/pn532_mifare_ultralight.cpp +++ b/esphome/components/pn532/pn532_mifare_ultralight.cpp @@ -1,3 +1,5 @@ +#include + #include "pn532.h" #include "esphome/core/log.h" @@ -9,25 +11,25 @@ static const char *const TAG = "pn532.mifare_ultralight"; std::unique_ptr PN532::read_mifare_ultralight_tag_(std::vector &uid) { if (!this->is_mifare_ultralight_formatted_()) { ESP_LOGD(TAG, "Not NDEF formatted"); - return std::unique_ptr{new nfc::NfcTag(uid, nfc::NFC_FORUM_TYPE_2)}; + return make_unique(uid, nfc::NFC_FORUM_TYPE_2); } uint8_t message_length; uint8_t message_start_index; if (!this->find_mifare_ultralight_ndef_(message_length, message_start_index)) { - return std::unique_ptr{new nfc::NfcTag(uid, nfc::NFC_FORUM_TYPE_2)}; + return make_unique(uid, nfc::NFC_FORUM_TYPE_2); } ESP_LOGVV(TAG, "message length: %d, start: %d", message_length, message_start_index); if (message_length == 0) { - return std::unique_ptr{new nfc::NfcTag(uid, nfc::NFC_FORUM_TYPE_2)}; + return make_unique(uid, nfc::NFC_FORUM_TYPE_2); } std::vector data; for (uint8_t page = nfc::MIFARE_ULTRALIGHT_DATA_START_PAGE; page < nfc::MIFARE_ULTRALIGHT_MAX_PAGE; page++) { std::vector page_data; if (!this->read_mifare_ultralight_page_(page, page_data)) { ESP_LOGE(TAG, "Error reading page %d", page); - return std::unique_ptr{new nfc::NfcTag(uid, nfc::NFC_FORUM_TYPE_2)}; + return make_unique(uid, nfc::NFC_FORUM_TYPE_2); } data.insert(data.end(), page_data.begin(), page_data.end()); @@ -38,7 +40,7 @@ std::unique_ptr PN532::read_mifare_ultralight_tag_(std::vector{new nfc::NfcTag(uid, nfc::NFC_FORUM_TYPE_2, data)}; + return make_unique(uid, nfc::NFC_FORUM_TYPE_2, data); } bool PN532::read_mifare_ultralight_page_(uint8_t page_num, std::vector &data) { diff --git a/esphome/components/socket/bsd_sockets_impl.cpp b/esphome/components/socket/bsd_sockets_impl.cpp index 1ad520a099..6fb00ce22d 100644 --- a/esphome/components/socket/bsd_sockets_impl.cpp +++ b/esphome/components/socket/bsd_sockets_impl.cpp @@ -1,5 +1,6 @@ #include "socket.h" #include "esphome/core/defines.h" +#include "esphome/core/helpers.h" #ifdef USE_SOCKET_IMPL_BSD_SOCKETS @@ -39,7 +40,7 @@ class BSDSocketImpl : public Socket { int fd = ::accept(fd_, addr, addrlen); if (fd == -1) return {}; - return std::unique_ptr{new BSDSocketImpl(fd)}; + return make_unique(fd); } int bind(const struct sockaddr *addr, socklen_t addrlen) override { return ::bind(fd_, addr, addrlen); } int close() override { diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 86c70088d4..6c0ee8399e 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -88,10 +88,15 @@ template T clamp(T val, T min, T max); */ float lerp(float completion, float start, float end); -/// std::make_unique +// Not all platforms we support target C++14 yet, so we can't unconditionally use std::make_unique. Provide our own +// implementation if needed, and otherwise pull std::make_unique into scope so that we have a uniform API. +#if __cplusplus >= 201402L +using std::make_unique; +#else template std::unique_ptr make_unique(Args &&...args) { return std::unique_ptr(new T(std::forward(args)...)); } +#endif /// Return a random 32 bit unsigned integer. uint32_t random_uint32(); From 5a90b83f6385f4d8b154bdb3a0c396d37c3385e2 Mon Sep 17 00:00:00 2001 From: jsuanet <75206491+jsuanet@users.noreply.github.com> Date: Tue, 14 Sep 2021 23:22:45 +0200 Subject: [PATCH 034/207] Fix unit of measurement fields for DSMR power consumed/delivered fields (#2304) Co-authored-by: Jos Suanet --- esphome/components/dsmr/sensor.py | 134 +++++++++++++++++++++++------- esphome/const.py | 2 + 2 files changed, 107 insertions(+), 29 deletions(-) diff --git a/esphome/components/dsmr/sensor.py b/esphome/components/dsmr/sensor.py index 2c05651d67..9d531293e9 100644 --- a/esphome/components/dsmr/sensor.py +++ b/esphome/components/dsmr/sensor.py @@ -13,9 +13,13 @@ from esphome.const import ( STATE_CLASS_NONE, STATE_CLASS_TOTAL_INCREASING, UNIT_AMPERE, + UNIT_CUBIC_METER, UNIT_EMPTY, + UNIT_KILOWATT, + UNIT_KILOWATT_HOURS, + UNIT_KILOVOLT_AMPS_REACTIVE_HOURS, + UNIT_KILOVOLT_AMPS_REACTIVE, UNIT_VOLT, - UNIT_WATT, ) from . import Dsmr, CONF_DSMR_ID @@ -26,40 +30,80 @@ CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(CONF_DSMR_ID): cv.use_id(Dsmr), cv.Optional("energy_delivered_lux"): sensor.sensor_schema( - "kWh", ICON_EMPTY, 3, DEVICE_CLASS_ENERGY, STATE_CLASS_TOTAL_INCREASING + UNIT_KILOWATT_HOURS, + ICON_EMPTY, + 3, + DEVICE_CLASS_ENERGY, + STATE_CLASS_TOTAL_INCREASING, ), cv.Optional("energy_delivered_tariff1"): sensor.sensor_schema( - "kWh", ICON_EMPTY, 3, DEVICE_CLASS_ENERGY, STATE_CLASS_TOTAL_INCREASING + UNIT_KILOWATT_HOURS, + ICON_EMPTY, + 3, + DEVICE_CLASS_ENERGY, + STATE_CLASS_TOTAL_INCREASING, ), cv.Optional("energy_delivered_tariff2"): sensor.sensor_schema( - "kWh", ICON_EMPTY, 3, DEVICE_CLASS_ENERGY, STATE_CLASS_TOTAL_INCREASING + UNIT_KILOWATT_HOURS, + ICON_EMPTY, + 3, + DEVICE_CLASS_ENERGY, + STATE_CLASS_TOTAL_INCREASING, ), cv.Optional("energy_returned_lux"): sensor.sensor_schema( - "kWh", ICON_EMPTY, 3, DEVICE_CLASS_ENERGY, STATE_CLASS_TOTAL_INCREASING + UNIT_KILOWATT_HOURS, + ICON_EMPTY, + 3, + DEVICE_CLASS_ENERGY, + STATE_CLASS_TOTAL_INCREASING, ), cv.Optional("energy_returned_tariff1"): sensor.sensor_schema( - "kWh", ICON_EMPTY, 3, DEVICE_CLASS_ENERGY, STATE_CLASS_TOTAL_INCREASING + UNIT_KILOWATT_HOURS, + ICON_EMPTY, + 3, + DEVICE_CLASS_ENERGY, + STATE_CLASS_TOTAL_INCREASING, ), cv.Optional("energy_returned_tariff2"): sensor.sensor_schema( - "kWh", ICON_EMPTY, 3, DEVICE_CLASS_ENERGY, STATE_CLASS_TOTAL_INCREASING + UNIT_KILOWATT_HOURS, + ICON_EMPTY, + 3, + DEVICE_CLASS_ENERGY, + STATE_CLASS_TOTAL_INCREASING, ), cv.Optional("total_imported_energy"): sensor.sensor_schema( - "kvarh", ICON_EMPTY, 3, DEVICE_CLASS_ENERGY, STATE_CLASS_NONE + UNIT_KILOVOLT_AMPS_REACTIVE_HOURS, + ICON_EMPTY, + 3, + DEVICE_CLASS_ENERGY, + STATE_CLASS_NONE, ), cv.Optional("total_exported_energy"): sensor.sensor_schema( - "kvarh", ICON_EMPTY, 3, DEVICE_CLASS_ENERGY, STATE_CLASS_NONE + UNIT_KILOVOLT_AMPS_REACTIVE_HOURS, + ICON_EMPTY, + 3, + DEVICE_CLASS_ENERGY, + STATE_CLASS_NONE, ), cv.Optional("power_delivered"): sensor.sensor_schema( - UNIT_WATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT + UNIT_KILOWATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT ), cv.Optional("power_returned"): sensor.sensor_schema( - UNIT_WATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT + UNIT_KILOWATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT ), cv.Optional("reactive_power_delivered"): sensor.sensor_schema( - "kvar", ICON_EMPTY, 3, DEVICE_CLASS_ENERGY, STATE_CLASS_NONE + UNIT_KILOVOLT_AMPS_REACTIVE, + ICON_EMPTY, + 3, + DEVICE_CLASS_POWER, + STATE_CLASS_MEASUREMENT, ), cv.Optional("reactive_power_returned"): sensor.sensor_schema( - "kvar", ICON_EMPTY, 3, DEVICE_CLASS_ENERGY, STATE_CLASS_MEASUREMENT + UNIT_KILOVOLT_AMPS_REACTIVE, + ICON_EMPTY, + 3, + DEVICE_CLASS_POWER, + STATE_CLASS_MEASUREMENT, ), cv.Optional("electricity_threshold"): sensor.sensor_schema( UNIT_EMPTY, ICON_EMPTY, 3, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE @@ -77,13 +121,13 @@ CONFIG_SCHEMA = cv.Schema( UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE ), cv.Optional("electricity_sags_l2"): sensor.sensor_schema( - UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_MEASUREMENT + UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE ), cv.Optional("electricity_sags_l3"): sensor.sensor_schema( UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE ), cv.Optional("electricity_swells_l1"): sensor.sensor_schema( - UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_MEASUREMENT + UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE ), cv.Optional("electricity_swells_l2"): sensor.sensor_schema( UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE @@ -101,40 +145,64 @@ CONFIG_SCHEMA = cv.Schema( UNIT_AMPERE, ICON_EMPTY, 1, DEVICE_CLASS_CURRENT, STATE_CLASS_MEASUREMENT ), cv.Optional("power_delivered_l1"): sensor.sensor_schema( - UNIT_WATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT + UNIT_KILOWATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT ), cv.Optional("power_delivered_l2"): sensor.sensor_schema( - UNIT_WATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT + UNIT_KILOWATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT ), cv.Optional("power_delivered_l3"): sensor.sensor_schema( - UNIT_WATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT + UNIT_KILOWATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT ), cv.Optional("power_returned_l1"): sensor.sensor_schema( - UNIT_WATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT + UNIT_KILOWATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT ), cv.Optional("power_returned_l2"): sensor.sensor_schema( - UNIT_WATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT + UNIT_KILOWATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT ), cv.Optional("power_returned_l3"): sensor.sensor_schema( - UNIT_WATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT + UNIT_KILOWATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT ), cv.Optional("reactive_power_delivered_l1"): sensor.sensor_schema( - UNIT_WATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT + UNIT_KILOVOLT_AMPS_REACTIVE, + ICON_EMPTY, + 3, + DEVICE_CLASS_POWER, + STATE_CLASS_MEASUREMENT, ), cv.Optional("reactive_power_delivered_l2"): sensor.sensor_schema( - UNIT_WATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT + UNIT_KILOVOLT_AMPS_REACTIVE, + ICON_EMPTY, + 3, + DEVICE_CLASS_POWER, + STATE_CLASS_MEASUREMENT, ), cv.Optional("reactive_power_delivered_l3"): sensor.sensor_schema( - UNIT_WATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT + UNIT_KILOVOLT_AMPS_REACTIVE, + ICON_EMPTY, + 3, + DEVICE_CLASS_POWER, + STATE_CLASS_MEASUREMENT, ), cv.Optional("reactive_power_returned_l1"): sensor.sensor_schema( - UNIT_WATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT + UNIT_KILOVOLT_AMPS_REACTIVE, + ICON_EMPTY, + 3, + DEVICE_CLASS_POWER, + STATE_CLASS_MEASUREMENT, ), cv.Optional("reactive_power_returned_l2"): sensor.sensor_schema( - UNIT_WATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT + UNIT_KILOVOLT_AMPS_REACTIVE, + ICON_EMPTY, + 3, + DEVICE_CLASS_POWER, + STATE_CLASS_MEASUREMENT, ), cv.Optional("reactive_power_returned_l3"): sensor.sensor_schema( - UNIT_WATT, ICON_EMPTY, 3, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT + UNIT_KILOVOLT_AMPS_REACTIVE, + ICON_EMPTY, + 3, + DEVICE_CLASS_POWER, + STATE_CLASS_MEASUREMENT, ), cv.Optional("voltage_l1"): sensor.sensor_schema( UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE, STATE_CLASS_NONE @@ -146,10 +214,18 @@ CONFIG_SCHEMA = cv.Schema( UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE, STATE_CLASS_NONE ), cv.Optional("gas_delivered"): sensor.sensor_schema( - "m³", ICON_EMPTY, 3, DEVICE_CLASS_GAS, STATE_CLASS_TOTAL_INCREASING + UNIT_CUBIC_METER, + ICON_EMPTY, + 3, + DEVICE_CLASS_GAS, + STATE_CLASS_TOTAL_INCREASING, ), cv.Optional("gas_delivered_be"): sensor.sensor_schema( - "m³", ICON_EMPTY, 3, DEVICE_CLASS_GAS, STATE_CLASS_TOTAL_INCREASING + UNIT_CUBIC_METER, + ICON_EMPTY, + 3, + DEVICE_CLASS_GAS, + STATE_CLASS_TOTAL_INCREASING, ), } ).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/const.py b/esphome/const.py index f342609538..92d18cde3a 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -786,7 +786,9 @@ UNIT_KELVIN = "K" UNIT_KILOGRAM = "kg" UNIT_KILOMETER = "km" UNIT_KILOMETER_PER_HOUR = "km/h" +UNIT_KILOVOLT_AMPS_REACTIVE = "kVAr" UNIT_KILOVOLT_AMPS_REACTIVE_HOURS = "kVArh" +UNIT_KILOWATT = "kW" UNIT_KILOWATT_HOURS = "kWh" UNIT_LUX = "lx" UNIT_METER = "m" From 103ba4c696361d1f34a9b071c7175e5be63f205d Mon Sep 17 00:00:00 2001 From: JonasEr <8407728+JonasEr@users.noreply.github.com> Date: Wed, 15 Sep 2021 01:06:43 +0300 Subject: [PATCH 035/207] Bug fix of NFC message & records becoming inaccessible in on_tag lambdas (#2309) --- esphome/components/nfc/ndef_message.h | 4 ++-- esphome/components/nfc/nfc_tag.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/esphome/components/nfc/ndef_message.h b/esphome/components/nfc/ndef_message.h index d2ffa9582e..20140e8c1a 100644 --- a/esphome/components/nfc/ndef_message.h +++ b/esphome/components/nfc/ndef_message.h @@ -22,7 +22,7 @@ class NdefMessage { } } - const std::vector> &get_records() { return this->records_; }; + const std::vector> &get_records() { return this->records_; }; bool add_record(std::unique_ptr record); bool add_text_record(const std::string &text); @@ -32,7 +32,7 @@ class NdefMessage { std::vector encode(); protected: - std::vector> records_; + std::vector> records_; }; } // namespace nfc diff --git a/esphome/components/nfc/nfc_tag.h b/esphome/components/nfc/nfc_tag.h index ab6ca650e4..2dfc431428 100644 --- a/esphome/components/nfc/nfc_tag.h +++ b/esphome/components/nfc/nfc_tag.h @@ -43,13 +43,13 @@ class NfcTag { std::vector &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::unique_ptr &get_ndef_message() { return this->ndef_message_; }; + const std::shared_ptr &get_ndef_message() { return this->ndef_message_; }; void set_ndef_message(std::unique_ptr ndef_message) { this->ndef_message_ = std::move(ndef_message); }; protected: std::vector uid_; std::string tag_type_; - std::unique_ptr ndef_message_; + std::shared_ptr ndef_message_; }; } // namespace nfc From de33cbd7e7c37670d5a3c4262af024ea222b986e Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Tue, 14 Sep 2021 22:14:49 -0300 Subject: [PATCH 036/207] Dsmr updates (#2157) * add option to use check_crc * ignore newline before ( in parsing * add gas delivered text for raw sensor * fix compile issue when not listing any sensor * make gas_mbus_id configurable * update dsmr lib for clang --- esphome/components/dsmr/__init__.py | 10 ++++++++-- esphome/components/dsmr/dsmr.cpp | 10 ++++++++-- esphome/components/dsmr/dsmr.h | 3 ++- esphome/components/dsmr/sensor.py | 5 ++++- esphome/components/dsmr/text_sensor.py | 13 ++++++++++--- platformio.ini | 2 +- 6 files changed, 33 insertions(+), 10 deletions(-) diff --git a/esphome/components/dsmr/__init__.py b/esphome/components/dsmr/__init__.py index df11a14ee8..1f1a2f980e 100644 --- a/esphome/components/dsmr/__init__.py +++ b/esphome/components/dsmr/__init__.py @@ -13,6 +13,8 @@ AUTO_LOAD = ["sensor", "text_sensor"] CONF_DSMR_ID = "dsmr_id" CONF_DECRYPTION_KEY = "decryption_key" +CONF_CRC_CHECK = "crc_check" +CONF_GAS_MBUS_ID = "gas_mbus_id" # Hack to prevent compile error due to ambiguity with lib namespace dsmr_ns = cg.esphome_ns.namespace("esphome::dsmr") @@ -41,19 +43,23 @@ CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(Dsmr), cv.Optional(CONF_DECRYPTION_KEY): _validate_key, + cv.Optional(CONF_CRC_CHECK, default=True): cv.boolean, + cv.Optional(CONF_GAS_MBUS_ID, default=1): cv.int_, } ).extend(uart.UART_DEVICE_SCHEMA) async def to_code(config): uart_component = await cg.get_variable(config[CONF_UART_ID]) - var = cg.new_Pvariable(config[CONF_ID], uart_component) + var = cg.new_Pvariable(config[CONF_ID], uart_component, config[CONF_CRC_CHECK]) if CONF_DECRYPTION_KEY in config: cg.add(var.set_decryption_key(config[CONF_DECRYPTION_KEY])) await cg.register_component(var, config) + cg.add_define("DSMR_GAS_MBUS_ID", config[CONF_GAS_MBUS_ID]) + # DSMR Parser - cg.add_library("glmnet/Dsmr", "0.3") + cg.add_library("glmnet/Dsmr", "0.5") # Crypto cg.add_library("rweather/Crypto", "0.2.0") diff --git a/esphome/components/dsmr/dsmr.cpp b/esphome/components/dsmr/dsmr.cpp index 39f05a28d1..7c1b406d42 100644 --- a/esphome/components/dsmr/dsmr.cpp +++ b/esphome/components/dsmr/dsmr.cpp @@ -37,6 +37,12 @@ void Dsmr::receive_telegram_() { return; } + // Some v2.2 or v3 meters will send a new value which starts with '(' + // in a new line while the value belongs to the previous ObisId. For + // proper parsing remove these new line characters + while (c == '(' && (telegram_[telegram_len_ - 1] == '\n' || telegram_[telegram_len_ - 1] == '\r')) + telegram_len_--; + telegram_[telegram_len_] = c; telegram_len_++; if (c == '!') { // footer: exclamation mark @@ -130,8 +136,8 @@ bool Dsmr::parse_telegram() { MyData data; ESP_LOGV(TAG, "Trying to parse"); ::dsmr::ParseResult res = - ::dsmr::P1Parser::parse(&data, telegram_, telegram_len_, - false); // Parse telegram according to data definition. Ignore unknown values. + ::dsmr::P1Parser::parse(&data, telegram_, telegram_len_, false, + this->crc_check_); // Parse telegram according to data definition. Ignore unknown values. if (res.err) { // Parsing error, show it auto err_str = res.fullError(telegram_, telegram_ + telegram_len_); diff --git a/esphome/components/dsmr/dsmr.h b/esphome/components/dsmr/dsmr.h index 984f2596db..dcb8f5f73d 100644 --- a/esphome/components/dsmr/dsmr.h +++ b/esphome/components/dsmr/dsmr.h @@ -48,7 +48,7 @@ using MyData = ::dsmr::ParsedData decryption_key_{}; + bool crc_check_; }; } // namespace dsmr } // namespace esphome diff --git a/esphome/components/dsmr/sensor.py b/esphome/components/dsmr/sensor.py index 9d531293e9..761009c766 100644 --- a/esphome/components/dsmr/sensor.py +++ b/esphome/components/dsmr/sensor.py @@ -244,4 +244,7 @@ async def to_code(config): cg.add(getattr(hub, f"set_{key}")(s)) sensors.append(f"F({key})") - cg.add_define("DSMR_SENSOR_LIST(F, sep)", cg.RawExpression(" sep ".join(sensors))) + if sensors: + cg.add_define( + "DSMR_SENSOR_LIST(F, sep)", cg.RawExpression(" sep ".join(sensors)) + ) diff --git a/esphome/components/dsmr/text_sensor.py b/esphome/components/dsmr/text_sensor.py index 821b07dc6b..339eea711f 100644 --- a/esphome/components/dsmr/text_sensor.py +++ b/esphome/components/dsmr/text_sensor.py @@ -71,6 +71,11 @@ CONFIG_SCHEMA = cv.Schema( cv.GenerateID(): cv.declare_id(text_sensor.TextSensor), } ), + cv.Optional("gas_delivered_text"): text_sensor.TEXT_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(text_sensor.TextSensor), + } + ), } ).extend(cv.COMPONENT_SCHEMA) @@ -89,6 +94,8 @@ async def to_code(config): cg.add(getattr(hub, f"set_{key}")(var)) text_sensors.append(f"F({key})") - cg.add_define( - "DSMR_TEXT_SENSOR_LIST(F, sep)", cg.RawExpression(" sep ".join(text_sensors)) - ) + if text_sensors: + cg.add_define( + "DSMR_TEXT_SENSOR_LIST(F, sep)", + cg.RawExpression(" sep ".join(text_sensors)), + ) diff --git a/platformio.ini b/platformio.ini index 3b1241f282..174832b067 100644 --- a/platformio.ini +++ b/platformio.ini @@ -34,7 +34,7 @@ lib_deps = 1655@1.0.2 ; TinyGPSPlus (has name conflict) 6865@1.0.0 ; TM1651 Battery Display 6306@1.0.3 ; HM3301 - glmnet/Dsmr@0.3 ; used by dsmr + glmnet/Dsmr@0.5 ; used by dsmr rweather/Crypto@0.2.0 ; used by dsmr esphome/noise-c@0.1.1 ; used by api dudanov/MideaUART@1.1.0 ; used by midea From b276ac0588d665351ed621ddfa0ad8f5de945a77 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 15 Sep 2021 16:39:13 +1200 Subject: [PATCH 037/207] Simple time.sleep in place of threading wait due to upgraded zeroconf (#2307) --- esphome/zeroconf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/zeroconf.py b/esphome/zeroconf.py index 443ed6a33a..e6853531f2 100644 --- a/esphome/zeroconf.py +++ b/esphome/zeroconf.py @@ -49,7 +49,7 @@ class HostResolver(RecordUpdateListener): next_ = now + delay delay *= 2 - zc.wait(min(next_, last) - now) + time.sleep(min(next_, last) - now) now = time.time() finally: zc.remove_listener(self) From 19014331d8225157cfa3390e36641c1c876b5db5 Mon Sep 17 00:00:00 2001 From: Maurice Makaay Date: Wed, 15 Sep 2021 08:48:27 +0200 Subject: [PATCH 038/207] Fix aioesphomeapi API logger with explicit api.port in the YAML. (#2310) --- esphome/components/api/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/api/client.py b/esphome/components/api/client.py index d8192eb88f..4a3944d33e 100644 --- a/esphome/components/api/client.py +++ b/esphome/components/api/client.py @@ -15,7 +15,7 @@ _LOGGER = logging.getLogger(__name__) async def async_run_logs(config, address): conf = config["api"] - port: int = conf[CONF_PORT] + port: int = int(conf[CONF_PORT]) password: str = conf[CONF_PASSWORD] noise_psk: Optional[str] = None if CONF_ENCRYPTION in conf: From 0ea77de98cfa8af51fcf2cef5f7745a7bdad7b94 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 15 Sep 2021 19:00:51 +1200 Subject: [PATCH 039/207] Start a wifi scan after saving station details (#2315) --- esphome/components/wifi/wifi_component.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index c5ba9b01af..0a1e347e8f 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -244,6 +244,8 @@ void WiFiComponent::save_wifi_sta(const std::string &ssid, const std::string &pa sta.set_ssid(ssid); sta.set_password(password); this->set_sta(sta); + + this->start_scanning(); } void WiFiComponent::start_connecting(const WiFiAP &ap, bool two) { From 2db8c42e1daeb47b4ee161446bdac38933e60078 Mon Sep 17 00:00:00 2001 From: Stefan Rado Date: Wed, 15 Sep 2021 10:23:35 +0200 Subject: [PATCH 040/207] Support direct relay state feedback for tuya climate component (#1668) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/tuya/climate/__init__.py | 59 ++++++++++++++----- .../components/tuya/climate/tuya_climate.cpp | 47 +++++++++++++++ .../components/tuya/climate/tuya_climate.h | 7 +++ 3 files changed, 98 insertions(+), 15 deletions(-) diff --git a/esphome/components/tuya/climate/__init__.py b/esphome/components/tuya/climate/__init__.py index 06c80964ee..471a8146e1 100644 --- a/esphome/components/tuya/climate/__init__.py +++ b/esphome/components/tuya/climate/__init__.py @@ -1,3 +1,4 @@ +from esphome import pins from esphome.components import climate import esphome.config_validation as cv import esphome.codegen as cg @@ -15,6 +16,8 @@ CODEOWNERS = ["@jesserockz"] CONF_ACTIVE_STATE_DATAPOINT = "active_state_datapoint" CONF_ACTIVE_STATE_HEATING_VALUE = "active_state_heating_value" CONF_ACTIVE_STATE_COOLING_VALUE = "active_state_cooling_value" +CONF_HEATING_STATE_PIN = "heating_state_pin" +CONF_COOLING_STATE_PIN = "cooling_state_pin" CONF_TARGET_TEMPERATURE_DATAPOINT = "target_temperature_datapoint" CONF_CURRENT_TEMPERATURE_DATAPOINT = "current_temperature_datapoint" CONF_TEMPERATURE_MULTIPLIER = "temperature_multiplier" @@ -69,14 +72,21 @@ def validate_temperature_multipliers(value): def validate_active_state_values(value): if CONF_ACTIVE_STATE_DATAPOINT not in value: - return value - if value[CONF_SUPPORTS_COOL] and CONF_ACTIVE_STATE_COOLING_VALUE not in value: - raise cv.Invalid( - ( - f"{CONF_ACTIVE_STATE_COOLING_VALUE} required if using " - f"{CONF_ACTIVE_STATE_DATAPOINT} and device supports cooling" + if CONF_ACTIVE_STATE_COOLING_VALUE in value: + raise cv.Invalid( + ( + f"{CONF_ACTIVE_STATE_DATAPOINT} required if using " + f"{CONF_ACTIVE_STATE_COOLING_VALUE}" + ) + ) + else: + if value[CONF_SUPPORTS_COOL] and CONF_ACTIVE_STATE_COOLING_VALUE not in value: + raise cv.Invalid( + ( + f"{CONF_ACTIVE_STATE_COOLING_VALUE} required if using " + f"{CONF_ACTIVE_STATE_DATAPOINT} and device supports cooling" + ) ) - ) return value @@ -91,6 +101,8 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_ACTIVE_STATE_DATAPOINT): cv.uint8_t, cv.Optional(CONF_ACTIVE_STATE_HEATING_VALUE, default=1): cv.uint8_t, cv.Optional(CONF_ACTIVE_STATE_COOLING_VALUE): cv.uint8_t, + cv.Optional(CONF_HEATING_STATE_PIN): pins.gpio_input_pin_schema, + cv.Optional(CONF_COOLING_STATE_PIN): pins.gpio_input_pin_schema, cv.Optional(CONF_TARGET_TEMPERATURE_DATAPOINT): cv.uint8_t, cv.Optional(CONF_CURRENT_TEMPERATURE_DATAPOINT): cv.uint8_t, cv.Optional(CONF_TEMPERATURE_MULTIPLIER): cv.positive_float, @@ -101,6 +113,8 @@ CONFIG_SCHEMA = cv.All( cv.has_at_least_one_key(CONF_TARGET_TEMPERATURE_DATAPOINT, CONF_SWITCH_DATAPOINT), validate_temperature_multipliers, validate_active_state_values, + cv.has_at_most_one_key(CONF_ACTIVE_STATE_DATAPOINT, CONF_HEATING_STATE_PIN), + cv.has_at_most_one_key(CONF_ACTIVE_STATE_DATAPOINT, CONF_COOLING_STATE_PIN), ) @@ -118,14 +132,29 @@ async def to_code(config): cg.add(var.set_switch_id(config[CONF_SWITCH_DATAPOINT])) if CONF_ACTIVE_STATE_DATAPOINT in config: cg.add(var.set_active_state_id(config[CONF_ACTIVE_STATE_DATAPOINT])) - if CONF_ACTIVE_STATE_HEATING_VALUE in config: - cg.add( - var.set_active_state_heating_value(config[CONF_ACTIVE_STATE_HEATING_VALUE]) - ) - if CONF_ACTIVE_STATE_COOLING_VALUE in config: - cg.add( - var.set_active_state_cooling_value(config[CONF_ACTIVE_STATE_COOLING_VALUE]) - ) + if CONF_ACTIVE_STATE_HEATING_VALUE in config: + cg.add( + var.set_active_state_heating_value( + config[CONF_ACTIVE_STATE_HEATING_VALUE] + ) + ) + if CONF_ACTIVE_STATE_COOLING_VALUE in config: + cg.add( + var.set_active_state_cooling_value( + config[CONF_ACTIVE_STATE_COOLING_VALUE] + ) + ) + else: + if CONF_HEATING_STATE_PIN in config: + heating_state_pin = await cg.gpio_pin_expression( + config[CONF_HEATING_STATE_PIN] + ) + cg.add(var.set_heating_state_pin(heating_state_pin)) + if CONF_COOLING_STATE_PIN in config: + cooling_state_pin = await cg.gpio_pin_expression( + config[CONF_COOLING_STATE_PIN] + ) + cg.add(var.set_cooling_state_pin(cooling_state_pin)) if CONF_TARGET_TEMPERATURE_DATAPOINT in config: cg.add(var.set_target_temperature_id(config[CONF_TARGET_TEMPERATURE_DATAPOINT])) if CONF_CURRENT_TEMPERATURE_DATAPOINT in config: diff --git a/esphome/components/tuya/climate/tuya_climate.cpp b/esphome/components/tuya/climate/tuya_climate.cpp index a4ce06476d..ae4424e438 100644 --- a/esphome/components/tuya/climate/tuya_climate.cpp +++ b/esphome/components/tuya/climate/tuya_climate.cpp @@ -31,6 +31,15 @@ void TuyaClimate::setup() { this->compute_state_(); this->publish_state(); }); + } else { + if (this->heating_state_pin_ != nullptr) { + this->heating_state_pin_->setup(); + this->heating_state_ = this->heating_state_pin_->digital_read(); + } + if (this->cooling_state_pin_ != nullptr) { + this->cooling_state_pin_->setup(); + this->cooling_state_ = this->cooling_state_pin_->digital_read(); + } } if (this->target_temperature_id_.has_value()) { this->parent_->register_listener(*this->target_temperature_id_, [this](const TuyaDatapoint &datapoint) { @@ -50,6 +59,34 @@ void TuyaClimate::setup() { } } +void TuyaClimate::loop() { + if (this->active_state_id_.has_value()) + return; + + bool state_changed = false; + if (this->heating_state_pin_ != nullptr) { + bool heating_state = this->heating_state_pin_->digital_read(); + if (heating_state != this->heating_state_) { + ESP_LOGV(TAG, "Heating state pin changed to: %s", ONOFF(heating_state)); + this->heating_state_ = heating_state; + state_changed = true; + } + } + if (this->cooling_state_pin_ != nullptr) { + bool cooling_state = this->cooling_state_pin_->digital_read(); + if (cooling_state != this->cooling_state_) { + ESP_LOGV(TAG, "Cooling state pin changed to: %s", ONOFF(cooling_state)); + this->cooling_state_ = cooling_state; + state_changed = true; + } + } + + if (state_changed) { + this->compute_state_(); + this->publish_state(); + } +} + void TuyaClimate::control(const climate::ClimateCall &call) { if (call.get_mode().has_value()) { const bool switch_state = *call.get_mode() != climate::CLIMATE_MODE_OFF; @@ -86,6 +123,8 @@ void TuyaClimate::dump_config() { ESP_LOGCONFIG(TAG, " Target Temperature has datapoint ID %u", *this->target_temperature_id_); if (this->current_temperature_id_.has_value()) ESP_LOGCONFIG(TAG, " Current Temperature has datapoint ID %u", *this->current_temperature_id_); + LOG_PIN(" Heating State Pin: ", this->heating_state_pin_); + LOG_PIN(" Cooling State Pin: ", this->cooling_state_pin_); } void TuyaClimate::compute_state_() { @@ -102,6 +141,7 @@ void TuyaClimate::compute_state_() { climate::ClimateAction target_action = climate::CLIMATE_ACTION_IDLE; if (this->active_state_id_.has_value()) { + // Use state from MCU datapoint if (this->supports_heat_ && this->active_state_heating_value_.has_value() && this->active_state_ == this->active_state_heating_value_) { target_action = climate::CLIMATE_ACTION_HEATING; @@ -109,6 +149,13 @@ void TuyaClimate::compute_state_() { this->active_state_ == this->active_state_cooling_value_) { target_action = climate::CLIMATE_ACTION_COOLING; } + } else if (this->heating_state_pin_ != nullptr || this->cooling_state_pin_ != nullptr) { + // Use state from input pins + if (this->heating_state_) { + target_action = climate::CLIMATE_ACTION_HEATING; + } else if (this->cooling_state_) { + target_action = climate::CLIMATE_ACTION_COOLING; + } } else { // Fallback to active state calc based on temp and hysteresis const float temp_diff = this->target_temperature - this->current_temperature; diff --git a/esphome/components/tuya/climate/tuya_climate.h b/esphome/components/tuya/climate/tuya_climate.h index f015bc337c..f1a0c13a77 100644 --- a/esphome/components/tuya/climate/tuya_climate.h +++ b/esphome/components/tuya/climate/tuya_climate.h @@ -10,6 +10,7 @@ namespace tuya { class TuyaClimate : public climate::Climate, public Component { public: void setup() override; + void loop() override; void dump_config() override; void set_supports_heat(bool supports_heat) { this->supports_heat_ = supports_heat; } void set_supports_cool(bool supports_cool) { this->supports_cool_ = supports_cool; } @@ -17,6 +18,8 @@ class TuyaClimate : public climate::Climate, public Component { void set_active_state_id(uint8_t state_id) { this->active_state_id_ = state_id; } void set_active_state_heating_value(uint8_t value) { this->active_state_heating_value_ = value; } void set_active_state_cooling_value(uint8_t value) { this->active_state_cooling_value_ = value; } + void set_heating_state_pin(GPIOPin *pin) { this->heating_state_pin_ = pin; } + void set_cooling_state_pin(GPIOPin *pin) { this->cooling_state_pin_ = pin; } void set_target_temperature_id(uint8_t target_temperature_id) { this->target_temperature_id_ = target_temperature_id; } @@ -51,12 +54,16 @@ class TuyaClimate : public climate::Climate, public Component { optional active_state_id_{}; optional active_state_heating_value_{}; optional active_state_cooling_value_{}; + GPIOPin *heating_state_pin_{nullptr}; + GPIOPin *cooling_state_pin_{nullptr}; optional target_temperature_id_{}; optional current_temperature_id_{}; float current_temperature_multiplier_{1.0f}; float target_temperature_multiplier_{1.0f}; float hysteresis_{1.0f}; uint8_t active_state_; + bool heating_state_{false}; + bool cooling_state_{false}; }; } // namespace tuya From d281e59f3a317019dbf41d742458c4e42f3b3800 Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Wed, 15 Sep 2021 08:40:52 -0300 Subject: [PATCH 041/207] ac_dimmer increase gate time for robotdyn (#1708) * ac_dimmer increate gate time for robotdyn * add explanation on longer gate enable time --- esphome/components/ac_dimmer/ac_dimmer.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/esphome/components/ac_dimmer/ac_dimmer.cpp b/esphome/components/ac_dimmer/ac_dimmer.cpp index 9a71f0e224..e86703bfe1 100644 --- a/esphome/components/ac_dimmer/ac_dimmer.cpp +++ b/esphome/components/ac_dimmer/ac_dimmer.cpp @@ -17,7 +17,10 @@ static AcDimmerDataStore *all_dimmers[32]; // NOLINT(cppcoreguidelines-avoid-no /// Time in microseconds the gate should be held high /// 10µs should be long enough for most triacs /// For reference: BT136 datasheet says 2µs nominal (page 7) -static const uint32_t GATE_ENABLE_TIME = 10; +/// However other factors like gate driver propagation time +/// are also considered and a really low value is not important +/// See also: https://github.com/esphome/issues/issues/1632 +static const uint32_t GATE_ENABLE_TIME = 50; /// Function called from timer interrupt /// Input is current time in microseconds (micros()) From 607ddaa632790b75d6a26f2bf5ceb17855ca51f9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Sep 2021 18:02:28 +0200 Subject: [PATCH 042/207] Bump aioesphomeapi from 9.0.0 to 9.1.0 (#2306) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 5ab68beb2e..64ffd366e1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,4 +10,4 @@ platformio==5.2.0 esptool==3.1 click==8.0.1 esphome-dashboard==20210908.0 -aioesphomeapi==9.0.0 +aioesphomeapi==9.1.0 From 6366ff6421d7cf807df66bc508891df33137786c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Sep 2021 18:02:41 +0200 Subject: [PATCH 043/207] Bump black from 21.8b0 to 21.9b0 (#2305) --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index 59085b33e2..bb735193f0 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,6 +1,6 @@ pylint==2.10.2 flake8==3.9.2 -black==21.8b0 +black==21.9b0 pexpect==4.8.0 pre-commit From c6dc8a11e24f9332bc6557c8733fd12d45ed3dc2 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Wed, 15 Sep 2021 19:01:31 +0200 Subject: [PATCH 044/207] Add namespace to all PlatformIO library references (#2296) * Remove unnecessary duplication in platformio.ini * Add namespace to all platformio library references * Add cmake-build-* to gitignore They're generated by the CLion add-on for each PlatformIO environment. Listing them all separately seems nonsensical. --- .gitignore | 5 +-- esphome/components/async_tcp/__init__.py | 2 +- esphome/components/fastled_base/__init__.py | 2 +- esphome/components/gps/__init__.py | 2 +- esphome/components/hm3301/sensor.py | 2 +- esphome/components/json/__init__.py | 2 +- esphome/components/mqtt/__init__.py | 2 +- esphome/components/neopixelbus/light.py | 2 +- esphome/components/tm1651/__init__.py | 2 +- platformio.ini | 46 +++++++++------------ 10 files changed, 29 insertions(+), 38 deletions(-) diff --git a/.gitignore b/.gitignore index 92d92e4b3b..cd7822bded 100644 --- a/.gitignore +++ b/.gitignore @@ -102,10 +102,7 @@ CMakeLists.txt .idea/**/dynamic.xml # CMake -cmake-build-debug/ -cmake-build-livingroom8266/ -cmake-build-livingroom32/ -cmake-build-release/ +cmake-build-*/ CMakeCache.txt CMakeFiles diff --git a/esphome/components/async_tcp/__init__.py b/esphome/components/async_tcp/__init__.py index 8938dc4671..22d4bb1fcb 100644 --- a/esphome/components/async_tcp/__init__.py +++ b/esphome/components/async_tcp/__init__.py @@ -12,4 +12,4 @@ async def to_code(config): cg.add_library("esphome/AsyncTCP-esphome", "1.2.2") elif CORE.is_esp8266: # https://github.com/OttoWinter/ESPAsyncTCP - cg.add_library("ESPAsyncTCP-esphome", "1.2.3") + cg.add_library("ottowinter/ESPAsyncTCP-esphome", "1.2.3") diff --git a/esphome/components/fastled_base/__init__.py b/esphome/components/fastled_base/__init__.py index f2d0bb1f38..62de036e62 100644 --- a/esphome/components/fastled_base/__init__.py +++ b/esphome/components/fastled_base/__init__.py @@ -44,5 +44,5 @@ async def new_fastled_light(config): # https://github.com/FastLED/FastLED/blob/master/library.json # 3.3.3 has an issue on ESP32 with RMT and fastled_clockless: # https://github.com/esphome/issues/issues/1375 - cg.add_library("FastLED", "3.3.2") + cg.add_library("fastled/FastLED", "3.3.2") return var diff --git a/esphome/components/gps/__init__.py b/esphome/components/gps/__init__.py index 0d5c3f3f3d..9d323e6e4d 100644 --- a/esphome/components/gps/__init__.py +++ b/esphome/components/gps/__init__.py @@ -99,4 +99,4 @@ async def to_code(config): cg.add(var.set_satellites_sensor(sens)) # https://platformio.org/lib/show/1655/TinyGPSPlus - cg.add_library("1655", "1.0.2") # TinyGPSPlus, has name conflict + cg.add_library("mikalhart/TinyGPSPlus", "1.0.2") diff --git a/esphome/components/hm3301/sensor.py b/esphome/components/hm3301/sensor.py index 7cd81fec1d..2c3c2f7221 100644 --- a/esphome/components/hm3301/sensor.py +++ b/esphome/components/hm3301/sensor.py @@ -110,4 +110,4 @@ async def to_code(config): cg.add(var.set_aqi_calculation_type(config[CONF_AQI][CONF_CALCULATION_TYPE])) # https://platformio.org/lib/show/6306/Grove%20-%20Laser%20PM2.5%20Sensor%20HM3301 - cg.add_library("6306", "1.0.3") + cg.add_library("seeed-studio/Grove - Laser PM2.5 Sensor HM3301", "1.0.3") diff --git a/esphome/components/json/__init__.py b/esphome/components/json/__init__.py index 7bdef6ea0e..9879754d9e 100644 --- a/esphome/components/json/__init__.py +++ b/esphome/components/json/__init__.py @@ -7,6 +7,6 @@ json_ns = cg.esphome_ns.namespace("json") @coroutine_with_priority(1.0) async def to_code(config): - cg.add_library("ArduinoJson-esphomelib", "5.13.3") + cg.add_library("ottowinter/ArduinoJson-esphomelib", "5.13.3") cg.add_define("USE_JSON") cg.add_global(json_ns.using) diff --git a/esphome/components/mqtt/__init__.py b/esphome/components/mqtt/__init__.py index 56ea9027af..19f78641df 100644 --- a/esphome/components/mqtt/__init__.py +++ b/esphome/components/mqtt/__init__.py @@ -214,7 +214,7 @@ async def to_code(config): await cg.register_component(var, config) # https://github.com/OttoWinter/async-mqtt-client/blob/master/library.json - cg.add_library("AsyncMqttClient-esphome", "0.8.4") + cg.add_library("ottowinter/AsyncMqttClient-esphome", "0.8.4") cg.add_define("USE_MQTT") cg.add_global(mqtt_ns.using) diff --git a/esphome/components/neopixelbus/light.py b/esphome/components/neopixelbus/light.py index 575f69b731..5eee5210f9 100644 --- a/esphome/components/neopixelbus/light.py +++ b/esphome/components/neopixelbus/light.py @@ -205,4 +205,4 @@ async def to_code(config): cg.add(var.set_pixel_order(getattr(ESPNeoPixelOrder, config[CONF_TYPE]))) # https://github.com/Makuna/NeoPixelBus/blob/master/library.json - cg.add_library("NeoPixelBus", "2.6.7") + cg.add_library("makuna/NeoPixelBus", "2.6.7") diff --git a/esphome/components/tm1651/__init__.py b/esphome/components/tm1651/__init__.py index f67a9f4512..d06c1bedde 100644 --- a/esphome/components/tm1651/__init__.py +++ b/esphome/components/tm1651/__init__.py @@ -50,7 +50,7 @@ async def to_code(config): cg.add(var.set_dio_pin(dio_pin)) # https://platformio.org/lib/show/6865/TM1651 - cg.add_library("6865", "1.0.1") + cg.add_library("freekode/TM1651", "1.0.1") BINARY_OUTPUT_ACTION_SCHEMA = maybe_simple_id( diff --git a/platformio.ini b/platformio.ini index 174832b067..89e9ce9374 100644 --- a/platformio.ini +++ b/platformio.ini @@ -4,7 +4,7 @@ ; It's *not* used during runtime. [platformio] -default_envs = esp8266 +default_envs = esp8266, esp32 src_dir = . include_dir = @@ -26,19 +26,18 @@ build_flags = [common] lib_deps = - AsyncMqttClient-esphome@0.8.4 - ArduinoJson-esphomelib@5.13.3 - esphome/ESPAsyncWebServer-esphome@1.3.0 - FastLED@3.3.2 - NeoPixelBus@2.6.7 - 1655@1.0.2 ; TinyGPSPlus (has name conflict) - 6865@1.0.0 ; TM1651 Battery Display - 6306@1.0.3 ; HM3301 - glmnet/Dsmr@0.5 ; used by dsmr - rweather/Crypto@0.2.0 ; used by dsmr - esphome/noise-c@0.1.1 ; used by api - dudanov/MideaUART@1.1.0 ; used by midea - + ottowinter/AsyncMqttClient-esphome@0.8.4 ; mqtt + ottowinter/ArduinoJson-esphomelib@5.13.3 ; json + esphome/ESPAsyncWebServer-esphome@1.3.0 ; web_server_base + fastled/FastLED@3.3.2 ; fastled_base + makuna/NeoPixelBus@2.6.7 ; neopixelbus + mikalhart/TinyGPSPlus@1.0.2 ; gps + freekode/TM1651@1.0.1 ; tm1651 + seeed-studio/Grove - Laser PM2.5 Sensor HM3301@1.0.3 ; hm3301 + glmnet/Dsmr@0.5 ; dsmr + rweather/Crypto@0.2.0 ; dsmr + esphome/noise-c@0.1.1 ; api + dudanov/MideaUART@1.1.0 ; midea build_flags = -DESPHOME_LOG_LEVEL=ESPHOME_LOG_LEVEL_VERY_VERBOSE src_filter = @@ -47,30 +46,25 @@ src_filter = +<.temp/all-include.cpp> [common:esp8266] +extends = common platform = platformio/espressif8266@3.1.0 framework = arduino board = nodemcuv2 lib_deps = ${common.lib_deps} - ESP8266WiFi - ESPAsyncTCP-esphome@1.2.3 - Update -build_flags = ${common.build_flags} -src_filter = ${common.src_filter} + ESP8266WiFi ; wifi (Arduino built-in) + Update ; ota (Arduino built-in) + ottowinter/ESPAsyncTCP-esphome@1.2.3 ; async_tcp [common:esp32] +extends = common platform = platformio/espressif32@3.2.0 framework = arduino board = nodemcu-32s lib_deps = ${common.lib_deps} - esphome/AsyncTCP-esphome@1.2.2 - Update -build_flags = - ${common.build_flags} -src_filter = - ${common.src_filter} - - + Hash ; ota (Arduino built-in) + esphome/AsyncTCP-esphome@1.2.2 ; async_tcp [env:esp8266] extends = common:esp8266 From c69b88bb554694c4059057717c10ee45f46580df Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Wed, 15 Sep 2021 19:10:42 +0200 Subject: [PATCH 045/207] Fix platformio.ini parser used by container build --- docker/platformio_install_deps.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/docker/platformio_install_deps.py b/docker/platformio_install_deps.py index 6f3e9f28d5..6b5860ffc5 100755 --- a/docker/platformio_install_deps.py +++ b/docker/platformio_install_deps.py @@ -3,18 +3,11 @@ # all platformio libraries in the global storage import configparser -import re import subprocess import sys -config = configparser.ConfigParser() +config = configparser.ConfigParser(inline_comment_prefixes=(';', )) config.read(sys.argv[1]) -libs = [] -for line in config['common']['lib_deps'].splitlines(): - # Format: '1655@1.0.2 ; TinyGPSPlus (has name conflict)' (includes comment) - m = re.search(r'([a-zA-Z0-9-_/]+@[0-9\.]+)', line) - if m is None: - continue - libs.append(m.group(1)) +libs = [x for x in config['common']['lib_deps'].splitlines() if len(x) != 0] -subprocess.check_call(['platformio', 'lib', '-g', 'install', *libs]) +subprocess.check_call(['platformio', 'lib', '-g', 'uninstall', *libs]) From aed140d80208032ecb6d3d3358b6c48b7dee95be Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Wed, 15 Sep 2021 19:13:30 +0200 Subject: [PATCH 046/207] Fix typo --- docker/platformio_install_deps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/platformio_install_deps.py b/docker/platformio_install_deps.py index 6b5860ffc5..5625bd4d01 100755 --- a/docker/platformio_install_deps.py +++ b/docker/platformio_install_deps.py @@ -10,4 +10,4 @@ config = configparser.ConfigParser(inline_comment_prefixes=(';', )) config.read(sys.argv[1]) libs = [x for x in config['common']['lib_deps'].splitlines() if len(x) != 0] -subprocess.check_call(['platformio', 'lib', '-g', 'uninstall', *libs]) +subprocess.check_call(['platformio', 'lib', '-g', 'install', *libs]) From 8f3a739da77e06b5ef18115248ee469ed0caa95f Mon Sep 17 00:00:00 2001 From: Matthew Mazzanti Date: Wed, 15 Sep 2021 13:59:58 -0400 Subject: [PATCH 047/207] Allow transforms and flashes to not update remote_values (#2313) --- esphome/components/light/light_call.cpp | 4 ++-- esphome/components/light/light_state.cpp | 14 ++++++++++---- esphome/components/light/light_state.h | 4 ++-- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/esphome/components/light/light_call.cpp b/esphome/components/light/light_call.cpp index 178a78a94c..dc1e7d39fb 100644 --- a/esphome/components/light/light_call.cpp +++ b/esphome/components/light/light_call.cpp @@ -77,7 +77,7 @@ void LightCall::perform() { ESP_LOGD(TAG, " Flash length: %.1fs", *this->flash_length_ / 1e3f); } - this->parent_->start_flash_(v, *this->flash_length_); + this->parent_->start_flash_(v, *this->flash_length_, this->publish_); } else if (this->has_transition_()) { // TRANSITION if (this->publish_) { @@ -92,7 +92,7 @@ void LightCall::perform() { this->parent_->stop_effect_(); } - this->parent_->start_transition_(v, *this->transition_length_); + this->parent_->start_transition_(v, *this->transition_length_, this->publish_); } else if (this->has_effect_()) { // EFFECT diff --git a/esphome/components/light/light_state.cpp b/esphome/components/light/light_state.cpp index 945d3910d5..d7f539a8d8 100644 --- a/esphome/components/light/light_state.cpp +++ b/esphome/components/light/light_state.cpp @@ -228,13 +228,16 @@ void LightState::stop_effect_() { this->active_effect_index_ = 0; } -void LightState::start_transition_(const LightColorValues &target, uint32_t length) { +void LightState::start_transition_(const LightColorValues &target, uint32_t length, bool set_remote_values) { this->transformer_ = this->output_->create_default_transition(); this->transformer_->setup(this->current_values, target, length); - this->remote_values = target; + + if (set_remote_values) { + this->remote_values = target; + } } -void LightState::start_flash_(const LightColorValues &target, uint32_t length) { +void LightState::start_flash_(const LightColorValues &target, uint32_t length, bool set_remote_values) { LightColorValues end_colors = this->remote_values; // If starting a flash if one is already happening, set end values to end values of current flash // Hacky but works @@ -243,7 +246,10 @@ void LightState::start_flash_(const LightColorValues &target, uint32_t length) { this->transformer_ = make_unique(*this); this->transformer_->setup(end_colors, target, length); - this->remote_values = target; + + if (set_remote_values) { + this->remote_values = target; + }; } void LightState::set_immediately_(const LightColorValues &target, bool set_remote_values) { diff --git a/esphome/components/light/light_state.h b/esphome/components/light/light_state.h index dd42aa76db..f73a4c3b17 100644 --- a/esphome/components/light/light_state.h +++ b/esphome/components/light/light_state.h @@ -154,10 +154,10 @@ class LightState : public Nameable, public Component { /// Internal method to stop the current effect (if one is active). void stop_effect_(); /// Internal method to start a transition to the target color with the given length. - void start_transition_(const LightColorValues &target, uint32_t length); + void start_transition_(const LightColorValues &target, uint32_t length, bool set_remote_values); /// Internal method to start a flash for the specified amount of time. - void start_flash_(const LightColorValues &target, uint32_t length); + void start_flash_(const LightColorValues &target, uint32_t length, bool set_remote_values); /// Internal method to set the color values to target immediately (with no transition). void set_immediately_(const LightColorValues &target, bool set_remote_values); From 2e49039c0187dac827292dae23d1c73786f8b0f7 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Sun, 19 Sep 2021 14:20:35 +0200 Subject: [PATCH 048/207] Reduce stale/lock gh actions interval (#2341) --- .github/stale.yml | 59 ------------------------------------- .github/workflows/lock.yml | 2 +- .github/workflows/stale.yml | 2 +- 3 files changed, 2 insertions(+), 61 deletions(-) delete mode 100644 .github/stale.yml diff --git a/.github/stale.yml b/.github/stale.yml deleted file mode 100644 index 225c029bd9..0000000000 --- a/.github/stale.yml +++ /dev/null @@ -1,59 +0,0 @@ -# Configuration for probot-stale - https://github.com/probot/stale - -# Number of days of inactivity before an Issue or Pull Request becomes stale -daysUntilStale: 60 - -# Number of days of inactivity before an Issue or Pull Request with the stale label is closed. -# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. -daysUntilClose: 7 - -# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) -onlyLabels: [] - -# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable -exemptLabels: - - not-stale - -# Set to true to ignore issues in a project (defaults to false) -exemptProjects: false - -# Set to true to ignore issues in a milestone (defaults to false) -exemptMilestones: true - -# Set to true to ignore issues with an assignee (defaults to false) -exemptAssignees: false - -# Label to use when marking as stale -staleLabel: stale - -# Comment to post when marking as stale. Set to `false` to disable -markComment: > - This issue has been automatically marked as stale because it has not had - recent activity. It will be closed if no further activity occurs. Thank you - for your contributions. - -# Comment to post when removing the stale label. -# unmarkComment: > -# Your comment here. - -# Comment to post when closing a stale Issue or Pull Request. -# closeComment: > -# Your comment here. - -# Limit the number of actions per hour, from 1-30. Default is 30 -limitPerRun: 10 - -# Limit to only `issues` or `pulls` -only: pulls - -# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': -# pulls: -# daysUntilStale: 30 -# markComment: > -# This pull request has been automatically marked as stale because it has not had -# recent activity. It will be closed if no further activity occurs. Thank you -# for your contributions. - -# issues: -# exemptLabels: -# - confirmed diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml index aedb9dd9b3..375b8f1db4 100644 --- a/.github/workflows/lock.yml +++ b/.github/workflows/lock.yml @@ -2,7 +2,7 @@ name: Lock on: schedule: - - cron: "0 * * * *" + - cron: '30 0 * * *' workflow_dispatch: permissions: diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index f26dd32929..712ae1a289 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -2,7 +2,7 @@ name: Stale on: schedule: - - cron: "0 * * * *" + - cron: '30 0 * * *' workflow_dispatch: permissions: From 64341d1d1887f16df87b88266402c15a109ac4e1 Mon Sep 17 00:00:00 2001 From: Paul Monigatti Date: Mon, 20 Sep 2021 04:30:41 +1200 Subject: [PATCH 049/207] Fix MQTT discovery for sensor state_class (#2331) --- esphome/components/sensor/sensor.cpp | 8 ++++---- esphome/components/sensor/sensor.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/esphome/components/sensor/sensor.cpp b/esphome/components/sensor/sensor.cpp index 128f36fc93..0dc0275715 100644 --- a/esphome/components/sensor/sensor.cpp +++ b/esphome/components/sensor/sensor.cpp @@ -6,15 +6,15 @@ namespace sensor { static const char *const TAG = "sensor"; -const LogString *state_class_to_string(StateClass state_class) { +std::string state_class_to_string(StateClass state_class) { switch (state_class) { case STATE_CLASS_MEASUREMENT: - return LOG_STR("measurement"); + return "measurement"; case STATE_CLASS_TOTAL_INCREASING: - return LOG_STR("total_increasing"); + return "total_increasing"; case STATE_CLASS_NONE: default: - return LOG_STR(""); + return ""; } } diff --git a/esphome/components/sensor/sensor.h b/esphome/components/sensor/sensor.h index 3fc993cb2c..d284f931b1 100644 --- a/esphome/components/sensor/sensor.h +++ b/esphome/components/sensor/sensor.h @@ -14,7 +14,7 @@ namespace sensor { if (!(obj)->get_device_class().empty()) { \ ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \ } \ - ESP_LOGCONFIG(TAG, "%s State Class: '%s'", prefix, LOG_STR_ARG(state_class_to_string((obj)->get_state_class()))); \ + ESP_LOGCONFIG(TAG, "%s State Class: '%s'", prefix, state_class_to_string((obj)->get_state_class()).c_str()); \ ESP_LOGCONFIG(TAG, "%s Unit of Measurement: '%s'", prefix, (obj)->get_unit_of_measurement().c_str()); \ ESP_LOGCONFIG(TAG, "%s Accuracy Decimals: %d", prefix, (obj)->get_accuracy_decimals()); \ if (!(obj)->get_icon().empty()) { \ @@ -37,7 +37,7 @@ enum StateClass : uint8_t { STATE_CLASS_TOTAL_INCREASING = 2, }; -const LogString *state_class_to_string(StateClass state_class); +std::string state_class_to_string(StateClass state_class); /** Base-class for all sensors. * From 68d547595ee2177319af3e2572e07da4798331c6 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Sun, 19 Sep 2021 18:31:20 +0200 Subject: [PATCH 050/207] Light transition fixes (#2320) --- esphome/components/light/addressable_light.cpp | 11 ++++++----- esphome/components/light/light_state.cpp | 3 +++ esphome/components/light/transformers.h | 8 +++++++- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/esphome/components/light/addressable_light.cpp b/esphome/components/light/addressable_light.cpp index a8fa2cd7ac..599d43d8b1 100644 --- a/esphome/components/light/addressable_light.cpp +++ b/esphome/components/light/addressable_light.cpp @@ -62,10 +62,13 @@ void AddressableLightTransformer::start() { } optional AddressableLightTransformer::apply() { - // Don't try to transition over running effects, instead immediately use the target values. write_state() and the - // effects pick up the change from current_values. + float smoothed_progress = LightTransitionTransformer::smoothed_progress(this->get_progress_()); + + // When running an output-buffer modifying effect, don't try to transition individual LEDs, but instead just fade the + // LightColorValues. write_state() then picks up the change in brightness, and the color change is picked up by the + // effects which respect it. if (this->light_.is_effect_active()) - return this->target_values_; + return LightColorValues::lerp(this->get_start_values(), this->get_target_values(), smoothed_progress); // Use a specialized transition for addressable lights: instead of using a unified transition for // all LEDs, we use the current state of each LED as the start. @@ -75,8 +78,6 @@ optional AddressableLightTransformer::apply() { // Instead, we "fake" the look of the LERP by using an exponential average over time and using // dynamically-calculated alpha values to match the look. - float smoothed_progress = LightTransitionTransformer::smoothed_progress(this->get_progress_()); - float denom = (1.0f - smoothed_progress); float alpha = denom == 0.0f ? 0.0f : (smoothed_progress - this->last_transition_progress_) / denom; diff --git a/esphome/components/light/light_state.cpp b/esphome/components/light/light_state.cpp index d7f539a8d8..398546a09f 100644 --- a/esphome/components/light/light_state.cpp +++ b/esphome/components/light/light_state.cpp @@ -121,6 +121,9 @@ void LightState::loop() { } if (this->transformer_->is_finished()) { + // if the transition has written directly to the output, current_values is outdated, so update it + this->current_values = this->transformer_->get_target_values(); + this->transformer_->stop(); this->transformer_ = nullptr; this->target_state_reached_callback_.call(); diff --git a/esphome/components/light/transformers.h b/esphome/components/light/transformers.h index d501d53f72..90646f4e61 100644 --- a/esphome/components/light/transformers.h +++ b/esphome/components/light/transformers.h @@ -12,12 +12,18 @@ namespace light { class LightTransitionTransformer : public LightTransformer { public: void start() override { - // When turning light on from off state, use colors from target state. + // When turning light on from off state, use target state and only increase brightness from zero. if (!this->start_values_.is_on() && this->target_values_.is_on()) { this->start_values_ = LightColorValues(this->target_values_); this->start_values_.set_brightness(0.0f); } + // When turning light off from on state, use source state and only decrease brightness to zero. + if (this->start_values_.is_on() && !this->target_values_.is_on()) { + this->target_values_ = LightColorValues(this->start_values_); + this->target_values_.set_brightness(0.0f); + } + // When changing color mode, go through off state, as color modes are orthogonal and there can't be two active. if (this->start_values_.get_color_mode() != this->target_values_.get_color_mode()) { this->changing_color_mode_ = true; From f76685fccf50f5bf64de80dd5b2a8295353f1798 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Sun, 19 Sep 2021 18:31:31 +0200 Subject: [PATCH 051/207] Cease using deprecated Cover methods in automations (#2326) --- esphome/components/cover/automation.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/cover/automation.h b/esphome/components/cover/automation.h index 0092f987f2..0b364e1e09 100644 --- a/esphome/components/cover/automation.h +++ b/esphome/components/cover/automation.h @@ -11,7 +11,7 @@ template class OpenAction : public Action { public: explicit OpenAction(Cover *cover) : cover_(cover) {} - void play(Ts... x) override { this->cover_->open(); } + void play(Ts... x) override { this->cover_->make_call().set_command_open().perform(); } protected: Cover *cover_; @@ -21,7 +21,7 @@ template class CloseAction : public Action { public: explicit CloseAction(Cover *cover) : cover_(cover) {} - void play(Ts... x) override { this->cover_->close(); } + void play(Ts... x) override { this->cover_->make_call().set_command_close().perform(); } protected: Cover *cover_; @@ -31,7 +31,7 @@ template class StopAction : public Action { public: explicit StopAction(Cover *cover) : cover_(cover) {} - void play(Ts... x) override { this->cover_->stop(); } + void play(Ts... x) override { this->cover_->make_call().set_command_stop().perform(); } protected: Cover *cover_; From 30eca885c9c55c92fa2d3b62dbe627b762443e90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= Date: Sun, 19 Sep 2021 18:46:17 +0200 Subject: [PATCH 052/207] Add `esp8266_disable_ssl_support:` config option (#2236) --- esphome/components/http_request/__init__.py | 6 ++++++ esphome/components/http_request/http_request.cpp | 2 ++ esphome/components/http_request/http_request.h | 5 +++++ esphome/const.py | 1 + esphome/core/defines.h | 1 + 5 files changed, 15 insertions(+) diff --git a/esphome/components/http_request/__init__.py b/esphome/components/http_request/__init__.py index 7dffdae27f..eea2f0d407 100644 --- a/esphome/components/http_request/__init__.py +++ b/esphome/components/http_request/__init__.py @@ -10,6 +10,7 @@ from esphome.const import ( CONF_METHOD, CONF_TRIGGER_ID, CONF_URL, + CONF_ESP8266_DISABLE_SSL_SUPPORT, ) from esphome.core import CORE, Lambda @@ -71,6 +72,9 @@ CONFIG_SCHEMA = cv.Schema( cv.GenerateID(): cv.declare_id(HttpRequestComponent), cv.Optional(CONF_USERAGENT, "ESPHome"): cv.string, cv.Optional(CONF_TIMEOUT, default="5s"): cv.positive_time_period_milliseconds, + cv.SplitDefault(CONF_ESP8266_DISABLE_SSL_SUPPORT, esp8266=False): cv.All( + cv.only_on_esp8266, cv.boolean + ), } ).extend(cv.COMPONENT_SCHEMA) @@ -98,6 +102,8 @@ async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) cg.add(var.set_timeout(config[CONF_TIMEOUT])) cg.add(var.set_useragent(config[CONF_USERAGENT])) + if CORE.is_esp8266 and not config[CONF_ESP8266_DISABLE_SSL_SUPPORT]: + cg.add_define("USE_HTTP_REQUEST_ESP8266_HTTPS") await cg.register_component(var, config) diff --git a/esphome/components/http_request/http_request.cpp b/esphome/components/http_request/http_request.cpp index 0d4e425147..3f5bf6da87 100644 --- a/esphome/components/http_request/http_request.cpp +++ b/esphome/components/http_request/http_request.cpp @@ -81,6 +81,7 @@ void HttpRequestComponent::send(const std::vector #ifdef ARDUINO_ARCH_ESP8266 std::shared_ptr HttpRequestComponent::get_wifi_client_() { +#ifdef USE_HTTP_REQUEST_ESP8266_HTTPS if (this->secure_) { if (this->wifi_client_secure_ == nullptr) { this->wifi_client_secure_ = std::make_shared(); @@ -89,6 +90,7 @@ std::shared_ptr HttpRequestComponent::get_wifi_client_() { } return this->wifi_client_secure_; } +#endif if (this->wifi_client_ == nullptr) { this->wifi_client_ = std::make_shared(); diff --git a/esphome/components/http_request/http_request.h b/esphome/components/http_request/http_request.h index 740a4580b4..fa29f8aa3a 100644 --- a/esphome/components/http_request/http_request.h +++ b/esphome/components/http_request/http_request.h @@ -3,6 +3,7 @@ #include "esphome/components/json/json_util.h" #include "esphome/core/automation.h" #include "esphome/core/component.h" +#include "esphome/core/defines.h" #include #include #include @@ -13,8 +14,10 @@ #endif #ifdef ARDUINO_ARCH_ESP8266 #include +#ifdef USE_HTTP_REQUEST_ESP8266_HTTPS #include #endif +#endif namespace esphome { namespace http_request { @@ -53,7 +56,9 @@ class HttpRequestComponent : public Component { std::list
headers_; #ifdef ARDUINO_ARCH_ESP8266 std::shared_ptr wifi_client_; +#ifdef USE_HTTP_REQUEST_ESP8266_HTTPS std::shared_ptr wifi_client_secure_; +#endif std::shared_ptr get_wifi_client_(); #endif }; diff --git a/esphome/const.py b/esphome/const.py index 92d18cde3a..a74068ab77 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -208,6 +208,7 @@ CONF_ENABLE_PIN = "enable_pin" CONF_ENABLE_TIME = "enable_time" CONF_ENERGY = "energy" CONF_ENTITY_ID = "entity_id" +CONF_ESP8266_DISABLE_SSL_SUPPORT = "esp8266_disable_ssl_support" CONF_ESP8266_RESTORE_FROM_FLASH = "esp8266_restore_from_flash" CONF_ESPHOME = "esphome" CONF_ETHERNET = "ethernet" diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 45bb3b82a5..e3976d378e 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -20,6 +20,7 @@ #define USE_ESP8266_PREFERENCES_FLASH #define USE_FAN #define USE_HOMEASSISTANT_TIME +#define USE_HTTP_REQUEST_ESP8266_HTTPS #define USE_I2C_MULTIPLEXER #define USE_JSON #define USE_LIGHT From 50da6308110d89b20bf4b1366c1fe296da7a23ca Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Sun, 19 Sep 2021 18:46:26 +0200 Subject: [PATCH 053/207] Apply color brightness to addressable light effects (#2321) --- esphome/components/light/addressable_light.cpp | 6 +++--- esphome/components/light/addressable_light.h | 3 +++ esphome/components/light/addressable_light_effect.h | 7 ++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/esphome/components/light/addressable_light.cpp b/esphome/components/light/addressable_light.cpp index 599d43d8b1..f3e6c0ef1d 100644 --- a/esphome/components/light/addressable_light.cpp +++ b/esphome/components/light/addressable_light.cpp @@ -27,7 +27,7 @@ std::unique_ptr AddressableLight::create_default_transition() return make_unique(*this); } -Color esp_color_from_light_color_values(LightColorValues val) { +Color color_from_light_color_values(LightColorValues val) { auto r = to_uint8_scale(val.get_color_brightness() * val.get_red()); auto g = to_uint8_scale(val.get_color_brightness() * val.get_green()); auto b = to_uint8_scale(val.get_color_brightness() * val.get_blue()); @@ -44,7 +44,7 @@ void AddressableLight::update_state(LightState *state) { return; // don't use LightState helper, gamma correction+brightness is handled by ESPColorView - this->all() = esp_color_from_light_color_values(val); + this->all() = color_from_light_color_values(val); this->schedule_show(); } @@ -54,7 +54,7 @@ void AddressableLightTransformer::start() { return; auto end_values = this->target_values_; - this->target_color_ = esp_color_from_light_color_values(end_values); + this->target_color_ = color_from_light_color_values(end_values); // our transition will handle brightness, disable brightness in correction. this->light_.correction_.set_local_brightness(255); diff --git a/esphome/components/light/addressable_light.h b/esphome/components/light/addressable_light.h index bba2158457..97f4a4687d 100644 --- a/esphome/components/light/addressable_light.h +++ b/esphome/components/light/addressable_light.h @@ -19,6 +19,9 @@ namespace light { using ESPColor ESPDEPRECATED("esphome::light::ESPColor is deprecated, use esphome::Color instead.", "v1.21") = Color; +/// Convert the color information from a `LightColorValues` object to a `Color` object (does not apply brightness). +Color color_from_light_color_values(LightColorValues val); + class AddressableLight : public LightOutput, public Component { public: virtual int32_t size() const = 0; diff --git a/esphome/components/light/addressable_light_effect.h b/esphome/components/light/addressable_light_effect.h index 1cb29dfa4e..358fe69c23 100644 --- a/esphome/components/light/addressable_light_effect.h +++ b/esphome/components/light/addressable_light_effect.h @@ -38,11 +38,8 @@ class AddressableLightEffect : public LightEffect { void stop() override { this->get_addressable_()->set_effect_active(false); } virtual void apply(AddressableLight &it, const Color ¤t_color) = 0; void apply() override { - LightColorValues color = this->state_->remote_values; - // not using any color correction etc. that will be handled by the addressable layer - Color current_color = - Color(static_cast(color.get_red() * 255), static_cast(color.get_green() * 255), - static_cast(color.get_blue() * 255), static_cast(color.get_white() * 255)); + // not using any color correction etc. that will be handled by the addressable layer through ESPColorCorrection + Color current_color = color_from_light_color_values(this->state_->remote_values); this->apply(*this->get_addressable_(), current_color); } From dbb195691b0ad784ac84d0273b48a69d31bad27c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 19 Sep 2021 19:22:28 +0200 Subject: [PATCH 054/207] Bump pylint from 2.10.2 to 2.11.1 (#2334) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Otto winter --- esphome/__main__.py | 14 +-- esphome/components/binary_sensor/__init__.py | 16 ++-- esphome/components/deep_sleep/__init__.py | 3 +- .../components/esp32_ble_beacon/__init__.py | 4 +- .../components/esp32_ble_tracker/__init__.py | 13 +-- esphome/components/font/__init__.py | 5 +- esphome/components/lcd_gpio/display.py | 3 +- esphome/components/ledc/output.py | 6 +- esphome/components/light/effects.py | 6 +- esphome/components/logger/__init__.py | 8 +- esphome/components/mqtt/__init__.py | 7 +- esphome/components/neopixelbus/light.py | 8 +- esphome/components/nextion/base_component.py | 6 +- esphome/components/ntc/sensor.py | 4 +- esphome/components/packages/__init__.py | 3 +- esphome/components/partition/light.py | 3 +- esphome/components/pmsx003/sensor.py | 4 +- esphome/components/remote_base/__init__.py | 16 ++-- esphome/components/rotary_encoder/sensor.py | 3 +- esphome/components/sensor/__init__.py | 6 +- esphome/components/substitutions/__init__.py | 7 +- esphome/components/time/__init__.py | 36 +++----- esphome/components/wifi/wpa2_eap.py | 1 + esphome/config.py | 76 ++++++---------- esphome/config_validation.py | 89 +++++++------------ esphome/core/__init__.py | 18 ++-- esphome/core/config.py | 15 +--- esphome/cpp_generator.py | 12 ++- esphome/cpp_helpers.py | 4 +- esphome/dashboard/dashboard.py | 46 +++++----- esphome/espota2.py | 6 +- esphome/helpers.py | 8 +- esphome/mqtt.py | 9 +- esphome/pins.py | 15 ++-- esphome/platformio_api.py | 16 ++-- esphome/storage_json.py | 4 +- esphome/wizard.py | 84 ++++++----------- esphome/writer.py | 10 +-- esphome/yaml_util.py | 7 +- requirements_test.txt | 2 +- 40 files changed, 219 insertions(+), 384 deletions(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index 121fa7cc9e..ba1019869d 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -72,7 +72,7 @@ def choose_upload_log_host(default, check_default, show_ota, show_mqtt, show_api if default == "OTA": return CORE.address if show_mqtt and "mqtt" in CORE.config: - options.append(("MQTT ({})".format(CORE.config["mqtt"][CONF_BROKER]), "MQTT")) + options.append((f"MQTT ({CORE.config['mqtt'][CONF_BROKER]})", "MQTT")) if default == "OTA": return "MQTT" if default is not None: @@ -415,30 +415,30 @@ def command_update_all(args): click.echo(f"{half_line}{middle_text}{half_line}") for f in files: - print("Updating {}".format(color(Fore.CYAN, f))) + print(f"Updating {color(Fore.CYAN, f)}") print("-" * twidth) print() rc = run_external_process( "esphome", "--dashboard", "run", f, "--no-logs", "--device", "OTA" ) if rc == 0: - print_bar("[{}] {}".format(color(Fore.BOLD_GREEN, "SUCCESS"), f)) + print_bar(f"[{color(Fore.BOLD_GREEN, 'SUCCESS')}] {f}") success[f] = True else: - print_bar("[{}] {}".format(color(Fore.BOLD_RED, "ERROR"), f)) + print_bar(f"[{color(Fore.BOLD_RED, 'ERROR')}] {f}") success[f] = False print() print() print() - print_bar("[{}]".format(color(Fore.BOLD_WHITE, "SUMMARY"))) + print_bar(f"[{color(Fore.BOLD_WHITE, 'SUMMARY')}]") failed = 0 for f in files: if success[f]: - print(" - {}: {}".format(f, color(Fore.GREEN, "SUCCESS"))) + print(f" - {f}: {color(Fore.GREEN, 'SUCCESS')}") else: - print(" - {}: {}".format(f, color(Fore.BOLD_RED, "FAILED"))) + print(f" - {f}: {color(Fore.BOLD_RED, 'FAILED')}") failed += 1 return failed diff --git a/esphome/components/binary_sensor/__init__.py b/esphome/components/binary_sensor/__init__.py index 9cd2a045ff..60ac4303a7 100644 --- a/esphome/components/binary_sensor/__init__.py +++ b/esphome/components/binary_sensor/__init__.py @@ -231,17 +231,16 @@ def parse_multi_click_timing_str(value): parts = value.lower().split(" ") if len(parts) != 5: raise cv.Invalid( - "Multi click timing grammar consists of exactly 5 words, not {}" - "".format(len(parts)) + f"Multi click timing grammar consists of exactly 5 words, not {len(parts)}" ) try: state = cv.boolean(parts[0]) except cv.Invalid: # pylint: disable=raise-missing-from - raise cv.Invalid("First word must either be ON or OFF, not {}".format(parts[0])) + raise cv.Invalid(f"First word must either be ON or OFF, not {parts[0]}") if parts[1] != "for": - raise cv.Invalid("Second word must be 'for', got {}".format(parts[1])) + raise cv.Invalid(f"Second word must be 'for', got {parts[1]}") if parts[2] == "at": if parts[3] == "least": @@ -250,8 +249,7 @@ def parse_multi_click_timing_str(value): key = CONF_MAX_LENGTH else: raise cv.Invalid( - "Third word after at must either be 'least' or 'most', got {}" - "".format(parts[3]) + f"Third word after at must either be 'least' or 'most', got {parts[3]}" ) try: length = cv.positive_time_period_milliseconds(parts[4]) @@ -296,13 +294,11 @@ def validate_multi_click_timing(value): new_state = v_.get(CONF_STATE, not state) if new_state == state: raise cv.Invalid( - "Timings must have alternating state. Indices {} and {} have " - "the same state {}".format(i, i + 1, state) + f"Timings must have alternating state. Indices {i} and {i + 1} have the same state {state}" ) if max_length is not None and max_length < min_length: raise cv.Invalid( - "Max length ({}) must be larger than min length ({})." - "".format(max_length, min_length) + f"Max length ({max_length}) must be larger than min length ({min_length})." ) state = new_state diff --git a/esphome/components/deep_sleep/__init__.py b/esphome/components/deep_sleep/__init__.py index b7bf27e79a..3f59ad52cf 100644 --- a/esphome/components/deep_sleep/__init__.py +++ b/esphome/components/deep_sleep/__init__.py @@ -16,8 +16,7 @@ def validate_pin_number(value): valid_pins = [0, 2, 4, 12, 13, 14, 15, 25, 26, 27, 32, 33, 34, 35, 36, 37, 38, 39] if value[CONF_NUMBER] not in valid_pins: raise cv.Invalid( - "Only pins {} support wakeup" - "".format(", ".join(str(x) for x in valid_pins)) + f"Only pins {', '.join(str(x) for x in valid_pins)} support wakeup" ) return value diff --git a/esphome/components/esp32_ble_beacon/__init__.py b/esphome/components/esp32_ble_beacon/__init__.py index 3e00692d3a..cd0cb4bed6 100644 --- a/esphome/components/esp32_ble_beacon/__init__.py +++ b/esphome/components/esp32_ble_beacon/__init__.py @@ -24,9 +24,7 @@ CONFIG_SCHEMA = cv.Schema( async def to_code(config): uuid = config[CONF_UUID].hex - uuid_arr = [ - cg.RawExpression("0x{}".format(uuid[i : i + 2])) for i in range(0, len(uuid), 2) - ] + uuid_arr = [cg.RawExpression(f"0x{uuid[i:i + 2]}") for i in range(0, len(uuid), 2)] var = cg.new_Pvariable(config[CONF_ID], uuid_arr) await cg.register_component(var, config) cg.add(var.set_major(config[CONF_MAJOR])) diff --git a/esphome/components/esp32_ble_tracker/__init__.py b/esphome/components/esp32_ble_tracker/__init__.py index fec0f6dcfb..162a4dee89 100644 --- a/esphome/components/esp32_ble_tracker/__init__.py +++ b/esphome/components/esp32_ble_tracker/__init__.py @@ -51,8 +51,7 @@ def validate_scan_parameters(config): if window > interval: raise cv.Invalid( - "Scan window ({}) needs to be smaller than scan interval ({})" - "".format(window, interval) + f"Scan window ({window}) needs to be smaller than scan interval ({interval})" ) if interval.total_milliseconds * 3 > duration.total_milliseconds: @@ -97,9 +96,7 @@ def bt_uuid(value): ) return value raise cv.Invalid( - "Service UUID must be in 16 bit '{}', 32 bit '{}', or 128 bit '{}' format".format( - bt_uuid16_format, bt_uuid32_format, bt_uuid128_format - ) + f"Service UUID must be in 16 bit '{bt_uuid16_format}', 32 bit '{bt_uuid32_format}', or 128 bit '{bt_uuid128_format}' format" ) @@ -112,9 +109,7 @@ def as_hex_array(value): cpp_array = [ f"0x{part}" for part in [value[i : i + 2] for i in range(0, len(value), 2)] ] - return cg.RawExpression( - "(uint8_t*)(const uint8_t[16]){{{}}}".format(",".join(cpp_array)) - ) + return cg.RawExpression(f"(uint8_t*)(const uint8_t[16]){{{','.join(cpp_array)}}}") def as_reversed_hex_array(value): @@ -123,7 +118,7 @@ def as_reversed_hex_array(value): f"0x{part}" for part in [value[i : i + 2] for i in range(0, len(value), 2)] ] return cg.RawExpression( - "(uint8_t*)(const uint8_t[16]){{{}}}".format(",".join(reversed(cpp_array))) + f"(uint8_t*)(const uint8_t[16]){{{','.join(reversed(cpp_array))}}}" ) diff --git a/esphome/components/font/__init__.py b/esphome/components/font/__init__.py index 16a7d8a612..e47a6f38af 100644 --- a/esphome/components/font/__init__.py +++ b/esphome/components/font/__init__.py @@ -61,8 +61,7 @@ def validate_pillow_installed(value): def validate_truetype_file(value): if value.endswith(".zip"): # for Google Fonts downloads raise cv.Invalid( - "Please unzip the font archive '{}' first and then use the .ttf files " - "inside.".format(value) + f"Please unzip the font archive '{value}' first and then use the .ttf files inside." ) if not value.endswith(".ttf"): raise cv.Invalid( @@ -131,7 +130,7 @@ async def to_code(config): ("a_char", glyph), ( "data", - cg.RawExpression(str(prog_arr) + " + " + str(glyph_args[glyph][0])), + cg.RawExpression(f"{str(prog_arr)} + {str(glyph_args[glyph][0])}"), ), ("offset_x", glyph_args[glyph][1]), ("offset_y", glyph_args[glyph][2]), diff --git a/esphome/components/lcd_gpio/display.py b/esphome/components/lcd_gpio/display.py index 6b0a2f69de..9fb635eafa 100644 --- a/esphome/components/lcd_gpio/display.py +++ b/esphome/components/lcd_gpio/display.py @@ -20,8 +20,7 @@ GPIOLCDDisplay = lcd_gpio_ns.class_("GPIOLCDDisplay", lcd_base.LCDDisplay) def validate_pin_length(value): if len(value) != 4 and len(value) != 8: raise cv.Invalid( - "LCD Displays can either operate in 4-pin or 8-pin mode," - "not {}-pin mode".format(len(value)) + f"LCD Displays can either operate in 4-pin or 8-pin mode,not {len(value)}-pin mode" ) return value diff --git a/esphome/components/ledc/output.py b/esphome/components/ledc/output.py index 83b1c6f096..0f5f186ff5 100644 --- a/esphome/components/ledc/output.py +++ b/esphome/components/ledc/output.py @@ -28,13 +28,11 @@ def validate_frequency(value): max_freq = calc_max_frequency(1) if value < min_freq: raise cv.Invalid( - "This frequency setting is not possible, please choose a higher " - "frequency (at least {}Hz)".format(int(min_freq)) + f"This frequency setting is not possible, please choose a higher frequency (at least {int(min_freq)}Hz)" ) if value > max_freq: raise cv.Invalid( - "This frequency setting is not possible, please choose a lower " - "frequency (at most {}Hz)".format(int(max_freq)) + f"This frequency setting is not possible, please choose a lower frequency (at most {int(max_freq)}Hz)" ) return value diff --git a/esphome/components/light/effects.py b/esphome/components/light/effects.py index be558a8140..4b2209c833 100644 --- a/esphome/components/light/effects.py +++ b/esphome/components/light/effects.py @@ -490,8 +490,7 @@ def validate_effects(allowed_effects): if key not in allowed_effects: errors.append( cv.Invalid( - "The effect '{}' is not allowed for this " - "light type".format(key), + f"The effect '{key}' is not allowed for this light type", [i], ) ) @@ -500,8 +499,7 @@ def validate_effects(allowed_effects): if name in names: errors.append( cv.Invalid( - "Found the effect name '{}' twice. All effects must have " - "unique names".format(name), + f"Found the effect name '{name}' twice. All effects must have unique names", [i], ) ) diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index 55178941ec..b821e8c5d8 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -86,8 +86,7 @@ def validate_local_no_higher_than_global(value): for tag, level in value.get(CONF_LOGS, {}).items(): if LOG_LEVEL_SEVERITY.index(level) > LOG_LEVEL_SEVERITY.index(global_level): raise EsphomeError( - "The local log level {} for {} must be less severe than the " - "global log level {}.".format(level, tag, global_level) + f"The local log level {level} for {tag} must be less severe than the global log level {global_level}." ) return value @@ -145,7 +144,7 @@ async def to_code(config): level = config[CONF_LEVEL] cg.add_define("USE_LOGGER") this_severity = LOG_LEVEL_SEVERITY.index(level) - cg.add_build_flag("-DESPHOME_LOG_LEVEL={}".format(LOG_LEVELS[level])) + cg.add_build_flag(f"-DESPHOME_LOG_LEVEL={LOG_LEVELS[level]}") verbose_severity = LOG_LEVEL_SEVERITY.index("VERBOSE") very_verbose_severity = LOG_LEVEL_SEVERITY.index("VERY_VERBOSE") @@ -220,8 +219,7 @@ def validate_printf(value): matches = re.findall(cfmt, value[CONF_FORMAT], flags=re.X) if len(matches) != len(value[CONF_ARGS]): raise cv.Invalid( - "Found {} printf-patterns ({}), but {} args were given!" - "".format(len(matches), ", ".join(matches), len(value[CONF_ARGS])) + f"Found {len(matches)} printf-patterns ({', '.join(matches)}), but {len(value[CONF_ARGS])} args were given!" ) return value diff --git a/esphome/components/mqtt/__init__.py b/esphome/components/mqtt/__init__.py index 19f78641df..73ee50ee65 100644 --- a/esphome/components/mqtt/__init__.py +++ b/esphome/components/mqtt/__init__.py @@ -266,8 +266,7 @@ async def to_code(config): if CONF_SSL_FINGERPRINTS in config: for fingerprint in config[CONF_SSL_FINGERPRINTS]: arr = [ - cg.RawExpression("0x{}".format(fingerprint[i : i + 2])) - for i in range(0, 40, 2) + cg.RawExpression(f"0x{fingerprint[i:i + 2]}") for i in range(0, 40, 2) ] cg.add(var.add_ssl_fingerprint(arr)) cg.add_build_flag("-DASYNC_TCP_SSL_ENABLED=1") @@ -353,9 +352,7 @@ def get_default_topic_for(data, component_type, name, suffix): sanitized_name = "".join( x for x in name.lower().replace(" ", "_") if x in allowlist ) - return "{}/{}/{}/{}".format( - data.topic_prefix, component_type, sanitized_name, suffix - ) + return f"{data.topic_prefix}/{component_type}/{sanitized_name}/{suffix}" async def register_mqtt_component(var, config): diff --git a/esphome/components/neopixelbus/light.py b/esphome/components/neopixelbus/light.py index 5eee5210f9..a86b4e4588 100644 --- a/esphome/components/neopixelbus/light.py +++ b/esphome/components/neopixelbus/light.py @@ -40,7 +40,7 @@ def validate_type(value): raise cv.Invalid("Must have B in type") rest = set(value) - set("RGBW") if rest: - raise cv.Invalid("Type has invalid color: {}".format(", ".join(rest))) + raise cv.Invalid(f"Type has invalid color: {', '.join(rest)}") if len(set(value)) != len(value): raise cv.Invalid("Type has duplicate color!") return value @@ -95,9 +95,7 @@ def validate_method_pin(value): for opt in (CONF_PIN, CONF_CLOCK_PIN, CONF_DATA_PIN): if opt in value and value[opt] not in pins_: raise cv.Invalid( - "Method {} only supports pin(s) {}".format( - method, ", ".join(f"GPIO{x}" for x in pins_) - ), + f"Method {method} only supports pin(s) {', '.join(f'GPIO{x}' for x in pins_)}", path=[CONF_METHOD], ) return value @@ -139,7 +137,7 @@ def format_method(config): if config[CONF_INVERT]: if method == "ESP8266_DMA": - variant = "Inverted" + variant + variant = f"Inverted{variant}" else: variant += "Inverted" diff --git a/esphome/components/nextion/base_component.py b/esphome/components/nextion/base_component.py index ce567bc3f1..75694ee4b2 100644 --- a/esphome/components/nextion/base_component.py +++ b/esphome/components/nextion/base_component.py @@ -30,16 +30,14 @@ CONF_FONT_ID = "font_id" def NextionName(value): - valid_chars = ascii_letters + digits + "." + valid_chars = f"{ascii_letters + digits}." if not isinstance(value, str) or len(value) > 29: raise cv.Invalid("Must be a string less than 29 characters") for char in value: if char not in valid_chars: raise cv.Invalid( - "Must only consist of upper/lowercase characters, numbers and the period '.'. The character '{}' cannot be used.".format( - char - ) + f"Must only consist of upper/lowercase characters, numbers and the period '.'. The character '{char}' cannot be used." ) return value diff --git a/esphome/components/ntc/sensor.py b/esphome/components/ntc/sensor.py index bf819ffd16..660208635c 100644 --- a/esphome/components/ntc/sensor.py +++ b/esphome/components/ntc/sensor.py @@ -105,9 +105,7 @@ def process_calibration(value): a, b, c = calc_steinhart_hart(value) else: raise cv.Invalid( - "Calibration parameter accepts either a list for steinhart-hart " - "calibration, or mapping for b-constant calibration, " - "not {}".format(type(value)) + f"Calibration parameter accepts either a list for steinhart-hart calibration, or mapping for b-constant calibration, not {type(value)}" ) return { diff --git a/esphome/components/packages/__init__.py b/esphome/components/packages/__init__.py index 330ffc2bf2..df0f0de13d 100644 --- a/esphome/components/packages/__init__.py +++ b/esphome/components/packages/__init__.py @@ -151,8 +151,7 @@ def do_packages_pass(config: dict): packages = CONFIG_SCHEMA(packages) if not isinstance(packages, dict): raise cv.Invalid( - "Packages must be a key to value mapping, got {} instead" - "".format(type(packages)) + f"Packages must be a key to value mapping, got {type(packages)} instead" ) for package_name, package_config in packages.items(): diff --git a/esphome/components/partition/light.py b/esphome/components/partition/light.py index 5ded6b906c..06bee2143e 100644 --- a/esphome/components/partition/light.py +++ b/esphome/components/partition/light.py @@ -20,8 +20,7 @@ PartitionLightOutput = partitions_ns.class_( def validate_from_to(value): if value[CONF_FROM] > value[CONF_TO]: raise cv.Invalid( - "From ({}) must not be larger than to ({})" - "".format(value[CONF_FROM], value[CONF_TO]) + f"From ({value[CONF_FROM]}) must not be larger than to ({value[CONF_TO]})" ) return value diff --git a/esphome/components/pmsx003/sensor.py b/esphome/components/pmsx003/sensor.py index 8b9e5c9af2..350117a235 100644 --- a/esphome/components/pmsx003/sensor.py +++ b/esphome/components/pmsx003/sensor.py @@ -63,9 +63,7 @@ SENSORS_TO_TYPE = { def validate_pmsx003_sensors(value): for key, types in SENSORS_TO_TYPE.items(): if key in value and value[CONF_TYPE] not in types: - raise cv.Invalid( - "{} does not have {} sensor!".format(value[CONF_TYPE], key) - ) + raise cv.Invalid(f"{value[CONF_TYPE]} does not have {key} sensor!") return value diff --git a/esphome/components/remote_base/__init__.py b/esphome/components/remote_base/__init__.py index d76dc6bc34..537ae2283c 100644 --- a/esphome/components/remote_base/__init__.py +++ b/esphome/components/remote_base/__init__.py @@ -491,8 +491,7 @@ def validate_raw_alternating(value): if i != 0: if this_negative == last_negative: raise cv.Invalid( - "Values must alternate between being positive and negative, " - "please see index {} and {}".format(i, i + 1), + f"Values must alternate between being positive and negative, please see index {i} and {i + 1}", [i], ) last_negative = this_negative @@ -619,13 +618,11 @@ def validate_rc_switch_code(value): for c in value: if c not in ("0", "1"): raise cv.Invalid( - "Invalid RCSwitch code character '{}'. Only '0' and '1' are allowed" - "".format(c) + f"Invalid RCSwitch code character '{c}'. Only '0' and '1' are allowed" ) if len(value) > 64: raise cv.Invalid( - "Maximum length for RCSwitch codes is 64, code '{}' has length {}" - "".format(value, len(value)) + f"Maximum length for RCSwitch codes is 64, code '{value}' has length {len(value)}" ) if not value: raise cv.Invalid("RCSwitch code must not be empty") @@ -638,14 +635,11 @@ def validate_rc_switch_raw_code(value): for c in value: if c not in ("0", "1", "x"): raise cv.Invalid( - "Invalid RCSwitch raw code character '{}'.Only '0', '1' and 'x' are allowed".format( - c - ) + f"Invalid RCSwitch raw code character '{c}'.Only '0', '1' and 'x' are allowed" ) if len(value) > 64: raise cv.Invalid( - "Maximum length for RCSwitch raw codes is 64, code '{}' has length {}" - "".format(value, len(value)) + f"Maximum length for RCSwitch raw codes is 64, code '{value}' has length {len(value)}" ) if not value: raise cv.Invalid("RCSwitch raw code must not be empty") diff --git a/esphome/components/rotary_encoder/sensor.py b/esphome/components/rotary_encoder/sensor.py index 311e63a7ba..e82b9d5f13 100644 --- a/esphome/components/rotary_encoder/sensor.py +++ b/esphome/components/rotary_encoder/sensor.py @@ -49,8 +49,7 @@ def validate_min_max_value(config): max_val = config[CONF_MAX_VALUE] if min_val >= max_val: raise cv.Invalid( - "Max value {} must be smaller than min value {}" - "".format(max_val, min_val) + f"Max value {max_val} must be smaller than min value {min_val}" ) return config diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index fd278be51e..ce4aafb3b0 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -109,8 +109,7 @@ def validate_send_first_at(value): send_every = value[CONF_SEND_EVERY] if send_first_at is not None and send_first_at > send_every: raise cv.Invalid( - "send_first_at must be smaller than or equal to send_every! {} <= {}" - "".format(send_first_at, send_every) + f"send_first_at must be smaller than or equal to send_every! {send_first_at} <= {send_every}" ) return value @@ -459,8 +458,7 @@ CONF_DEGREE = "degree" def validate_calibrate_polynomial(config): if config[CONF_DEGREE] >= len(config[CONF_DATAPOINTS]): raise cv.Invalid( - "Degree is too high! Maximum possible degree with given datapoints is " - "{}".format(len(config[CONF_DATAPOINTS]) - 1), + f"Degree is too high! Maximum possible degree with given datapoints is {len(config[CONF_DATAPOINTS]) - 1}", [CONF_DEGREE], ) return config diff --git a/esphome/components/substitutions/__init__.py b/esphome/components/substitutions/__init__.py index e7a5e24ee6..0cef417c15 100644 --- a/esphome/components/substitutions/__init__.py +++ b/esphome/components/substitutions/__init__.py @@ -25,8 +25,7 @@ def validate_substitution_key(value): for char in value: if char not in VALID_SUBSTITUTIONS_CHARACTERS: raise cv.Invalid( - "Substitution must only consist of upper/lowercase characters, the underscore " - "and numbers. The character '{}' cannot be used".format(char) + f"Substitution must only consist of upper/lowercase characters, the underscore and numbers. The character '{char}' cannot be used" ) return value @@ -42,6 +41,7 @@ async def to_code(config): pass +# pylint: disable=consider-using-f-string VARIABLE_PROG = re.compile( "\\$([{0}]+|\\{{[{0}]*\\}})".format(VALID_SUBSTITUTIONS_CHARACTERS) ) @@ -133,8 +133,7 @@ def do_substitution_pass(config, command_line_substitutions): with cv.prepend_path("substitutions"): if not isinstance(substitutions, dict): raise cv.Invalid( - "Substitutions must be a key to value mapping, got {}" - "".format(type(substitutions)) + f"Substitutions must be a key to value mapping, got {type(substitutions)}" ) replace_keys = [] diff --git a/esphome/components/time/__init__.py b/esphome/components/time/__init__.py index 4872c89f88..d548575d72 100644 --- a/esphome/components/time/__init__.py +++ b/esphome/components/time/__init__.py @@ -67,9 +67,7 @@ def _week_of_month(dt): def _tz_dst_str(dt): td = datetime.timedelta(hours=dt.hour, minutes=dt.minute, seconds=dt.second) - return "M{}.{}.{}/{}".format( - dt.month, _week_of_month(dt), dt.isoweekday() % 7, _tz_timedelta(td) - ) + return f"M{dt.month}.{_week_of_month(dt)}.{dt.isoweekday() % 7}/{_tz_timedelta(td)}" def _safe_tzname(tz, dt): @@ -88,7 +86,7 @@ def _non_dst_tz(tz, dt): _LOGGER.info( "Detected timezone '%s' with UTC offset %s", tzname, _tz_timedelta(utcoffset) ) - tzbase = "{}{}".format(tzname, _tz_timedelta(-1 * utcoffset)) + tzbase = f"{tzname}{_tz_timedelta(-1 * utcoffset)}" return tzbase @@ -129,14 +127,9 @@ def convert_tz(pytz_obj): dst_ends_utc = transition_times[idx2] dst_ends_local = dst_ends_utc + utcoffset_on - tzbase = "{}{}".format(tzname_off, _tz_timedelta(-1 * utcoffset_off)) + tzbase = f"{tzname_off}{_tz_timedelta(-1 * utcoffset_off)}" - tzext = "{}{},{},{}".format( - tzname_on, - _tz_timedelta(-1 * utcoffset_on), - _tz_dst_str(dst_begins_local), - _tz_dst_str(dst_ends_local), - ) + tzext = f"{tzname_on}{_tz_timedelta(-1 * utcoffset_on)},{_tz_dst_str(dst_begins_local)},{_tz_dst_str(dst_ends_local)}" _LOGGER.info( "Detected timezone '%s' with UTC offset %s and daylight saving time from " "%s to %s", @@ -176,9 +169,7 @@ def _parse_cron_part(part, min_value, max_value, special_mapping): data = part.split("/") if len(data) > 2: raise cv.Invalid( - "Can't have more than two '/' in one time expression, got {}".format( - part - ) + f"Can't have more than two '/' in one time expression, got {part}" ) offset, repeat = data offset_n = 0 @@ -194,18 +185,14 @@ def _parse_cron_part(part, min_value, max_value, special_mapping): except ValueError: # pylint: disable=raise-missing-from raise cv.Invalid( - "Repeat for '/' time expression must be an integer, got {}".format( - repeat - ) + f"Repeat for '/' time expression must be an integer, got {repeat}" ) return set(range(offset_n, max_value + 1, repeat_n)) if "-" in part: data = part.split("-") if len(data) > 2: raise cv.Invalid( - "Can't have more than two '-' in range time expression '{}'".format( - part - ) + f"Can't have more than two '-' in range time expression '{part}'" ) begin, end = data begin_n = _parse_cron_int( @@ -233,13 +220,11 @@ def cron_expression_validator(name, min_value, max_value, special_mapping=None): for v in value: if not isinstance(v, int): raise cv.Invalid( - "Expected integer for {} '{}', got {}".format(v, name, type(v)) + f"Expected integer for {v} '{name}', got {type(v)}" ) if v < min_value or v > max_value: raise cv.Invalid( - "{} {} is out of range (min={} max={}).".format( - name, v, min_value, max_value - ) + f"{name} {v} is out of range (min={min_value} max={max_value})." ) return list(sorted(value)) value = cv.string(value) @@ -295,8 +280,7 @@ def validate_cron_raw(value): value = value.split(" ") if len(value) != 6: raise cv.Invalid( - "Cron expression must consist of exactly 6 space-separated parts, " - "not {}".format(len(value)) + f"Cron expression must consist of exactly 6 space-separated parts, not {len(value)}" ) seconds, minutes, hours, days_of_month, months, days_of_week = value return { diff --git a/esphome/components/wifi/wpa2_eap.py b/esphome/components/wifi/wpa2_eap.py index 071737ccd7..3cb60e6175 100644 --- a/esphome/components/wifi/wpa2_eap.py +++ b/esphome/components/wifi/wpa2_eap.py @@ -57,6 +57,7 @@ def wrapped_load_pem_private_key(value, password): def read_relative_config_path(value): + # pylint: disable=unspecified-encoding return Path(CORE.relative_config_path(value)).read_text() diff --git a/esphome/config.py b/esphome/config.py index de261f7eba..e49293e6b2 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -42,7 +42,7 @@ def iter_components(config): yield domain, component, conf if component.is_platform_component: for p_config in conf: - p_name = "{}.{}".format(domain, p_config[CONF_PLATFORM]) + p_name = f"{domain}.{p_config[CONF_PLATFORM]}" platform = get_platform(domain, p_config[CONF_PLATFORM]) yield p_name, platform, p_config @@ -238,10 +238,7 @@ def do_id_pass(result): # type: (Config) -> None # No declared ID with this name import difflib - error = ( - "Couldn't find ID '{}'. Please check you have defined " - "an ID with that name in your configuration.".format(id.id) - ) + error = f"Couldn't find ID '{id.id}'. Please check you have defined an ID with that name in your configuration." # Find candidates matches = difflib.get_close_matches( id.id, [v[0].id for v in result.declare_ids if v[0].is_manual] @@ -257,9 +254,7 @@ def do_id_pass(result): # type: (Config) -> None continue if not match.type.inherits_from(id.type): result.add_str_error( - "ID '{}' of type {} doesn't inherit from {}. Please " - "double check your ID is pointing to the correct value" - "".format(id.id, match.type, id.type), + f"ID '{id.id}' of type {match.type} doesn't inherit from {id.type}. Please double check your ID is pointing to the correct value", path, ) @@ -497,7 +492,7 @@ def validate_config(config, command_line_substitutions): for dependency in comp.dependencies: if dependency not in config: result.add_str_error( - "Component {} requires component {}" "".format(domain, dependency), + f"Component {domain} requires component {dependency}", path, ) success = False @@ -508,8 +503,7 @@ def validate_config(config, command_line_substitutions): for conflict in comp.conflicts_with: if conflict in config: result.add_str_error( - "Component {} cannot be used together with component {}" - "".format(domain, conflict), + f"Component {domain} cannot be used together with component {conflict}", path, ) success = False @@ -518,7 +512,7 @@ def validate_config(config, command_line_substitutions): if CORE.esp_platform not in comp.esp_platforms: result.add_str_error( - "Component {} doesn't support {}.".format(domain, CORE.esp_platform), + f"Component {domain} doesn't support {CORE.esp_platform}.", path, ) continue @@ -529,8 +523,7 @@ def validate_config(config, command_line_substitutions): and not isinstance(conf, core.AutoLoad) ): result.add_str_error( - "Component {} cannot be loaded via YAML " - "(no CONFIG_SCHEMA).".format(domain), + f"Component {domain} cannot be loaded via YAML (no CONFIG_SCHEMA).", path, ) continue @@ -540,8 +533,7 @@ def validate_config(config, command_line_substitutions): result[domain] = conf = [conf] if not isinstance(comp.multi_conf, bool) and len(conf) > comp.multi_conf: result.add_str_error( - "Component {} supports a maximum of {} " - "entries ({} found).".format(domain, comp.multi_conf, len(conf)), + f"Component {domain} supports a maximum of {comp.multi_conf} entries ({len(conf)} found).", path, ) continue @@ -636,18 +628,14 @@ def _format_vol_invalid(ex, config): if isinstance(ex, ExtraKeysInvalid): if ex.candidates: - message += "[{}] is an invalid option for [{}]. Did you mean {}?".format( - ex.path[-1], paren, ", ".join(f"[{x}]" for x in ex.candidates) - ) + message += f"[{ex.path[-1]}] is an invalid option for [{paren}]. Did you mean {', '.join(f'[{x}]' for x in ex.candidates)}?" else: - message += "[{}] is an invalid option for [{}]. Please check the indentation.".format( - ex.path[-1], paren - ) + message += f"[{ex.path[-1]}] is an invalid option for [{paren}]. Please check the indentation." elif "extra keys not allowed" in str(ex): - message += "[{}] is an invalid option for [{}].".format(ex.path[-1], paren) + message += f"[{ex.path[-1]}] is an invalid option for [{paren}]." elif isinstance(ex, vol.RequiredFieldInvalid): if ex.msg == "required key not provided": - message += "'{}' is a required option for [{}].".format(ex.path[-1], paren) + message += f"'{ex.path[-1]}' is a required option for [{paren}]." else: # Required has set a custom error message message += ex.msg @@ -700,7 +688,7 @@ def line_info(config, path, highlight=True): obj = config.get_deepest_document_range_for_path(path) if obj: mark = obj.start_mark - source = "[source {}:{}]".format(mark.document, mark.line + 1) + source = f"[source {mark.document}:{mark.line + 1}]" return color(Fore.CYAN, source) return "None" @@ -724,9 +712,7 @@ def dump_dict(config, path, at_root=True): if at_root: error = config.get_error_for_path(path) if error is not None: - ret += ( - "\n" + color(Fore.BOLD_RED, _format_vol_invalid(error, config)) + "\n" - ) + ret += f"\n{color(Fore.BOLD_RED, _format_vol_invalid(error, config))}\n" if isinstance(conf, (list, tuple)): multiline = True @@ -738,11 +724,7 @@ def dump_dict(config, path, at_root=True): path_ = path + [i] error = config.get_error_for_path(path_) if error is not None: - ret += ( - "\n" - + color(Fore.BOLD_RED, _format_vol_invalid(error, config)) - + "\n" - ) + ret += f"\n{color(Fore.BOLD_RED, _format_vol_invalid(error, config))}\n" sep = "- " if config.is_in_error_path(path_): @@ -751,10 +733,10 @@ def dump_dict(config, path, at_root=True): msg = indent(msg) inf = line_info(config, path_, highlight=config.is_in_error_path(path_)) if inf is not None: - msg = inf + "\n" + msg + msg = f"{inf}\n{msg}" elif msg: msg = msg[2:] - ret += sep + msg + "\n" + ret += f"{sep + msg}\n" elif isinstance(conf, dict): multiline = True if not conf: @@ -765,11 +747,7 @@ def dump_dict(config, path, at_root=True): path_ = path + [k] error = config.get_error_for_path(path_) if error is not None: - ret += ( - "\n" - + color(Fore.BOLD_RED, _format_vol_invalid(error, config)) - + "\n" - ) + ret += f"\n{color(Fore.BOLD_RED, _format_vol_invalid(error, config))}\n" st = f"{k}: " if config.is_in_error_path(path_): @@ -778,30 +756,30 @@ def dump_dict(config, path, at_root=True): inf = line_info(config, path_, highlight=config.is_in_error_path(path_)) if m: - msg = "\n" + indent(msg) + msg = f"\n{indent(msg)}" if inf is not None: if m: - msg = " " + inf + msg + msg = f" {inf}{msg}" else: - msg = msg + " " + inf - ret += st + msg + "\n" + msg = f"{msg} {inf}" + ret += f"{st + msg}\n" elif isinstance(conf, str): if is_secret(conf): - conf = "!secret {}".format(is_secret(conf)) + conf = f"!secret {is_secret(conf)}" if not conf: conf += "''" if len(conf) > 80: - conf = "|-\n" + indent(conf) + conf = f"|-\n{indent(conf)}" error = config.get_error_for_path(path) col = Fore.BOLD_RED if error else Fore.KEEP ret += color(col, str(conf)) elif isinstance(conf, core.Lambda): if is_secret(conf): - conf = "!secret {}".format(is_secret(conf)) + conf = f"!secret {is_secret(conf)}" - conf = "!lambda |-\n" + indent(str(conf.value)) + conf = f"!lambda |-\n{indent(str(conf.value))}" error = config.get_error_for_path(path) col = Fore.BOLD_RED if error else Fore.KEEP ret += color(col, conf) @@ -860,7 +838,7 @@ def read_config(command_line_substitutions): errstr = color(Fore.BOLD_RED, f"{domain}:") errline = line_info(res, path) if errline: - errstr += " " + errline + errstr += f" {errline}" safe_print(errstr) safe_print(indent(dump_dict(res, path)[0])) return None diff --git a/esphome/config_validation.py b/esphome/config_validation.py index fb659c41ea..a864c65dc7 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -277,8 +277,7 @@ def string_strict(value): if isinstance(value, str): return value raise Invalid( - "Must be string, got {}. did you forget putting quotes " - "around the value?".format(type(value)) + f"Must be string, got {type(value)}. did you forget putting quotes around the value?" ) @@ -311,8 +310,7 @@ def boolean(value): if value in ("false", "no", "off", "disable"): return False raise Invalid( - "Expected boolean value, but cannot convert {} to a boolean. " - "Please use 'true' or 'false'".format(value) + f"Expected boolean value, but cannot convert {value} to a boolean. Please use 'true' or 'false'" ) @@ -358,8 +356,7 @@ def int_(value): if int(value) == value: return int(value) raise Invalid( - "This option only accepts integers with no fractional part. Please remove " - "the fractional part from {}".format(value) + f"This option only accepts integers with no fractional part. Please remove the fractional part from {value}" ) value = string_strict(value).lower() base = 10 @@ -424,20 +421,17 @@ def validate_id_name(value): raise Invalid( "Dashes are not supported in IDs, please use underscores instead." ) - valid_chars = ascii_letters + digits + "_" + valid_chars = f"{ascii_letters + digits}_" for char in value: if char not in valid_chars: raise Invalid( - "IDs must only consist of upper/lowercase characters, the underscore" - "character and numbers. The character '{}' cannot be used" - "".format(char) + f"IDs must only consist of upper/lowercase characters, the underscorecharacter and numbers. The character '{char}' cannot be used" ) if value in RESERVED_IDS: raise Invalid(f"ID '{value}' is reserved internally and cannot be used") if value in CORE.loaded_integrations: raise Invalid( - "ID '{}' conflicts with the name of an esphome integration, please use " - "another ID name.".format(value) + f"ID '{value}' conflicts with the name of an esphome integration, please use another ID name." ) return value @@ -525,7 +519,7 @@ def has_at_least_one_key(*keys): raise Invalid("expected dictionary") if not any(k in keys for k in obj): - raise Invalid("Must contain at least one of {}.".format(", ".join(keys))) + raise Invalid(f"Must contain at least one of {', '.join(keys)}.") return obj return validate @@ -540,9 +534,9 @@ def has_exactly_one_key(*keys): number = sum(k in keys for k in obj) if number > 1: - raise Invalid("Cannot specify more than one of {}.".format(", ".join(keys))) + raise Invalid(f"Cannot specify more than one of {', '.join(keys)}.") if number < 1: - raise Invalid("Must contain exactly one of {}.".format(", ".join(keys))) + raise Invalid(f"Must contain exactly one of {', '.join(keys)}.") return obj return validate @@ -557,7 +551,7 @@ def has_at_most_one_key(*keys): number = sum(k in keys for k in obj) if number > 1: - raise Invalid("Cannot specify more than one of {}.".format(", ".join(keys))) + raise Invalid(f"Cannot specify more than one of {', '.join(keys)}.") return obj return validate @@ -572,9 +566,7 @@ def has_none_or_all_keys(*keys): number = sum(k in keys for k in obj) if number != 0 and number != len(keys): - raise Invalid( - "Must specify either none or all of {}.".format(", ".join(keys)) - ) + raise Invalid(f"Must specify either none or all of {', '.join(keys)}.") return obj return validate @@ -632,8 +624,7 @@ def time_period_str_unit(value): if isinstance(value, int): raise Invalid( - "Don't know what '{0}' means as it has no time *unit*! Did you mean " - "'{0}s'?".format(value) + f"Don't know what '{value}' means as it has no time *unit*! Did you mean '{value}s'?" ) if isinstance(value, TimePeriod): value = str(value) @@ -659,7 +650,7 @@ def time_period_str_unit(value): match = re.match(r"^([-+]?[0-9]*\.?[0-9]*)\s*(\w*)$", value) if match is None: - raise Invalid("Expected time period with unit, " "got {}".format(value)) + raise Invalid(f"Expected time period with unit, got {value}") kwarg = unit_to_kwarg[one_of(*unit_to_kwarg)(match.group(2))] return TimePeriod(**{kwarg: float(match.group(1))}) @@ -796,7 +787,7 @@ METRIC_SUFFIXES = { def float_with_unit(quantity, regex_suffix, optional_unit=False): pattern = re.compile( - r"^([-+]?[0-9]*\.?[0-9]*)\s*(\w*?)" + regex_suffix + r"$", re.UNICODE + f"^([-+]?[0-9]*\\.?[0-9]*)\\s*(\\w*?){regex_suffix}$", re.UNICODE ) def validator(value): @@ -812,7 +803,7 @@ def float_with_unit(quantity, regex_suffix, optional_unit=False): mantissa = float(match.group(1)) if match.group(2) not in METRIC_SUFFIXES: - raise Invalid("Invalid {} suffix {}".format(quantity, match.group(2))) + raise Invalid(f"Invalid {quantity} suffix {match.group(2)}") multiplier = METRIC_SUFFIXES[match.group(2)] return mantissa * multiplier @@ -879,12 +870,11 @@ def validate_bytes(value): mantissa = int(match.group(1)) if match.group(2) not in METRIC_SUFFIXES: - raise Invalid("Invalid metric suffix {}".format(match.group(2))) + raise Invalid(f"Invalid metric suffix {match.group(2)}") multiplier = METRIC_SUFFIXES[match.group(2)] if multiplier < 1: raise Invalid( - "Only suffixes with positive exponents are supported. " - "Got {}".format(match.group(2)) + f"Only suffixes with positive exponents are supported. Got {match.group(2)}" ) return int(mantissa * multiplier) @@ -1184,10 +1174,8 @@ def one_of(*values, **kwargs): option = str(value) matches = difflib.get_close_matches(option, options_) if matches: - raise Invalid( - "Unknown value '{}', did you mean {}?" - "".format(value, ", ".join(f"'{x}'" for x in matches)) - ) + matches_str = ", ".join(f"'{x}'" for x in matches) + raise Invalid(f"Unknown value '{value}', did you mean {matches_str}?") raise Invalid(f"Unknown value '{value}', valid options are {options}.") return value @@ -1229,13 +1217,10 @@ def lambda_(value): entity_id_parts = re.split(LAMBDA_ENTITY_ID_PROG, value.value) if len(entity_id_parts) != 1: entity_ids = " ".join( - "'{}'".format(entity_id_parts[i]) for i in range(1, len(entity_id_parts), 2) + f"'{entity_id_parts[i]}'" for i in range(1, len(entity_id_parts), 2) ) raise Invalid( - "Lambda contains reference to entity-id-style ID {}. " - "The id() wrapper only works for ESPHome-internal types. For importing " - "states from Home Assistant use the 'homeassistant' sensor platforms." - "".format(entity_ids) + f"Lambda contains reference to entity-id-style ID {entity_ids}. The id() wrapper only works for ESPHome-internal types. For importing states from Home Assistant use the 'homeassistant' sensor platforms." ) return value @@ -1259,9 +1244,7 @@ def returning_lambda(value): def dimensions(value): if isinstance(value, list): if len(value) != 2: - raise Invalid( - "Dimensions must have a length of two, not {}".format(len(value)) - ) + raise Invalid(f"Dimensions must have a length of two, not {len(value)}") try: width, height = int(value[0]), int(value[1]) except ValueError: @@ -1301,19 +1284,16 @@ def directory(value): if data["content"]: return value raise Invalid( - "Could not find directory '{}'. Please make sure it exists (full path: {})." - "".format(path, os.path.abspath(path)) + f"Could not find directory '{path}'. Please make sure it exists (full path: {os.path.abspath(path)})." ) if not os.path.exists(path): raise Invalid( - "Could not find directory '{}'. Please make sure it exists (full path: {})." - "".format(path, os.path.abspath(path)) + f"Could not find directory '{path}'. Please make sure it exists (full path: {os.path.abspath(path)})." ) if not os.path.isdir(path): raise Invalid( - "Path '{}' is not a directory (full path: {})." - "".format(path, os.path.abspath(path)) + f"Path '{path}' is not a directory (full path: {os.path.abspath(path)})." ) return value @@ -1340,19 +1320,16 @@ def file_(value): if data["content"]: return value raise Invalid( - "Could not find file '{}'. Please make sure it exists (full path: {})." - "".format(path, os.path.abspath(path)) + f"Could not find file '{path}'. Please make sure it exists (full path: {os.path.abspath(path)})." ) if not os.path.exists(path): raise Invalid( - "Could not find file '{}'. Please make sure it exists (full path: {})." - "".format(path, os.path.abspath(path)) + f"Could not find file '{path}'. Please make sure it exists (full path: {os.path.abspath(path)})." ) if not os.path.isfile(path): raise Invalid( - "Path '{}' is not a file (full path: {})." - "".format(path, os.path.abspath(path)) + f"Path '{path}' is not a file (full path: {os.path.abspath(path)})." ) return value @@ -1405,7 +1382,7 @@ def typed_schema(schemas, **kwargs): value = value.copy() schema_option = value.pop(key, default_schema_option) if schema_option is None: - raise Invalid(key + " not specified!") + raise Invalid(f"{key} not specified!") key_v = key_validator(schema_option) value = schemas[key_v](value) value[key] = key_v @@ -1498,8 +1475,7 @@ def validate_registry_entry(name, registry): value = {value: {}} if not isinstance(value, dict): raise Invalid( - "{} must consist of key-value mapping! Got {}" - "".format(name.title(), value) + f"{name.title()} must consist of key-value mapping! Got {value}" ) key = next((x for x in value if x not in ignore_keys), None) if key is None: @@ -1509,9 +1485,8 @@ def validate_registry_entry(name, registry): key2 = next((x for x in value if x != key and x not in ignore_keys), None) if key2 is not None: raise Invalid( - "Cannot have two {0}s in one item. Key '{1}' overrides '{2}'! " - "Did you forget to indent the block inside the {0}?" - "".format(name, key, key2) + f"Cannot have two {name}s in one item. Key '{key}' overrides '{key2}'! " + f"Did you forget to indent the block inside the {key}?" ) if value[key] is None: diff --git a/esphome/core/__init__.py b/esphome/core/__init__.py index c45d0969e1..e89f64d14c 100644 --- a/esphome/core/__init__.py +++ b/esphome/core/__init__.py @@ -624,8 +624,7 @@ class EsphomeCore: expression = statement(expression) if not isinstance(expression, Statement): raise ValueError( - "Add '{}' must be expression or statement, not {}" - "".format(expression, type(expression)) + f"Add '{expression}' must be expression or statement, not {type(expression)}" ) self.main_statements.append(expression) @@ -639,8 +638,7 @@ class EsphomeCore: expression = statement(expression) if not isinstance(expression, Statement): raise ValueError( - "Add '{}' must be expression or statement, not {}" - "".format(expression, type(expression)) + f"Add '{expression}' must be expression or statement, not {type(expression)}" ) self.global_statements.append(expression) _LOGGER.debug("Adding global: %s", expression) @@ -649,8 +647,7 @@ class EsphomeCore: def add_library(self, library): if not isinstance(library, Library): raise ValueError( - "Library {} must be instance of Library, not {}" - "".format(library, type(library)) + f"Library {library} must be instance of Library, not {type(library)}" ) for other in self.libraries[:]: if other.name != library.name or other.name is None or library.name is None: @@ -660,9 +657,8 @@ class EsphomeCore: # Other is using a/the same repository, takes precendence break raise ValueError( - "Adding named Library with repository failed! Libraries {} and {} " + f"Adding named Library with repository failed! Libraries {library} and {other} " "requested with conflicting repositories!" - "".format(library, other) ) if library.repository is not None: @@ -681,9 +677,8 @@ class EsphomeCore: break raise ValueError( - "Version pinning failed! Libraries {} and {} " + f"Version pinning failed! Libraries {library} and {other} " "requested with conflicting versions!" - "".format(library, other) ) else: _LOGGER.debug("Adding library: %s", library) @@ -702,8 +697,7 @@ class EsphomeCore: pass else: raise ValueError( - "Define {} must be string or Define, not {}" - "".format(define, type(define)) + f"Define {define} must be string or Define, not {type(define)}" ) self.defines.add(define) _LOGGER.debug("Adding define: %s", define) diff --git a/esphome/core/config.py b/esphome/core/config.py index 45b8809f1e..222a775ee6 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -61,9 +61,7 @@ def validate_board(value: str): if value not in boardlist: raise cv.Invalid( - "Could not find board '{}'. Valid boards are {}".format( - value, ", ".join(sorted(boardlist)) - ) + f"Could not find board '{value}'. Valid boards are {', '.join(sorted(boardlist))}" ) return value @@ -102,9 +100,7 @@ def validate_arduino_version(value): and value_ not in PLATFORMIO_ESP8266_LUT ): raise cv.Invalid( - "Unfortunately the arduino framework version '{}' is unsupported " - "at this time. You can override this by manually using " - "espressif8266@".format(value) + f"Unfortunately the arduino framework version '{value}' is unsupported at this time. You can override this by manually using espressif8266@" ) if value_ in PLATFORMIO_ESP8266_LUT: return PLATFORMIO_ESP8266_LUT[value_] @@ -115,9 +111,7 @@ def validate_arduino_version(value): and value_ not in PLATFORMIO_ESP32_LUT ): raise cv.Invalid( - "Unfortunately the arduino framework version '{}' is unsupported " - "at this time. You can override this by manually using " - "espressif32@".format(value) + f"Unfortunately the arduino framework version '{value}' is unsupported at this time. You can override this by manually using espressif32@" ) if value_ in PLATFORMIO_ESP32_LUT: return PLATFORMIO_ESP32_LUT[value_] @@ -141,8 +135,7 @@ def valid_include(value): _, ext = os.path.splitext(value) if ext not in VALID_INCLUDE_EXTS: raise cv.Invalid( - "Include has invalid file extension {} - valid extensions are {}" - "".format(ext, ", ".join(VALID_INCLUDE_EXTS)) + f"Include has invalid file extension {ext} - valid extensions are {', '.join(VALID_INCLUDE_EXTS)}" ) return value diff --git a/esphome/cpp_generator.py b/esphome/cpp_generator.py index eda378e5eb..0442e2633b 100644 --- a/esphome/cpp_generator.py +++ b/esphome/cpp_generator.py @@ -182,7 +182,7 @@ class ArrayInitializer(Expression): cpp += f" {arg},\n" cpp += "}" else: - cpp = "{" + ", ".join(str(arg) for arg in self.args) + "}" + cpp = f"{{{', '.join(str(arg) for arg in self.args)}}}" return cpp @@ -348,13 +348,11 @@ def safe_exp(obj: SafeExpType) -> Expression: return float_ if isinstance(obj, ID): raise ValueError( - "Object {} is an ID. Did you forget to register the variable?" - "".format(obj) + f"Object {obj} is an ID. Did you forget to register the variable?" ) if inspect.isgenerator(obj): raise ValueError( - "Object {} is a coroutine. Did you forget to await the expression with " - "'await'?".format(obj) + f"Object {obj} is a coroutine. Did you forget to await the expression with 'await'?" ) raise ValueError("Object is not an expression", obj) @@ -703,7 +701,7 @@ class MockObj(Expression): return str(self.base) def __repr__(self): - return "MockObj<{}>".format(str(self.base)) + return f"MockObj<{str(self.base)}>" @property def _(self) -> "MockObj": @@ -761,7 +759,7 @@ class MockObjEnum(MockObj): self._is_class = kwargs.pop("is_class") base = kwargs.pop("base") if self._is_class: - base = base + "::" + self._enum + base = f"{base}::{self._enum}" kwargs["op"] = "::" kwargs["base"] = base MockObj.__init__(self, *args, **kwargs) diff --git a/esphome/cpp_helpers.py b/esphome/cpp_helpers.py index 7912e4ae06..33fb485c2b 100644 --- a/esphome/cpp_helpers.py +++ b/esphome/cpp_helpers.py @@ -52,9 +52,7 @@ async def register_component(var, config): id_ = str(var.base) if id_ not in CORE.component_ids: raise ValueError( - "Component ID {} was not declared to inherit from Component, " - "or was registered twice. Please create a bug report with your " - "configuration.".format(id_) + f"Component ID {id_} was not declared to inherit from Component, or was registered twice. Please create a bug report with your configuration." ) CORE.component_ids.remove(id_) if CONF_SETUP_PRIORITY in config: diff --git a/esphome/dashboard/dashboard.py b/esphome/dashboard/dashboard.py index 97f9d60693..38689675af 100644 --- a/esphome/dashboard/dashboard.py +++ b/esphome/dashboard/dashboard.py @@ -368,7 +368,7 @@ class WizardRequestHandler(BaseHandler): if k in ("name", "platform", "board", "ssid", "psk", "password") } kwargs["ota_password"] = secrets.token_hex(16) - destination = settings.rel_path(kwargs["name"] + ".yaml") + destination = settings.rel_path(f"{kwargs['name']}.yaml") wizard.wizard_write(path=destination, **kwargs) self.set_status(200) self.finish() @@ -512,7 +512,7 @@ class MDNSStatusThread(threading.Thread): while not STOP_EVENT.is_set(): entries = _list_dashboard_entries() stat.request_query( - {entry.filename: entry.name + ".local." for entry in entries} + {entry.filename: f"{entry.name}.local." for entry in entries} ) PING_REQUEST.wait() @@ -795,27 +795,27 @@ def make_app(debug=get_bool_env(ENV_DEV)): rel = settings.relative_url app = tornado.web.Application( [ - (rel + "", MainRequestHandler), - (rel + "login", LoginHandler), - (rel + "logout", LogoutHandler), - (rel + "logs", EsphomeLogsHandler), - (rel + "upload", EsphomeUploadHandler), - (rel + "compile", EsphomeCompileHandler), - (rel + "validate", EsphomeValidateHandler), - (rel + "clean-mqtt", EsphomeCleanMqttHandler), - (rel + "clean", EsphomeCleanHandler), - (rel + "vscode", EsphomeVscodeHandler), - (rel + "ace", EsphomeAceEditorHandler), - (rel + "update-all", EsphomeUpdateAllHandler), - (rel + "info", InfoRequestHandler), - (rel + "edit", EditRequestHandler), - (rel + "download.bin", DownloadBinaryRequestHandler), - (rel + "serial-ports", SerialPortRequestHandler), - (rel + "ping", PingRequestHandler), - (rel + "delete", DeleteRequestHandler), - (rel + "undo-delete", UndoDeleteRequestHandler), - (rel + "wizard.html", WizardRequestHandler), - (rel + r"static/(.*)", StaticFileHandler, {"path": get_static_path()}), + (f"{rel}", MainRequestHandler), + (f"{rel}login", LoginHandler), + (f"{rel}logout", LogoutHandler), + (f"{rel}logs", EsphomeLogsHandler), + (f"{rel}upload", EsphomeUploadHandler), + (f"{rel}compile", EsphomeCompileHandler), + (f"{rel}validate", EsphomeValidateHandler), + (f"{rel}clean-mqtt", EsphomeCleanMqttHandler), + (f"{rel}clean", EsphomeCleanHandler), + (f"{rel}vscode", EsphomeVscodeHandler), + (f"{rel}ace", EsphomeAceEditorHandler), + (f"{rel}update-all", EsphomeUpdateAllHandler), + (f"{rel}info", InfoRequestHandler), + (f"{rel}edit", EditRequestHandler), + (f"{rel}download.bin", DownloadBinaryRequestHandler), + (f"{rel}serial-ports", SerialPortRequestHandler), + (f"{rel}ping", PingRequestHandler), + (f"{rel}delete", DeleteRequestHandler), + (f"{rel}undo-delete", UndoDeleteRequestHandler), + (f"{rel}wizard.html", WizardRequestHandler), + (f"{rel}static/(.*)", StaticFileHandler, {"path": get_static_path()}), ], **app_settings, ) diff --git a/esphome/espota2.py b/esphome/espota2.py index 351f6feda9..f8a2fab94c 100644 --- a/esphome/espota2.py +++ b/esphome/espota2.py @@ -52,9 +52,7 @@ class ProgressBar: return self.last_progress = new_progress block = int(round(bar_length * progress)) - text = "\rUploading: [{0}] {1}% {2}".format( - "=" * block + " " * (bar_length - block), new_progress, status - ) + text = f"\rUploading: [{'=' * block + ' ' * (bar_length - block)}] {new_progress}% {status}" sys.stderr.write(text) sys.stderr.flush() @@ -154,7 +152,7 @@ def check_error(data, expect): if not isinstance(expect, (list, tuple)): expect = [expect] if dat not in expect: - raise OTAError("Unexpected response from ESP: 0x{:02X}".format(data[0])) + raise OTAError(f"Unexpected response from ESP: 0x{data[0]:02X}") def send_check(sock, data, msg): diff --git a/esphome/helpers.py b/esphome/helpers.py index a1cb4367c5..3420b2cc12 100644 --- a/esphome/helpers.py +++ b/esphome/helpers.py @@ -54,7 +54,7 @@ def cpp_string_escape(string, encoding="utf-8"): result += f"\\{character:03o}" else: result += chr(character) - return '"' + result + '"' + return f'"{result}"' def run_system_command(*args): @@ -107,7 +107,7 @@ def _resolve_with_zeroconf(host): "host network mode?" ) from err try: - info = zc.resolve_host(host + ".") + info = zc.resolve_host(f"{host}.") except Exception as err: raise EsphomeError(f"Error resolving mDNS hostname: {err}") from err finally: @@ -136,9 +136,7 @@ def resolve_ip_address(host): return socket.gethostbyname(host) except OSError as err: errs.append(str(err)) - raise EsphomeError( - "Error resolving IP address: {}" "".format(", ".join(errs)) - ) from err + raise EsphomeError(f"Error resolving IP address: {', '.join(errs)}") from err def get_bool_env(var, default=False): diff --git a/esphome/mqtt.py b/esphome/mqtt.py index 9be87b5c5d..07602e8ced 100644 --- a/esphome/mqtt.py +++ b/esphome/mqtt.py @@ -104,9 +104,9 @@ def show_logs(config, topic=None, username=None, password=None, client_id=None): if CONF_LOG_TOPIC in conf: topic = config[CONF_MQTT][CONF_LOG_TOPIC][CONF_TOPIC] elif CONF_TOPIC_PREFIX in config[CONF_MQTT]: - topic = config[CONF_MQTT][CONF_TOPIC_PREFIX] + "/debug" + topic = f"{config[CONF_MQTT][CONF_TOPIC_PREFIX]}/debug" else: - topic = config[CONF_ESPHOME][CONF_NAME] + "/debug" + topic = f"{config[CONF_ESPHOME][CONF_NAME]}/debug" else: _LOGGER.error("MQTT isn't setup, can't start MQTT logs") return 1 @@ -158,9 +158,8 @@ def get_fingerprint(config): sha1 = hashlib.sha1(cert_der).hexdigest() - safe_print("SHA1 Fingerprint: " + color(Fore.CYAN, sha1)) + safe_print(f"SHA1 Fingerprint: {color(Fore.CYAN, sha1)}") safe_print( - "Copy the string above into mqtt.ssl_fingerprints section of {}" - "".format(CORE.config_path) + f"Copy the string above into mqtt.ssl_fingerprints section of {CORE.config_path}" ) return 0 diff --git a/esphome/pins.py b/esphome/pins.py index ff4ed9d9c1..c717424ff3 100644 --- a/esphome/pins.py +++ b/esphome/pins.py @@ -77,8 +77,7 @@ def validate_gpio_pin(value): raise cv.Invalid(f"ESP32-C3: Invalid pin number: {value}") if value in _ESP32C3_SDIO_PINS: raise cv.Invalid( - "This pin cannot be used on ESP32-C3s and is already used by " - "the flash interface (function: {})".format(_ESP_SDIO_PINS[value]) + f"This pin cannot be used on ESP32-C3s and is already used by the flash interface (function: {_ESP_SDIO_PINS[value]})" ) return value if CORE.is_esp32: @@ -86,8 +85,7 @@ def validate_gpio_pin(value): raise cv.Invalid(f"ESP32: Invalid pin number: {value}") if value in _ESP_SDIO_PINS: raise cv.Invalid( - "This pin cannot be used on ESP32s and is already used by " - "the flash interface (function: {})".format(_ESP_SDIO_PINS[value]) + f"This pin cannot be used on ESP32s and is already used by the flash interface (function: {_ESP_SDIO_PINS[value]})" ) if 9 <= value <= 10: _LOGGER.warning( @@ -105,8 +103,7 @@ def validate_gpio_pin(value): raise cv.Invalid(f"ESP8266: Invalid pin number: {value}") if value in _ESP_SDIO_PINS: raise cv.Invalid( - "This pin cannot be used on ESP8266s and is already used by " - "the flash interface (function: {})".format(_ESP_SDIO_PINS[value]) + f"This pin cannot be used on ESP8266s and is already used by the flash interface (function: {_ESP_SDIO_PINS[value]})" ) if 9 <= value <= 10: _LOGGER.warning( @@ -144,8 +141,7 @@ def output_pin(value): if CORE.is_esp32: if 34 <= value <= 39: raise cv.Invalid( - "ESP32: GPIO{} (34-39) can only be used as an " - "input pin.".format(value) + f"ESP32: GPIO{value} (34-39) can only be used as an input pin." ) return value if CORE.is_esp8266: @@ -278,8 +274,7 @@ def validate_has_interrupt(value): if CORE.is_esp8266: if value[CONF_NUMBER] >= 16: raise cv.Invalid( - "Pins GPIO16 and GPIO17 do not support interrupts and cannot be used " - "here, got {}".format(value[CONF_NUMBER]) + f"Pins GPIO16 and GPIO17 do not support interrupts and cannot be used here, got {value[CONF_NUMBER]}" ) return value diff --git a/esphome/platformio_api.py b/esphome/platformio_api.py index 3d7e9a6d48..6506a44e88 100644 --- a/esphome/platformio_api.py +++ b/esphome/platformio_api.py @@ -39,7 +39,7 @@ def patch_structhash(): command.clean_build_dir = patched_clean_build_dir -IGNORE_LIB_WARNINGS = r"(?:" + "|".join(["Hash", "Update"]) + r")" +IGNORE_LIB_WARNINGS = f"(?:{'|'.join(['Hash', 'Update'])})" FILTER_PLATFORMIO_LINES = [ r"Verbose mode can be enabled via `-v, --verbose` option.*", r"CONFIGURATION: https://docs.platformio.org/.*", @@ -48,13 +48,9 @@ FILTER_PLATFORMIO_LINES = [ r"PACKAGES: .*", r"LDF: Library Dependency Finder -> http://bit.ly/configure-pio-ldf.*", r"LDF Modes: Finder ~ chain, Compatibility ~ soft.*", - r"Looking for " + IGNORE_LIB_WARNINGS + r" library in registry", - r"Warning! Library `.*'" - + IGNORE_LIB_WARNINGS - + r".*` has not been found in PlatformIO Registry.", - r"You can ignore this message, if `.*" - + IGNORE_LIB_WARNINGS - + r".*` is a built-in library.*", + f"Looking for {IGNORE_LIB_WARNINGS} library in registry", + f"Warning! Library `.*'{IGNORE_LIB_WARNINGS}.*` has not been found in PlatformIO Registry.", + f"You can ignore this message, if `.*{IGNORE_LIB_WARNINGS}.*` is a built-in library.*", r"Scanning dependencies...", r"Found \d+ compatible libraries", r"Memory Usage -> http://bit.ly/pio-memory-usage", @@ -296,6 +292,6 @@ class IDEData: # Windows if cc_path.endswith(".exe"): - return cc_path[:-7] + "addr2line.exe" + return f"{cc_path[:-7]}addr2line.exe" - return cc_path[:-3] + "addr2line" + return f"{cc_path[:-3]}addr2line" diff --git a/esphome/storage_json.py b/esphome/storage_json.py index c0fbc6edf7..517bb508ba 100644 --- a/esphome/storage_json.py +++ b/esphome/storage_json.py @@ -94,7 +94,7 @@ class StorageJSON: } def to_json(self): - return json.dumps(self.as_dict(), indent=2) + "\n" + return f"{json.dumps(self.as_dict(), indent=2)}\n" def save(self, path): write_file_if_changed(path, self.to_json()) @@ -214,7 +214,7 @@ class EsphomeStorageJSON: self.last_update_check_str = new.strftime("%Y-%m-%dT%H:%M:%S") def to_json(self): # type: () -> dict - return json.dumps(self.as_dict(), indent=2) + "\n" + return f"{json.dumps(self.as_dict(), indent=2)}\n" def save(self, path): # type: (str) -> None write_file_if_changed(path, self.to_json()) diff --git a/esphome/wizard.py b/esphome/wizard.py index 3f989dd93d..abb17e3119 100644 --- a/esphome/wizard.py +++ b/esphome/wizard.py @@ -74,19 +74,20 @@ def wizard_file(**kwargs): # Configure API if "password" in kwargs: - config += ' password: "{0}"\n'.format(kwargs["password"]) + config += f" password: \"{kwargs['password']}\"\n" # Configure OTA config += "\nota:\n" if "ota_password" in kwargs: - config += ' password: "{0}"'.format(kwargs["ota_password"]) + config += f" password: \"{kwargs['ota_password']}\"" elif "password" in kwargs: - config += ' password: "{0}"'.format(kwargs["password"]) + config += f" password: \"{kwargs['password']}\"" # Configuring wifi config += "\n\nwifi:\n" if "ssid" in kwargs: + # pylint: disable=consider-using-f-string config += """ ssid: "{ssid}" password: "{psk}" """.format( @@ -99,6 +100,7 @@ def wizard_file(**kwargs): networks: """ + # pylint: disable=consider-using-f-string config += """ # Enable fallback hotspot (captive portal) in case wifi connection fails ap: @@ -126,7 +128,7 @@ def wizard_write(path, **kwargs): platform = kwargs["platform"] write_file(path, wizard_file(**kwargs)) - storage = StorageJSON.from_wizard(name, name + ".local", platform, board) + storage = StorageJSON.from_wizard(name, f"{name}.local", platform, board) storage_path = ext_storage_path(os.path.dirname(path), os.path.basename(path)) storage.save(storage_path) @@ -168,14 +170,12 @@ def strip_accents(value): def wizard(path): if not path.endswith(".yaml") and not path.endswith(".yml"): safe_print( - "Please make your configuration file {} have the extension .yaml or .yml" - "".format(color(Fore.CYAN, path)) + f"Please make your configuration file {color(Fore.CYAN, path)} have the extension .yaml or .yml" ) return 1 if os.path.exists(path): safe_print( - "Uh oh, it seems like {} already exists, please delete that file first " - "or chose another configuration file.".format(color(Fore.CYAN, path)) + f"Uh oh, it seems like {color(Fore.CYAN, path)} already exists, please delete that file first or chose another configuration file." ) return 2 safe_print("Hi there!") @@ -191,17 +191,13 @@ def wizard(path): sleep(3.0) safe_print() safe_print_step(1, CORE_BIG) - safe_print( - "First up, please choose a " + color(Fore.GREEN, "name") + " for your node." - ) + safe_print(f"First up, please choose a {color(Fore.GREEN, 'name')} for your node.") safe_print( "It should be a unique name that can be used to identify the device later." ) sleep(1) safe_print( - "For example, I like calling the node in my living room {}.".format( - color(Fore.BOLD_WHITE, "livingroom") - ) + f"For example, I like calling the node in my living room {color(Fore.BOLD_WHITE, 'livingroom')}." ) safe_print() sleep(1) @@ -222,13 +218,11 @@ def wizard(path): name = strip_accents(name).lower().replace(" ", "-") name = strip_accents(name).lower().replace("_", "-") name = "".join(c for c in name if c in ALLOWED_NAME_CHARS) - safe_print( - 'Shall I use "{}" as the name instead?'.format(color(Fore.CYAN, name)) - ) + safe_print(f'Shall I use "{color(Fore.CYAN, name)}" as the name instead?') sleep(0.5) name = default_input("(name [{}]): ", name) - safe_print('Great! Your node is now called "{}".'.format(color(Fore.CYAN, name))) + safe_print(f'Great! Your node is now called "{color(Fore.CYAN, name)}".') sleep(1) safe_print_step(2, ESP_BIG) safe_print( @@ -236,11 +230,7 @@ def wizard(path): "firmwares for it." ) safe_print( - "Are you using an " - + color(Fore.GREEN, "ESP32") - + " or " - + color(Fore.GREEN, "ESP8266") - + " platform? (Choose ESP8266 for Sonoff devices)" + f"Are you using an {color(Fore.GREEN, 'ESP32')} or {color(Fore.GREEN, 'ESP8266')} platform? (Choose ESP8266 for Sonoff devices)" ) while True: sleep(0.5) @@ -252,12 +242,9 @@ def wizard(path): break except vol.Invalid: safe_print( - "Unfortunately, I can't find an espressif microcontroller called " - '"{}". Please try again.'.format(platform) + f'Unfortunately, I can\'t find an espressif microcontroller called "{platform}". Please try again.' ) - safe_print( - "Thanks! You've chosen {} as your platform.".format(color(Fore.CYAN, platform)) - ) + safe_print(f"Thanks! You've chosen {color(Fore.CYAN, platform)} as your platform.") safe_print() sleep(1) @@ -270,24 +257,20 @@ def wizard(path): "http://docs.platformio.org/en/latest/platforms/espressif8266.html#boards" ) - safe_print( - "Next, I need to know what " + color(Fore.GREEN, "board") + " you're using." - ) + safe_print(f"Next, I need to know what {color(Fore.GREEN, 'board')} you're using.") sleep(0.5) - safe_print( - "Please go to {} and choose a board.".format(color(Fore.GREEN, board_link)) - ) + safe_print(f"Please go to {color(Fore.GREEN, board_link)} and choose a board.") if platform == "ESP32": - safe_print("(Type " + color(Fore.GREEN, "esp01_1m") + " for Sonoff devices)") + safe_print(f"(Type {color(Fore.GREEN, 'esp01_1m')} for Sonoff devices)") safe_print() # Don't sleep because user needs to copy link if platform == "ESP32": - safe_print('For example "{}".'.format(color(Fore.BOLD_WHITE, "nodemcu-32s"))) + safe_print(f"For example \"{color(Fore.BOLD_WHITE, 'nodemcu-32s')}\".") boards = list(ESP32_BOARD_PINS.keys()) else: - safe_print('For example "{}".'.format(color(Fore.BOLD_WHITE, "nodemcuv2"))) + safe_print(f"For example \"{color(Fore.BOLD_WHITE, 'nodemcuv2')}\".") boards = list(ESP8266_BOARD_PINS.keys()) - safe_print("Options: {}".format(", ".join(sorted(boards)))) + safe_print(f"Options: {', '.join(sorted(boards))}") while True: board = input(color(Fore.BOLD_WHITE, "(board): ")) @@ -302,9 +285,7 @@ def wizard(path): sleep(0.25) safe_print() - safe_print( - "Way to go! You've chosen {} as your board.".format(color(Fore.CYAN, board)) - ) + safe_print(f"Way to go! You've chosen {color(Fore.CYAN, board)} as your board.") safe_print() sleep(1) @@ -313,12 +294,10 @@ def wizard(path): safe_print() sleep(1) safe_print( - "First, what's the " - + color(Fore.GREEN, "SSID") - + f" (the name) of the WiFi network {name} should connect to?" + f"First, what's the {color(Fore.GREEN, 'SSID')} (the name) of the WiFi network {name} should connect to?" ) sleep(1.5) - safe_print('For example "{}".'.format(color(Fore.BOLD_WHITE, "Abraham Linksys"))) + safe_print(f"For example \"{color(Fore.BOLD_WHITE, 'Abraham Linksys')}\".") while True: ssid = input(color(Fore.BOLD_WHITE, "(ssid): ")) try: @@ -328,27 +307,23 @@ def wizard(path): safe_print( color( Fore.RED, - 'Unfortunately, "{}" doesn\'t seem to be a valid SSID. ' - "Please try again.".format(ssid), + f'Unfortunately, "{ssid}" doesn\'t seem to be a valid SSID. Please try again.', ) ) safe_print() sleep(1) safe_print( - 'Thank you very much! You\'ve just chosen "{}" as your SSID.' - "".format(color(Fore.CYAN, ssid)) + f'Thank you very much! You\'ve just chosen "{color(Fore.CYAN, ssid)}" as your SSID.' ) safe_print() sleep(0.75) safe_print( - "Now please state the " - + color(Fore.GREEN, "password") - + " of the WiFi network so that I can connect to it (Leave empty for no password)" + f"Now please state the {color(Fore.GREEN, 'password')} of the WiFi network so that I can connect to it (Leave empty for no password)" ) safe_print() - safe_print('For example "{}"'.format(color(Fore.BOLD_WHITE, "PASSWORD42"))) + safe_print(f"For example \"{color(Fore.BOLD_WHITE, 'PASSWORD42')}\"") sleep(0.5) psk = input(color(Fore.BOLD_WHITE, "(PSK): ")) safe_print( @@ -362,8 +337,7 @@ def wizard(path): "(over the air) and integrates into Home Assistant with a native API." ) safe_print( - "This can be insecure if you do not trust the WiFi network. Do you want to set " - "a " + color(Fore.GREEN, "password") + " for connecting to this ESP?" + f"This can be insecure if you do not trust the WiFi network. Do you want to set a {color(Fore.GREEN, 'password')} for connecting to this ESP?" ) safe_print() sleep(0.25) diff --git a/esphome/writer.py b/esphome/writer.py index 09ed284173..19953916c7 100644 --- a/esphome/writer.py +++ b/esphome/writer.py @@ -96,7 +96,7 @@ def get_include_text(): includes = "\n".join(includes) if not includes: continue - include_text += includes + "\n" + include_text += f"{includes}\n" return include_text @@ -134,7 +134,7 @@ def migrate_src_version_0_to_1(): content, count = replace_file_content( content, r'#include "esphomelib/application.h"', - CPP_INCLUDE_BEGIN + "\n" + CPP_INCLUDE_END, + f"{CPP_INCLUDE_BEGIN}\n{CPP_INCLUDE_END}", ) if count == 0: _LOGGER.error( @@ -322,7 +322,7 @@ def write_platformio_ini(content): ) else: content_format = INI_BASE_FORMAT - full_file = content_format[0] + INI_AUTO_GENERATE_BEGIN + "\n" + content + full_file = f"{content_format[0] + INI_AUTO_GENERATE_BEGIN}\n{content}" full_file += INI_AUTO_GENERATE_END + content_format[1] write_file_if_changed(path, full_file) @@ -444,9 +444,9 @@ def write_cpp(code_s): global_s = '#include "esphome.h"\n' global_s += CORE.cpp_global_section - full_file = code_format[0] + CPP_INCLUDE_BEGIN + "\n" + global_s + CPP_INCLUDE_END + full_file = f"{code_format[0] + CPP_INCLUDE_BEGIN}\n{global_s}{CPP_INCLUDE_END}" full_file += ( - code_format[1] + CPP_AUTO_GENERATE_BEGIN + "\n" + code_s + CPP_AUTO_GENERATE_END + f"{code_format[1] + CPP_AUTO_GENERATE_BEGIN}\n{code_s}{CPP_AUTO_GENERATE_END}" ) full_file += code_format[2] write_file_if_changed(path, full_file) diff --git a/esphome/yaml_util.py b/esphome/yaml_util.py index 10417e6de4..bdadbbd43a 100644 --- a/esphome/yaml_util.py +++ b/esphome/yaml_util.py @@ -183,9 +183,7 @@ class ESPHomeLoader(yaml.SafeLoader): # pylint: disable=too-many-ancestors raise yaml.constructor.ConstructorError( "While constructing a mapping", node.start_mark, - "Expected a mapping for merging, but found {}".format( - type(item) - ), + f"Expected a mapping for merging, but found {type(item)}", value_node.start_mark, ) merge_pairs.extend(item.items()) @@ -193,8 +191,7 @@ class ESPHomeLoader(yaml.SafeLoader): # pylint: disable=too-many-ancestors raise yaml.constructor.ConstructorError( "While constructing a mapping", node.start_mark, - "Expected a mapping or list of mappings for merging, " - "but found {}".format(type(value)), + f"Expected a mapping or list of mappings for merging, but found {type(value)}", value_node.start_mark, ) diff --git a/requirements_test.txt b/requirements_test.txt index bb735193f0..1d09bb6388 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,4 +1,4 @@ -pylint==2.10.2 +pylint==2.11.1 flake8==3.9.2 black==21.9b0 pexpect==4.8.0 From 53bd197c44882a4ce1fd6d440ad2458306612ffd Mon Sep 17 00:00:00 2001 From: Stefan Rado Date: Sun, 19 Sep 2021 23:17:43 +0200 Subject: [PATCH 055/207] Add eco mode to tuya climate component (#1860) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/tuya/climate/__init__.py | 17 +++++++ .../components/tuya/climate/tuya_climate.cpp | 47 +++++++++++++++++-- .../components/tuya/climate/tuya_climate.h | 13 +++++ 3 files changed, 74 insertions(+), 3 deletions(-) diff --git a/esphome/components/tuya/climate/__init__.py b/esphome/components/tuya/climate/__init__.py index 471a8146e1..275a87edd3 100644 --- a/esphome/components/tuya/climate/__init__.py +++ b/esphome/components/tuya/climate/__init__.py @@ -23,6 +23,8 @@ CONF_CURRENT_TEMPERATURE_DATAPOINT = "current_temperature_datapoint" CONF_TEMPERATURE_MULTIPLIER = "temperature_multiplier" CONF_CURRENT_TEMPERATURE_MULTIPLIER = "current_temperature_multiplier" CONF_TARGET_TEMPERATURE_MULTIPLIER = "target_temperature_multiplier" +CONF_ECO_DATAPOINT = "eco_datapoint" +CONF_ECO_TEMPERATURE = "eco_temperature" TuyaClimate = tuya_ns.class_("TuyaClimate", climate.Climate, cg.Component) @@ -90,6 +92,14 @@ def validate_active_state_values(value): return value +def validate_eco_values(value): + if CONF_ECO_TEMPERATURE in value and CONF_ECO_DATAPOINT not in value: + raise cv.Invalid( + f"{CONF_ECO_DATAPOINT} required if using {CONF_ECO_TEMPERATURE}" + ) + return value + + CONFIG_SCHEMA = cv.All( climate.CLIMATE_SCHEMA.extend( { @@ -108,6 +118,8 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_TEMPERATURE_MULTIPLIER): cv.positive_float, cv.Optional(CONF_CURRENT_TEMPERATURE_MULTIPLIER): cv.positive_float, cv.Optional(CONF_TARGET_TEMPERATURE_MULTIPLIER): cv.positive_float, + cv.Optional(CONF_ECO_DATAPOINT): cv.uint8_t, + cv.Optional(CONF_ECO_TEMPERATURE): cv.temperature, } ).extend(cv.COMPONENT_SCHEMA), cv.has_at_least_one_key(CONF_TARGET_TEMPERATURE_DATAPOINT, CONF_SWITCH_DATAPOINT), @@ -115,6 +127,7 @@ CONFIG_SCHEMA = cv.All( validate_active_state_values, cv.has_at_most_one_key(CONF_ACTIVE_STATE_DATAPOINT, CONF_HEATING_STATE_PIN), cv.has_at_most_one_key(CONF_ACTIVE_STATE_DATAPOINT, CONF_COOLING_STATE_PIN), + validate_eco_values, ) @@ -179,3 +192,7 @@ async def to_code(config): config[CONF_TARGET_TEMPERATURE_MULTIPLIER] ) ) + if CONF_ECO_DATAPOINT in config: + cg.add(var.set_eco_id(config[CONF_ECO_DATAPOINT])) + if CONF_ECO_TEMPERATURE in config: + cg.add(var.set_eco_temperature(config[CONF_ECO_TEMPERATURE])) diff --git a/esphome/components/tuya/climate/tuya_climate.cpp b/esphome/components/tuya/climate/tuya_climate.cpp index ae4424e438..293bfb4f88 100644 --- a/esphome/components/tuya/climate/tuya_climate.cpp +++ b/esphome/components/tuya/climate/tuya_climate.cpp @@ -43,8 +43,9 @@ void TuyaClimate::setup() { } if (this->target_temperature_id_.has_value()) { this->parent_->register_listener(*this->target_temperature_id_, [this](const TuyaDatapoint &datapoint) { - this->target_temperature = datapoint.value_int * this->target_temperature_multiplier_; - ESP_LOGV(TAG, "MCU reported target temperature is: %.1f", this->target_temperature); + this->manual_temperature_ = datapoint.value_int * this->target_temperature_multiplier_; + ESP_LOGV(TAG, "MCU reported manual target temperature is: %.1f", this->manual_temperature_); + this->compute_target_temperature_(); this->compute_state_(); this->publish_state(); }); @@ -57,6 +58,15 @@ void TuyaClimate::setup() { this->publish_state(); }); } + if (this->eco_id_.has_value()) { + this->parent_->register_listener(*this->eco_id_, [this](const TuyaDatapoint &datapoint) { + this->eco_ = datapoint.value_bool; + ESP_LOGV(TAG, "MCU reported eco is: %s", ONOFF(this->eco_)); + this->compute_preset_(); + this->compute_target_temperature_(); + this->publish_state(); + }); + } } void TuyaClimate::loop() { @@ -100,16 +110,29 @@ void TuyaClimate::control(const climate::ClimateCall &call) { this->parent_->set_integer_datapoint_value(*this->target_temperature_id_, (int) (target_temperature / this->target_temperature_multiplier_)); } + + if (call.get_preset().has_value()) { + const climate::ClimatePreset preset = *call.get_preset(); + if (this->eco_id_.has_value()) { + const bool eco = preset == climate::CLIMATE_PRESET_ECO; + ESP_LOGV(TAG, "Setting eco: %s", ONOFF(eco)); + this->parent_->set_boolean_datapoint_value(*this->eco_id_, eco); + } + } } climate::ClimateTraits TuyaClimate::traits() { auto traits = climate::ClimateTraits(); + traits.set_supports_action(true); traits.set_supports_current_temperature(this->current_temperature_id_.has_value()); if (supports_heat_) traits.add_supported_mode(climate::CLIMATE_MODE_HEAT); if (supports_cool_) traits.add_supported_mode(climate::CLIMATE_MODE_COOL); - traits.set_supports_action(true); + if (this->eco_id_.has_value()) { + traits.add_supported_preset(climate::CLIMATE_PRESET_NONE); + traits.add_supported_preset(climate::CLIMATE_PRESET_ECO); + } return traits; } @@ -125,6 +148,24 @@ void TuyaClimate::dump_config() { ESP_LOGCONFIG(TAG, " Current Temperature has datapoint ID %u", *this->current_temperature_id_); LOG_PIN(" Heating State Pin: ", this->heating_state_pin_); LOG_PIN(" Cooling State Pin: ", this->cooling_state_pin_); + if (this->eco_id_.has_value()) + ESP_LOGCONFIG(TAG, " Eco has datapoint ID %u", *this->eco_id_); +} + +void TuyaClimate::compute_preset_() { + if (this->eco_) { + this->preset = climate::CLIMATE_PRESET_ECO; + } else { + this->preset = climate::CLIMATE_PRESET_NONE; + } +} + +void TuyaClimate::compute_target_temperature_() { + if (this->eco_ && this->eco_temperature_.has_value()) { + this->target_temperature = *this->eco_temperature_; + } else { + this->target_temperature = this->manual_temperature_; + } } void TuyaClimate::compute_state_() { diff --git a/esphome/components/tuya/climate/tuya_climate.h b/esphome/components/tuya/climate/tuya_climate.h index f1a0c13a77..ec19d05308 100644 --- a/esphome/components/tuya/climate/tuya_climate.h +++ b/esphome/components/tuya/climate/tuya_climate.h @@ -32,15 +32,24 @@ class TuyaClimate : public climate::Climate, public Component { void set_target_temperature_multiplier(float temperature_multiplier) { this->target_temperature_multiplier_ = temperature_multiplier; } + void set_eco_id(uint8_t eco_id) { this->eco_id_ = eco_id; } + void set_eco_temperature(float eco_temperature) { this->eco_temperature_ = eco_temperature; } void set_tuya_parent(Tuya *parent) { this->parent_ = parent; } protected: /// Override control to change settings of the climate device. void control(const climate::ClimateCall &call) override; + /// Return the traits of this controller. climate::ClimateTraits traits() override; + /// Re-compute the active preset of this climate controller. + void compute_preset_(); + + /// Re-compute the target temperature of this climate controller. + void compute_target_temperature_(); + /// Re-compute the state of this climate controller. void compute_state_(); @@ -61,9 +70,13 @@ class TuyaClimate : public climate::Climate, public Component { float current_temperature_multiplier_{1.0f}; float target_temperature_multiplier_{1.0f}; float hysteresis_{1.0f}; + optional eco_id_{}; + optional eco_temperature_{}; uint8_t active_state_; bool heating_state_{false}; bool cooling_state_{false}; + float manual_temperature_; + bool eco_; }; } // namespace tuya From c60c61820453a50557f21f79e2d6196be7ff7050 Mon Sep 17 00:00:00 2001 From: Luca Gugelmann <69755789+lgugelmann@users.noreply.github.com> Date: Sun, 19 Sep 2021 23:19:20 +0200 Subject: [PATCH 056/207] Fix SPIDevice::write_byte16 to actually take a 16 bit argument (#2345) --- esphome/components/spi/spi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/spi/spi.h b/esphome/components/spi/spi.h index a4a2e11def..eb8f9ce7ce 100644 --- a/esphome/components/spi/spi.h +++ b/esphome/components/spi/spi.h @@ -246,7 +246,7 @@ class SPIDevice { return this->parent_->template write_byte(data); } - void write_byte16(uint8_t data) { + void write_byte16(uint16_t data) { return this->parent_->template write_byte16(data); } From a9908982565d1f386595dd29c2ef2d46fd3089ce Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 20 Sep 2021 00:33:10 +0200 Subject: [PATCH 057/207] Add readv and writev for more efficient API packets (#2342) --- esphome/components/api/api_connection.cpp | 29 ++-- esphome/components/api/api_frame_helper.cpp | 127 ++++++++++-------- esphome/components/api/api_frame_helper.h | 5 +- .../components/socket/bsd_sockets_impl.cpp | 48 +++++++ esphome/components/socket/headers.h | 6 + .../components/socket/lwip_raw_tcp_impl.cpp | 87 +++++++++--- esphome/components/socket/socket.h | 2 + 7 files changed, 220 insertions(+), 84 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 31a530c04d..0fa4ca6397 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -702,15 +702,7 @@ bool APIConnection::send_log_message(int level, const char *tag, const char *lin // string message = 3; buffer.encode_string(3, line, strlen(line)); // SubscribeLogsResponse - 29 - bool success = this->send_buffer(buffer, 29); - if (!success) { - buffer = this->create_buffer(); - // bool send_failed = 4; - buffer.encode_bool(4, true); - return this->send_buffer(buffer, 29); - } else { - return true; - } + return this->send_buffer(buffer, 29); } HelloResponse APIConnection::hello(const HelloRequest &msg) { @@ -783,8 +775,23 @@ void APIConnection::subscribe_home_assistant_states(const SubscribeHomeAssistant bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) { if (this->remove_) return false; - if (!this->helper_->can_write_without_blocking()) - return false; + if (!this->helper_->can_write_without_blocking()) { + delay(0); + APIError err = helper_->loop(); + if (err != APIError::OK) { + on_fatal_error(); + ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", client_info_.c_str(), api_error_to_str(err), errno); + return false; + } + if (!this->helper_->can_write_without_blocking()) { + // SubscribeLogsResponse + if (message_type != 29) { + ESP_LOGV(TAG, "Cannot send message because of TCP buffer space"); + } + delay(0); + return false; + } + } APIError err = this->helper_->write_packet(message_type, buffer.get_buffer()->data(), buffer.get_buffer()->size()); if (err == APIError::WOULD_BLOCK) diff --git a/esphome/components/api/api_frame_helper.cpp b/esphome/components/api/api_frame_helper.cpp index e68831e594..15014b7937 100644 --- a/esphome/components/api/api_frame_helper.cpp +++ b/esphome/components/api/api_frame_helper.cpp @@ -125,13 +125,6 @@ APIError APINoiseFrameHelper::init() { HELPER_LOG("Setting nonblocking failed with errno %d", errno); return APIError::TCP_NONBLOCKING_FAILED; } - int enable = 1; - err = socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int)); - if (err != 0) { - state_ = State::FAILED; - HELPER_LOG("Setting nodelay failed with errno %d", errno); - return APIError::TCP_NODELAY_FAILED; - } // init prologue prologue_.insert(prologue_.end(), PROLOGUE_INIT, PROLOGUE_INIT + strlen(PROLOGUE_INIT)); @@ -494,12 +487,13 @@ APIError APINoiseFrameHelper::write_packet(uint16_t type, const uint8_t *payload size_t total_len = 3 + mbuf.size; tmpbuf[1] = (uint8_t)(mbuf.size >> 8); tmpbuf[2] = (uint8_t) mbuf.size; + + struct iovec iov; + iov.iov_base = &tmpbuf[0]; + iov.iov_len = total_len; + // write raw to not have two packets sent if NAGLE disabled - aerr = write_raw_(&tmpbuf[0], total_len); - if (aerr != APIError::OK) { - return aerr; - } - return APIError::OK; + return write_raw_(&iov, 1); } APIError APINoiseFrameHelper::try_send_tx_buf_() { // try send from tx_buf @@ -526,16 +520,19 @@ APIError APINoiseFrameHelper::try_send_tx_buf_() { * @param data The data to write * @param len The length of data */ -APIError APINoiseFrameHelper::write_raw_(const uint8_t *data, size_t len) { - if (len == 0) +APIError APINoiseFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) { + if (iovcnt == 0) return APIError::OK; int err; APIError aerr; - // uncomment for even more debugging + size_t total_write_len = 0; + for (int i = 0; i < iovcnt; i++) { #ifdef HELPER_LOG_PACKETS - ESP_LOGVV(TAG, "Sending raw: %s", hexencode(data, len).c_str()); + ESP_LOGVV(TAG, "Sending raw: %s", hexencode(reinterpret_cast(iov[i].iov_base), iov[i].iov_len).c_str()); #endif + total_write_len += iov[i].iov_len; + } if (!tx_buf_.empty()) { // try to empty tx_buf_ first @@ -546,41 +543,56 @@ APIError APINoiseFrameHelper::write_raw_(const uint8_t *data, size_t len) { if (!tx_buf_.empty()) { // tx buf not empty, can't write now because then stream would be inconsistent - tx_buf_.insert(tx_buf_.end(), data, data + len); + for (int i = 0; i < iovcnt; i++) { + tx_buf_.insert(tx_buf_.end(), reinterpret_cast(iov[i].iov_base), + reinterpret_cast(iov[i].iov_base) + iov[i].iov_len); + } return APIError::OK; } - ssize_t sent = socket_->write(data, len); + ssize_t sent = socket_->writev(iov, iovcnt); if (is_would_block(sent)) { // operation would block, add buffer to tx_buf - tx_buf_.insert(tx_buf_.end(), data, data + len); + for (int i = 0; i < iovcnt; i++) { + tx_buf_.insert(tx_buf_.end(), reinterpret_cast(iov[i].iov_base), + reinterpret_cast(iov[i].iov_base) + iov[i].iov_len); + } return APIError::OK; } else if (sent == -1) { // an error occured state_ = State::FAILED; HELPER_LOG("Socket write failed with errno %d", errno); return APIError::SOCKET_WRITE_FAILED; - } else if (sent != len) { + } else if (sent != total_write_len) { // partially sent, add end to tx_buf - tx_buf_.insert(tx_buf_.end(), data + sent, data + len); + size_t to_consume = sent; + for (int i = 0; i < iovcnt; i++) { + if (to_consume >= iov[i].iov_len) { + to_consume -= iov[i].iov_len; + } else { + tx_buf_.insert(tx_buf_.end(), reinterpret_cast(iov[i].iov_base) + to_consume, + reinterpret_cast(iov[i].iov_base) + iov[i].iov_len); + to_consume = 0; + } + } return APIError::OK; } // fully sent return APIError::OK; } APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, size_t len) { - APIError aerr; - uint8_t header[3]; header[0] = 0x01; // indicator header[1] = (uint8_t)(len >> 8); header[2] = (uint8_t) len; - aerr = write_raw_(header, 3); - if (aerr != APIError::OK) - return aerr; - aerr = write_raw_(data, len); - return aerr; + struct iovec iov[2]; + iov[0].iov_base = header; + iov[0].iov_len = 3; + iov[1].iov_base = const_cast(data); + iov[1].iov_len = len; + + return write_raw_(iov, 2); } /** Initiate the data structures for the handshake. @@ -709,13 +721,6 @@ APIError APIPlaintextFrameHelper::init() { HELPER_LOG("Setting nonblocking failed with errno %d", errno); return APIError::TCP_NONBLOCKING_FAILED; } - int enable = 1; - err = socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int)); - if (err != 0) { - state_ = State::FAILED; - HELPER_LOG("Setting nodelay failed with errno %d", errno); - return APIError::TCP_NODELAY_FAILED; - } state_ = State::DATA; return APIError::OK; @@ -863,15 +868,13 @@ APIError APIPlaintextFrameHelper::write_packet(uint16_t type, const uint8_t *pay ProtoVarInt(payload_len).encode(header); ProtoVarInt(type).encode(header); - aerr = write_raw_(&header[0], header.size()); - if (aerr != APIError::OK) { - return aerr; - } - aerr = write_raw_(payload, payload_len); - if (aerr != APIError::OK) { - return aerr; - } - return APIError::OK; + struct iovec iov[2]; + iov[0].iov_base = &header[0]; + iov[0].iov_len = header.size(); + iov[1].iov_base = const_cast(payload); + iov[1].iov_len = payload_len; + + return write_raw_(iov, 2); } APIError APIPlaintextFrameHelper::try_send_tx_buf_() { // try send from tx_buf @@ -896,16 +899,19 @@ APIError APIPlaintextFrameHelper::try_send_tx_buf_() { * @param data The data to write * @param len The length of data */ -APIError APIPlaintextFrameHelper::write_raw_(const uint8_t *data, size_t len) { - if (len == 0) +APIError APIPlaintextFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) { + if (iovcnt == 0) return APIError::OK; int err; APIError aerr; - // uncomment for even more debugging + size_t total_write_len = 0; + for (int i = 0; i < iovcnt; i++) { #ifdef HELPER_LOG_PACKETS - ESP_LOGVV(TAG, "Sending raw: %s", hexencode(data, len).c_str()); + ESP_LOGVV(TAG, "Sending raw: %s", hexencode(reinterpret_cast(iov[i].iov_base), iov[i].iov_len).c_str()); #endif + total_write_len += iov[i].iov_len; + } if (!tx_buf_.empty()) { // try to empty tx_buf_ first @@ -916,23 +922,38 @@ APIError APIPlaintextFrameHelper::write_raw_(const uint8_t *data, size_t len) { if (!tx_buf_.empty()) { // tx buf not empty, can't write now because then stream would be inconsistent - tx_buf_.insert(tx_buf_.end(), data, data + len); + for (int i = 0; i < iovcnt; i++) { + tx_buf_.insert(tx_buf_.end(), reinterpret_cast(iov[i].iov_base), + reinterpret_cast(iov[i].iov_base) + iov[i].iov_len); + } return APIError::OK; } - ssize_t sent = socket_->write(data, len); + ssize_t sent = socket_->writev(iov, iovcnt); if (is_would_block(sent)) { // operation would block, add buffer to tx_buf - tx_buf_.insert(tx_buf_.end(), data, data + len); + for (int i = 0; i < iovcnt; i++) { + tx_buf_.insert(tx_buf_.end(), reinterpret_cast(iov[i].iov_base), + reinterpret_cast(iov[i].iov_base) + iov[i].iov_len); + } return APIError::OK; } else if (sent == -1) { // an error occured state_ = State::FAILED; HELPER_LOG("Socket write failed with errno %d", errno); return APIError::SOCKET_WRITE_FAILED; - } else if (sent != len) { + } else if (sent != total_write_len) { // partially sent, add end to tx_buf - tx_buf_.insert(tx_buf_.end(), data + sent, data + len); + size_t to_consume = sent; + for (int i = 0; i < iovcnt; i++) { + if (to_consume >= iov[i].iov_len) { + to_consume -= iov[i].iov_len; + } else { + tx_buf_.insert(tx_buf_.end(), reinterpret_cast(iov[i].iov_base) + to_consume, + reinterpret_cast(iov[i].iov_base) + iov[i].iov_len); + to_consume = 0; + } + } return APIError::OK; } // fully sent diff --git a/esphome/components/api/api_frame_helper.h b/esphome/components/api/api_frame_helper.h index a9a653cf4f..44df629b2f 100644 --- a/esphome/components/api/api_frame_helper.h +++ b/esphome/components/api/api_frame_helper.h @@ -58,6 +58,7 @@ const char *api_error_to_str(APIError err); class APIFrameHelper { public: + virtual ~APIFrameHelper() = default; virtual APIError init() = 0; virtual APIError loop() = 0; virtual APIError read_packet(ReadPacketBuffer *buffer) = 0; @@ -96,7 +97,7 @@ class APINoiseFrameHelper : public APIFrameHelper { APIError try_read_frame_(ParsedFrame *frame); APIError try_send_tx_buf_(); APIError write_frame_(const uint8_t *data, size_t len); - APIError write_raw_(const uint8_t *data, size_t len); + APIError write_raw_(const struct iovec *iov, int iovcnt); APIError init_handshake_(); APIError check_handshake_finished_(); void send_explicit_handshake_reject_(const std::string &reason); @@ -154,7 +155,7 @@ class APIPlaintextFrameHelper : public APIFrameHelper { APIError try_read_frame_(ParsedFrame *frame); APIError try_send_tx_buf_(); - APIError write_raw_(const uint8_t *data, size_t len); + APIError write_raw_(const struct iovec *iov, int iovcnt); std::unique_ptr socket_; diff --git a/esphome/components/socket/bsd_sockets_impl.cpp b/esphome/components/socket/bsd_sockets_impl.cpp index 6fb00ce22d..aa1bcd3b3c 100644 --- a/esphome/components/socket/bsd_sockets_impl.cpp +++ b/esphome/components/socket/bsd_sockets_impl.cpp @@ -6,6 +6,10 @@ #include +#ifdef ARDUINO_ARCH_ESP32 +#include +#endif + namespace esphome { namespace socket { @@ -76,7 +80,51 @@ class BSDSocketImpl : public Socket { } int listen(int backlog) override { return ::listen(fd_, backlog); } ssize_t read(void *buf, size_t len) override { return ::read(fd_, buf, len); } + ssize_t readv(const struct iovec *iov, int iovcnt) override { +#if defined(ARDUINO_ARCH_ESP32) && ESP_IDF_VERSION_MAJOR < 4 + // esp-idf v3 doesn't have readv, emulate it + ssize_t ret = 0; + for (int i = 0; i < iovcnt; i++) { + ssize_t err = this->read(reinterpret_cast(iov[i].iov_base), iov[i].iov_len); + if (err == -1) { + if (ret != 0) + // if we already read some don't return an error + break; + return err; + } + ret += err; + if (err != iov[i].iov_len) + break; + } + return ret; +#else + return ::readv(fd_, iov, iovcnt); +#endif + } ssize_t write(const void *buf, size_t len) override { return ::write(fd_, buf, len); } + ssize_t send(void *buf, size_t len, int flags) { return ::send(fd_, buf, len, flags); } + ssize_t writev(const struct iovec *iov, int iovcnt) override { +#if defined(ARDUINO_ARCH_ESP32) && ESP_IDF_VERSION_MAJOR < 4 + // esp-idf v3 doesn't have writev, emulate it + ssize_t ret = 0; + for (int i = 0; i < iovcnt; i++) { + ssize_t err = + this->send(reinterpret_cast(iov[i].iov_base), iov[i].iov_len, i == iovcnt - 1 ? 0 : MSG_MORE); + if (err == -1) { + if (ret != 0) + // if we already wrote some don't return an error + break; + return err; + } + ret += err; + if (err != iov[i].iov_len) + break; + } + return ret; +#else + return ::writev(fd_, iov, iovcnt); +#endif + } int setblocking(bool blocking) override { int fl = ::fcntl(fd_, F_GETFL, 0); if (blocking) { diff --git a/esphome/components/socket/headers.h b/esphome/components/socket/headers.h index da710b760e..fbe8f929a0 100644 --- a/esphome/components/socket/headers.h +++ b/esphome/components/socket/headers.h @@ -81,6 +81,11 @@ struct sockaddr_storage { }; typedef uint32_t socklen_t; +struct iovec { + void *iov_base; + size_t iov_len; +}; + #ifdef ARDUINO_ARCH_ESP8266 // arduino-esp8266 declares a global vars called INADDR_NONE/ANY which are invalid with the define #ifdef INADDR_ANY @@ -104,6 +109,7 @@ typedef uint32_t socklen_t; #include #include #include +#include #include #include #include diff --git a/esphome/components/socket/lwip_raw_tcp_impl.cpp b/esphome/components/socket/lwip_raw_tcp_impl.cpp index 2147e36632..366f0972ef 100644 --- a/esphome/components/socket/lwip_raw_tcp_impl.cpp +++ b/esphome/components/socket/lwip_raw_tcp_impl.cpp @@ -371,7 +371,23 @@ class LWIPRawImpl : public Socket { return read; } - ssize_t write(const void *buf, size_t len) override { + ssize_t readv(const struct iovec *iov, int iovcnt) override { + ssize_t ret = 0; + for (int i = 0; i < iovcnt; i++) { + ssize_t err = read(reinterpret_cast(iov[i].iov_base), iov[i].iov_len); + if (err == -1) { + if (ret != 0) + // if we already read some don't return an error + break; + return err; + } + ret += err; + if (err != iov[i].iov_len) + break; + } + return ret; + } + ssize_t internal_write(const void *buf, size_t len) { if (pcb_ == nullptr) { errno = ECONNRESET; return -1; @@ -400,25 +416,60 @@ class LWIPRawImpl : public Socket { errno = ECONNRESET; return -1; } - if (tcp_nagle_disabled(pcb_)) { - LWIP_LOG("tcp_output(%p)", pcb_); - err = tcp_output(pcb_); - if (err == ERR_ABRT) { - LWIP_LOG(" -> err ERR_ABRT"); - // sometimes lwip returns ERR_ABRT for no apparent reason - // the connection works fine afterwards, and back with ESPAsyncTCP we - // indirectly also ignored this error - // FIXME: figure out where this is returned and what it means in this context - return to_send; - } - if (err != ERR_OK) { - LWIP_LOG(" -> err %d", err); - errno = ECONNRESET; - return -1; - } - } return to_send; } + int internal_output() { + LWIP_LOG("tcp_output(%p)", pcb_); + err_t err = tcp_output(pcb_); + if (err == ERR_ABRT) { + LWIP_LOG(" -> err ERR_ABRT"); + // sometimes lwip returns ERR_ABRT for no apparent reason + // the connection works fine afterwards, and back with ESPAsyncTCP we + // indirectly also ignored this error + // FIXME: figure out where this is returned and what it means in this context + return 0; + } + if (err != ERR_OK) { + LWIP_LOG(" -> err %d", err); + errno = ECONNRESET; + return -1; + } + return 0; + } + ssize_t write(const void *buf, size_t len) override { + ssize_t written = internal_write(buf, len); + if (written == -1) + return -1; + if (written == 0) + // no need to output if nothing written + return 0; + int err = internal_output(); + if (err == -1) + return -1; + return written; + } + ssize_t writev(const struct iovec *iov, int iovcnt) override { + ssize_t written = 0; + for (int i = 0; i < iovcnt; i++) { + ssize_t err = internal_write(reinterpret_cast(iov[i].iov_base), iov[i].iov_len); + if (err == -1) { + if (written != 0) + // if we already read some don't return an error + break; + return err; + } + written += err; + if (err != iov[i].iov_len) + break; + } + if (written == 0) + // no need to output if nothing written + return 0; + int err = internal_output(); + if (err == -1) + return -1; + return written; + } int setblocking(bool blocking) override { if (pcb_ == nullptr) { errno = ECONNRESET; diff --git a/esphome/components/socket/socket.h b/esphome/components/socket/socket.h index 7a5ce79161..9920610bf5 100644 --- a/esphome/components/socket/socket.h +++ b/esphome/components/socket/socket.h @@ -31,7 +31,9 @@ class Socket { virtual int setsockopt(int level, int optname, const void *optval, socklen_t optlen) = 0; virtual int listen(int backlog) = 0; virtual ssize_t read(void *buf, size_t len) = 0; + virtual ssize_t readv(const struct iovec *iov, int iovcnt) = 0; virtual ssize_t write(const void *buf, size_t len) = 0; + virtual ssize_t writev(const struct iovec *iov, int iovcnt) = 0; virtual int setblocking(bool blocking) = 0; virtual int loop() { return 0; }; }; From 272ceadbb00d5980aa00c0ad5043878a330d275f Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 20 Sep 2021 09:07:38 +0200 Subject: [PATCH 058/207] Redo docker build system with buildkit+multi-stage and cache pio packages (#2338) --- .devcontainer/devcontainer.json | 3 +- .github/workflows/ci-docker.yml | 5 + .github/workflows/ci.yml | 152 +++++++++--------- .github/workflows/docker-lint-build.yml | 100 ------------ .github/workflows/release.yml | 20 ++- docker/Dockerfile | 135 ++++++++++++++-- docker/Dockerfile.dev | 1 - docker/Dockerfile.hassio | 25 --- docker/Dockerfile.lint | 10 -- docker/build.py | 94 +++++------ docker/docker_entrypoint.sh | 18 +++ .../etc/cont-init.d/10-requirements.sh | 0 .../etc/cont-init.d/20-nginx.sh | 0 .../hassio-rootfs/etc/cont-init.d/30-dirs.sh | 9 ++ .../etc/nginx/includes/mime.types | 0 .../etc/nginx/includes/proxy_params.conf | 0 .../etc/nginx/includes/server_params.conf | 0 .../etc/nginx/includes/ssl_params.conf | 0 .../etc/nginx/nginx.conf | 0 .../etc/nginx/servers/direct-ssl.disabled | 0 .../etc/nginx/servers/direct.disabled | 0 .../etc/nginx/servers/ingress.conf | 0 .../etc/services.d/esphome/finish | 0 .../etc/services.d/esphome/run | 3 + .../etc/services.d/nginx/finish | 0 .../etc/services.d/nginx/run | 0 esphome/platformio_api.py | 4 +- script/ci-custom.py | 4 +- script/devcontainer-post-create | 5 +- 29 files changed, 295 insertions(+), 293 deletions(-) delete mode 100644 .github/workflows/docker-lint-build.yml delete mode 100644 docker/Dockerfile.dev delete mode 100644 docker/Dockerfile.hassio delete mode 100644 docker/Dockerfile.lint create mode 100755 docker/docker_entrypoint.sh rename docker/{rootfs => hassio-rootfs}/etc/cont-init.d/10-requirements.sh (100%) rename docker/{rootfs => hassio-rootfs}/etc/cont-init.d/20-nginx.sh (100%) create mode 100644 docker/hassio-rootfs/etc/cont-init.d/30-dirs.sh rename docker/{rootfs => hassio-rootfs}/etc/nginx/includes/mime.types (100%) rename docker/{rootfs => hassio-rootfs}/etc/nginx/includes/proxy_params.conf (100%) rename docker/{rootfs => hassio-rootfs}/etc/nginx/includes/server_params.conf (100%) rename docker/{rootfs => hassio-rootfs}/etc/nginx/includes/ssl_params.conf (100%) rename docker/{rootfs => hassio-rootfs}/etc/nginx/nginx.conf (100%) rename docker/{rootfs => hassio-rootfs}/etc/nginx/servers/direct-ssl.disabled (100%) rename docker/{rootfs => hassio-rootfs}/etc/nginx/servers/direct.disabled (100%) rename docker/{rootfs => hassio-rootfs}/etc/nginx/servers/ingress.conf (100%) rename docker/{rootfs => hassio-rootfs}/etc/services.d/esphome/finish (100%) rename docker/{rootfs => hassio-rootfs}/etc/services.d/esphome/run (89%) rename docker/{rootfs => hassio-rootfs}/etc/services.d/nginx/finish (100%) rename docker/{rootfs => hassio-rootfs}/etc/services.d/nginx/run (100%) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 3904962d7c..433e5d2792 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,7 +1,6 @@ { "name": "ESPHome Dev", - "context": "..", - "dockerFile": "../docker/Dockerfile.dev", + "image": "esphome/esphome-lint:dev", "postCreateCommand": [ "script/devcontainer-post-create" ], diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index 45fd3e141b..33b15cb1dc 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -27,6 +27,11 @@ jobs: uses: actions/setup-python@v2 with: python-version: '3.9' + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - name: Set up QEMU + uses: docker/setup-qemu-action@v1 + - name: Set TAG run: | echo "TAG=check" >> $GITHUB_ENV diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b00e1b3835..875db0f9fd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,61 +9,7 @@ on: pull_request: jobs: - ci-with-container: - name: ${{ matrix.name }} - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - include: - - id: clang-format - name: Run script/clang-format - - id: clang-tidy - name: Run script/clang-tidy for ESP8266 - options: --environment esp8266-tidy --grep ARDUINO_ARCH_ESP8266 - - id: clang-tidy - name: Run script/clang-tidy for ESP32 1/4 - options: --environment esp32-tidy --split-num 4 --split-at 1 - - id: clang-tidy - name: Run script/clang-tidy for ESP32 2/4 - options: --environment esp32-tidy --split-num 4 --split-at 2 - - id: clang-tidy - name: Run script/clang-tidy for ESP32 3/4 - options: --environment esp32-tidy --split-num 4 --split-at 3 - - id: clang-tidy - name: Run script/clang-tidy for ESP32 4/4 - options: --environment esp32-tidy --split-num 4 --split-at 4 - - # cpp lint job runs with esphome-lint docker image so that clang-format-* - # doesn't have to be installed - container: ghcr.io/esphome/esphome-lint:1.2 - steps: - - uses: actions/checkout@v2 - - - name: Register problem matchers - run: | - echo "::add-matcher::.github/workflows/matchers/clang-tidy.json" - echo "::add-matcher::.github/workflows/matchers/gcc.json" - - # Also run git-diff-index so that the step is marked as failed on formatting errors, - # since clang-format doesn't do anything but change files if -i is passed. - - name: Run clang-format - run: | - script/clang-format -i - git diff-index --quiet HEAD -- - if: ${{ matrix.id == 'clang-format' }} - - - name: Run clang-tidy - run: script/clang-tidy --all-headers --fix ${{ matrix.options }} - if: ${{ matrix.id == 'clang-tidy' }} - - - name: Suggested changes - run: script/ci-suggest-changes - if: always() - ci: - # Don't use the esphome-lint docker image because it may contain outdated requirements. - # This way, all dependencies are cached via the cache action. name: ${{ matrix.name }} runs-on: ubuntu-latest strategy: @@ -77,48 +23,85 @@ jobs: - id: test file: tests/test1.yaml name: Test tests/test1.yaml + pio_cache_key: test1 - id: test file: tests/test2.yaml name: Test tests/test2.yaml + pio_cache_key: test2 - id: test file: tests/test3.yaml name: Test tests/test3.yaml + pio_cache_key: test1 - id: test file: tests/test4.yaml name: Test tests/test4.yaml + pio_cache_key: test4 - id: test file: tests/test5.yaml name: Test tests/test5.yaml + pio_cache_key: test5 - id: pytest name: Run pytest + - id: clang-format + name: Run script/clang-format + - id: clang-tidy + name: Run script/clang-tidy for ESP8266 + options: --environment esp8266-tidy --grep ARDUINO_ARCH_ESP8266 + pio_cache_key: tidyesp8266 + - id: clang-tidy + name: Run script/clang-tidy for ESP32 1/4 + options: --environment esp32-tidy --split-num 4 --split-at 1 + pio_cache_key: tidyesp32 + - id: clang-tidy + name: Run script/clang-tidy for ESP32 2/4 + options: --environment esp32-tidy --split-num 4 --split-at 2 + pio_cache_key: tidyesp32 + - id: clang-tidy + name: Run script/clang-tidy for ESP32 3/4 + options: --environment esp32-tidy --split-num 4 --split-at 3 + pio_cache_key: tidyesp32 + - id: clang-tidy + name: Run script/clang-tidy for ESP32 4/4 + options: --environment esp32-tidy --split-num 4 --split-at 4 + pio_cache_key: tidyesp32 steps: - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v2 + id: python with: python-version: '3.7' - name: Cache pip modules - uses: actions/cache@v1 + uses: actions/cache@v2 with: path: ~/.cache/pip - key: esphome-pip-3.7-${{ hashFiles('setup.py') }} + key: pip-${{ steps.python.outputs.python-version }}-${{ hashFiles('requirements*.txt') }} restore-keys: | - esphome-pip-3.7- - - # Use per test platformio cache because tests have different platform versions - - name: Cache ~/.platformio - uses: actions/cache@v1 - with: - path: ~/.platformio - key: test-home-platformio-${{ matrix.file }}-${{ hashFiles('esphome/core/config.py') }} - restore-keys: | - test-home-platformio-${{ matrix.file }}- - if: ${{ matrix.id == 'test' }} + pip-${{ steps.python.outputs.python-version }}- - name: Set up python environment - run: script/setup + run: | + pip3 install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt + pip3 install -e . + + # Use per check platformio cache because checks use different parts + - name: Cache platformio + uses: actions/cache@v2 + with: + path: ~/.platformio + key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }} + restore-keys: | + platformio-${{ matrix.pio_cache_key }}- + if: matrix.id == 'test' || matrix.id == 'clang-tidy' + + - name: Install clang tools + run: | + sudo apt-get install \ + clang-format-11 \ + clang-tidy-11 + if: matrix.id == 'clang-tidy' || matrix.id == 'clang-format' - name: Register problem matchers run: | @@ -127,20 +110,45 @@ jobs: echo "::add-matcher::.github/workflows/matchers/python.json" echo "::add-matcher::.github/workflows/matchers/pytest.json" echo "::add-matcher::.github/workflows/matchers/gcc.json" + echo "::add-matcher::.github/workflows/matchers/clang-tidy.json" - name: Lint Custom run: | script/ci-custom.py script/build_codeowners.py --check - if: ${{ matrix.id == 'ci-custom' }} + if: matrix.id == 'ci-custom' + - name: Lint Python run: script/lint-python - if: ${{ matrix.id == 'lint-python' }} + if: matrix.id == 'lint-python' - run: esphome compile ${{ matrix.file }} - if: ${{ matrix.id == 'test' }} + if: matrix.id == 'test' + env: + # Also cache libdeps, store them in a ~/.platformio subfolder + PLATFORMIO_LIBDEPS_DIR: ~/.platformio/libdeps - name: Run pytest run: | pytest -vv --tb=native tests - if: ${{ matrix.id == 'pytest' }} + if: matrix.id == 'pytest' + + # Also run git-diff-index so that the step is marked as failed on formatting errors, + # since clang-format doesn't do anything but change files if -i is passed. + - name: Run clang-format + run: | + script/clang-format -i + git diff-index --quiet HEAD -- + if: matrix.id == 'clang-format' + + - name: Run clang-tidy + run: | + script/clang-tidy --all-headers --fix ${{ matrix.options }} + if: matrix.id == 'clang-tidy' + env: + # Also cache libdeps, store them in a ~/.platformio subfolder + PLATFORMIO_LIBDEPS_DIR: ~/.platformio/libdeps + + - name: Suggested changes + run: script/ci-suggest-changes + if: always() && (matrix.id == 'clang-tidy' || matrix.id == 'clang-format') diff --git a/.github/workflows/docker-lint-build.yml b/.github/workflows/docker-lint-build.yml deleted file mode 100644 index 8350d4c719..0000000000 --- a/.github/workflows/docker-lint-build.yml +++ /dev/null @@ -1,100 +0,0 @@ -name: Build and publish lint docker image - -# Only run when docker paths change -on: - push: - branches: [dev] - paths: - - 'docker/Dockerfile.lint' - - 'requirements.txt' - - 'requirements_optional.txt' - - 'requirements_test.txt' - - 'platformio.ini' - - '.github/workflows/docker-lint-build.yml' - -jobs: - deploy-docker: - name: Build and publish docker containers - if: github.repository == 'esphome/esphome' - runs-on: ubuntu-latest - strategy: - matrix: - arch: [amd64, armv7, aarch64] - build_type: ["lint"] - steps: - - uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: '3.9' - - name: Set TAG - run: | - echo "TAG=1.2" >> $GITHUB_ENV - - - name: Run build - run: | - docker/build.py \ - --tag "${TAG}" \ - --arch "${{ matrix.arch }}" \ - --build-type "${{ matrix.build_type }}" \ - build - - - name: Log in to docker hub - uses: docker/login-action@v1 - with: - username: ${{ secrets.DOCKER_USER }} - password: ${{ secrets.DOCKER_PASSWORD }} - - name: Log in to the GitHub container registry - uses: docker/login-action@v1 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Run push - run: | - docker/build.py \ - --tag "${TAG}" \ - --arch "${{ matrix.arch }}" \ - --build-type "${{ matrix.build_type }}" \ - push - - deploy-docker-manifest: - if: github.repository == 'esphome/esphome' - runs-on: ubuntu-latest - needs: [deploy-docker] - strategy: - matrix: - build_type: ["lint"] - steps: - - uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: '3.9' - - name: Set TAG - run: | - echo "TAG=1.2" >> $GITHUB_ENV - - name: Enable experimental manifest support - run: | - mkdir -p ~/.docker - echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json - - - name: Log in to docker hub - uses: docker/login-action@v1 - with: - username: ${{ secrets.DOCKER_USER }} - password: ${{ secrets.DOCKER_PASSWORD }} - - name: Log in to the GitHub container registry - uses: docker/login-action@v1 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Run manifest - run: | - docker/build.py \ - --tag "${TAG}" \ - --build-type "${{ matrix.build_type }}" \ - manifest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3dc4952422..afd893d065 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -57,7 +57,7 @@ jobs: strategy: matrix: arch: [amd64, armv7, aarch64] - build_type: ["ha-addon", "docker"] + build_type: ["ha-addon", "docker", "lint"] steps: - uses: actions/checkout@v2 - name: Set up Python @@ -65,13 +65,10 @@ jobs: with: python-version: '3.9' - - name: Run build - run: | - docker/build.py \ - --tag "${{ needs.init.outputs.tag }}" \ - --arch "${{ matrix.arch }}" \ - --build-type "${{ matrix.build_type }}" \ - build + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - name: Set up QEMU + uses: docker/setup-qemu-action@v1 - name: Log in to docker hub uses: docker/login-action@v1 @@ -85,13 +82,14 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Run push + - name: Build and push run: | docker/build.py \ --tag "${{ needs.init.outputs.tag }}" \ --arch "${{ matrix.arch }}" \ --build-type "${{ matrix.build_type }}" \ - push + build \ + --push deploy-docker-manifest: if: github.repository == 'esphome/esphome' @@ -99,7 +97,7 @@ jobs: needs: [init, deploy-docker] strategy: matrix: - build_type: ["ha-addon", "docker"] + build_type: ["ha-addon", "docker", "lint"] steps: - uses: actions/checkout@v2 - name: Set up Python diff --git a/docker/Dockerfile b/docker/Dockerfile index 907c041119..d3ee219de8 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,5 +1,55 @@ -ARG BUILD_FROM=esphome/esphome-base:latest -FROM ${BUILD_FROM} +# Build these with the build.py script +# Example: +# python3 docker/build.py --tag dev --arch amd64 --build-type docker build + +# One of "docker", "hassio" +ARG BASEIMGTYPE=docker + +FROM ghcr.io/hassio-addons/debian-base/amd64:5.0.0 AS base-hassio-amd64 +FROM ghcr.io/hassio-addons/debian-base/aarch64:5.0.0 AS base-hassio-arm64 +FROM ghcr.io/hassio-addons/debian-base/armv7:5.0.0 AS base-hassio-armv7 +FROM debian:bullseye-20210816-slim AS base-docker-amd64 +FROM debian:bullseye-20210816-slim AS base-docker-arm64 +FROM debian:bullseye-20210816-slim AS base-docker-armv7 + +# Use TARGETARCH/TARGETVARIANT defined by docker +# https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope +FROM base-${BASEIMGTYPE}-${TARGETARCH}${TARGETVARIANT} AS base + +RUN \ + apt-get update \ + # Use pinned versions so that we get updates with build caching + && apt-get install -y --no-install-recommends \ + python3=3.9.2-3 \ + python3-pip=20.3.4-4 \ + python3-setuptools=52.0.0-4 \ + python3-pil=8.1.2+dfsg-0.3 \ + python3-cryptography=3.3.2-1 \ + iputils-ping=3:20210202-1 \ + git=1:2.30.2-1 \ + curl=7.74.0-1.3+b1 \ + && rm -rf \ + /tmp/* \ + /var/{cache,log}/* \ + /var/lib/apt/lists/* + +ENV \ + # Fix click python3 lang warning https://click.palletsprojects.com/en/7.x/python3/ + LANG=C.UTF-8 LC_ALL=C.UTF-8 \ + # Store globally installed pio libs in /piolibs + PLATFORMIO_GLOBALLIB_DIR=/piolibs + +RUN \ + # Ubuntu python3-pip is missing wheel + pip3 install --no-cache-dir \ + wheel==0.36.2 \ + platformio==5.2.0 \ + # Change some platformio settings + && platformio settings set enable_telemetry No \ + && platformio settings set check_libraries_interval 1000000 \ + && platformio settings set check_platformio_interval 1000000 \ + && platformio settings set check_platforms_interval 1000000 \ + && mkdir -p /piolibs # First install requirements to leverage caching when requirements don't change COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini / @@ -7,9 +57,14 @@ RUN \ pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \ && /platformio_install_deps.py /platformio.ini -# Then copy esphome and install -COPY . . -RUN pip3 install --no-cache-dir -e . + + +# ======================= docker-type image ======================= +FROM base AS docker + +# Copy esphome and install +COPY . /esphome +RUN pip3 install --no-cache-dir -e /esphome # Settings for dashboard ENV USERNAME="" PASSWORD="" @@ -17,14 +72,74 @@ ENV USERNAME="" PASSWORD="" # Expose the dashboard to Docker EXPOSE 6052 -# Run healthcheck (heartbeat) -HEALTHCHECK --interval=30s --timeout=30s \ - CMD curl --fail http://localhost:6052 || exit 1 +COPY docker/docker_entrypoint.sh /entrypoint.sh # The directory the user should mount their configuration files to +VOLUME /config WORKDIR /config -# Set entrypoint to esphome so that the user doesn't have to type 'esphome' +# Set entrypoint to esphome (via a script) so that the user doesn't have to type 'esphome' # in every docker command twice -ENTRYPOINT ["esphome"] +ENTRYPOINT ["/entrypoint.sh"] # When no arguments given, start the dashboard in the workdir CMD ["dashboard", "/config"] + + + + +# ======================= hassio-type image ======================= +FROM base AS hassio + +RUN \ + apt-get update \ + # Use pinned versions so that we get updates with build caching + && apt-get install -y --no-install-recommends \ + nginx=1.18.0-6.1 \ + && rm -rf \ + /tmp/* \ + /var/{cache,log}/* \ + /var/lib/apt/lists/* + +ARG BUILD_VERSION=dev + +# Copy root filesystem +COPY docker/hassio-rootfs/ / + +# Copy esphome and install +COPY . /esphome +RUN pip3 install --no-cache-dir -e /esphome + +# Labels +LABEL \ + io.hass.name="ESPHome" \ + io.hass.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \ + io.hass.type="addon" \ + io.hass.version="${BUILD_VERSION}" + # io.hass.arch is inherited from addon-debian-base + + + + +# ======================= lint-type image ======================= +FROM base AS lint + +ENV \ + PLATFORMIO_CORE_DIR=/esphome/.temp/platformio + +RUN \ + apt-get update \ + # Use pinned versions so that we get updates with build caching + && apt-get install -y --no-install-recommends \ + clang-format-11=1:11.0.1-2 \ + clang-tidy-11=1:11.0.1-2 \ + patch=2.7.6-7 \ + software-properties-common=0.96.20.2-2.1 \ + nano=5.4-2 \ + build-essential=12.9 \ + python3-dev=3.9.2-3 \ + && rm -rf \ + /tmp/* \ + /var/{cache,log}/* \ + /var/lib/apt/lists/* + +VOLUME ["/esphome"] +WORKDIR /esphome diff --git a/docker/Dockerfile.dev b/docker/Dockerfile.dev deleted file mode 100644 index c646ce989b..0000000000 --- a/docker/Dockerfile.dev +++ /dev/null @@ -1 +0,0 @@ -FROM esphome/esphome-lint:1.2 diff --git a/docker/Dockerfile.hassio b/docker/Dockerfile.hassio deleted file mode 100644 index ad80074ada..0000000000 --- a/docker/Dockerfile.hassio +++ /dev/null @@ -1,25 +0,0 @@ -ARG BUILD_FROM=esphome/esphome-hassio-base:latest -FROM ${BUILD_FROM} - -# First install requirements to leverage caching when requirements don't change -COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini / -RUN \ - pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \ - && /platformio_install_deps.py /platformio.ini - -# Copy root filesystem -COPY docker/rootfs/ / - -# Then copy esphome and install -COPY . /opt/esphome/ -RUN pip3 install --no-cache-dir -e /opt/esphome - -# Build arguments -ARG BUILD_VERSION=dev - -# Labels -LABEL \ - io.hass.name="ESPHome" \ - io.hass.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \ - io.hass.type="addon" \ - io.hass.version=${BUILD_VERSION} diff --git a/docker/Dockerfile.lint b/docker/Dockerfile.lint deleted file mode 100644 index 3a090c3b41..0000000000 --- a/docker/Dockerfile.lint +++ /dev/null @@ -1,10 +0,0 @@ -ARG BUILD_FROM=esphome/esphome-lint-base:latest -FROM ${BUILD_FROM} - -COPY requirements.txt requirements_optional.txt requirements_test.txt docker/platformio_install_deps.py platformio.ini / -RUN \ - pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt -r /requirements_test.txt \ - && /platformio_install_deps.py /platformio.ini - -VOLUME ["/esphome"] -WORKDIR /esphome diff --git a/docker/build.py b/docker/build.py index c926b3653b..1904457989 100755 --- a/docker/build.py +++ b/docker/build.py @@ -2,7 +2,7 @@ from dataclasses import dataclass import subprocess import argparse -import platform +from platform import machine import shlex import re import sys @@ -24,9 +24,6 @@ TYPE_LINT = 'lint' TYPES = [TYPE_DOCKER, TYPE_HA_ADDON, TYPE_LINT] -BASE_VERSION = "4.2.0" - - parser = argparse.ArgumentParser() parser.add_argument("--tag", type=str, required=True, help="The main docker tag to push to. If a version number also adds latest and/or beta tag") parser.add_argument("--arch", choices=ARCHS, required=False, help="The architecture to build for") @@ -34,27 +31,17 @@ parser.add_argument("--build-type", choices=TYPES, required=True, help="The type parser.add_argument("--dry-run", action="store_true", help="Don't run any commands, just print them") subparsers = parser.add_subparsers(help="Action to perform", dest="command", required=True) build_parser = subparsers.add_parser("build", help="Build the image") -push_parser = subparsers.add_parser("push", help="Tag the already built image and push it to docker hub") +build_parser.add_argument("--push", help="Also push the images") manifest_parser = subparsers.add_parser("manifest", help="Create a manifest from already pushed images") - -# only lists some possibilities, doesn't have to be perfect -# https://stackoverflow.com/a/45125525 -UNAME_TO_ARCH = { - "x86_64": ARCH_AMD64, - "aarch64": ARCH_AARCH64, - "aarch64_be": ARCH_AARCH64, - "arm": ARCH_ARMV7, -} - - @dataclass(frozen=True) class DockerParams: - build_from: str build_to: str manifest_to: str - dockerfile: str + baseimgtype: str + platform: str + target: str @classmethod def for_type_arch(cls, build_type, arch): @@ -63,18 +50,28 @@ class DockerParams: TYPE_HA_ADDON: "esphome/esphome-hassio", TYPE_LINT: "esphome/esphome-lint" }[build_type] - build_from = f"ghcr.io/{prefix}-base-{arch}:{BASE_VERSION}" build_to = f"{prefix}-{arch}" - dockerfile = { - TYPE_DOCKER: "docker/Dockerfile", - TYPE_HA_ADDON: "docker/Dockerfile.hassio", - TYPE_LINT: "docker/Dockerfile.lint", + baseimgtype = { + TYPE_DOCKER: "docker", + TYPE_HA_ADDON: "hassio", + TYPE_LINT: "docker", + }[build_type] + platform = { + ARCH_AMD64: "linux/amd64", + ARCH_ARMV7: "linux/arm/v7", + ARCH_AARCH64: "linux/arm64", + }[arch] + target = { + TYPE_DOCKER: "docker", + TYPE_HA_ADDON: "hassio", + TYPE_LINT: "lint", }[build_type] return cls( - build_from=build_from, build_to=build_to, manifest_to=prefix, - dockerfile=dockerfile + baseimgtype=baseimgtype, + platform=platform, + target=target, ) @@ -117,41 +114,26 @@ def main(): CHANNEL_RELEASE: "latest", }[channel] cache_img = f"ghcr.io/{params.build_to}:{cache_tag}" - run_command("docker", "pull", cache_img, ignore_error=True) - # 2. register QEMU binfmt (if not host arch) - is_native = UNAME_TO_ARCH.get(platform.machine()) == args.arch - if not is_native: - run_command( - "docker", "run", "--rm", "--privileged", "multiarch/qemu-user-static:5.2.0-2", - "--reset", "-p", "yes" - ) - - # 3. build - run_command( - "docker", "build", - "--build-arg", f"BUILD_FROM={params.build_from}", - "--build-arg", f"BUILD_VERSION={args.tag}", - "--tag", f"{params.build_to}:{args.tag}", - "--cache-from", cache_img, - "--file", params.dockerfile, - "." - ) - elif args.command == "push": - params = DockerParams.for_type_arch(args.build_type, args.arch) imgs = [f"{params.build_to}:{tag}" for tag in tags_to_push] imgs += [f"ghcr.io/{params.build_to}:{tag}" for tag in tags_to_push] - src = imgs[0] - # 1. tag images - for img in imgs[1:]: - run_command( - "docker", "tag", src, img - ) - # 2. push images + + # 3. build + cmd = [ + "docker", "buildx", "build", + "--build-arg", f"BASEIMGTYPE={params.baseimgtype}", + "--build-arg", f"BUILD_VERSION={args.tag}", + "--cache-from", cache_img, + "--file", "docker/Dockerfile", + "--platform", params.platform, + "--target", params.target, + ] for img in imgs: - run_command( - "docker", "push", img - ) + cmd += ["--tag", img] + if args.push: + cmd.append("--push") + + run_command(*cmd, ".") elif args.command == "manifest": manifest = DockerParams.for_type_arch(args.build_type, ARCH_AMD64).manifest_to diff --git a/docker/docker_entrypoint.sh b/docker/docker_entrypoint.sh new file mode 100755 index 0000000000..b3905d1fed --- /dev/null +++ b/docker/docker_entrypoint.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +# If /cache is mounted, use that as PIO's coredir +# otherwise use path in /config (so that PIO packages aren't downloaded on each compile) + +if [[ -d /cache ]]; then + export PLATFORMIO_CORE_DIR=/cache/platformio +else + export PLATFORMIO_CORE_DIR=/config/.esphome/platformio +fi + +if [[ ! -d "${PLATFORMIO_CORE_DIR}" ]]; then + echo "Creating cache directory ${PLATFORMIO_CORE_DIR}" + echo "You can change this behavior by mounting a directory to the container's /cache directory." + mkdir -p "${PLATFORMIO_CORE_DIR}" +fi + +exec esphome "$@" diff --git a/docker/rootfs/etc/cont-init.d/10-requirements.sh b/docker/hassio-rootfs/etc/cont-init.d/10-requirements.sh similarity index 100% rename from docker/rootfs/etc/cont-init.d/10-requirements.sh rename to docker/hassio-rootfs/etc/cont-init.d/10-requirements.sh diff --git a/docker/rootfs/etc/cont-init.d/20-nginx.sh b/docker/hassio-rootfs/etc/cont-init.d/20-nginx.sh similarity index 100% rename from docker/rootfs/etc/cont-init.d/20-nginx.sh rename to docker/hassio-rootfs/etc/cont-init.d/20-nginx.sh diff --git a/docker/hassio-rootfs/etc/cont-init.d/30-dirs.sh b/docker/hassio-rootfs/etc/cont-init.d/30-dirs.sh new file mode 100644 index 0000000000..301fe4db63 --- /dev/null +++ b/docker/hassio-rootfs/etc/cont-init.d/30-dirs.sh @@ -0,0 +1,9 @@ +#!/usr/bin/with-contenv bashio +# ============================================================================== +# Community Hass.io Add-ons: ESPHome +# This files creates all directories used by esphome +# ============================================================================== + +PLATFORMIO_CORE_DIR=/data/cache/platformio + +mkdir -p "${PLATFORMIO_CORE_DIR}" diff --git a/docker/rootfs/etc/nginx/includes/mime.types b/docker/hassio-rootfs/etc/nginx/includes/mime.types similarity index 100% rename from docker/rootfs/etc/nginx/includes/mime.types rename to docker/hassio-rootfs/etc/nginx/includes/mime.types diff --git a/docker/rootfs/etc/nginx/includes/proxy_params.conf b/docker/hassio-rootfs/etc/nginx/includes/proxy_params.conf similarity index 100% rename from docker/rootfs/etc/nginx/includes/proxy_params.conf rename to docker/hassio-rootfs/etc/nginx/includes/proxy_params.conf diff --git a/docker/rootfs/etc/nginx/includes/server_params.conf b/docker/hassio-rootfs/etc/nginx/includes/server_params.conf similarity index 100% rename from docker/rootfs/etc/nginx/includes/server_params.conf rename to docker/hassio-rootfs/etc/nginx/includes/server_params.conf diff --git a/docker/rootfs/etc/nginx/includes/ssl_params.conf b/docker/hassio-rootfs/etc/nginx/includes/ssl_params.conf similarity index 100% rename from docker/rootfs/etc/nginx/includes/ssl_params.conf rename to docker/hassio-rootfs/etc/nginx/includes/ssl_params.conf diff --git a/docker/rootfs/etc/nginx/nginx.conf b/docker/hassio-rootfs/etc/nginx/nginx.conf similarity index 100% rename from docker/rootfs/etc/nginx/nginx.conf rename to docker/hassio-rootfs/etc/nginx/nginx.conf diff --git a/docker/rootfs/etc/nginx/servers/direct-ssl.disabled b/docker/hassio-rootfs/etc/nginx/servers/direct-ssl.disabled similarity index 100% rename from docker/rootfs/etc/nginx/servers/direct-ssl.disabled rename to docker/hassio-rootfs/etc/nginx/servers/direct-ssl.disabled diff --git a/docker/rootfs/etc/nginx/servers/direct.disabled b/docker/hassio-rootfs/etc/nginx/servers/direct.disabled similarity index 100% rename from docker/rootfs/etc/nginx/servers/direct.disabled rename to docker/hassio-rootfs/etc/nginx/servers/direct.disabled diff --git a/docker/rootfs/etc/nginx/servers/ingress.conf b/docker/hassio-rootfs/etc/nginx/servers/ingress.conf similarity index 100% rename from docker/rootfs/etc/nginx/servers/ingress.conf rename to docker/hassio-rootfs/etc/nginx/servers/ingress.conf diff --git a/docker/rootfs/etc/services.d/esphome/finish b/docker/hassio-rootfs/etc/services.d/esphome/finish similarity index 100% rename from docker/rootfs/etc/services.d/esphome/finish rename to docker/hassio-rootfs/etc/services.d/esphome/finish diff --git a/docker/rootfs/etc/services.d/esphome/run b/docker/hassio-rootfs/etc/services.d/esphome/run similarity index 89% rename from docker/rootfs/etc/services.d/esphome/run rename to docker/hassio-rootfs/etc/services.d/esphome/run index f806c50929..6218b200bd 100755 --- a/docker/rootfs/etc/services.d/esphome/run +++ b/docker/hassio-rootfs/etc/services.d/esphome/run @@ -22,5 +22,8 @@ if bashio::config.has_value 'relative_url'; then export ESPHOME_DASHBOARD_RELATIVE_URL=$(bashio::config 'relative_url') fi +export PLATFORMIO_CORE_DIR=/data/cache/platformio +export PLATFORMIO_GLOBALLIB_DIR=/piolibs + bashio::log.info "Starting ESPHome dashboard..." exec esphome dashboard /config/esphome --socket /var/run/esphome.sock --hassio diff --git a/docker/rootfs/etc/services.d/nginx/finish b/docker/hassio-rootfs/etc/services.d/nginx/finish similarity index 100% rename from docker/rootfs/etc/services.d/nginx/finish rename to docker/hassio-rootfs/etc/services.d/nginx/finish diff --git a/docker/rootfs/etc/services.d/nginx/run b/docker/hassio-rootfs/etc/services.d/nginx/run similarity index 100% rename from docker/rootfs/etc/services.d/nginx/run rename to docker/hassio-rootfs/etc/services.d/nginx/run diff --git a/esphome/platformio_api.py b/esphome/platformio_api.py index 6506a44e88..a99081a650 100644 --- a/esphome/platformio_api.py +++ b/esphome/platformio_api.py @@ -67,8 +67,8 @@ FILTER_PLATFORMIO_LINES = [ def run_platformio_cli(*args, **kwargs) -> Union[str, int]: os.environ["PLATFORMIO_FORCE_COLOR"] = "true" os.environ["PLATFORMIO_BUILD_DIR"] = os.path.abspath(CORE.relative_pioenvs_path()) - os.environ["PLATFORMIO_LIBDEPS_DIR"] = os.path.abspath( - CORE.relative_piolibdeps_path() + os.environ.setdefault( + "PLATFORMIO_LIBDEPS_DIR", os.path.abspath(CORE.relative_piolibdeps_path()) ) cmd = ["platformio"] + list(args) diff --git a/script/ci-custom.py b/script/ci-custom.py index cdc450a96b..3191842d8c 100755 --- a/script/ci-custom.py +++ b/script/ci-custom.py @@ -217,7 +217,9 @@ def lint_ext_check(fname): ) -@lint_file_check(exclude=["docker/rootfs/*", "docker/*.py", "script/*", "setup.py"]) +@lint_file_check( + exclude=["**.sh", "docker/hassio-rootfs/**", "docker/*.py", "script/*", "setup.py"] +) def lint_executable_bit(fname): ex = EXECUTABLE_BIT[fname] if ex != 100644: diff --git a/script/devcontainer-post-create b/script/devcontainer-post-create index 4b4dc5b6c9..120ab3307d 100755 --- a/script/devcontainer-post-create +++ b/script/devcontainer-post-create @@ -12,7 +12,6 @@ if [ ! -f $cpp_json ]; then pio init --ide vscode --silent sed -i "/\\/workspaces\/esphome\/include/d" $cpp_json else - echo "Cpp environment already configured. To reconfigure it you could run one the following commands:" - echo " pio init --ide vscode -e livingroom8266" - echo " pio init --ide vscode -e livingroom32" + echo "Cpp environment already configured. To reconfigure it you can run one the following commands:" + echo " pio init --ide vscode" fi From 5f21b925dad3a99819402bb294cef9182c956c45 Mon Sep 17 00:00:00 2001 From: synco Date: Mon, 20 Sep 2021 19:12:50 +1200 Subject: [PATCH 059/207] Calculating the AC only component of the samples (#1906) Co-authored-by: Synco Reynders Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- .../components/ct_clamp/ct_clamp_sensor.cpp | 46 ++++--------------- esphome/components/ct_clamp/ct_clamp_sensor.h | 15 +++--- 2 files changed, 18 insertions(+), 43 deletions(-) diff --git a/esphome/components/ct_clamp/ct_clamp_sensor.cpp b/esphome/components/ct_clamp/ct_clamp_sensor.cpp index c27134d6ac..130cdfefba 100644 --- a/esphome/components/ct_clamp/ct_clamp_sensor.cpp +++ b/esphome/components/ct_clamp/ct_clamp_sensor.cpp @@ -8,18 +8,6 @@ namespace ct_clamp { static const char *const TAG = "ct_clamp"; -void CTClampSensor::setup() { - this->is_calibrating_offset_ = true; - this->high_freq_.start(); - this->set_timeout("calibrate_offset", this->sample_duration_, [this]() { - this->high_freq_.stop(); - this->is_calibrating_offset_ = false; - if (this->num_samples_ != 0) { - this->offset_ = this->sample_sum_ / this->num_samples_; - } - }); -} - void CTClampSensor::dump_config() { LOG_SENSOR("", "CT Clamp Sensor", this); ESP_LOGCONFIG(TAG, " Sample Duration: %.2fs", this->sample_duration_ / 1e3f); @@ -27,9 +15,6 @@ void CTClampSensor::dump_config() { } void CTClampSensor::update() { - if (this->is_calibrating_offset_) - return; - // Update only starts the sampling phase, in loop() the actual sampling is happening. // Request a high loop() execution interval during sampling phase. @@ -46,20 +31,23 @@ void CTClampSensor::update() { return; } - float raw = this->sample_sum_ / this->num_samples_; - float irms = std::sqrt(raw); - ESP_LOGD(TAG, "'%s' - Raw Value: %.2fA", this->name_.c_str(), irms); - this->publish_state(irms); + float dc = this->sample_sum_ / this->num_samples_; + float var = (this->sample_squared_sum_ / this->num_samples_) - dc * dc; + float ac = std::sqrt(var); + ESP_LOGD(TAG, "'%s' - Got %d samples", this->name_.c_str(), this->num_samples_); + ESP_LOGD(TAG, "'%s' - Raw AC Value: %.3fA", this->name_.c_str(), ac); + this->publish_state(ac); }); // Set sampling values this->is_sampling_ = true; this->num_samples_ = 0; this->sample_sum_ = 0.0f; + this->sample_squared_sum_ = 0.0f; } void CTClampSensor::loop() { - if (!this->is_sampling_ && !this->is_calibrating_offset_) + if (!this->is_sampling_) return; // Perform a single sample @@ -67,22 +55,8 @@ void CTClampSensor::loop() { if (isnan(value)) return; - if (this->is_calibrating_offset_) { - this->sample_sum_ += value; - this->num_samples_++; - return; - } - - // Adjust DC offset via low pass filter (exponential moving average) - const float alpha = 0.001f; - this->offset_ = this->offset_ * (1 - alpha) + value * alpha; - - // Filtered value centered around the mid-point (0V) - float filtered = value - this->offset_; - - // IRMS is sqrt(∑v_i²) - float sq = filtered * filtered; - this->sample_sum_ += sq; + this->sample_sum_ += value; + this->sample_squared_sum_ += value * value; this->num_samples_++; } diff --git a/esphome/components/ct_clamp/ct_clamp_sensor.h b/esphome/components/ct_clamp/ct_clamp_sensor.h index c709f6718b..2f201c11a0 100644 --- a/esphome/components/ct_clamp/ct_clamp_sensor.h +++ b/esphome/components/ct_clamp/ct_clamp_sensor.h @@ -10,7 +10,6 @@ namespace ct_clamp { class CTClampSensor : public sensor::Sensor, public PollingComponent { public: - void setup() override; void update() override; void loop() override; void dump_config() override; @@ -35,17 +34,19 @@ class CTClampSensor : public sensor::Sensor, public PollingComponent { * * Diagram: https://learn.openenergymonitor.org/electricity-monitoring/ct-sensors/interface-with-arduino * - * This is automatically calculated with an exponential moving average/digital low pass filter. - * - * 0.5 is a good initial approximation to start with for most ESP8266 setups. + * The current clamp only measures AC, so any DC component is an unwanted artifact from the + * sampling circuit. The AC component is essentially the same as the calculating the Standard-Deviation, + * which can be done by cumulating 3 values per sample: + * 1) Number of samples + * 2) Sum of samples + * 3) Sum of sample squared + * https://en.wikipedia.org/wiki/Root_mean_square */ - float offset_ = 0.5f; float sample_sum_ = 0.0f; + float sample_squared_sum_ = 0.0f; uint32_t num_samples_ = 0; bool is_sampling_ = false; - /// Calibrate offset value once at boot - bool is_calibrating_offset_ = false; }; } // namespace ct_clamp From 82eca13d7b84a8972836b078531c1236bdd991bc Mon Sep 17 00:00:00 2001 From: besteru <69540218+besteru@users.noreply.github.com> Date: Mon, 20 Sep 2021 10:14:44 +0300 Subject: [PATCH 060/207] Fix error reporting for DHT bit read loop (#2344) --- esphome/components/dht/dht.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esphome/components/dht/dht.cpp b/esphome/components/dht/dht.cpp index a7f5747d68..734dde20a8 100644 --- a/esphome/components/dht/dht.cpp +++ b/esphome/components/dht/dht.cpp @@ -122,6 +122,8 @@ bool HOT ICACHE_RAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, break; } } + if (error_code != 0) + break; start_time = micros(); uint32_t end_time = start_time; @@ -136,6 +138,8 @@ bool HOT ICACHE_RAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, break; } } + if (error_code != 0) + break; if (i < 0) continue; From fff5ba03c25757ee45ad2b587a5948ebbd38f6a7 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 20 Sep 2021 09:29:09 +0200 Subject: [PATCH 061/207] Also run docker CI when requirements change (#2347) --- .github/workflows/ci-docker.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index 33b15cb1dc..12f5a7dfc2 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -7,11 +7,15 @@ on: paths: - 'docker/**' - '.github/workflows/**' + - 'requirements*.txt' + - 'platformio.ini' pull_request: paths: - 'docker/**' - '.github/workflows/**' + - 'requirements*.txt' + - 'platformio.ini' jobs: check-docker: From 945ed5d3bda2a1080f875eb6ad796e72c38adbb2 Mon Sep 17 00:00:00 2001 From: synco Date: Mon, 20 Sep 2021 19:29:47 +1200 Subject: [PATCH 062/207] Added graphing component (#2109) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: Oxan van Leeuwen Co-authored-by: Synco Reynders Co-authored-by: Otto winter --- CODEOWNERS | 1 + esphome/components/display/display_buffer.cpp | 7 + esphome/components/display/display_buffer.h | 28 ++ esphome/components/graph/__init__.py | 216 +++++++++++ esphome/components/graph/graph.cpp | 361 ++++++++++++++++++ esphome/components/graph/graph.h | 178 +++++++++ esphome/const.py | 14 + esphome/core/defines.h | 1 + 8 files changed, 806 insertions(+) create mode 100644 esphome/components/graph/__init__.py create mode 100644 esphome/components/graph/graph.cpp create mode 100644 esphome/components/graph/graph.h diff --git a/CODEOWNERS b/CODEOWNERS index 8aa96d14af..ab3a0815ce 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -54,6 +54,7 @@ esphome/components/fingerprint_grow/* @OnFreund @loongyh esphome/components/globals/* @esphome/core esphome/components/gpio/* @esphome/core esphome/components/gps/* @coogle +esphome/components/graph/* @synco esphome/components/havells_solar/* @sourabhjaiswal esphome/components/hbridge/fan/* @WeekendWarrior esphome/components/hbridge/light/* @DotNetDann diff --git a/esphome/components/display/display_buffer.cpp b/esphome/components/display/display_buffer.cpp index 9c4ee3189f..bbb7444cb5 100644 --- a/esphome/components/display/display_buffer.cpp +++ b/esphome/components/display/display_buffer.cpp @@ -233,6 +233,13 @@ void DisplayBuffer::image(int x, int y, Image *image, Color color_on, Color colo } } +#ifdef USE_GRAPH +void DisplayBuffer::graph(int x, int y, graph::Graph *graph, Color color_on) { graph->draw(this, x, y, color_on); } +void DisplayBuffer::legend(int x, int y, graph::Graph *graph, Color color_on) { + graph->draw_legend(this, x, y, color_on); +} +#endif // USE_GRAPH + void DisplayBuffer::get_text_bounds(int x, int y, const char *text, Font *font, TextAlign align, int *x1, int *y1, int *width, int *height) { int x_offset, baseline; diff --git a/esphome/components/display/display_buffer.h b/esphome/components/display/display_buffer.h index b89eab0dba..3f89d3f8d2 100644 --- a/esphome/components/display/display_buffer.h +++ b/esphome/components/display/display_buffer.h @@ -9,6 +9,10 @@ #include "esphome/components/time/real_time_clock.h" #endif +#ifdef USE_GRAPH +#include "esphome/components/graph/graph.h" +#endif + namespace esphome { namespace display { @@ -273,6 +277,30 @@ class DisplayBuffer { */ void image(int x, int y, Image *image, Color color_on = COLOR_ON, Color color_off = COLOR_OFF); +#ifdef USE_GRAPH + /** Draw the `graph` with the top-left corner at [x,y] to the screen. + * + * @param x The x coordinate of the upper left corner. + * @param y The y coordinate of the upper left corner. + * @param graph The graph id to draw + * @param color_on The color to replace in binary images for the on bits. + */ + void graph(int x, int y, graph::Graph *graph, Color color_on = COLOR_ON); + + /** Draw the `legend` for graph with the top-left corner at [x,y] to the screen. + * + * @param x The x coordinate of the upper left corner. + * @param y The y coordinate of the upper left corner. + * @param graph The graph id for which the legend applies to + * @param graph The graph id for which the legend applies to + * @param graph The graph id for which the legend applies to + * @param name_font The font used for the trace name + * @param value_font The font used for the trace value and units + * @param color_on The color of the border + */ + void legend(int x, int y, graph::Graph *graph, Color color_on = COLOR_ON); +#endif // USE_GRAPH + /** Get the text bounds of the given string. * * @param x The x coordinate to place the string at, can be 0 if only interested in dimensions. diff --git a/esphome/components/graph/__init__.py b/esphome/components/graph/__init__.py new file mode 100644 index 0000000000..12acfee869 --- /dev/null +++ b/esphome/components/graph/__init__.py @@ -0,0 +1,216 @@ +from esphome.components.font import Font +from esphome.components import sensor, color +import esphome.config_validation as cv +import esphome.codegen as cg +from esphome.const import ( + CONF_COLOR, + CONF_DIRECTION, + CONF_DURATION, + CONF_ID, + CONF_LEGEND, + CONF_NAME, + CONF_NAME_FONT, + CONF_SHOW_LINES, + CONF_SHOW_UNITS, + CONF_SHOW_VALUES, + CONF_VALUE_FONT, + CONF_WIDTH, + CONF_SENSOR, + CONF_HEIGHT, + CONF_MIN_VALUE, + CONF_MAX_VALUE, + CONF_MIN_RANGE, + CONF_MAX_RANGE, + CONF_LINE_THICKNESS, + CONF_LINE_TYPE, + CONF_X_GRID, + CONF_Y_GRID, + CONF_BORDER, + CONF_TRACES, +) + +CODEOWNERS = ["@synco"] + +DEPENDENCIES = ["display", "sensor"] +MULTI_CONF = True + +graph_ns = cg.esphome_ns.namespace("graph") +Graph_ = graph_ns.class_("Graph", cg.Component) +GraphTrace = graph_ns.class_("GraphTrace") +GraphLegend = graph_ns.class_("GraphLegend") + +LineType = graph_ns.enum("LineType") +LINE_TYPE = { + "SOLID": LineType.LINE_TYPE_SOLID, + "DOTTED": LineType.LINE_TYPE_DOTTED, + "DASHED": LineType.LINE_TYPE_DASHED, +} + +DirectionType = graph_ns.enum("DirectionType") +DIRECTION_TYPE = { + "AUTO": DirectionType.DIRECTION_TYPE_AUTO, + "HORIZONTAL": DirectionType.DIRECTION_TYPE_HORIZONTAL, + "VERTICAL": DirectionType.DIRECTION_TYPE_VERTICAL, +} + +ValuePositionType = graph_ns.enum("ValuePositionType") +VALUE_POSITION_TYPE = { + "NONE": ValuePositionType.VALUE_POSITION_TYPE_NONE, + "AUTO": ValuePositionType.VALUE_POSITION_TYPE_AUTO, + "BESIDE": ValuePositionType.VALUE_POSITION_TYPE_BESIDE, + "BELOW": ValuePositionType.VALUE_POSITION_TYPE_BELOW, +} + + +GRAPH_TRACE_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(GraphTrace), + cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor), + cv.Optional(CONF_NAME): cv.string, + cv.Optional(CONF_LINE_THICKNESS): cv.positive_int, + cv.Optional(CONF_LINE_TYPE): cv.enum(LINE_TYPE, upper=True), + cv.Optional(CONF_COLOR): cv.use_id(color.ColorStruct), + } +) + +GRAPH_LEGEND_SCHEMA = cv.Schema( + { + cv.GenerateID(CONF_ID): cv.declare_id(GraphLegend), + cv.Required(CONF_NAME_FONT): cv.use_id(Font), + cv.Optional(CONF_VALUE_FONT): cv.use_id(Font), + cv.Optional(CONF_WIDTH): cv.positive_not_null_int, + cv.Optional(CONF_HEIGHT): cv.positive_not_null_int, + cv.Optional(CONF_BORDER): cv.boolean, + cv.Optional(CONF_SHOW_LINES): cv.boolean, + cv.Optional(CONF_SHOW_VALUES): cv.enum(VALUE_POSITION_TYPE, upper=True), + cv.Optional(CONF_SHOW_UNITS): cv.boolean, + cv.Optional(CONF_DIRECTION): cv.enum(DIRECTION_TYPE, upper=True), + } +) + + +GRAPH_SCHEMA = cv.Schema( + { + cv.Required(CONF_ID): cv.declare_id(Graph_), + cv.Required(CONF_DURATION): cv.positive_time_period_seconds, + cv.Required(CONF_WIDTH): cv.positive_not_null_int, + cv.Required(CONF_HEIGHT): cv.positive_not_null_int, + cv.Optional(CONF_X_GRID): cv.positive_time_period_seconds, + cv.Optional(CONF_Y_GRID): cv.float_range(min=0, min_included=False), + cv.Optional(CONF_BORDER): cv.boolean, + # Single trace options in base + cv.Optional(CONF_SENSOR): cv.use_id(sensor.Sensor), + cv.Optional(CONF_LINE_THICKNESS): cv.positive_int, + cv.Optional(CONF_LINE_TYPE): cv.enum(LINE_TYPE, upper=True), + cv.Optional(CONF_COLOR): cv.use_id(color.ColorStruct), + # Axis specific options (Future feature may be to add second Y-axis) + cv.Optional(CONF_MIN_VALUE): cv.float_, + cv.Optional(CONF_MAX_VALUE): cv.float_, + cv.Optional(CONF_MIN_RANGE): cv.float_range(min=0, min_included=False), + cv.Optional(CONF_MAX_RANGE): cv.float_range(min=0, min_included=False), + cv.Optional(CONF_TRACES): cv.ensure_list(GRAPH_TRACE_SCHEMA), + cv.Optional(CONF_LEGEND): cv.ensure_list(GRAPH_LEGEND_SCHEMA), + } +) + + +def _relocate_fields_to_subfolder(config, subfolder, subschema): + fields = [k.schema for k in subschema.schema.keys()] + fields.remove(CONF_ID) + if subfolder in config: + # Ensure no ambigious fields in base of config + for f in fields: + if f in config: + raise cv.Invalid( + "You cannot use the '" + + str(f) + + "' field when already using 'traces:'. " + "Please move it into 'traces:' entry." + ) + else: + # Copy over all fields to subfolder: + trace = {} + for f in fields: + if f in config: + trace[f] = config.pop(f) + config[subfolder] = cv.ensure_list(subschema)(trace) + return config + + +def _relocate_trace(config): + return _relocate_fields_to_subfolder(config, CONF_TRACES, GRAPH_TRACE_SCHEMA) + + +CONFIG_SCHEMA = cv.All( + GRAPH_SCHEMA, + _relocate_trace, +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + cg.add(var.set_duration(config[CONF_DURATION])) + cg.add(var.set_width(config[CONF_WIDTH])) + cg.add(var.set_height(config[CONF_HEIGHT])) + await cg.register_component(var, config) + + # Graph options + if CONF_X_GRID in config: + cg.add(var.set_grid_x(config[CONF_X_GRID])) + if CONF_Y_GRID in config: + cg.add(var.set_grid_y(config[CONF_Y_GRID])) + if CONF_BORDER in config: + cg.add(var.set_border(config[CONF_BORDER])) + # Axis related options + if CONF_MIN_VALUE in config: + cg.add(var.set_min_value(config[CONF_MIN_VALUE])) + if CONF_MAX_VALUE in config: + cg.add(var.set_max_value(config[CONF_MAX_VALUE])) + if CONF_MIN_RANGE in config: + cg.add(var.set_min_range(config[CONF_MIN_RANGE])) + if CONF_MAX_RANGE in config: + cg.add(var.set_max_range(config[CONF_MAX_RANGE])) + # Trace options + for trace in config[CONF_TRACES]: + tr = cg.new_Pvariable(trace[CONF_ID], GraphTrace()) + sens = await cg.get_variable(trace[CONF_SENSOR]) + cg.add(tr.set_sensor(sens)) + if CONF_NAME in trace: + cg.add(tr.set_name(trace[CONF_NAME])) + else: + cg.add(tr.set_name(trace[CONF_SENSOR].id)) + if CONF_LINE_THICKNESS in trace: + cg.add(tr.set_line_thickness(trace[CONF_LINE_THICKNESS])) + if CONF_LINE_TYPE in trace: + cg.add(tr.set_line_type(trace[CONF_LINE_TYPE])) + if CONF_COLOR in trace: + c = await cg.get_variable(trace[CONF_COLOR]) + cg.add(tr.set_line_color(c)) + cg.add(var.add_trace(tr)) + # Add legend + if CONF_LEGEND in config: + lgd = config[CONF_LEGEND][0] + legend = cg.new_Pvariable(lgd[CONF_ID], GraphLegend()) + if CONF_NAME_FONT in lgd: + font = await cg.get_variable(lgd[CONF_NAME_FONT]) + cg.add(legend.set_name_font(font)) + if CONF_VALUE_FONT in lgd: + font = await cg.get_variable(lgd[CONF_VALUE_FONT]) + cg.add(legend.set_value_font(font)) + if CONF_WIDTH in lgd: + cg.add(legend.set_width(lgd[CONF_WIDTH])) + if CONF_HEIGHT in lgd: + cg.add(legend.set_height(lgd[CONF_HEIGHT])) + if CONF_BORDER in lgd: + cg.add(legend.set_border(lgd[CONF_BORDER])) + if CONF_SHOW_LINES in lgd: + cg.add(legend.set_lines(lgd[CONF_SHOW_LINES])) + if CONF_SHOW_VALUES in lgd: + cg.add(legend.set_values(lgd[CONF_SHOW_VALUES])) + if CONF_SHOW_UNITS in lgd: + cg.add(legend.set_units(lgd[CONF_SHOW_UNITS])) + if CONF_DIRECTION in lgd: + cg.add(legend.set_direction(lgd[CONF_DIRECTION])) + cg.add(var.add_legend(legend)) + + cg.add_define("USE_GRAPH") diff --git a/esphome/components/graph/graph.cpp b/esphome/components/graph/graph.cpp new file mode 100644 index 0000000000..6ede553fdb --- /dev/null +++ b/esphome/components/graph/graph.cpp @@ -0,0 +1,361 @@ +#include "graph.h" +#include "esphome/components/display/display_buffer.h" +#include "esphome/core/color.h" +#include "esphome/core/log.h" +#include "esphome/core/esphal.h" +#include +#include +#include // std::cout, std::fixed +#include +namespace esphome { +namespace graph { + +using namespace display; + +static const char *const TAG = "graph"; +static const char *const TAGL = "graphlegend"; + +void HistoryData::init(int length) { + this->length_ = length; + this->samples_.resize(length, NAN); + this->last_sample_ = millis(); +} + +void HistoryData::take_sample(float data) { + uint32_t tm = millis(); + uint32_t dt = tm - last_sample_; + last_sample_ = tm; + + // Step data based on time + this->period_ += dt; + while (this->period_ >= this->update_time_) { + this->samples_[this->count_] = data; + this->period_ -= this->update_time_; + this->count_ = (this->count_ + 1) % this->length_; + ESP_LOGV(TAG, "Updating trace with value: %f", data); + } + if (!isnan(data)) { + // Recalc recent max/min + this->recent_min_ = data; + this->recent_max_ = data; + for (int i = 0; i < this->length_; i++) { + if (!isnan(this->samples_[i])) { + if (this->recent_max_ < this->samples_[i]) + this->recent_max_ = this->samples_[i]; + if (this->recent_min_ > this->samples_[i]) + this->recent_min_ = this->samples_[i]; + } + } + } +} + +void GraphTrace::init(Graph *g) { + ESP_LOGI(TAG, "Init trace for sensor %s", this->get_name().c_str()); + this->data_.init(g->get_width()); + sensor_->add_on_state_callback([this](float state) { this->data_.take_sample(state); }); + this->data_.set_update_time_ms(g->get_duration() * 1000 / g->get_width()); +} + +void Graph::draw(DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Color color) { + /// Plot border + if (this->border_) { + buff->horizontal_line(x_offset, y_offset, this->width_, color); + buff->horizontal_line(x_offset, y_offset + this->height_ - 1, this->width_, color); + buff->vertical_line(x_offset, y_offset, this->height_, color); + buff->vertical_line(x_offset + this->width_ - 1, y_offset, this->height_, color); + } + /// Determine best y-axis scale and range + float ymin = NAN; + float ymax = NAN; + for (auto *trace : traces_) { + float mx = trace->get_tracedata()->get_recent_max(); + float mn = trace->get_tracedata()->get_recent_min(); + if (isnan(ymax) || (ymax < mx)) + ymax = mx; + if (isnan(ymin) || (ymin > mn)) + ymin = mn; + } + // Adjust if manually overridden + if (!isnan(this->min_value_)) + ymin = this->min_value_; + if (!isnan(this->max_value_)) + ymax = this->max_value_; + + float yrange = ymax - ymin; + if (yrange > this->max_range_) { + // Look back in trace data to best-fit into local range + float mx = NAN; + float mn = NAN; + for (int16_t i = 0; i < this->width_; i++) { + for (auto *trace : traces_) { + float v = trace->get_tracedata()->get_value(i); + if (!isnan(v)) { + if ((v - mn) > this->max_range_) + break; + if ((mx - v) > this->max_range_) + break; + if (isnan(mx) || (v > mx)) + mx = v; + if (isnan(mn) || (v < mn)) + mn = v; + } + } + } + yrange = this->max_range_; + if (!isnan(mn)) { + ymin = mn; + ymax = ymin + this->max_range_; + } + ESP_LOGV(TAG, "Graphing at max_range. Using local min %f, max %f", mn, mx); + } + + float y_per_div = this->min_range_; + if (!isnan(this->gridspacing_y_)) { + y_per_div = this->gridspacing_y_; + } + // Restrict drawing too many gridlines + if (yrange > 10 * y_per_div) { + while (yrange > 10 * y_per_div) { + y_per_div *= 2; + } + ESP_LOGW(TAG, "Graphing reducing y-scale to prevent too many gridlines"); + } + + // Adjust limits to nice y_per_div boundaries + int yn = int(ymin / y_per_div); + int ym = int(ymax / y_per_div) + int(1 * (fmodf(ymax, y_per_div) != 0)); + ymin = yn * y_per_div; + ymax = ym * y_per_div; + yrange = ymax - ymin; + + /// Draw grid + if (!isnan(this->gridspacing_y_)) { + for (int y = yn; y <= ym; y++) { + int16_t py = (int16_t) roundf((this->height_ - 1) * (1.0 - (float) (y - yn) / (ym - yn))); + for (int x = 0; x < this->width_; x += 2) { + buff->draw_pixel_at(x_offset + x, y_offset + py, color); + } + } + } + if (!isnan(this->gridspacing_x_) && (this->gridspacing_x_ > 0)) { + int n = this->duration_ / this->gridspacing_x_; + // Restrict drawing too many gridlines + if (n > 20) { + while (n > 20) { + n /= 2; + } + ESP_LOGW(TAG, "Graphing reducing x-scale to prevent too many gridlines"); + } + for (int i = 0; i <= n; i++) { + for (int y = 0; y < this->height_; y += 2) { + buff->draw_pixel_at(x_offset + i * (this->width_ - 1) / n, y_offset + y, color); + } + } + } + + /// Draw traces + ESP_LOGV(TAG, "Updating graph. ymin %f, ymax %f", ymin, ymax); + for (auto *trace : traces_) { + Color c = trace->get_line_color(); + uint16_t thick = trace->get_line_thickness(); + for (int16_t i = 0; i < this->width_; i++) { + float v = (trace->get_tracedata()->get_value(i) - ymin) / yrange; + if (!isnan(v) && (thick > 0)) { + int16_t x = this->width_ - 1 - i; + uint8_t b = (i % (thick * LineType::PATTERN_LENGTH)) / thick; + if (((uint8_t) trace->get_line_type() & (1 << b)) == (1 << b)) { + int16_t y = (int16_t) roundf((this->height_ - 1) * (1.0 - v)) - thick / 2; + for (int16_t t = 0; t < thick; t++) { + buff->draw_pixel_at(x_offset + x, y_offset + y + t, c); + } + } + } + } + } +} + +/// Determine the best coordinates of drawing text + lines +void GraphLegend::init(Graph *g) { + parent_ = g; + + // Determine maximum expected text and value width / height + int txtw = 0, txtos = 0, txtbl = 0, txth = 0; + int valw = 0, valos = 0, valbl = 0, valh = 0; + int lt = 0; + for (auto *trace : g->traces_) { + std::string txtstr = trace->get_name(); + int fw, fos, fbl, fh; + this->font_label->measure(txtstr.c_str(), &fw, &fos, &fbl, &fh); + if (fw > txtw) + txtw = fw; + if (fh > txth) + txth = fh; + if (trace->get_line_thickness() > lt) + lt = trace->get_line_thickness(); + ESP_LOGI(TAGL, " %s %d %d", txtstr.c_str(), fw, fh); + + if (this->values_ != VALUE_POSITION_TYPE_NONE) { + std::stringstream ss; + ss << std::fixed << std::setprecision(trace->sensor_->get_accuracy_decimals()) << trace->sensor_->get_state(); + std::string valstr = ss.str(); + if (this->units_) { + valstr += trace->sensor_->get_unit_of_measurement(); + } + this->font_value->measure(valstr.c_str(), &fw, &fos, &fbl, &fh); + if (fw > valw) + valw = fw; + if (fh > valh) + valh = fh; + ESP_LOGI(TAGL, " %s %d %d", valstr.c_str(), fw, fh); + } + } + // Add extra margin + txtw *= 1.2; + valw *= 1.2; + + uint8_t n = g->traces_.size(); + uint16_t w = this->width_; + uint16_t h = this->height_; + DirectionType dir = this->direction_; + ValuePositionType valpos = this->values_; + if (!this->font_value) { + valpos = VALUE_POSITION_TYPE_NONE; + } + // Line sample always goes below text for compactness + this->yl = txth + (txth / 4) + lt / 2; + + if (dir == DIRECTION_TYPE_AUTO) { + dir = DIRECTION_TYPE_HORIZONTAL; // as default + if (h > 0) { + dir = DIRECTION_TYPE_VERTICAL; + } + } + + if (valpos == VALUE_POSITION_TYPE_AUTO) { + // TODO: do something smarter?? - fit to w and h? + valpos = VALUE_POSITION_TYPE_BELOW; + } + + if (valpos == VALUE_POSITION_TYPE_BELOW) { + this->yv = txth + (txth / 4); + if (this->lines_) + this->yv += txth / 4 + lt; + } else if (valpos == VALUE_POSITION_TYPE_BESIDE) { + this->xv = (txtw + valw) / 2; + } + + // If width or height is specified we divide evenly within, else we do tight-fit + if (w == 0) { + this->x0 = txtw / 2; + this->xs = txtw; + if (valpos == VALUE_POSITION_TYPE_BELOW) { + this->xs = std::max(txtw, valw); + ; + this->x0 = this->xs / 2; + } else if (valpos == VALUE_POSITION_TYPE_BESIDE) { + this->xs = txtw + valw; + } + if (dir == DIRECTION_TYPE_VERTICAL) { + this->width_ = this->xs; + } else { + this->width_ = this->xs * n; + } + } else { + this->xs = w / n; + this->x0 = this->xs / 2; + } + + if (h == 0) { + this->ys = txth; + if (valpos == VALUE_POSITION_TYPE_BELOW) { + this->ys = txth + txth / 2 + valh; + if (this->lines_) { + this->ys += lt; + } + } else if (valpos == VALUE_POSITION_TYPE_BESIDE) { + if (this->lines_) { + this->ys = std::max(txth + txth / 4 + lt + txth / 4, valh + valh / 4); + } else { + this->ys = std::max(txth + txth / 4, valh + valh / 4); + } + this->height_ = this->ys * n; + } + if (dir == DIRECTION_TYPE_HORIZONTAL) { + this->height_ = this->ys; + } else { + this->height_ = this->ys * n; + } + } else { + this->ys = h / n; + } + + if (dir == DIRECTION_TYPE_HORIZONTAL) { + this->ys = 0; + } else { + this->xs = 0; + } +} + +void Graph::draw_legend(display::DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Color color) { + if (!legend_) + return; + + /// Plot border + if (this->border_) { + int w = legend_->width_; + int h = legend_->height_; + buff->horizontal_line(x_offset, y_offset, w, color); + buff->horizontal_line(x_offset, y_offset + h - 1, w, color); + buff->vertical_line(x_offset, y_offset, h, color); + buff->vertical_line(x_offset + w - 1, y_offset, h, color); + } + + int x = x_offset + legend_->x0; + int y = y_offset; + for (auto *trace : traces_) { + std::string txtstr = trace->get_name(); + ESP_LOGV(TAG, " %s", txtstr.c_str()); + + buff->printf(x, y, legend_->font_label, trace->get_line_color(), TextAlign::TOP_CENTER, "%s", txtstr.c_str()); + + if (legend_->lines_) { + uint16_t thick = trace->get_line_thickness(); + for (int16_t i = 0; i < legend_->x0 * 4 / 3; i++) { + uint8_t b = (i % (thick * LineType::PATTERN_LENGTH)) / thick; + if (((uint8_t) trace->get_line_type() & (1 << b)) == (1 << b)) { + buff->vertical_line(x - legend_->x0 * 2 / 3 + i, y + legend_->yl - thick / 2, thick, trace->get_line_color()); + } + } + } + + if (legend_->values_ != VALUE_POSITION_TYPE_NONE) { + int xv = x + legend_->xv; + int yv = y + legend_->yv; + std::stringstream ss; + ss << std::fixed << std::setprecision(trace->sensor_->get_accuracy_decimals()) << trace->sensor_->get_state(); + std::string valstr = ss.str(); + if (legend_->units_) { + valstr += trace->sensor_->get_unit_of_measurement(); + } + buff->printf(xv, yv, legend_->font_value, trace->get_line_color(), TextAlign::TOP_CENTER, "%s", valstr.c_str()); + ESP_LOGV(TAG, " value: %s", valstr.c_str()); + } + x += legend_->xs; + y += legend_->ys; + } +} + +void Graph::setup() { + for (auto *trace : traces_) { + trace->init(this); + } +} + +void Graph::dump_config() { + for (auto *trace : traces_) { + ESP_LOGCONFIG(TAG, "Graph for sensor %s", trace->get_name().c_str()); + } +} + +} // namespace graph +} // namespace esphome diff --git a/esphome/components/graph/graph.h b/esphome/components/graph/graph.h new file mode 100644 index 0000000000..8f6e74f67a --- /dev/null +++ b/esphome/components/graph/graph.h @@ -0,0 +1,178 @@ +#pragma once +#include +#include "esphome/core/color.h" +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" + +namespace esphome { + +// forward declare DisplayBuffer +namespace display { +class DisplayBuffer; +class Font; +} // namespace display + +namespace graph { + +class Graph; + +const Color COLOR_ON(255, 255, 255, 255); + +/// Bit pattern defines the line-type +enum LineType { + LINE_TYPE_SOLID = 0b1111, + LINE_TYPE_DOTTED = 0b0101, + LINE_TYPE_DASHED = 0b1110, + // Following defines number of bits used to define line pattern + PATTERN_LENGTH = 4 +}; + +enum DirectionType { + DIRECTION_TYPE_AUTO, + DIRECTION_TYPE_HORIZONTAL, + DIRECTION_TYPE_VERTICAL, +}; + +enum ValuePositionType { + VALUE_POSITION_TYPE_NONE, + VALUE_POSITION_TYPE_AUTO, + VALUE_POSITION_TYPE_BESIDE, + VALUE_POSITION_TYPE_BELOW +}; + +class GraphLegend { + public: + void init(Graph *g); + void set_name_font(display::Font *font) { this->font_label = font; } + void set_value_font(display::Font *font) { this->font_value = font; } + void set_width(uint32_t width) { this->width_ = width; } + void set_height(uint32_t height) { this->height_ = height; } + void set_border(bool val) { this->border_ = val; } + void set_lines(bool val) { this->lines_ = val; } + void set_values(ValuePositionType val) { this->values_ = val; } + void set_units(bool val) { this->units_ = val; } + void set_direction(DirectionType val) { this->direction_ = val; } + + protected: + uint32_t width_{0}; + uint32_t height_{0}; + bool border_{true}; + bool lines_{true}; + ValuePositionType values_{VALUE_POSITION_TYPE_AUTO}; + bool units_{true}; + DirectionType direction_{DIRECTION_TYPE_AUTO}; + display::Font *font_label{nullptr}; + display::Font *font_value{nullptr}; + // Calculated values + Graph *parent_{nullptr}; + // (x0) (xs,ys) (xs,ys) + // ------> LABEL1 -------> LABEL2 -------> ... + // | \(xv,yv) \ . + // | \ \-> VALUE1+units + // (0,yl)| \-> VALUE1+units + // v (top_center) + // LINE_SAMPLE + int x0{0}; // X-offset to centre of label text + int xs{0}; // X spacing between labels + int ys{0}; // Y spacing between labels + int yl{0}; // Y spacing from label to line sample + int xv{0}; // X distance between label to value text + int yv{0}; // Y distance between label to value text + friend Graph; +}; + +class HistoryData { + public: + void init(int length); + ~HistoryData(); + void set_update_time_ms(uint32_t update_time_ms) { update_time_ = update_time_ms; } + void take_sample(float data); + int get_length() const { return length_; } + float get_value(int idx) const { return samples_[(count_ + length_ - 1 - idx) % length_]; } + float get_recent_max() const { return recent_max_; } + float get_recent_min() const { return recent_min_; } + + protected: + uint32_t last_sample_; + uint32_t period_{0}; /// in ms + uint32_t update_time_{0}; /// in ms + int length_; + int count_{0}; + float recent_min_{NAN}; + float recent_max_{NAN}; + std::vector samples_; +}; + +class GraphTrace { + public: + void init(Graph *g); + void set_name(std::string name) { name_ = name; } + void set_sensor(sensor::Sensor *sensor) { sensor_ = sensor; } + uint8_t get_line_thickness() { return this->line_thickness_; } + void set_line_thickness(uint8_t val) { this->line_thickness_ = val; } + enum LineType get_line_type() { return this->line_type_; } + void set_line_type(enum LineType val) { this->line_type_ = val; } + Color get_line_color() { return this->line_color_; } + void set_line_color(Color val) { this->line_color_ = val; } + const std::string get_name(void) { return name_; } + const HistoryData *get_tracedata() { return &data_; } + + protected: + sensor::Sensor *sensor_{nullptr}; + std::string name_{""}; + uint8_t line_thickness_{3}; + enum LineType line_type_ { LINE_TYPE_SOLID }; + Color line_color_{COLOR_ON}; + HistoryData data_; + + friend Graph; + friend GraphLegend; +}; + +class Graph : public Component { + public: + void draw(display::DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Color color); + void draw_legend(display::DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Color color); + + void setup() override; + float get_setup_priority() const override { return setup_priority::PROCESSOR; } + void dump_config() override; + + void set_duration(uint32_t duration) { duration_ = duration; } + void set_width(uint32_t width) { width_ = width; } + void set_height(uint32_t height) { height_ = height; } + void set_min_value(float val) { this->min_value_ = val; } + void set_max_value(float val) { this->max_value_ = val; } + void set_min_range(float val) { this->min_range_ = val; } + void set_max_range(float val) { this->max_range_ = val; } + void set_grid_x(float val) { this->gridspacing_x_ = val; } + void set_grid_y(float val) { this->gridspacing_y_ = val; } + void set_border(bool val) { this->border_ = val; } + void add_trace(GraphTrace *trace) { traces_.push_back(trace); } + void add_legend(GraphLegend *legend) { + this->legend_ = legend; + legend->init(this); + } + uint32_t get_duration() { return duration_; } + uint32_t get_width() { return width_; } + uint32_t get_height() { return height_; } + + protected: + uint32_t duration_; /// in seconds + uint32_t width_; /// in pixels + uint32_t height_; /// in pixels + float min_value_{NAN}; + float max_value_{NAN}; + float min_range_{1.0}; + float max_range_{NAN}; + float gridspacing_x_{NAN}; + float gridspacing_y_{NAN}; + bool border_{true}; + std::vector traces_; + GraphLegend *legend_{nullptr}; + + friend GraphLegend; +}; + +} // namespace graph +} // namespace esphome diff --git a/esphome/const.py b/esphome/const.py index a74068ab77..598b6351b4 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -90,6 +90,7 @@ CONF_BIT_DEPTH = "bit_depth" CONF_BLUE = "blue" CONF_BOARD = "board" CONF_BOARD_FLASH_MODE = "board_flash_mode" +CONF_BORDER = "border" CONF_BRANCH = "branch" CONF_BRIGHTNESS = "brightness" CONF_BROKER = "broker" @@ -327,6 +328,7 @@ CONF_LAMBDA = "lambda" CONF_LAST_CONFIDENCE = "last_confidence" CONF_LAST_FINGER_ID = "last_finger_id" CONF_LATITUDE = "latitude" +CONF_LEGEND = "legend" CONF_LENGTH = "length" CONF_LEVEL = "level" CONF_LG = "lg" @@ -334,6 +336,8 @@ CONF_LIBRARIES = "libraries" CONF_LIGHT = "light" CONF_LIGHTNING_ENERGY = "lightning_energy" CONF_LIGHTNING_THRESHOLD = "lightning_threshold" +CONF_LINE_THICKNESS = "line_thickness" +CONF_LINE_TYPE = "line_type" CONF_LOADED_INTEGRATIONS = "loaded_integrations" CONF_LOCAL = "local" CONF_LOG_TOPIC = "log_topic" @@ -355,6 +359,7 @@ CONF_MAX_HEATING_RUN_TIME = "max_heating_run_time" CONF_MAX_LENGTH = "max_length" CONF_MAX_LEVEL = "max_level" CONF_MAX_POWER = "max_power" +CONF_MAX_RANGE = "max_range" CONF_MAX_REFRESH_RATE = "max_refresh_rate" CONF_MAX_SPEED = "max_speed" CONF_MAX_TEMPERATURE = "max_temperature" @@ -376,6 +381,7 @@ CONF_MIN_IDLE_TIME = "min_idle_time" CONF_MIN_LENGTH = "min_length" CONF_MIN_LEVEL = "min_level" CONF_MIN_POWER = "min_power" +CONF_MIN_RANGE = "min_range" CONF_MIN_TEMPERATURE = "min_temperature" CONF_MIN_VALUE = "min_value" CONF_MINUTE = "minute" @@ -393,6 +399,7 @@ CONF_MQTT_ID = "mqtt_id" CONF_MULTIPLEXER = "multiplexer" CONF_MULTIPLY = "multiply" CONF_NAME = "name" +CONF_NAME_FONT = "name_font" CONF_NBITS = "nbits" CONF_NEC = "nec" CONF_NETWORKS = "networks" @@ -584,6 +591,9 @@ CONF_SERVICES = "services" CONF_SET_POINT_MINIMUM_DIFFERENTIAL = "set_point_minimum_differential" CONF_SETUP_MODE = "setup_mode" CONF_SETUP_PRIORITY = "setup_priority" +CONF_SHOW_LINES = "show_lines" +CONF_SHOW_UNITS = "show_units" +CONF_SHOW_VALUES = "show_values" CONF_SHUNT_RESISTANCE = "shunt_resistance" CONF_SHUNT_VOLTAGE = "shunt_voltage" CONF_SHUTDOWN_MESSAGE = "shutdown_message" @@ -659,6 +669,7 @@ CONF_TOLERANCE = "tolerance" CONF_TOPIC = "topic" CONF_TOPIC_PREFIX = "topic_prefix" CONF_TOTAL = "total" +CONF_TRACES = "traces" CONF_TRANSITION_LENGTH = "transition_length" CONF_TRIGGER_ID = "trigger_id" CONF_TRIGGER_PIN = "trigger_pin" @@ -681,6 +692,7 @@ CONF_USE_ADDRESS = "use_address" CONF_USERNAME = "username" CONF_UUID = "uuid" CONF_VALUE = "value" +CONF_VALUE_FONT = "value_font" CONF_VARIABLES = "variables" CONF_VARIANT = "variant" CONF_VERSION = "version" @@ -704,6 +716,8 @@ CONF_WILL_MESSAGE = "will_message" CONF_WIND_DIRECTION_DEGREES = "wind_direction_degrees" CONF_WIND_SPEED = "wind_speed" CONF_WINDOW_SIZE = "window_size" +CONF_X_GRID = "x_grid" +CONF_Y_GRID = "y_grid" CONF_ZERO = "zero" ENV_NOGITIGNORE = "ESPHOME_NOGITIGNORE" diff --git a/esphome/core/defines.h b/esphome/core/defines.h index e3976d378e..89010ce246 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -19,6 +19,7 @@ #define USE_DEEP_SLEEP #define USE_ESP8266_PREFERENCES_FLASH #define USE_FAN +#define USE_GRAPH #define USE_HOMEASSISTANT_TIME #define USE_HTTP_REQUEST_ESP8266_HTTPS #define USE_I2C_MULTIPLEXER From 81685573e1fc760259d6d0eae467807acdfdd479 Mon Sep 17 00:00:00 2001 From: poptix Date: Mon, 20 Sep 2021 02:44:18 -0500 Subject: [PATCH 063/207] Properly calculate negative temperatures in sm300d2 (#2335) Co-authored-by: Matt Hallacy --- esphome/components/sm300d2/sm300d2.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/esphome/components/sm300d2/sm300d2.cpp b/esphome/components/sm300d2/sm300d2.cpp index d542582fcb..e41a4855db 100644 --- a/esphome/components/sm300d2/sm300d2.cpp +++ b/esphome/components/sm300d2/sm300d2.cpp @@ -29,8 +29,11 @@ void SM300D2Sensor::update() { } uint16_t calculated_checksum = this->sm300d2_checksum_(response); + // Occasionally the checksum has a +/- 0x80 offset. Negative temperatures are + // responsible for some of these. The rest are unknown/undocumented. if ((calculated_checksum != response[SM300D2_RESPONSE_LENGTH - 1]) && - (calculated_checksum - 0x80 != response[SM300D2_RESPONSE_LENGTH - 1])) { + (calculated_checksum - 0x80 != response[SM300D2_RESPONSE_LENGTH - 1]) && + (calculated_checksum + 0x80 != response[SM300D2_RESPONSE_LENGTH - 1])) { ESP_LOGW(TAG, "SM300D2 Checksum doesn't match: 0x%02X!=0x%02X", response[SM300D2_RESPONSE_LENGTH - 1], calculated_checksum); this->status_set_warning(); @@ -46,7 +49,10 @@ void SM300D2Sensor::update() { const uint16_t tvoc = (response[6] * 256) + response[7]; const uint16_t pm_2_5 = (response[8] * 256) + response[9]; const uint16_t pm_10_0 = (response[10] * 256) + response[11]; - const float temperature = response[12] + (response[13] * 0.1); + // A negative value is indicated by adding 0x80 (128) to the temperature value + const float temperature = ((response[12] + (response[13] * 0.1)) > 128) + ? (((response[12] + (response[13] * 0.1)) - 128) * -1) + : response[12] + (response[13] * 0.1); const float humidity = response[14] + (response[15] * 0.1); ESP_LOGD(TAG, "Received CO₂: %u ppm", co2); From 5e345783bdc68773c81012388bf5891c2f229ed4 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 20 Sep 2021 09:55:18 +0200 Subject: [PATCH 064/207] Fix docker release deploy push flag (#2348) --- docker/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/build.py b/docker/build.py index 1904457989..cd8dca3b87 100755 --- a/docker/build.py +++ b/docker/build.py @@ -31,7 +31,7 @@ parser.add_argument("--build-type", choices=TYPES, required=True, help="The type parser.add_argument("--dry-run", action="store_true", help="Don't run any commands, just print them") subparsers = parser.add_subparsers(help="Action to perform", dest="command", required=True) build_parser = subparsers.add_parser("build", help="Build the image") -build_parser.add_argument("--push", help="Also push the images") +build_parser.add_argument("--push", help="Also push the images", action="store_true") manifest_parser = subparsers.add_parser("manifest", help="Create a manifest from already pushed images") From 3052c64dd707139b5b4ccca478915423d54d10e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alja=C5=BE=20Srebrni=C4=8D?= Date: Mon, 20 Sep 2021 10:08:08 +0200 Subject: [PATCH 065/207] Add invert_colors option for st7735 (#2327) --- esphome/components/st7735/display.py | 3 +++ esphome/components/st7735/st7735.cpp | 7 ++++++- esphome/components/st7735/st7735.h | 4 +++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/esphome/components/st7735/display.py b/esphome/components/st7735/display.py index c1ede4e0ce..ae31f604a5 100644 --- a/esphome/components/st7735/display.py +++ b/esphome/components/st7735/display.py @@ -23,6 +23,7 @@ CONF_ROW_START = "row_start" CONF_COL_START = "col_start" CONF_EIGHT_BIT_COLOR = "eight_bit_color" CONF_USE_BGR = "use_bgr" +CONF_INVERT_COLORS = "invert_colors" SPIST7735 = st7735_ns.class_( "ST7735", cg.PollingComponent, display.DisplayBuffer, spi.SPIDevice @@ -58,6 +59,7 @@ CONFIG_SCHEMA = cv.All( cv.Required(CONF_ROW_START): cv.int_, cv.Optional(CONF_EIGHT_BIT_COLOR, default=False): cv.boolean, cv.Optional(CONF_USE_BGR, default=False): cv.boolean, + cv.Optional(CONF_INVERT_COLORS, default=False): cv.boolean, } ) .extend(cv.COMPONENT_SCHEMA) @@ -90,6 +92,7 @@ async def to_code(config): config[CONF_ROW_START], config[CONF_EIGHT_BIT_COLOR], config[CONF_USE_BGR], + config[CONF_INVERT_COLORS], ) await setup_st7735(var, config) await spi.register_spi_device(var, config) diff --git a/esphome/components/st7735/st7735.cpp b/esphome/components/st7735/st7735.cpp index b44f76a9e6..1f14136637 100644 --- a/esphome/components/st7735/st7735.cpp +++ b/esphome/components/st7735/st7735.cpp @@ -220,12 +220,14 @@ static const uint8_t PROGMEM // clang-format on static const char *const TAG = "st7735"; -ST7735::ST7735(ST7735Model model, int width, int height, int colstart, int rowstart, bool eightbitcolor, bool usebgr) +ST7735::ST7735(ST7735Model model, int width, int height, int colstart, int rowstart, bool eightbitcolor, bool usebgr, + bool invert_colors) : model_(model), colstart_(colstart), rowstart_(rowstart), eightbitcolor_(eightbitcolor), usebgr_(usebgr), + invert_colors_(invert_colors), width_(width), height_(height) {} @@ -281,6 +283,9 @@ void ST7735::setup() { } sendcommand_(ST77XX_MADCTL, &data, 1); + if (this->invert_colors_) + sendcommand_(ST77XX_INVON, nullptr, 0); + this->init_internal_(this->get_buffer_length()); memset(this->buffer_, 0x00, this->get_buffer_length()); } diff --git a/esphome/components/st7735/st7735.h b/esphome/components/st7735/st7735.h index 0eb4ccadd8..c049fb9e83 100644 --- a/esphome/components/st7735/st7735.h +++ b/esphome/components/st7735/st7735.h @@ -37,7 +37,8 @@ class ST7735 : public PollingComponent, public spi::SPIDevice { public: - ST7735(ST7735Model model, int width, int height, int colstart, int rowstart, bool eightbitcolor, bool usebgr); + ST7735(ST7735Model model, int width, int height, int colstart, int rowstart, bool eightbitcolor, bool usebgr, + bool invert_colors); void dump_config() override; void setup() override; @@ -77,6 +78,7 @@ class ST7735 : public PollingComponent, uint8_t colstart_ = 0, rowstart_ = 0; bool eightbitcolor_ = false; bool usebgr_ = false; + bool invert_colors_ = false; int16_t width_ = 80, height_ = 80; // Watch heap size GPIOPin *reset_pin_{nullptr}; From 9ebe075f9b85ff5940748df3ce393bccc1e72c88 Mon Sep 17 00:00:00 2001 From: Christian Taedcke Date: Mon, 20 Sep 2021 10:12:32 +0200 Subject: [PATCH 066/207] Add deep sleep wakeup from touch (#1238) (#2281) --- esphome/components/deep_sleep/__init__.py | 5 ++++ .../deep_sleep/deep_sleep_component.cpp | 7 ++++++ .../deep_sleep/deep_sleep_component.h | 3 +++ .../components/esp32_touch/binary_sensor.py | 3 +++ .../components/esp32_touch/esp32_touch.cpp | 25 ++++++++++++++++--- esphome/components/esp32_touch/esp32_touch.h | 4 ++- 6 files changed, 43 insertions(+), 4 deletions(-) diff --git a/esphome/components/deep_sleep/__init__.py b/esphome/components/deep_sleep/__init__.py index 3f59ad52cf..1e07b75173 100644 --- a/esphome/components/deep_sleep/__init__.py +++ b/esphome/components/deep_sleep/__init__.py @@ -44,6 +44,7 @@ EXT1_WAKEUP_MODES = { CONF_WAKEUP_PIN_MODE = "wakeup_pin_mode" CONF_ESP32_EXT1_WAKEUP = "esp32_ext1_wakeup" +CONF_TOUCH_WAKEUP = "touch_wakeup" CONFIG_SCHEMA = cv.Schema( { @@ -67,6 +68,7 @@ CONFIG_SCHEMA = cv.Schema( } ), ), + cv.Optional(CONF_TOUCH_WAKEUP): cv.All(cv.only_on_esp32, cv.boolean), } ).extend(cv.COMPONENT_SCHEMA) @@ -95,6 +97,9 @@ async def to_code(config): ) cg.add(var.set_ext1_wakeup(struct)) + if CONF_TOUCH_WAKEUP in config: + cg.add(var.set_touch_wakeup(config[CONF_TOUCH_WAKEUP])) + cg.add_define("USE_DEEP_SLEEP") diff --git a/esphome/components/deep_sleep/deep_sleep_component.cpp b/esphome/components/deep_sleep/deep_sleep_component.cpp index 00f660f41a..9c354c8513 100644 --- a/esphome/components/deep_sleep/deep_sleep_component.cpp +++ b/esphome/components/deep_sleep/deep_sleep_component.cpp @@ -44,6 +44,7 @@ void DeepSleepComponent::set_wakeup_pin_mode(WakeupPinMode wakeup_pin_mode) { this->wakeup_pin_mode_ = wakeup_pin_mode; } void DeepSleepComponent::set_ext1_wakeup(Ext1Wakeup ext1_wakeup) { this->ext1_wakeup_ = ext1_wakeup; } +void DeepSleepComponent::set_touch_wakeup(bool touch_wakeup) { this->touch_wakeup_ = touch_wakeup; } #endif void DeepSleepComponent::set_run_duration(uint32_t time_ms) { this->run_duration_ = time_ms; } void DeepSleepComponent::begin_sleep(bool manual) { @@ -80,6 +81,12 @@ void DeepSleepComponent::begin_sleep(bool manual) { if (this->ext1_wakeup_.has_value()) { esp_sleep_enable_ext1_wakeup(this->ext1_wakeup_->mask, this->ext1_wakeup_->wakeup_mode); } + + if (this->touch_wakeup_.has_value() && *(this->touch_wakeup_)) { + esp_sleep_enable_touchpad_wakeup(); + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); + } + esp_deep_sleep_start(); #endif diff --git a/esphome/components/deep_sleep/deep_sleep_component.h b/esphome/components/deep_sleep/deep_sleep_component.h index 5050bd6b4d..575f7be72b 100644 --- a/esphome/components/deep_sleep/deep_sleep_component.h +++ b/esphome/components/deep_sleep/deep_sleep_component.h @@ -53,6 +53,8 @@ class DeepSleepComponent : public Component { void set_wakeup_pin_mode(WakeupPinMode wakeup_pin_mode); void set_ext1_wakeup(Ext1Wakeup ext1_wakeup); + + void set_touch_wakeup(bool touch_wakeup); #endif /// Set a duration in ms for how long the code should run before entering deep sleep mode. void set_run_duration(uint32_t time_ms); @@ -74,6 +76,7 @@ class DeepSleepComponent : public Component { optional wakeup_pin_; WakeupPinMode wakeup_pin_mode_{WAKEUP_PIN_MODE_IGNORE}; optional ext1_wakeup_; + optional touch_wakeup_; #endif optional run_duration_; bool next_enter_deep_sleep_{false}; diff --git a/esphome/components/esp32_touch/binary_sensor.py b/esphome/components/esp32_touch/binary_sensor.py index 300de23f08..198a43a738 100644 --- a/esphome/components/esp32_touch/binary_sensor.py +++ b/esphome/components/esp32_touch/binary_sensor.py @@ -15,6 +15,7 @@ ESP_PLATFORMS = [ESP_PLATFORM_ESP32] DEPENDENCIES = ["esp32_touch"] CONF_ESP32_TOUCH_ID = "esp32_touch_id" +CONF_WAKEUP_THRESHOLD = "wakeup_threshold" TOUCH_PADS = { 4: cg.global_ns.TOUCH_PAD_NUM0, @@ -47,6 +48,7 @@ CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend( cv.GenerateID(CONF_ESP32_TOUCH_ID): cv.use_id(ESP32TouchComponent), cv.Required(CONF_PIN): validate_touch_pad, cv.Required(CONF_THRESHOLD): cv.uint16_t, + cv.Optional(CONF_WAKEUP_THRESHOLD, default=0): cv.uint16_t, } ) @@ -58,6 +60,7 @@ async def to_code(config): config[CONF_NAME], TOUCH_PADS[config[CONF_PIN]], config[CONF_THRESHOLD], + config[CONF_WAKEUP_THRESHOLD], ) await binary_sensor.register_binary_sensor(var, config) cg.add(hub.register_touch_pad(var)) diff --git a/esphome/components/esp32_touch/esp32_touch.cpp b/esphome/components/esp32_touch/esp32_touch.cpp index a990e632af..9ae671a4a9 100644 --- a/esphome/components/esp32_touch/esp32_touch.cpp +++ b/esphome/components/esp32_touch/esp32_touch.cpp @@ -133,15 +133,34 @@ void ESP32TouchComponent::loop() { } void ESP32TouchComponent::on_shutdown() { + bool is_wakeup_source = false; + if (this->iir_filter_enabled_()) { touch_pad_filter_stop(); touch_pad_filter_delete(); } - touch_pad_deinit(); + + for (auto *child : this->children_) { + if (child->get_wakeup_threshold() != 0) { + if (!is_wakeup_source) { + is_wakeup_source = true; + // Touch sensor FSM mode must be 'TOUCH_FSM_MODE_TIMER' to use it to wake-up. + touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER); + } + + // No filter available when using as wake-up source. + touch_pad_config(child->get_touch_pad(), child->get_wakeup_threshold()); + } + } + + if (!is_wakeup_source) { + touch_pad_deinit(); + } } -ESP32TouchBinarySensor::ESP32TouchBinarySensor(const std::string &name, touch_pad_t touch_pad, uint16_t threshold) - : BinarySensor(name), touch_pad_(touch_pad), threshold_(threshold) {} +ESP32TouchBinarySensor::ESP32TouchBinarySensor(const std::string &name, touch_pad_t touch_pad, uint16_t threshold, + uint16_t wakeup_threshold) + : BinarySensor(name), touch_pad_(touch_pad), threshold_(threshold), wakeup_threshold_(wakeup_threshold) {} } // namespace esp32_touch } // namespace esphome diff --git a/esphome/components/esp32_touch/esp32_touch.h b/esphome/components/esp32_touch/esp32_touch.h index 45d459a2ff..8b92a482c0 100644 --- a/esphome/components/esp32_touch/esp32_touch.h +++ b/esphome/components/esp32_touch/esp32_touch.h @@ -57,12 +57,13 @@ class ESP32TouchComponent : public Component { /// Simple helper class to expose a touch pad value as a binary sensor. class ESP32TouchBinarySensor : public binary_sensor::BinarySensor { public: - ESP32TouchBinarySensor(const std::string &name, touch_pad_t touch_pad, uint16_t threshold); + ESP32TouchBinarySensor(const std::string &name, touch_pad_t touch_pad, uint16_t threshold, uint16_t wakeup_threshold); touch_pad_t get_touch_pad() const { return touch_pad_; } uint16_t get_threshold() const { return threshold_; } void set_threshold(uint16_t threshold) { threshold_ = threshold; } uint16_t get_value() const { return value_; } + uint16_t get_wakeup_threshold() const { return wakeup_threshold_; } protected: friend ESP32TouchComponent; @@ -70,6 +71,7 @@ class ESP32TouchBinarySensor : public binary_sensor::BinarySensor { touch_pad_t touch_pad_; uint16_t threshold_; uint16_t value_; + const uint16_t wakeup_threshold_; }; } // namespace esp32_touch From 7452ef23b11575b2f8097f811f64015ec121eae2 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Mon, 20 Sep 2021 10:16:59 +0200 Subject: [PATCH 067/207] Add ESPHOME_VERSION_CODE define (#2324) --- esphome/core/version.h | 3 +++ esphome/writer.py | 14 ++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/esphome/core/version.h b/esphome/core/version.h index b64f581b25..7336ebf7f8 100644 --- a/esphome/core/version.h +++ b/esphome/core/version.h @@ -6,4 +6,7 @@ // // This file is only used by static analyzers and IDEs. +#include "esphome/core/macros.h" + #define ESPHOME_VERSION "dev" +#define ESPHOME_VERSION_CODE VERSION_CODE(2099, 12, 0) diff --git a/esphome/writer.py b/esphome/writer.py index 19953916c7..2e1e19de54 100644 --- a/esphome/writer.py +++ b/esphome/writer.py @@ -342,7 +342,9 @@ DEFINES_H_FORMAT = ESPHOME_H_FORMAT = """\ """ VERSION_H_FORMAT = """\ #pragma once +#include "esphome/core/macros.h" #define ESPHOME_VERSION "{}" +#define ESPHOME_VERSION_CODE VERSION_CODE({}, {}, {}) """ DEFINES_H_TARGET = "esphome/core/defines.h" VERSION_H_TARGET = "esphome/core/version.h" @@ -415,8 +417,7 @@ def copy_src_tree(): CORE.relative_src_path("esphome.h"), ESPHOME_H_FORMAT.format(include_s) ) write_file_if_changed( - CORE.relative_src_path("esphome", "core", "version.h"), - VERSION_H_FORMAT.format(__version__), + CORE.relative_src_path("esphome", "core", "version.h"), generate_version_h() ) @@ -426,6 +427,15 @@ def generate_defines_h(): return DEFINES_H_FORMAT.format("\n".join(define_content_l)) +def generate_version_h(): + match = re.match(r"^(\d+)\.(\d+).(\d+)-?\w*$", __version__) + if not match: + raise EsphomeError(f"Could not parse version {__version__}.") + return VERSION_H_FORMAT.format( + __version__, match.group(1), match.group(2), match.group(3) + ) + + def write_cpp(code_s): path = CORE.relative_src_path("main.cpp") if os.path.isfile(path): From 2d7f8b3bdfc664478958af77270c27e90f845d4f Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 20 Sep 2021 10:31:48 +0200 Subject: [PATCH 068/207] Install python requirements after apt ones for better caching (#2349) * Install python requirements after apt ones for better caching * Fix buildkit caching works differently --- docker/Dockerfile | 21 ++++++++++++++++----- docker/build.py | 10 +++++----- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index d3ee219de8..9c3e864e43 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -51,17 +51,17 @@ RUN \ && platformio settings set check_platforms_interval 1000000 \ && mkdir -p /piolibs + + +# ======================= docker-type image ======================= +FROM base AS docker + # First install requirements to leverage caching when requirements don't change COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini / RUN \ pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \ && /platformio_install_deps.py /platformio.ini - - -# ======================= docker-type image ======================= -FROM base AS docker - # Copy esphome and install COPY . /esphome RUN pip3 install --no-cache-dir -e /esphome @@ -104,6 +104,12 @@ ARG BUILD_VERSION=dev # Copy root filesystem COPY docker/hassio-rootfs/ / +# First install requirements to leverage caching when requirements don't change +COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini / +RUN \ + pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \ + && /platformio_install_deps.py /platformio.ini + # Copy esphome and install COPY . /esphome RUN pip3 install --no-cache-dir -e /esphome @@ -141,5 +147,10 @@ RUN \ /var/{cache,log}/* \ /var/lib/apt/lists/* +COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini / +RUN \ + pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \ + && /platformio_install_deps.py /platformio.ini + VOLUME ["/esphome"] WORKDIR /esphome diff --git a/docker/build.py b/docker/build.py index cd8dca3b87..1157d8287a 100755 --- a/docker/build.py +++ b/docker/build.py @@ -109,9 +109,9 @@ def main(): # 1. pull cache image params = DockerParams.for_type_arch(args.build_type, args.arch) cache_tag = { - CHANNEL_DEV: "dev", - CHANNEL_BETA: "beta", - CHANNEL_RELEASE: "latest", + CHANNEL_DEV: "cache-dev", + CHANNEL_BETA: "cache-beta", + CHANNEL_RELEASE: "cache-latest", }[channel] cache_img = f"ghcr.io/{params.build_to}:{cache_tag}" @@ -123,7 +123,7 @@ def main(): "docker", "buildx", "build", "--build-arg", f"BASEIMGTYPE={params.baseimgtype}", "--build-arg", f"BUILD_VERSION={args.tag}", - "--cache-from", cache_img, + "--cache-from", f"type=registry,ref={cache_img}", "--file", "docker/Dockerfile", "--platform", params.platform, "--target", params.target, @@ -131,7 +131,7 @@ def main(): for img in imgs: cmd += ["--tag", img] if args.push: - cmd.append("--push") + cmd += ["--push", "--cache-to", f"type=registry,ref={cache_img},mode=max"] run_command(*cmd, ".") elif args.command == "manifest": From 1e8e471dec19ceafba1997b1d9663f7912f244a2 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Mon, 20 Sep 2021 11:16:31 +0200 Subject: [PATCH 069/207] Introduce call_dump_config() indirection (#2325) --- esphome/components/mqtt/mqtt_component.cpp | 6 ++++++ esphome/components/mqtt/mqtt_component.h | 2 ++ esphome/core/application.cpp | 2 +- esphome/core/component.cpp | 3 ++- esphome/core/component.h | 4 ++++ 5 files changed, 15 insertions(+), 2 deletions(-) diff --git a/esphome/components/mqtt/mqtt_component.cpp b/esphome/components/mqtt/mqtt_component.cpp index 6e376d0fef..3ed9aafb42 100644 --- a/esphome/components/mqtt/mqtt_component.cpp +++ b/esphome/components/mqtt/mqtt_component.cpp @@ -186,6 +186,12 @@ void MQTTComponent::call_loop() { this->schedule_resend_state(); } } +void MQTTComponent::call_dump_config() { + if (this->is_internal()) + return; + + this->dump_config(); +} void MQTTComponent::schedule_resend_state() { this->resend_state_ = true; } std::string MQTTComponent::unique_id() { return ""; } bool MQTTComponent::is_connected_() const { return global_mqtt_client->is_connected(); } diff --git a/esphome/components/mqtt/mqtt_component.h b/esphome/components/mqtt/mqtt_component.h index 83a0c06644..668162da5a 100644 --- a/esphome/components/mqtt/mqtt_component.h +++ b/esphome/components/mqtt/mqtt_component.h @@ -62,6 +62,8 @@ class MQTTComponent : public Component { void call_loop() override; + void call_dump_config() override; + /// Send discovery info the Home Assistant, override this. virtual void send_discovery(JsonObject &root, SendDiscoveryConfig &config) = 0; diff --git a/esphome/core/application.cpp b/esphome/core/application.cpp index 5ab1d973f4..d59ad23a5e 100644 --- a/esphome/core/application.cpp +++ b/esphome/core/application.cpp @@ -105,7 +105,7 @@ void Application::loop() { #endif } - this->components_[this->dump_config_at_]->dump_config(); + this->components_[this->dump_config_at_]->call_dump_config(); this->dump_config_at_++; } } diff --git a/esphome/core/component.cpp b/esphome/core/component.cpp index 7682c083a5..e3b32978cd 100644 --- a/esphome/core/component.cpp +++ b/esphome/core/component.cpp @@ -64,8 +64,9 @@ bool Component::cancel_timeout(const std::string &name) { // NOLINT } void Component::call_loop() { this->loop(); } - void Component::call_setup() { this->setup(); } +void Component::call_dump_config() { this->dump_config(); } + uint32_t Component::get_component_state() const { return this->component_state_; } void Component::call() { uint32_t state = this->component_state_ & COMPONENT_STATE_MASK; diff --git a/esphome/core/component.h b/esphome/core/component.h index ea87ebcdfe..2654504fe8 100644 --- a/esphome/core/component.h +++ b/esphome/core/component.h @@ -148,8 +148,12 @@ class Component { const char *get_component_source() const; protected: + friend class Application; + virtual void call_loop(); virtual void call_setup(); + virtual void call_dump_config(); + /** Set an interval function with a unique name. Empty name means no cancelling possible. * * This will call f every interval ms. Can be cancelled via CancelInterval(). From ac0d921413c3884752193fe568fa82853f0f99e9 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 20 Sep 2021 11:47:51 +0200 Subject: [PATCH 070/207] ESP-IDF support and generic target platforms (#2303) * Socket refactor and SSL * esp-idf temp * Fixes * Echo component and noise * Add noise API transport support * Updates * ESP-IDF * Complete * Fixes * Fixes * Versions update * New i2c APIs * Complete i2c refactor * SPI migration * Revert ESP Preferences migration, too complex for now * OTA support * Remove echo again * Remove ssl again * GPIOFlags updates * Rename esphal and ICACHE_RAM_ATTR * Make ESP32 arduino compilable again * Fix GPIO flags * Complete pin registry refactor and fixes * Fixes to make test1 compile * Remove sdkconfig file * Ignore sdkconfig file * Fixes in reviewing * Make test2 compile * Make test4 compile * Make test5 compile * Run clang-format * Fix lint errors * Use esp-idf APIs instead of btStart * Another round of fixes * Start implementing ESP8266 * Make test3 compile * Guard esp8266 code * Lint * Reformat * Fixes * Fixes v2 * more fixes * ESP-IDF tidy target * Convert ARDUINO_ARCH_ESPxx * Update WiFiSignalSensor * Update time ifdefs * OTA needs millis from hal * RestartSwitch needs delay from hal * ESP-IDF Uart * Fix OTA blank password * Allow setting sdkconfig * Fix idf partitions and allow setting sdkconfig from yaml * Re-add read/write compat APIs and fix esp8266 uart * Fix esp8266 store log strings in flash * Fix ESP32 arduino preferences not initialized * Update ifdefs * Change how sdkconfig change is detected * Add checks to ci-custom and fix them * Run clang-format * Add esp-idf clang-tidy target and fix errors * Fixes from clang-tidy idf round 2 * Fixes from compiling tests with esp-idf * Run clang-format * Switch test5.yaml to esp-idf * Implement ESP8266 Preferences * Lint * Re-do PIO package version selection a bit * Fix arduinoespressif32 package version * Fix unit tests * Lint * Lint fixes * Fix readv/writev not defined * Fix graphing component * Re-add all old options from core/config.py Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- .github/workflows/ci.yml | 6 +- .gitignore | 2 + CODEOWNERS | 3 + esphome/__main__.py | 2 +- esphome/codegen.py | 3 + esphome/components/a4988/a4988.h | 2 +- esphome/components/ac_dimmer/ac_dimmer.cpp | 47 +- esphome/components/ac_dimmer/ac_dimmer.h | 20 +- esphome/components/ac_dimmer/output.py | 25 +- esphome/components/adc/adc_sensor.cpp | 29 +- esphome/components/adc/adc_sensor.h | 14 +- esphome/components/adc/sensor.py | 37 +- esphome/components/ade7953/ade7953.cpp | 33 +- esphome/components/ade7953/ade7953.h | 75 +- esphome/components/ade7953/sensor.py | 5 +- esphome/components/ads1115/ads1115.cpp | 3 +- esphome/components/aht10/aht10.cpp | 44 +- .../airthings_ble/airthings_listener.cpp | 2 +- .../airthings_ble/airthings_listener.h | 3 +- .../airthings_wave_plus.cpp | 4 +- .../airthings_wave_plus/airthings_wave_plus.h | 4 +- .../components/airthings_wave_plus/sensor.py | 6 +- esphome/components/am2320/am2320.cpp | 3 +- esphome/components/am43/am43.cpp | 3 +- esphome/components/am43/am43.h | 2 +- esphome/components/am43/cover/am43_cover.cpp | 2 +- esphome/components/am43/cover/am43_cover.h | 2 +- esphome/components/anova/anova.cpp | 2 +- esphome/components/anova/anova.h | 2 +- esphome/components/apds9960/apds9960.cpp | 1 + esphome/components/api/api_connection.cpp | 5 +- esphome/components/api/api_frame_helper.cpp | 1 + esphome/components/api/api_pb2.cpp | 1 + esphome/components/api/api_server.cpp | 4 +- esphome/components/api/proto.h | 1 + esphome/components/as3935/as3935.h | 2 +- esphome/components/as3935_i2c/as3935_i2c.cpp | 8 +- esphome/components/async_tcp/__init__.py | 6 + .../atc_mithermometer/atc_mithermometer.cpp | 2 +- .../atc_mithermometer/atc_mithermometer.h | 2 +- esphome/components/b_parasite/b_parasite.cpp | 4 +- esphome/components/b_parasite/b_parasite.h | 4 +- .../bang_bang/bang_bang_climate.cpp | 3 +- esphome/components/bh1750/bh1750.cpp | 3 +- esphome/components/binary_sensor/automation.h | 1 + esphome/components/ble_client/automation.h | 2 +- esphome/components/ble_client/ble_client.cpp | 2 +- esphome/components/ble_client/ble_client.h | 2 +- .../components/ble_client/sensor/automation.h | 2 +- .../ble_client/sensor/ble_sensor.cpp | 2 +- .../components/ble_client/sensor/ble_sensor.h | 2 +- .../ble_client/switch/ble_switch.cpp | 2 +- .../components/ble_client/switch/ble_switch.h | 2 +- .../ble_presence/ble_presence_device.cpp | 2 +- .../ble_presence/ble_presence_device.h | 2 +- .../components/ble_rssi/ble_rssi_sensor.cpp | 2 +- esphome/components/ble_rssi/ble_rssi_sensor.h | 2 +- .../components/ble_scanner/ble_scanner.cpp | 2 +- esphome/components/ble_scanner/ble_scanner.h | 2 +- esphome/components/bme280/bme280.cpp | 2 +- esphome/components/bme680/bme680.cpp | 1 + .../components/bme680_bsec/bme680_bsec.cpp | 2 +- esphome/components/bmp280/bmp280.cpp | 2 +- esphome/components/captive_portal/__init__.py | 19 +- .../captive_portal/captive_portal.cpp | 14 +- .../captive_portal/captive_portal.h | 4 + esphome/components/ccs811/ccs811.cpp | 5 +- esphome/components/climate/climate.cpp | 16 +- esphome/components/climate_ir/climate_ir.cpp | 2 +- esphome/components/cover/cover.cpp | 2 +- .../components/ct_clamp/ct_clamp_sensor.cpp | 2 +- esphome/components/ct_clamp/ct_clamp_sensor.h | 2 +- .../components/dallas/dallas_component.cpp | 2 +- esphome/components/dallas/esp_one_wire.cpp | 38 +- esphome/components/dallas/esp_one_wire.h | 3 +- esphome/components/debug/debug_component.cpp | 32 +- esphome/components/deep_sleep/__init__.py | 2 +- .../deep_sleep/deep_sleep_component.cpp | 30 +- .../deep_sleep/deep_sleep_component.h | 16 +- esphome/components/demo/demo_sensor.h | 2 +- esphome/components/dht/dht.cpp | 6 +- esphome/components/dht/dht.h | 6 +- esphome/components/display/display_buffer.cpp | 23 +- esphome/components/display/display_buffer.h | 1 + esphome/components/dsmr/__init__.py | 19 +- esphome/components/dsmr/dsmr.cpp | 7 +- esphome/components/dsmr/dsmr.h | 4 + .../duty_cycle/duty_cycle_sensor.cpp | 6 +- .../components/duty_cycle/duty_cycle_sensor.h | 8 +- esphome/components/duty_cycle/sensor.py | 4 +- esphome/components/e131/__init__.py | 17 +- esphome/components/e131/e131.cpp | 8 +- esphome/components/e131/e131.h | 4 + .../e131/e131_addressable_light_effect.cpp | 4 + .../e131/e131_addressable_light_effect.h | 4 + esphome/components/e131/e131_packet.cpp | 14 +- esphome/components/endstop/endstop_cover.cpp | 1 + esphome/components/esp32/__init__.py | 379 ++++++++ esphome/{ => components/esp32}/boards.py | 225 ----- esphome/components/esp32/const.py | 21 + esphome/components/esp32/core.cpp | 89 ++ esphome/components/esp32/gpio.py | 201 ++++ esphome/components/esp32/gpio_arduino.cpp | 107 +++ esphome/components/esp32/gpio_arduino.h | 36 + esphome/components/esp32/gpio_idf.cpp | 49 + esphome/components/esp32/gpio_idf.h | 96 ++ esphome/components/esp32/preferences.cpp | 99 ++ esphome/components/esp32/preferences.h | 12 + esphome/components/esp32_ble/__init__.py | 9 +- esphome/components/esp32_ble/ble.cpp | 36 +- esphome/components/esp32_ble/ble.h | 2 +- .../components/esp32_ble/ble_advertising.cpp | 5 +- .../components/esp32_ble/ble_advertising.h | 2 +- esphome/components/esp32_ble/ble_uuid.cpp | 6 +- esphome/components/esp32_ble/ble_uuid.h | 4 +- esphome/components/esp32_ble/queue.h | 8 +- .../components/esp32_ble_beacon/__init__.py | 9 +- .../esp32_ble_beacon/esp32_ble_beacon.cpp | 33 +- .../esp32_ble_beacon/esp32_ble_beacon.h | 2 +- .../components/esp32_ble_server/__init__.py | 9 +- .../components/esp32_ble_server/ble_2901.cpp | 2 +- .../components/esp32_ble_server/ble_2901.h | 2 +- .../components/esp32_ble_server/ble_2902.cpp | 4 +- .../components/esp32_ble_server/ble_2902.h | 2 +- .../esp32_ble_server/ble_characteristic.cpp | 2 +- .../esp32_ble_server/ble_characteristic.h | 4 +- .../esp32_ble_server/ble_descriptor.cpp | 5 +- .../esp32_ble_server/ble_descriptor.h | 2 +- .../esp32_ble_server/ble_server.cpp | 2 +- .../components/esp32_ble_server/ble_server.h | 2 +- .../esp32_ble_server/ble_service.cpp | 2 +- .../components/esp32_ble_server/ble_service.h | 2 +- .../components/esp32_ble_tracker/__init__.py | 8 +- .../components/esp32_ble_tracker/automation.h | 2 +- .../esp32_ble_tracker/esp32_ble_tracker.cpp | 41 +- .../esp32_ble_tracker/esp32_ble_tracker.h | 2 +- esphome/components/esp32_ble_tracker/queue.h | 7 +- esphome/components/esp32_camera/__init__.py | 31 +- .../components/esp32_camera/esp32_camera.cpp | 7 +- .../components/esp32_camera/esp32_camera.h | 4 +- esphome/components/esp32_dac/esp32_dac.cpp | 19 +- esphome/components/esp32_dac/esp32_dac.h | 8 +- esphome/components/esp32_dac/output.py | 4 +- esphome/components/esp32_hall/esp32_hall.cpp | 10 +- esphome/components/esp32_hall/esp32_hall.h | 2 +- esphome/components/esp32_hall/sensor.py | 3 +- esphome/components/esp32_improv/__init__.py | 5 +- .../esp32_improv/esp32_improv_component.cpp | 2 +- .../esp32_improv/esp32_improv_component.h | 2 +- esphome/components/esp32_touch/__init__.py | 3 +- .../components/esp32_touch/binary_sensor.py | 8 +- .../components/esp32_touch/esp32_touch.cpp | 5 +- esphome/components/esp32_touch/esp32_touch.h | 9 +- esphome/components/esp8266/__init__.py | 213 +++++ esphome/components/esp8266/boards.py | 266 ++++++ esphome/components/esp8266/const.py | 8 + esphome/components/esp8266/core.cpp | 37 + esphome/components/esp8266/gpio.cpp | 96 ++ esphome/components/esp8266/gpio.h | 38 + esphome/components/esp8266/gpio.py | 170 ++++ esphome/components/esp8266/preferences.cpp | 263 +++++ esphome/components/esp8266/preferences.h | 14 + .../components/esp8266_pwm/esp8266_pwm.cpp | 4 +- esphome/components/esp8266_pwm/esp8266_pwm.h | 8 +- esphome/components/esp8266_pwm/output.py | 3 +- esphome/components/ethernet/__init__.py | 20 +- .../ethernet/ethernet_component.cpp | 24 +- .../components/ethernet/ethernet_component.h | 21 +- .../exposure_notifications.cpp | 2 +- .../exposure_notifications.h | 2 +- esphome/components/ezo/ezo.cpp | 5 +- esphome/components/fan/fan_state.cpp | 2 +- .../components/fastled_base/fastled_light.cpp | 4 + .../components/fastled_base/fastled_light.h | 4 + esphome/components/fastled_clockless/light.py | 3 +- esphome/components/fastled_spi/light.py | 17 +- .../fingerprint_grow/fingerprint_grow.cpp | 2 +- esphome/components/globals/__init__.py | 12 +- .../components/globals/globals_component.h | 40 +- .../gpio/binary_sensor/gpio_binary_sensor.h | 2 +- .../gpio/output/gpio_binary_output.h | 2 +- esphome/components/gpio/switch/gpio_switch.h | 2 +- esphome/components/gps/__init__.py | 5 +- esphome/components/gps/gps.cpp | 4 + esphome/components/gps/gps.h | 4 + esphome/components/gps/time/gps_time.cpp | 4 + esphome/components/gps/time/gps_time.h | 4 + esphome/components/graph/graph.cpp | 30 +- esphome/components/hdc1080/hdc1080.cpp | 17 +- esphome/components/hlw8012/hlw8012.h | 10 +- esphome/components/hlw8012/sensor.py | 8 +- .../hm3301/abstract_aqi_calculator.h | 5 + esphome/components/hm3301/aqi_calculator.h | 4 + .../hm3301/aqi_calculator_factory.h | 4 + esphome/components/hm3301/caqi_calculator.h | 4 + esphome/components/hm3301/hm3301.cpp | 4 + esphome/components/hm3301/hm3301.h | 4 + esphome/components/hm3301/sensor.py | 1 + esphome/components/http_request/__init__.py | 50 +- .../components/http_request/http_request.cpp | 10 +- .../components/http_request/http_request.h | 10 +- esphome/components/htu21d/htu21d.cpp | 17 +- esphome/components/hx711/hx711.h | 2 +- esphome/components/i2c/__init__.py | 71 +- esphome/components/i2c/i2c.cpp | 291 +----- esphome/components/i2c/i2c.h | 284 ++---- esphome/components/i2c/i2c_bus.h | 46 + esphome/components/i2c/i2c_bus_arduino.cpp | 97 ++ esphome/components/i2c/i2c_bus_arduino.h | 37 + esphome/components/i2c/i2c_bus_esp_idf.cpp | 147 +++ esphome/components/i2c/i2c_bus_esp_idf.h | 41 + .../components/ili9341/ili9341_display.cpp | 5 +- esphome/components/improv/improv.cpp | 2 + esphome/components/improv/improv.h | 5 + esphome/components/ina219/ina219.cpp | 10 +- esphome/components/ina226/ina226.cpp | 10 +- esphome/components/ina3221/ina3221.cpp | 5 +- .../inkbird_ibsth1_mini.cpp | 6 +- .../inkbird_ibsth1_mini/inkbird_ibsth1_mini.h | 2 +- esphome/components/inkplate6/display.py | 5 +- esphome/components/inkplate6/inkplate.cpp | 162 ++-- esphome/components/inkplate6/inkplate.h | 67 +- .../integration/integration_sensor.cpp | 3 +- .../integration/integration_sensor.h | 1 + esphome/components/json/__init__.py | 6 + esphome/components/json/json_util.cpp | 4 + esphome/components/json/json_util.h | 4 + esphome/components/lcd_base/lcd_display.cpp | 1 + .../components/lcd_gpio/gpio_lcd_display.cpp | 11 +- .../components/lcd_gpio/gpio_lcd_display.h | 2 +- .../lcd_pcf8574/pcf8574_display.cpp | 1 + esphome/components/ledc/ledc_output.cpp | 107 ++- esphome/components/ledc/ledc_output.h | 8 +- esphome/components/ledc/output.py | 3 +- esphome/components/light/light_state.cpp | 2 +- esphome/components/light/light_transformer.h | 3 + esphome/components/logger/__init__.py | 2 +- esphome/components/logger/logger.cpp | 74 +- esphome/components/logger/logger.h | 27 +- esphome/components/max31856/max31856.cpp | 4 +- esphome/components/max31856/max31856.h | 4 +- esphome/components/max31865/max31865.cpp | 6 +- esphome/components/max31865/max31865.h | 6 +- esphome/components/max7219/max7219.cpp | 3 +- .../components/max7219digit/max7219digit.cpp | 3 +- .../components/max7219digit/max7219digit.h | 4 +- esphome/components/max7219digit/max7219font.h | 4 +- esphome/components/mcp23008/mcp23008.h | 2 +- esphome/components/mcp23016/__init__.py | 61 +- esphome/components/mcp23016/mcp23016.cpp | 27 +- esphome/components/mcp23016/mcp23016.h | 23 +- esphome/components/mcp23017/mcp23017.h | 2 +- esphome/components/mcp23s08/mcp23s08.h | 2 +- esphome/components/mcp23s17/mcp23s17.h | 2 +- .../mcp23x08_base/mcp23x08_base.cpp | 22 +- .../components/mcp23x08_base/mcp23x08_base.h | 4 +- .../mcp23x17_base/mcp23x17_base.cpp | 22 +- .../components/mcp23x17_base/mcp23x17_base.h | 4 +- esphome/components/mcp23xxx_base/__init__.py | 79 +- .../mcp23xxx_base/mcp23xxx_base.cpp | 15 +- .../components/mcp23xxx_base/mcp23xxx_base.h | 26 +- esphome/components/mcp3008/mcp3008.h | 2 +- esphome/components/mcp4725/mcp4725.cpp | 7 +- esphome/components/mcp9808/mcp9808.cpp | 10 +- esphome/components/mdns/__init__.py | 25 + esphome/components/mdns/mdns_component.cpp | 74 ++ esphome/components/mdns/mdns_component.h | 37 + .../components/mdns/mdns_esp32_arduino.cpp | 27 + esphome/components/mdns/mdns_esp8266.cpp | 32 + esphome/components/mdns/mdns_esp_idf.cpp | 52 + esphome/components/midea/adapter.cpp | 4 + esphome/components/midea/adapter.h | 5 + esphome/components/midea/air_conditioner.cpp | 4 + esphome/components/midea/air_conditioner.h | 5 + esphome/components/midea/appliance_base.h | 14 +- esphome/components/midea/automations.h | 5 + esphome/components/midea/climate.py | 3 +- esphome/components/midea/midea_ir.h | 3 + esphome/components/mpr121/mpr121.cpp | 1 + esphome/components/mqtt/__init__.py | 1 + .../components/mqtt/custom_mqtt_device.cpp | 5 + esphome/components/mqtt/custom_mqtt_device.h | 5 + .../components/mqtt/mqtt_binary_sensor.cpp | 2 + esphome/components/mqtt/mqtt_binary_sensor.h | 3 +- esphome/components/mqtt/mqtt_client.cpp | 44 +- esphome/components/mqtt/mqtt_client.h | 12 +- esphome/components/mqtt/mqtt_climate.cpp | 4 +- esphome/components/mqtt/mqtt_climate.h | 2 + esphome/components/mqtt/mqtt_component.cpp | 5 + esphome/components/mqtt/mqtt_component.h | 6 + esphome/components/mqtt/mqtt_cover.cpp | 2 + esphome/components/mqtt/mqtt_cover.h | 2 + esphome/components/mqtt/mqtt_fan.cpp | 2 + esphome/components/mqtt/mqtt_fan.h | 2 + esphome/components/mqtt/mqtt_light.cpp | 2 + esphome/components/mqtt/mqtt_light.h | 2 + esphome/components/mqtt/mqtt_number.cpp | 2 + esphome/components/mqtt/mqtt_number.h | 2 + esphome/components/mqtt/mqtt_select.cpp | 2 + esphome/components/mqtt/mqtt_select.h | 2 + esphome/components/mqtt/mqtt_sensor.cpp | 2 + esphome/components/mqtt/mqtt_sensor.h | 2 + esphome/components/mqtt/mqtt_switch.cpp | 2 + esphome/components/mqtt/mqtt_switch.h | 2 + esphome/components/mqtt/mqtt_text_sensor.cpp | 2 + esphome/components/mqtt/mqtt_text_sensor.h | 2 + .../sensor/mqtt_subscribe_sensor.cpp | 5 + .../sensor/mqtt_subscribe_sensor.h | 6 + .../mqtt_subscribe_text_sensor.cpp | 4 + .../text_sensor/mqtt_subscribe_text_sensor.h | 6 + esphome/components/ms5611/ms5611.cpp | 1 + esphome/components/my9231/my9231.h | 3 +- esphome/components/neopixelbus/light.py | 7 +- .../neopixelbus/neopixelbus_light.h | 6 +- esphome/components/network/__init__.py | 12 +- esphome/components/network/ip_address.h | 45 + esphome/components/network/util.cpp | 54 ++ esphome/components/network/util.h | 16 + esphome/components/nextion/display.py | 4 +- esphome/components/nextion/nextion.h | 19 +- esphome/components/nextion/nextion_upload.cpp | 32 +- .../nextion/sensor/nextion_sensor.cpp | 2 +- esphome/components/ntc/ntc.cpp | 2 +- esphome/components/number/automation.cpp | 10 +- esphome/components/number/automation.h | 4 +- esphome/components/number/number.cpp | 2 +- esphome/components/ota/__init__.py | 18 +- esphome/components/ota/ota_component.cpp | 432 ++++++--- esphome/components/ota/ota_component.h | 27 +- esphome/components/pca9685/pca9685_output.h | 4 +- esphome/components/pcf8574/__init__.py | 61 +- esphome/components/pcf8574/pcf8574.cpp | 36 +- esphome/components/pcf8574/pcf8574.h | 23 +- esphome/components/pid/pid_autotuner.cpp | 2 +- esphome/components/pid/pid_climate.cpp | 2 +- esphome/components/pid/pid_controller.h | 6 +- esphome/components/pipsolar/pipsolar.cpp | 2 +- esphome/components/pipsolar/pipsolar.h | 2 +- esphome/components/pmsa003i/pmsa003i.cpp | 1 + esphome/components/pn532/pn532.cpp | 1 + esphome/components/pn532_i2c/pn532_i2c.cpp | 3 +- .../components/power_supply/power_supply.h | 2 +- .../prometheus/prometheus_handler.cpp | 8 +- .../prometheus/prometheus_handler.h | 4 + .../pulse_counter/pulse_counter_sensor.cpp | 14 +- .../pulse_counter/pulse_counter_sensor.h | 24 +- .../pulse_meter/pulse_meter_sensor.cpp | 6 +- .../pulse_meter/pulse_meter_sensor.h | 8 +- .../components/pulse_width/pulse_width.cpp | 4 +- esphome/components/pulse_width/pulse_width.h | 12 +- esphome/components/pulse_width/sensor.py | 4 +- .../pvvx_mithermometer/pvvx_mithermometer.cpp | 2 +- .../pvvx_mithermometer/pvvx_mithermometer.h | 2 +- esphome/components/qmc5883l/qmc5883l.cpp | 7 +- esphome/components/rc522/rc522.cpp | 10 +- esphome/components/rc522/rc522.h | 2 +- esphome/components/rc522_i2c/rc522_i2c.cpp | 5 +- esphome/components/remote_base/__init__.py | 7 +- .../components/remote_base/midea_protocol.h | 7 +- .../components/remote_base/remote_base.cpp | 2 +- esphome/components/remote_base/remote_base.h | 14 +- .../remote_base/samsung_protocol.cpp | 1 + .../remote_base/toshiba_ac_protocol.cpp | 1 + .../components/remote_receiver/__init__.py | 4 +- .../remote_receiver/remote_receiver.h | 16 +- .../remote_receiver/remote_receiver_esp32.cpp | 2 +- .../remote_receiver_esp8266.cpp | 10 +- .../remote_transmitter/remote_transmitter.h | 8 +- .../remote_transmitter_esp32.cpp | 2 +- .../remote_transmitter_esp8266.cpp | 2 +- .../resistance/resistance_sensor.cpp | 2 +- esphome/components/restart/restart_switch.cpp | 1 + .../rotary_encoder/rotary_encoder.cpp | 23 +- .../rotary_encoder/rotary_encoder.h | 14 +- esphome/components/rotary_encoder/sensor.py | 8 +- esphome/components/ruuvi_ble/ruuvi_ble.cpp | 2 +- esphome/components/ruuvi_ble/ruuvi_ble.h | 2 +- esphome/components/ruuvitag/ruuvitag.cpp | 2 +- esphome/components/ruuvitag/ruuvitag.h | 2 +- esphome/components/scd30/scd30.cpp | 17 +- esphome/components/sdp3x/sdp3x.cpp | 14 +- esphome/components/sensor/automation.h | 14 +- esphome/components/sensor/filter.cpp | 19 +- esphome/components/servo/servo.cpp | 1 + esphome/components/servo/servo.h | 2 +- esphome/components/sgp30/sgp30.cpp | 9 +- esphome/components/sgp40/sgp40.cpp | 15 +- esphome/components/sht3xd/sht3xd.cpp | 2 +- esphome/components/sht4x/sht4x.cpp | 4 +- esphome/components/shtcx/shtcx.cpp | 3 +- .../components/shutdown/shutdown_switch.cpp | 11 +- esphome/components/slow_pwm/slow_pwm_output.h | 2 +- esphome/components/sm16716/sm16716.h | 3 +- esphome/components/sm2135/sm2135.h | 3 +- esphome/components/sn74hc595/__init__.py | 33 +- esphome/components/sn74hc595/sn74hc595.cpp | 26 +- esphome/components/sn74hc595/sn74hc595.h | 16 +- esphome/components/sntp/sntp_component.cpp | 8 +- .../components/socket/bsd_sockets_impl.cpp | 13 +- esphome/components/socket/headers.h | 8 +- .../components/socket/lwip_raw_tcp_impl.cpp | 3 +- esphome/components/spi/spi.cpp | 73 +- esphome/components/spi/spi.h | 43 +- esphome/components/sps30/sps30.cpp | 2 +- .../components/ssd1306_base/ssd1306_base.h | 2 +- .../components/ssd1306_i2c/ssd1306_i2c.cpp | 4 +- .../components/ssd1322_base/ssd1322_base.h | 2 +- .../components/ssd1325_base/ssd1325_base.h | 2 +- .../components/ssd1327_base/ssd1327_base.h | 2 +- .../components/ssd1327_i2c/ssd1327_i2c.cpp | 4 +- .../components/ssd1331_base/ssd1331_base.h | 2 +- .../components/ssd1351_base/ssd1351_base.h | 2 +- esphome/components/st7735/st7735.cpp | 17 +- .../status/status_binary_sensor.cpp | 4 +- .../status_led/light/status_led_light.h | 2 +- esphome/components/status_led/status_led.h | 2 +- esphome/components/stepper/stepper.cpp | 1 + esphome/components/sts3x/sts3x.cpp | 2 +- esphome/components/sun/sun.h | 4 +- esphome/components/switch/switch.cpp | 2 +- esphome/components/sx1509/__init__.py | 68 +- .../sx1509/output/sx1509_float_output.cpp | 3 +- esphome/components/sx1509/sx1509.cpp | 51 +- esphome/components/sx1509/sx1509.h | 22 +- esphome/components/sx1509/sx1509_gpio_pin.cpp | 13 +- esphome/components/sx1509/sx1509_gpio_pin.h | 13 +- esphome/components/tca9548a/__init__.py | 37 +- esphome/components/tca9548a/tca9548a.cpp | 43 +- esphome/components/tca9548a/tca9548a.h | 24 +- esphome/components/tcs34725/tcs34725.cpp | 1 + .../template/number/template_number.cpp | 4 +- .../template/select/template_select.cpp | 2 +- .../template/sensor/template_sensor.cpp | 3 +- .../thermostat/thermostat_climate.cpp | 14 +- esphome/components/time/automation.cpp | 1 + esphome/components/time/real_time_clock.cpp | 2 +- .../time_based/time_based_cover.cpp | 1 + .../components/tlc59208f/tlc59208f_output.cpp | 2 +- .../components/tlc59208f/tlc59208f_output.h | 1 + esphome/components/tlc5947/tlc5947.h | 3 +- esphome/components/tm1637/tm1637.cpp | 29 +- esphome/components/tm1637/tm1637.h | 2 +- esphome/components/tm1651/__init__.py | 15 +- esphome/components/tm1651/tm1651.cpp | 4 + esphome/components/tm1651/tm1651.h | 14 +- esphome/components/tmp102/tmp102.cpp | 9 +- .../components/tof10120/tof10120_sensor.cpp | 8 +- esphome/components/toshiba/toshiba.cpp | 2 +- .../total_daily_energy/total_daily_energy.cpp | 4 +- .../total_daily_energy/total_daily_energy.h | 1 + esphome/components/tsl2591/tsl2591.cpp | 3 +- esphome/components/ttp229_bsf/ttp229_bsf.h | 2 +- esphome/components/ttp229_lsf/ttp229_lsf.cpp | 6 +- .../components/tuya/climate/tuya_climate.cpp | 2 +- esphome/components/tuya/tuya.cpp | 5 +- esphome/components/tx20/sensor.py | 4 +- esphome/components/tx20/tx20.cpp | 8 +- esphome/components/tx20/tx20.h | 8 +- esphome/components/uart/__init__.py | 45 +- esphome/components/uart/uart.cpp | 50 +- esphome/components/uart/uart.h | 147 +-- esphome/components/uart/uart_component.cpp | 24 + esphome/components/uart/uart_component.h | 67 ++ ...2.cpp => uart_component_esp32_arduino.cpp} | 103 +- .../uart/uart_component_esp32_arduino.h | 40 + ...esp8266.cpp => uart_component_esp8266.cpp} | 163 ++-- .../components/uart/uart_component_esp8266.h | 79 ++ .../uart/uart_component_esp_idf.cpp | 201 ++++ .../components/uart/uart_component_esp_idf.h | 39 + esphome/components/uln2003/uln2003.h | 2 +- .../ultrasonic/ultrasonic_sensor.cpp | 20 +- .../components/ultrasonic/ultrasonic_sensor.h | 7 +- esphome/components/uptime/uptime_sensor.cpp | 1 + esphome/components/vl53l0x/vl53l0x_sensor.cpp | 6 +- esphome/components/vl53l0x/vl53l0x_sensor.h | 2 +- esphome/components/web_server/web_server.cpp | 7 +- esphome/components/web_server/web_server.h | 4 + .../web_server_base/web_server_base.cpp | 12 +- .../web_server_base/web_server_base.h | 4 + esphome/components/wifi/__init__.py | 23 +- esphome/components/wifi/wifi_component.cpp | 81 +- esphome/components/wifi/wifi_component.h | 57 +- ...2.cpp => wifi_component_esp32_arduino.cpp} | 80 +- .../wifi/wifi_component_esp8266.cpp | 69 +- .../wifi/wifi_component_esp_idf.cpp | 901 ++++++++++++++++++ .../wifi_info/wifi_info_text_sensor.h | 18 +- .../wifi_signal/wifi_signal_sensor.h | 2 +- esphome/components/wled/__init__.py | 2 +- esphome/components/wled/wled_light_effect.cpp | 8 +- esphome/components/wled/wled_light_effect.h | 4 + esphome/components/xiaomi_ble/xiaomi_ble.cpp | 2 +- esphome/components/xiaomi_ble/xiaomi_ble.h | 2 +- .../components/xiaomi_cgd1/xiaomi_cgd1.cpp | 2 +- esphome/components/xiaomi_cgd1/xiaomi_cgd1.h | 2 +- .../components/xiaomi_cgdk2/xiaomi_cgdk2.cpp | 2 +- .../components/xiaomi_cgdk2/xiaomi_cgdk2.h | 2 +- .../components/xiaomi_cgg1/xiaomi_cgg1.cpp | 2 +- esphome/components/xiaomi_cgg1/xiaomi_cgg1.h | 2 +- .../components/xiaomi_cgpr1/xiaomi_cgpr1.cpp | 2 +- .../components/xiaomi_cgpr1/xiaomi_cgpr1.h | 2 +- .../xiaomi_gcls002/xiaomi_gcls002.cpp | 2 +- .../xiaomi_gcls002/xiaomi_gcls002.h | 2 +- .../xiaomi_hhccjcy01/xiaomi_hhccjcy01.cpp | 2 +- .../xiaomi_hhccjcy01/xiaomi_hhccjcy01.h | 2 +- .../xiaomi_hhccpot002/xiaomi_hhccpot002.cpp | 2 +- .../xiaomi_hhccpot002/xiaomi_hhccpot002.h | 2 +- .../xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.cpp | 2 +- .../xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.h | 2 +- .../xiaomi_lywsd02/xiaomi_lywsd02.cpp | 2 +- .../xiaomi_lywsd02/xiaomi_lywsd02.h | 2 +- .../xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp | 2 +- .../xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.h | 2 +- .../xiaomi_lywsdcgq/xiaomi_lywsdcgq.cpp | 2 +- .../xiaomi_lywsdcgq/xiaomi_lywsdcgq.h | 2 +- .../xiaomi_mhoc401/xiaomi_mhoc401.cpp | 2 +- .../xiaomi_mhoc401/xiaomi_mhoc401.h | 2 +- .../xiaomi_miscale/xiaomi_miscale.cpp | 2 +- .../xiaomi_miscale/xiaomi_miscale.h | 2 +- .../xiaomi_miscale2/xiaomi_miscale2.cpp | 2 +- .../xiaomi_miscale2/xiaomi_miscale2.h | 2 +- .../xiaomi_mjyd02yla/xiaomi_mjyd02yla.cpp | 2 +- .../xiaomi_mjyd02yla/xiaomi_mjyd02yla.h | 2 +- .../xiaomi_mue4094rt/xiaomi_mue4094rt.cpp | 2 +- .../xiaomi_mue4094rt/xiaomi_mue4094rt.h | 2 +- .../xiaomi_wx08zm/xiaomi_wx08zm.cpp | 2 +- .../components/xiaomi_wx08zm/xiaomi_wx08zm.h | 2 +- esphome/components/zyaura/sensor.py | 8 +- esphome/components/zyaura/zyaura.cpp | 14 +- esphome/components/zyaura/zyaura.h | 16 +- esphome/config.py | 724 ++++++++------ esphome/config_validation.py | 127 ++- esphome/const.py | 32 +- esphome/core/__init__.py | 82 +- esphome/core/application.cpp | 21 +- esphome/core/application.h | 1 - esphome/core/application_esp32.cpp | 21 - esphome/core/application_esp8266.cpp | 14 - esphome/core/component.cpp | 4 +- esphome/core/component.h | 2 +- esphome/core/config.py | 229 ++--- esphome/core/defines.h | 43 +- esphome/core/esphal.cpp | 303 ------ esphome/core/esphal.h | 128 --- esphome/core/gpio.h | 98 ++ esphome/core/hal.h | 47 + esphome/core/helpers.cpp | 52 +- esphome/core/helpers.h | 12 +- esphome/core/log.cpp | 2 +- esphome/core/log.h | 12 +- esphome/core/macros.h | 4 +- esphome/core/preferences.cpp | 303 ------ esphome/core/preferences.h | 127 +-- esphome/core/scheduler.cpp | 3 +- esphome/core/util.cpp | 105 -- esphome/core/util.h | 17 - esphome/cpp_generator.py | 4 + esphome/cpp_helpers.py | 15 +- esphome/cpp_types.py | 4 +- esphome/dashboard/dashboard.py | 10 +- esphome/final_validate.py | 24 - esphome/helpers.py | 12 +- esphome/loader.py | 47 +- esphome/pins.py | 443 +++------ esphome/platformio_api.py | 4 +- esphome/storage_json.py | 31 +- esphome/wizard.py | 16 +- esphome/writer.py | 114 +-- platformio.ini | 77 +- requirements.txt | 4 + script/ci-custom.py | 27 +- script/clang-tidy | 9 +- script/helpers.py | 25 +- .../binary_sensor/test_binary_sensor.py | 2 +- tests/dummy_main.cpp | 6 - tests/test1.yaml | 72 +- tests/test2.yaml | 6 +- tests/test3.yaml | 2 +- tests/test4.yaml | 19 +- tests/test5.yaml | 24 +- tests/unit_tests/test_core.py | 4 +- tests/unit_tests/test_cpp_helpers.py | 10 - tests/unit_tests/test_pins.py | 346 ------- tests/unit_tests/test_wizard.py | 2 +- 583 files changed, 9008 insertions(+), 5420 deletions(-) create mode 100644 esphome/components/esp32/__init__.py rename esphome/{ => components/esp32}/boards.py (77%) create mode 100644 esphome/components/esp32/const.py create mode 100644 esphome/components/esp32/core.cpp create mode 100644 esphome/components/esp32/gpio.py create mode 100644 esphome/components/esp32/gpio_arduino.cpp create mode 100644 esphome/components/esp32/gpio_arduino.h create mode 100644 esphome/components/esp32/gpio_idf.cpp create mode 100644 esphome/components/esp32/gpio_idf.h create mode 100644 esphome/components/esp32/preferences.cpp create mode 100644 esphome/components/esp32/preferences.h create mode 100644 esphome/components/esp8266/__init__.py create mode 100644 esphome/components/esp8266/boards.py create mode 100644 esphome/components/esp8266/const.py create mode 100644 esphome/components/esp8266/core.cpp create mode 100644 esphome/components/esp8266/gpio.cpp create mode 100644 esphome/components/esp8266/gpio.h create mode 100644 esphome/components/esp8266/gpio.py create mode 100644 esphome/components/esp8266/preferences.cpp create mode 100644 esphome/components/esp8266/preferences.h create mode 100644 esphome/components/i2c/i2c_bus.h create mode 100644 esphome/components/i2c/i2c_bus_arduino.cpp create mode 100644 esphome/components/i2c/i2c_bus_arduino.h create mode 100644 esphome/components/i2c/i2c_bus_esp_idf.cpp create mode 100644 esphome/components/i2c/i2c_bus_esp_idf.h create mode 100644 esphome/components/mdns/__init__.py create mode 100644 esphome/components/mdns/mdns_component.cpp create mode 100644 esphome/components/mdns/mdns_component.h create mode 100644 esphome/components/mdns/mdns_esp32_arduino.cpp create mode 100644 esphome/components/mdns/mdns_esp8266.cpp create mode 100644 esphome/components/mdns/mdns_esp_idf.cpp create mode 100644 esphome/components/network/ip_address.h create mode 100644 esphome/components/network/util.cpp create mode 100644 esphome/components/network/util.h create mode 100644 esphome/components/uart/uart_component.cpp create mode 100644 esphome/components/uart/uart_component.h rename esphome/components/uart/{uart_esp32.cpp => uart_component_esp32_arduino.cpp} (64%) create mode 100644 esphome/components/uart/uart_component_esp32_arduino.h rename esphome/components/uart/{uart_esp8266.cpp => uart_component_esp8266.cpp} (60%) create mode 100644 esphome/components/uart/uart_component_esp8266.h create mode 100644 esphome/components/uart/uart_component_esp_idf.cpp create mode 100644 esphome/components/uart/uart_component_esp_idf.h rename esphome/components/wifi/{wifi_component_esp32.cpp => wifi_component_esp32_arduino.cpp} (89%) create mode 100644 esphome/components/wifi/wifi_component_esp_idf.cpp delete mode 100644 esphome/core/application_esp32.cpp delete mode 100644 esphome/core/application_esp8266.cpp delete mode 100644 esphome/core/esphal.cpp delete mode 100644 esphome/core/esphal.h create mode 100644 esphome/core/gpio.h create mode 100644 esphome/core/hal.h delete mode 100644 esphome/core/preferences.cpp delete mode 100644 tests/unit_tests/test_pins.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 875db0f9fd..c2c434a9b8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,7 +46,7 @@ jobs: name: Run script/clang-format - id: clang-tidy name: Run script/clang-tidy for ESP8266 - options: --environment esp8266-tidy --grep ARDUINO_ARCH_ESP8266 + options: --environment esp8266-tidy --grep USE_ESP8266 pio_cache_key: tidyesp8266 - id: clang-tidy name: Run script/clang-tidy for ESP32 1/4 @@ -64,6 +64,10 @@ jobs: name: Run script/clang-tidy for ESP32 4/4 options: --environment esp32-tidy --split-num 4 --split-at 4 pio_cache_key: tidyesp32 + - id: clang-tidy + name: Run script/clang-tidy for ESP32 esp-idf + options: --environment esp32-idf-tidy --grep USE_ESP_IDF + pio_cache_key: tidyesp32-idf steps: - uses: actions/checkout@v2 diff --git a/.gitignore b/.gitignore index cd7822bded..c52909f7c9 100644 --- a/.gitignore +++ b/.gitignore @@ -124,3 +124,5 @@ tests/.esphome/ /.temp-clang-tidy.cpp /.temp/ .pio/ + +sdkconfig.* diff --git a/CODEOWNERS b/CODEOWNERS index ab3a0815ce..385ec4d892 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -44,9 +44,11 @@ esphome/components/dfplayer/* @glmnet esphome/components/dht/* @OttoWinter esphome/components/ds1307/* @badbadc0ffee esphome/components/dsmr/* @glmnet @zuidwijk +esphome/components/esp32/* @esphome/core esphome/components/esp32_ble/* @jesserockz esphome/components/esp32_ble_server/* @jesserockz esphome/components/esp32_improv/* @jesserockz +esphome/components/esp8266/* @esphome/core esphome/components/exposure_notifications/* @OttoWinter esphome/components/ezo/* @ssieb esphome/components/fastled_base/* @OttoWinter @@ -81,6 +83,7 @@ esphome/components/mcp23x17_base/* @jesserockz esphome/components/mcp23xxx_base/* @jesserockz esphome/components/mcp2515/* @danielschramm @mvturnho esphome/components/mcp9808/* @k7hpn +esphome/components/mdns/* @esphome/core esphome/components/midea/* @dudanov esphome/components/mitsubishi/* @RubyBailey esphome/components/network/* @esphome/core diff --git a/esphome/__main__.py b/esphome/__main__.py index ba1019869d..6bc7e065ce 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -245,7 +245,7 @@ def upload_program(config, args, host): ota_conf = config[CONF_OTA] remote_port = ota_conf[CONF_PORT] - password = ota_conf[CONF_PASSWORD] + password = ota_conf.get(CONF_PASSWORD, "") return espota2.run_ota(host, remote_port, password, CORE.firmware_bin) diff --git a/esphome/codegen.py b/esphome/codegen.py index c05cc5efca..1b38fd9ed2 100644 --- a/esphome/codegen.py +++ b/esphome/codegen.py @@ -30,6 +30,7 @@ from esphome.cpp_generator import ( # noqa add_library, add_build_flag, add_define, + add_platformio_option, get_variable, get_variable_with_full_id, process_lambda, @@ -78,4 +79,6 @@ from esphome.cpp_types import ( # noqa JsonObjectConstRef, Controller, GPIOPin, + InternalGPIOPin, + gpio_Flags, ) diff --git a/esphome/components/a4988/a4988.h b/esphome/components/a4988/a4988.h index 5be0f3ce69..0fe7891110 100644 --- a/esphome/components/a4988/a4988.h +++ b/esphome/components/a4988/a4988.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/stepper/stepper.h" namespace esphome { diff --git a/esphome/components/ac_dimmer/ac_dimmer.cpp b/esphome/components/ac_dimmer/ac_dimmer.cpp index e86703bfe1..a7f1e6f3a9 100644 --- a/esphome/components/ac_dimmer/ac_dimmer.cpp +++ b/esphome/components/ac_dimmer/ac_dimmer.cpp @@ -1,10 +1,16 @@ +#ifdef USE_ARDUINO + #include "ac_dimmer.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" +#include -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #include #endif +#ifdef USE_ESP32_FRAMEWORK_ARDUINO +#include +#endif namespace esphome { namespace ac_dimmer { @@ -25,7 +31,7 @@ static const uint32_t GATE_ENABLE_TIME = 50; /// Function called from timer interrupt /// Input is current time in microseconds (micros()) /// Returns when next "event" is expected in µs, or 0 if no such event known. -uint32_t ICACHE_RAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) { +uint32_t IRAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) { // If no ZC signal received yet. if (this->crossed_zero_at == 0) return 0; @@ -37,13 +43,13 @@ uint32_t ICACHE_RAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) { if (this->enable_time_us != 0 && time_since_zc >= this->enable_time_us) { this->enable_time_us = 0; - this->gate_pin->digital_write(true); + this->gate_pin.digital_write(true); // Prevent too short pulses - this->disable_time_us = max(this->disable_time_us, time_since_zc + GATE_ENABLE_TIME); + this->disable_time_us = std::max(this->disable_time_us, time_since_zc + GATE_ENABLE_TIME); } if (this->disable_time_us != 0 && time_since_zc >= this->disable_time_us) { this->disable_time_us = 0; - this->gate_pin->digital_write(false); + this->gate_pin.digital_write(false); } if (time_since_zc < this->enable_time_us) @@ -63,7 +69,7 @@ uint32_t ICACHE_RAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) { } /// Run timer interrupt code and return in how many µs the next event is expected -uint32_t ICACHE_RAM_ATTR HOT timer_interrupt() { +uint32_t IRAM_ATTR HOT timer_interrupt() { // run at least with 1kHz uint32_t min_dt_us = 1000; uint32_t now = micros(); @@ -80,7 +86,7 @@ uint32_t ICACHE_RAM_ATTR HOT timer_interrupt() { } /// GPIO interrupt routine, called when ZC pin triggers -void ICACHE_RAM_ATTR HOT AcDimmerDataStore::gpio_intr() { +void IRAM_ATTR HOT AcDimmerDataStore::gpio_intr() { uint32_t prev_crossed = this->crossed_zero_at; // 50Hz mains frequency should give a half cycle of 10ms a 60Hz will give 8.33ms @@ -97,7 +103,7 @@ void ICACHE_RAM_ATTR HOT AcDimmerDataStore::gpio_intr() { if (this->value == 65535) { // fully on, enable output immediately - this->gate_pin->digital_write(true); + this->gate_pin.digital_write(true); } else if (this->init_cycle) { // send a full cycle this->init_cycle = false; @@ -105,29 +111,29 @@ void ICACHE_RAM_ATTR HOT AcDimmerDataStore::gpio_intr() { this->disable_time_us = cycle_time_us; } else if (this->value == 0) { // fully off, disable output immediately - this->gate_pin->digital_write(false); + this->gate_pin.digital_write(false); } else { if (this->method == DIM_METHOD_TRAILING) { this->enable_time_us = 1; // cannot be 0 - this->disable_time_us = max((uint32_t) 10, this->value * this->cycle_time_us / 65535); + this->disable_time_us = std::max((uint32_t) 10, this->value * this->cycle_time_us / 65535); } else { // calculate time until enable in µs: (1.0-value)*cycle_time, but with integer arithmetic // also take into account min_power auto min_us = this->cycle_time_us * this->min_power / 1000; - this->enable_time_us = max((uint32_t) 1, ((65535 - this->value) * (this->cycle_time_us - min_us)) / 65535); + this->enable_time_us = std::max((uint32_t) 1, ((65535 - this->value) * (this->cycle_time_us - min_us)) / 65535); if (this->method == DIM_METHOD_LEADING_PULSE) { // Minimum pulse time should be enough for the triac to trigger when it is close to the ZC zone // this is for brightness near 99% - this->disable_time_us = max(this->enable_time_us + GATE_ENABLE_TIME, (uint32_t) cycle_time_us / 10); + this->disable_time_us = std::max(this->enable_time_us + GATE_ENABLE_TIME, (uint32_t) cycle_time_us / 10); } else { - this->gate_pin->digital_write(false); + this->gate_pin.digital_write(false); this->disable_time_us = this->cycle_time_us; } } } } -void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store) { +void IRAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store) { // Attaching pin interrupts on the same pin will override the previous interrupt // However, the user expects that multiple dimmers sharing the same ZC pin will work. // We solve this in a bit of a hacky way: On each pin interrupt, we check all dimmers @@ -141,11 +147,11 @@ void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store } } -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 // ESP32 implementation, uses basically the same code but needs to wrap // timer_interrupt() function to auto-reschedule static hw_timer_t *dimmer_timer = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_timer_intr() { timer_interrupt(); } +void IRAM_ATTR HOT AcDimmerDataStore::s_timer_intr() { timer_interrupt(); } #endif void AcDimmer::setup() { @@ -174,15 +180,16 @@ void AcDimmer::setup() { if (setup_zero_cross_pin) { this->zero_cross_pin_->setup(); this->store_.zero_cross_pin = this->zero_cross_pin_->to_isr(); - this->zero_cross_pin_->attach_interrupt(&AcDimmerDataStore::s_gpio_intr, &this->store_, FALLING); + this->zero_cross_pin_->attach_interrupt(&AcDimmerDataStore::s_gpio_intr, &this->store_, + gpio::INTERRUPT_FALLING_EDGE); } -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 // Uses ESP8266 waveform (soft PWM) class // PWM and AcDimmer can even run at the same time this way setTimer1Callback(&timer_interrupt); #endif -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 // 80 Divider -> 1 count=1µs dimmer_timer = timerBegin(0, 80, true); timerAttachInterrupt(dimmer_timer, &AcDimmerDataStore::s_timer_intr, true); @@ -218,3 +225,5 @@ void AcDimmer::dump_config() { } // namespace ac_dimmer } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/ac_dimmer/ac_dimmer.h b/esphome/components/ac_dimmer/ac_dimmer.h index 00da061cfd..fd1bbc28db 100644 --- a/esphome/components/ac_dimmer/ac_dimmer.h +++ b/esphome/components/ac_dimmer/ac_dimmer.h @@ -1,7 +1,9 @@ #pragma once +#ifdef USE_ARDUINO + #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/output/float_output.h" namespace esphome { @@ -11,11 +13,11 @@ enum DimMethod { DIM_METHOD_LEADING_PULSE = 0, DIM_METHOD_LEADING, DIM_METHOD_TR struct AcDimmerDataStore { /// Zero-cross pin - ISRInternalGPIOPin *zero_cross_pin; + ISRInternalGPIOPin zero_cross_pin; /// Zero-cross pin number - used to share ZC pin across multiple dimmers uint8_t zero_cross_pin_number; /// Output pin to write to - ISRInternalGPIOPin *gate_pin; + ISRInternalGPIOPin gate_pin; /// Value of the dimmer - 0 to 65535. uint16_t value; /// Minimum power for activation @@ -37,7 +39,7 @@ struct AcDimmerDataStore { void gpio_intr(); static void s_gpio_intr(AcDimmerDataStore *store); -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 static void s_timer_intr(); #endif }; @@ -47,16 +49,16 @@ class AcDimmer : public output::FloatOutput, public Component { void setup() override; void dump_config() override; - void set_gate_pin(GPIOPin *gate_pin) { gate_pin_ = gate_pin; } - void set_zero_cross_pin(GPIOPin *zero_cross_pin) { zero_cross_pin_ = zero_cross_pin; } + void set_gate_pin(InternalGPIOPin *gate_pin) { gate_pin_ = gate_pin; } + void set_zero_cross_pin(InternalGPIOPin *zero_cross_pin) { zero_cross_pin_ = zero_cross_pin; } void set_init_with_half_cycle(bool init_with_half_cycle) { init_with_half_cycle_ = init_with_half_cycle; } void set_method(DimMethod method) { method_ = method; } protected: void write_state(float state) override; - GPIOPin *gate_pin_; - GPIOPin *zero_cross_pin_; + InternalGPIOPin *gate_pin_; + InternalGPIOPin *zero_cross_pin_; AcDimmerDataStore store_; bool init_with_half_cycle_; DimMethod method_; @@ -64,3 +66,5 @@ class AcDimmer : public output::FloatOutput, public Component { } // namespace ac_dimmer } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/ac_dimmer/output.py b/esphome/components/ac_dimmer/output.py index 2c37d325eb..c39fc382b6 100644 --- a/esphome/components/ac_dimmer/output.py +++ b/esphome/components/ac_dimmer/output.py @@ -19,17 +19,20 @@ DIM_METHODS = { CONF_GATE_PIN = "gate_pin" CONF_ZERO_CROSS_PIN = "zero_cross_pin" CONF_INIT_WITH_HALF_CYCLE = "init_with_half_cycle" -CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend( - { - cv.Required(CONF_ID): cv.declare_id(AcDimmer), - cv.Required(CONF_GATE_PIN): pins.internal_gpio_output_pin_schema, - cv.Required(CONF_ZERO_CROSS_PIN): pins.internal_gpio_input_pin_schema, - cv.Optional(CONF_INIT_WITH_HALF_CYCLE, default=True): cv.boolean, - cv.Optional(CONF_METHOD, default="leading pulse"): cv.enum( - DIM_METHODS, upper=True, space="_" - ), - } -).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cv.All( + output.FLOAT_OUTPUT_SCHEMA.extend( + { + cv.Required(CONF_ID): cv.declare_id(AcDimmer), + cv.Required(CONF_GATE_PIN): pins.internal_gpio_output_pin_schema, + cv.Required(CONF_ZERO_CROSS_PIN): pins.internal_gpio_input_pin_schema, + cv.Optional(CONF_INIT_WITH_HALF_CYCLE, default=True): cv.boolean, + cv.Optional(CONF_METHOD, default="leading pulse"): cv.enum( + DIM_METHODS, upper=True, space="_" + ), + } + ).extend(cv.COMPONENT_SCHEMA), + cv.only_with_arduino, +) async def to_code(config): diff --git a/esphome/components/adc/adc_sensor.cpp b/esphome/components/adc/adc_sensor.cpp index 4106a0c49b..9a05c1d66b 100644 --- a/esphome/components/adc/adc_sensor.cpp +++ b/esphome/components/adc/adc_sensor.cpp @@ -2,6 +2,7 @@ #include "esphome/core/log.h" #ifdef USE_ADC_SENSOR_VCC +#include ADC_MODE(ADC_VCC) #endif @@ -10,7 +11,7 @@ namespace adc { static const char *const TAG = "adc"; -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 void ADCSensor::set_attenuation(adc_atten_t attenuation) { this->attenuation_ = attenuation; } inline adc1_channel_t gpio_to_adc1(uint8_t pin) { @@ -57,28 +58,28 @@ inline adc1_channel_t gpio_to_adc1(uint8_t pin) { void ADCSensor::setup() { ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str()); #ifndef USE_ADC_SENSOR_VCC - GPIOPin(this->pin_, INPUT).setup(); + pin_->setup(); #endif -#ifdef ARDUINO_ARCH_ESP32 - adc1_config_channel_atten(gpio_to_adc1(pin_), attenuation_); +#ifdef USE_ESP32 + adc1_config_channel_atten(gpio_to_adc1(pin_->get_pin()), attenuation_); adc1_config_width(ADC_WIDTH_BIT_12); #if !CONFIG_IDF_TARGET_ESP32C3 && !CONFIG_IDF_TARGET_ESP32H2 - adc_gpio_init(ADC_UNIT_1, (adc_channel_t) gpio_to_adc1(pin_)); + adc_gpio_init(ADC_UNIT_1, (adc_channel_t) gpio_to_adc1(pin_->get_pin())); #endif #endif } void ADCSensor::dump_config() { LOG_SENSOR("", "ADC Sensor", this); -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #ifdef USE_ADC_SENSOR_VCC ESP_LOGCONFIG(TAG, " Pin: VCC"); #else - ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_); + LOG_PIN(" Pin: ", pin_); #endif #endif -#ifdef ARDUINO_ARCH_ESP32 - ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_); +#ifdef USE_ESP32 + LOG_PIN(" Pin: ", pin_); switch (this->attenuation_) { case ADC_ATTEN_DB_0: ESP_LOGCONFIG(TAG, " Attenuation: 0db (max 1.1V)"); @@ -105,8 +106,8 @@ void ADCSensor::update() { this->publish_state(value_v); } float ADCSensor::sample() { -#ifdef ARDUINO_ARCH_ESP32 - int raw = adc1_get_raw(gpio_to_adc1(pin_)); +#ifdef USE_ESP32 + int raw = adc1_get_raw(gpio_to_adc1(pin_->get_pin())); float value_v = raw / 4095.0f; #if CONFIG_IDF_TARGET_ESP32 switch (this->attenuation_) { @@ -146,15 +147,15 @@ float ADCSensor::sample() { return value_v; #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #ifdef USE_ADC_SENSOR_VCC return ESP.getVcc() / 1024.0f; // NOLINT(readability-static-accessed-through-instance) #else - return analogRead(this->pin_) / 1024.0f; // NOLINT + return analogRead(this->pin_->get_pin()) / 1024.0f; // NOLINT #endif #endif } -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; } #endif diff --git a/esphome/components/adc/adc_sensor.h b/esphome/components/adc/adc_sensor.h index 4591ed758d..b8c702be4e 100644 --- a/esphome/components/adc/adc_sensor.h +++ b/esphome/components/adc/adc_sensor.h @@ -1,12 +1,12 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/core/defines.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/voltage_sampler/voltage_sampler.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include "driver/adc.h" #endif @@ -15,7 +15,7 @@ namespace adc { class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler { public: -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 /// Set the attenuation for this pin. Only available on the ESP32. void set_attenuation(adc_atten_t attenuation); #endif @@ -27,17 +27,17 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage void dump_config() override; /// `HARDWARE_LATE` setup priority. float get_setup_priority() const override; - void set_pin(uint8_t pin) { this->pin_ = pin; } + void set_pin(InternalGPIOPin *pin) { this->pin_ = pin; } float sample() override; -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 std::string unique_id() override; #endif protected: - uint8_t pin_; + InternalGPIOPin *pin_; -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 adc_atten_t attenuation_{ADC_ATTEN_DB_0}; #endif }; diff --git a/esphome/components/adc/sensor.py b/esphome/components/adc/sensor.py index 7c32e4a923..9a0407d0f4 100644 --- a/esphome/components/adc/sensor.py +++ b/esphome/components/adc/sensor.py @@ -5,11 +5,13 @@ from esphome.components import sensor, voltage_sampler from esphome.const import ( CONF_ATTENUATION, CONF_ID, + CONF_INPUT, CONF_PIN, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT, UNIT_VOLT, ) +from esphome.core import CORE AUTO_LOAD = ["voltage_sampler"] @@ -23,10 +25,34 @@ ATTENUATION_MODES = { def validate_adc_pin(value): - vcc = str(value).upper() - if vcc == "VCC": - return cv.only_on_esp8266(vcc) - return pins.analog_pin(value) + if str(value).upper() == "VCC": + return cv.only_on_esp8266("VCC") + + if CORE.is_esp32: + from esphome.components.esp32 import is_esp32c3 + + value = pins.internal_gpio_input_pin_number(value) + if is_esp32c3(): + if not (0 <= value <= 4): # ADC1 + raise cv.Invalid("ESP32-C3: Only pins 0 though 4 support ADC.") + if not (32 <= value <= 39): # ADC1 + raise cv.Invalid("ESP32: Only pins 32 though 39 support ADC.") + elif CORE.is_esp8266: + from esphome.components.esp8266.gpio import CONF_ANALOG + + value = pins.internal_gpio_pin_number({CONF_ANALOG: True, CONF_INPUT: True})( + value + ) + + if value != 17: # A0 + raise cv.Invalid("ESP8266: Only pin A0 (GPIO17) supports ADC.") + return pins.gpio_pin_schema( + {CONF_ANALOG: True, CONF_INPUT: True}, internal=True + )(value) + else: + raise NotImplementedError + + return pins.internal_gpio_input_pin_schema(value) adc_ns = cg.esphome_ns.namespace("adc") @@ -62,7 +88,8 @@ async def to_code(config): if config[CONF_PIN] == "VCC": cg.add_define("USE_ADC_SENSOR_VCC") else: - cg.add(var.set_pin(config[CONF_PIN])) + pin = await cg.gpio_pin_expression(config[CONF_PIN]) + cg.add(var.set_pin(pin)) if CONF_ATTENUATION in config: cg.add(var.set_attenuation(config[CONF_ATTENUATION])) diff --git a/esphome/components/ade7953/ade7953.cpp b/esphome/components/ade7953/ade7953.cpp index 0e6d5624c1..2c61fc6a44 100644 --- a/esphome/components/ade7953/ade7953.cpp +++ b/esphome/components/ade7953/ade7953.cpp @@ -8,9 +8,7 @@ static const char *const TAG = "ade7953"; void ADE7953::dump_config() { ESP_LOGCONFIG(TAG, "ADE7953:"); - if (this->has_irq_) { - ESP_LOGCONFIG(TAG, " IRQ Pin: GPIO%u", this->irq_pin_number_); - } + LOG_PIN(" IRQ Pin: ", irq_pin_); LOG_I2C_DEVICE(this); LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Voltage Sensor", this->voltage_sensor_); @@ -20,27 +18,28 @@ void ADE7953::dump_config() { LOG_SENSOR(" ", "Active Power B Sensor", this->active_power_b_sensor_); } -#define ADE_PUBLISH_(name, factor) \ - if ((name) && this->name##_sensor_) { \ - float value = *(name) / (factor); \ +#define ADE_PUBLISH_(name, val, factor) \ + if (err == i2c::ERROR_OK && this->name##_sensor_) { \ + float value = (val) / (factor); \ this->name##_sensor_->publish_state(value); \ } -#define ADE_PUBLISH(name, factor) ADE_PUBLISH_(name, factor) +#define ADE_PUBLISH(name, val, factor) ADE_PUBLISH_(name, val, factor) void ADE7953::update() { if (!this->is_setup_) return; - auto active_power_a = this->ade_read_(0x0312); - ADE_PUBLISH(active_power_a, 154.0f); - auto active_power_b = this->ade_read_(0x0313); - ADE_PUBLISH(active_power_b, 154.0f); - auto current_a = this->ade_read_(0x031A); - ADE_PUBLISH(current_a, 100000.0f); - auto current_b = this->ade_read_(0x031B); - ADE_PUBLISH(current_b, 100000.0f); - auto voltage = this->ade_read_(0x031C); - ADE_PUBLISH(voltage, 26000.0f); + uint32_t val; + i2c::ErrorCode err = ade_read_32_(0x0312, &val); + ADE_PUBLISH(active_power_a, (int32_t) val, 154.0f); + err = ade_read_32_(0x0313, &val); + ADE_PUBLISH(active_power_b, (int32_t) val, 154.0f); + err = ade_read_32_(0x031A, &val); + ADE_PUBLISH(current_a, (uint32_t) val, 100000.0f); + err = ade_read_32_(0x031B, &val); + ADE_PUBLISH(current_b, (uint32_t) val, 100000.0f); + err = ade_read_32_(0x031C, &val); + ADE_PUBLISH(voltage, (uint32_t) val, 26000.0f); // auto apparent_power_a = this->ade_read_(0x0310); // auto apparent_power_b = this->ade_read_(0x0311); diff --git a/esphome/components/ade7953/ade7953.h b/esphome/components/ade7953/ade7953.h index 5a582a991a..c6fb383ed8 100644 --- a/esphome/components/ade7953/ade7953.h +++ b/esphome/components/ade7953/ade7953.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/i2c/i2c.h" #include "esphome/components/sensor/sensor.h" @@ -10,10 +10,7 @@ namespace ade7953 { class ADE7953 : public i2c::I2CDevice, public PollingComponent { public: - void set_irq_pin(uint8_t irq_pin) { - has_irq_ = true; - irq_pin_number_ = irq_pin; - } + void set_irq_pin(InternalGPIOPin *irq_pin) { irq_pin_ = irq_pin; } void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; } void set_current_a_sensor(sensor::Sensor *current_a_sensor) { current_a_sensor_ = current_a_sensor; } void set_current_b_sensor(sensor::Sensor *current_b_sensor) { current_b_sensor_ = current_b_sensor; } @@ -25,15 +22,13 @@ class ADE7953 : public i2c::I2CDevice, public PollingComponent { } void setup() override { - if (this->has_irq_) { - auto pin = GPIOPin(this->irq_pin_number_, INPUT); - this->irq_pin_ = &pin; + if (this->irq_pin_ != nullptr) { this->irq_pin_->setup(); } this->set_timeout(100, [this]() { - this->ade_write_(0x0010, 0x04); - this->ade_write_(0x00FE, 0xAD); - this->ade_write_(0x0120, 0x0030); + this->ade_write_8_(0x0010, 0x04); + this->ade_write_8_(0x00FE, 0xAD); + this->ade_write_16_(0x0120, 0x0030); this->is_setup_ = true; }); } @@ -43,31 +38,51 @@ class ADE7953 : public i2c::I2CDevice, public PollingComponent { void update() override; protected: - template bool ade_write_(uint16_t reg, T value) { + i2c::ErrorCode ade_write_8_(uint16_t reg, uint8_t value) { std::vector data; data.push_back(reg >> 8); data.push_back(reg >> 0); - for (int i = sizeof(T) - 1; i >= 0; i--) - data.push_back(value >> (i * 8)); - return this->write_bytes_raw(data); + data.push_back(value); + return write(data.data(), data.size()); } - template optional ade_read_(uint16_t reg) { - uint8_t hi = reg >> 8; - uint8_t lo = reg >> 0; - if (!this->write_bytes_raw({hi, lo})) - return {}; - auto ret = this->read_bytes_raw(); - if (!ret.has_value()) - return {}; - T result = 0; - for (int i = 0, j = sizeof(T) - 1; i < sizeof(T); i++, j--) - result |= T((*ret)[i]) << (j * 8); - return result; + i2c::ErrorCode ade_write_16_(uint16_t reg, uint16_t value) { + std::vector data; + data.push_back(reg >> 8); + data.push_back(reg >> 0); + data.push_back(value >> 8); + data.push_back(value >> 0); + return write(data.data(), data.size()); + } + i2c::ErrorCode ade_write_32_(uint16_t reg, uint32_t value) { + std::vector data; + data.push_back(reg >> 8); + data.push_back(reg >> 0); + data.push_back(value >> 24); + data.push_back(value >> 16); + data.push_back(value >> 8); + data.push_back(value >> 0); + return write(data.data(), data.size()); + } + i2c::ErrorCode ade_read_32_(uint16_t reg, uint32_t *value) { + uint8_t reg_data[2]; + reg_data[0] = reg >> 8; + reg_data[1] = reg >> 0; + i2c::ErrorCode err = write(reg_data, 2); + if (err != i2c::ERROR_OK) + return err; + uint8_t recv[4]; + err = read(recv, 4); + if (err != i2c::ERROR_OK) + return err; + *value = 0; + *value |= ((uint32_t) recv[0]) << 24; + *value |= ((uint32_t) recv[1]) << 24; + *value |= ((uint32_t) recv[2]) << 24; + *value |= ((uint32_t) recv[3]) << 24; + return i2c::ERROR_OK; } - bool has_irq_ = false; - uint8_t irq_pin_number_; - GPIOPin *irq_pin_{nullptr}; + InternalGPIOPin *irq_pin_ = nullptr; bool is_setup_{false}; sensor::Sensor *voltage_sensor_{nullptr}; sensor::Sensor *current_a_sensor_{nullptr}; diff --git a/esphome/components/ade7953/sensor.py b/esphome/components/ade7953/sensor.py index 80dafe2417..d02f466091 100644 --- a/esphome/components/ade7953/sensor.py +++ b/esphome/components/ade7953/sensor.py @@ -29,7 +29,7 @@ CONFIG_SCHEMA = ( cv.Schema( { cv.GenerateID(): cv.declare_id(ADE7953), - cv.Optional(CONF_IRQ_PIN): pins.input_pin, + cv.Optional(CONF_IRQ_PIN): pins.internal_gpio_input_pin_schema, cv.Optional(CONF_VOLTAGE): sensor.sensor_schema( unit_of_measurement=UNIT_VOLT, accuracy_decimals=1, @@ -73,7 +73,8 @@ async def to_code(config): await i2c.register_i2c_device(var, config) if CONF_IRQ_PIN in config: - cg.add(var.set_irq_pin(config[CONF_IRQ_PIN])) + irq_pin = await cg.gpio_pin_expression(config[CONF_IRQ_PIN]) + cg.add(var.set_irq_pin(irq_pin)) for key in [ CONF_VOLTAGE, diff --git a/esphome/components/ads1115/ads1115.cpp b/esphome/components/ads1115/ads1115.cpp index 92f0ffbe3a..beb379db93 100644 --- a/esphome/components/ads1115/ads1115.cpp +++ b/esphome/components/ads1115/ads1115.cpp @@ -1,5 +1,6 @@ #include "ads1115.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace ads1115 { @@ -159,7 +160,7 @@ float ADS1115Component::request_measurement(ADS1115Sensor *sensor) { float ADS1115Sensor::sample() { return this->parent_->request_measurement(this); } void ADS1115Sensor::update() { float v = this->parent_->request_measurement(this); - if (!isnan(v)) { + if (!std::isnan(v)) { ESP_LOGD(TAG, "'%s': Got Voltage=%fV", this->get_name().c_str(), v); this->publish_state(v); } diff --git a/esphome/components/aht10/aht10.cpp b/esphome/components/aht10/aht10.cpp index da8c6e844e..713199212c 100644 --- a/esphome/components/aht10/aht10.cpp +++ b/esphome/components/aht10/aht10.cpp @@ -14,6 +14,7 @@ #include "aht10.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace aht10 { @@ -33,8 +34,19 @@ void AHT10Component::setup() { this->mark_failed(); return; } - uint8_t data; - if (!this->read_byte(0, &data, AHT10_DEFAULT_DELAY)) { + uint8_t data = 0; + if (this->write(&data, 1) != i2c::ERROR_OK) { + ESP_LOGD(TAG, "Communication with AHT10 failed!"); + this->mark_failed(); + return; + } + delay(AHT10_DEFAULT_DELAY); + if (this->read(&data, 1) != i2c::ERROR_OK) { + ESP_LOGD(TAG, "Communication with AHT10 failed!"); + this->mark_failed(); + return; + } + if (this->read(&data, 1) != i2c::ERROR_OK) { ESP_LOGD(TAG, "Communication with AHT10 failed!"); this->mark_failed(); return; @@ -55,15 +67,26 @@ void AHT10Component::update() { return; } uint8_t data[6]; - uint8_t delay = AHT10_DEFAULT_DELAY; + uint8_t delay_ms = AHT10_DEFAULT_DELAY; if (this->humidity_sensor_ != nullptr) - delay = AHT10_HUMIDITY_DELAY; + delay_ms = AHT10_HUMIDITY_DELAY; + bool success = false; for (int i = 0; i < AHT10_ATTEMPTS; ++i) { - ESP_LOGVV(TAG, "Attempt %u at %6ld", i, millis()); + ESP_LOGVV(TAG, "Attempt %d at %6u", i, millis()); delay_microseconds_accurate(4); - if (!this->read_bytes(0, data, 6, delay)) { + + uint8_t reg = 0; + if (this->write(®, 1) != i2c::ERROR_OK) { ESP_LOGD(TAG, "Communication with AHT10 failed, waiting..."); - } else if ((data[0] & 0x80) == 0x80) { // Bit[7] = 0b1, device is busy + continue; + } + delay(delay_ms); + if (this->read(data, 6) != i2c::ERROR_OK) { + ESP_LOGD(TAG, "Communication with AHT10 failed, waiting..."); + continue; + } + + if ((data[0] & 0x80) == 0x80) { // Bit[7] = 0b1, device is busy ESP_LOGD(TAG, "AHT10 is busy, waiting..."); } else if (data[1] == 0x0 && data[2] == 0x0 && (data[3] >> 4) == 0x0) { // Unrealistic humidity (0x0) @@ -80,11 +103,12 @@ void AHT10Component::update() { } } else { // data is valid, we can break the loop - ESP_LOGVV(TAG, "Answer at %6ld", millis()); + ESP_LOGVV(TAG, "Answer at %6u", millis()); + success = true; break; } } - if ((data[0] & 0x80) == 0x80) { + if (!success || (data[0] & 0x80) == 0x80) { ESP_LOGE(TAG, "Measurements reading timed-out!"); this->status_set_warning(); return; @@ -105,7 +129,7 @@ void AHT10Component::update() { this->temperature_sensor_->publish_state(temperature); } if (this->humidity_sensor_ != nullptr) { - if (isnan(humidity)) + if (std::isnan(humidity)) ESP_LOGW(TAG, "Invalid humidity! Sensor reported 0%% Hum"); this->humidity_sensor_->publish_state(humidity); } diff --git a/esphome/components/airthings_ble/airthings_listener.cpp b/esphome/components/airthings_ble/airthings_listener.cpp index a843bcb145..951961cb1b 100644 --- a/esphome/components/airthings_ble/airthings_listener.cpp +++ b/esphome/components/airthings_ble/airthings_listener.cpp @@ -1,7 +1,7 @@ #include "airthings_listener.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace airthings_ble { diff --git a/esphome/components/airthings_ble/airthings_listener.h b/esphome/components/airthings_ble/airthings_listener.h index cd240ac1ba..52f69ea970 100644 --- a/esphome/components/airthings_ble/airthings_listener.h +++ b/esphome/components/airthings_ble/airthings_listener.h @@ -1,10 +1,9 @@ #pragma once -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include "esphome/core/component.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" -#include namespace esphome { namespace airthings_ble { diff --git a/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp b/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp index 6b4780726d..a8153ae87a 100644 --- a/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp +++ b/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp @@ -1,6 +1,6 @@ #include "airthings_wave_plus.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32_FRAMEWORK_ARDUINO namespace esphome { namespace airthings_wave_plus { @@ -141,4 +141,4 @@ void AirthingsWavePlus::setup() {} } // namespace airthings_wave_plus } // namespace esphome -#endif // ARDUINO_ARCH_ESP32 +#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/airthings_wave_plus/airthings_wave_plus.h b/esphome/components/airthings_wave_plus/airthings_wave_plus.h index 88d7a7a01b..5677f05a62 100644 --- a/esphome/components/airthings_wave_plus/airthings_wave_plus.h +++ b/esphome/components/airthings_wave_plus/airthings_wave_plus.h @@ -1,6 +1,6 @@ #pragma once -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32_FRAMEWORK_ARDUINO #include #include @@ -72,4 +72,4 @@ class AirthingsWavePlus : public PollingComponent, public ble_client::BLEClientN } // namespace airthings_wave_plus } // namespace esphome -#endif // ARDUINO_ARCH_ESP32 +#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/airthings_wave_plus/sensor.py b/esphome/components/airthings_wave_plus/sensor.py index 4109fca700..8b902ea81c 100644 --- a/esphome/components/airthings_wave_plus/sensor.py +++ b/esphome/components/airthings_wave_plus/sensor.py @@ -34,7 +34,7 @@ AirthingsWavePlus = airthings_wave_plus_ns.class_( ) -CONFIG_SCHEMA = ( +CONFIG_SCHEMA = cv.All( cv.Schema( { cv.GenerateID(): cv.declare_id(AirthingsWavePlus), @@ -83,7 +83,9 @@ CONFIG_SCHEMA = ( } ) .extend(cv.polling_component_schema("5mins")) - .extend(ble_client.BLE_CLIENT_SCHEMA) + .extend(ble_client.BLE_CLIENT_SCHEMA), + # Until BLEUUID reference removed + cv.only_with_arduino, ) diff --git a/esphome/components/am2320/am2320.cpp b/esphome/components/am2320/am2320.cpp index 7e8795dd30..b53eb69464 100644 --- a/esphome/components/am2320/am2320.cpp +++ b/esphome/components/am2320/am2320.cpp @@ -5,6 +5,7 @@ #include "am2320.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace am2320 { @@ -77,7 +78,7 @@ bool AM2320Component::read_bytes_(uint8_t a_register, uint8_t *data, uint8_t len if (conversion > 0) delay(conversion); - return this->parent_->raw_receive(this->address_, data, len); + return this->read(data, len) == i2c::ERROR_OK; } bool AM2320Component::read_data_(uint8_t *data) { diff --git a/esphome/components/am43/am43.cpp b/esphome/components/am43/am43.cpp index 94be194a1d..2130b334be 100644 --- a/esphome/components/am43/am43.cpp +++ b/esphome/components/am43/am43.cpp @@ -1,7 +1,8 @@ #include "am43.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace am43 { diff --git a/esphome/components/am43/am43.h b/esphome/components/am43/am43.h index 025d075b68..8dfe83e3a3 100644 --- a/esphome/components/am43/am43.h +++ b/esphome/components/am43/am43.h @@ -6,7 +6,7 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/am43/am43_base.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include diff --git a/esphome/components/am43/cover/am43_cover.cpp b/esphome/components/am43/cover/am43_cover.cpp index 1067ce9ebb..fd337ba17b 100644 --- a/esphome/components/am43/cover/am43_cover.cpp +++ b/esphome/components/am43/cover/am43_cover.cpp @@ -1,7 +1,7 @@ #include "am43_cover.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace am43 { diff --git a/esphome/components/am43/cover/am43_cover.h b/esphome/components/am43/cover/am43_cover.h index dba1391b63..f33f2d1734 100644 --- a/esphome/components/am43/cover/am43_cover.h +++ b/esphome/components/am43/cover/am43_cover.h @@ -6,7 +6,7 @@ #include "esphome/components/cover/cover.h" #include "esphome/components/am43/am43_base.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include diff --git a/esphome/components/anova/anova.cpp b/esphome/components/anova/anova.cpp index d0ea818669..8e0724ad13 100644 --- a/esphome/components/anova/anova.cpp +++ b/esphome/components/anova/anova.cpp @@ -1,7 +1,7 @@ #include "anova.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace anova { diff --git a/esphome/components/anova/anova.h b/esphome/components/anova/anova.h index e033513013..554024e389 100644 --- a/esphome/components/anova/anova.h +++ b/esphome/components/anova/anova.h @@ -6,7 +6,7 @@ #include "esphome/components/climate/climate.h" #include "anova_base.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include diff --git a/esphome/components/apds9960/apds9960.cpp b/esphome/components/apds9960/apds9960.cpp index 15b0cb39f8..9ee873ac64 100644 --- a/esphome/components/apds9960/apds9960.cpp +++ b/esphome/components/apds9960/apds9960.cpp @@ -1,5 +1,6 @@ #include "apds9960.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace apds9960 { diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 0fa4ca6397..450375f7cd 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1,7 +1,8 @@ #include "api_connection.h" #include "esphome/core/log.h" -#include "esphome/core/util.h" +#include "esphome/components/network/util.h" #include "esphome/core/version.h" +#include "esphome/core/hal.h" #include #ifdef USE_DEEP_SLEEP @@ -48,7 +49,7 @@ void APIConnection::loop() { if (this->remove_) return; - if (!network_is_connected()) { + if (!network::is_connected()) { // when network is disconnected force disconnect immediately // don't wait for timeout this->on_fatal_error(); diff --git a/esphome/components/api/api_frame_helper.cpp b/esphome/components/api/api_frame_helper.cpp index 15014b7937..00f28457ae 100644 --- a/esphome/components/api/api_frame_helper.cpp +++ b/esphome/components/api/api_frame_helper.cpp @@ -3,6 +3,7 @@ #include "esphome/core/log.h" #include "esphome/core/helpers.h" #include "proto.h" +#include namespace esphome { namespace api { diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index d6b85d257c..580e147245 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -2,6 +2,7 @@ // See scripts/api_protobuf/api_protobuf.py #include "api_pb2.h" #include "esphome/core/log.h" +#include namespace esphome { namespace api { diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index d84a35747c..8728597537 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -5,6 +5,8 @@ #include "esphome/core/log.h" #include "esphome/core/util.h" #include "esphome/core/version.h" +#include "esphome/core/hal.h" +#include "esphome/components/network/util.h" #include #ifdef USE_LOGGER @@ -130,7 +132,7 @@ void APIServer::loop() { } void APIServer::dump_config() { ESP_LOGCONFIG(TAG, "API Server:"); - ESP_LOGCONFIG(TAG, " Address: %s:%u", network_get_address().c_str(), this->port_); + ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->port_); } bool APIServer::uses_password() const { return !this->password_.empty(); } bool APIServer::check_password(const std::string &password) const { diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index dd054bab7e..edbc916b01 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -246,6 +246,7 @@ class ProtoWriteBuffer { class ProtoMessage { public: + virtual ~ProtoMessage() = default; virtual void encode(ProtoWriteBuffer buffer) const = 0; void decode(const uint8_t *buffer, size_t length); #ifdef HAS_PROTO_MESSAGE_DUMP diff --git a/esphome/components/as3935/as3935.h b/esphome/components/as3935/as3935.h index 76c7697d38..2e65aab4d1 100644 --- a/esphome/components/as3935/as3935.h +++ b/esphome/components/as3935/as3935.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/binary_sensor/binary_sensor.h" diff --git a/esphome/components/as3935_i2c/as3935_i2c.cpp b/esphome/components/as3935_i2c/as3935_i2c.cpp index 1a1cd8fe82..3a7fa7bf84 100644 --- a/esphome/components/as3935_i2c/as3935_i2c.cpp +++ b/esphome/components/as3935_i2c/as3935_i2c.cpp @@ -25,8 +25,12 @@ void I2CAS3935Component::write_register(uint8_t reg, uint8_t mask, uint8_t bits, uint8_t I2CAS3935Component::read_register(uint8_t reg) { uint8_t value; - if (!this->read_byte(reg, &value, 2)) { - ESP_LOGW(TAG, "Read failed!"); + if (write(®, 1) != i2c::ERROR_OK) { + ESP_LOGW(TAG, "Writing register failed!"); + return 0; + } + if (read(&value, 1) != i2c::ERROR_OK) { + ESP_LOGW(TAG, "Reading register failed!"); return 0; } return value; diff --git a/esphome/components/async_tcp/__init__.py b/esphome/components/async_tcp/__init__.py index 22d4bb1fcb..8789448792 100644 --- a/esphome/components/async_tcp/__init__.py +++ b/esphome/components/async_tcp/__init__.py @@ -1,9 +1,15 @@ # Dummy integration to allow relying on AsyncTCP import esphome.codegen as cg +import esphome.config_validation as cv from esphome.core import CORE, coroutine_with_priority CODEOWNERS = ["@OttoWinter"] +CONFIG_SCHEMA = cv.All( + cv.Schema({}), + cv.only_with_arduino, +) + @coroutine_with_priority(200.0) async def to_code(config): diff --git a/esphome/components/atc_mithermometer/atc_mithermometer.cpp b/esphome/components/atc_mithermometer/atc_mithermometer.cpp index 1dba259143..b04d634103 100644 --- a/esphome/components/atc_mithermometer/atc_mithermometer.cpp +++ b/esphome/components/atc_mithermometer/atc_mithermometer.cpp @@ -1,7 +1,7 @@ #include "atc_mithermometer.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace atc_mithermometer { diff --git a/esphome/components/atc_mithermometer/atc_mithermometer.h b/esphome/components/atc_mithermometer/atc_mithermometer.h index eff14811be..291c1d96cd 100644 --- a/esphome/components/atc_mithermometer/atc_mithermometer.h +++ b/esphome/components/atc_mithermometer/atc_mithermometer.h @@ -4,7 +4,7 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace atc_mithermometer { diff --git a/esphome/components/b_parasite/b_parasite.cpp b/esphome/components/b_parasite/b_parasite.cpp index 3098d462d2..bc1463fd1c 100644 --- a/esphome/components/b_parasite/b_parasite.cpp +++ b/esphome/components/b_parasite/b_parasite.cpp @@ -1,7 +1,7 @@ #include "b_parasite.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace b_parasite { @@ -79,4 +79,4 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { } // namespace b_parasite } // namespace esphome -#endif // ARDUINO_ARCH_ESP32 +#endif // USE_ESP32 diff --git a/esphome/components/b_parasite/b_parasite.h b/esphome/components/b_parasite/b_parasite.h index 04f648ab63..bdd9a01b83 100644 --- a/esphome/components/b_parasite/b_parasite.h +++ b/esphome/components/b_parasite/b_parasite.h @@ -4,7 +4,7 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace b_parasite { @@ -37,4 +37,4 @@ class BParasite : public Component, public esp32_ble_tracker::ESPBTDeviceListene } // namespace b_parasite } // namespace esphome -#endif // ARDUINO_ARCH_ESP32 +#endif // USE_ESP32 diff --git a/esphome/components/bang_bang/bang_bang_climate.cpp b/esphome/components/bang_bang/bang_bang_climate.cpp index 4b41684707..5645f46f1c 100644 --- a/esphome/components/bang_bang/bang_bang_climate.cpp +++ b/esphome/components/bang_bang/bang_bang_climate.cpp @@ -69,7 +69,8 @@ void BangBangClimate::compute_state_() { this->switch_to_action_(climate::CLIMATE_ACTION_OFF); return; } - if (isnan(this->current_temperature) || isnan(this->target_temperature_low) || isnan(this->target_temperature_high)) { + if (std::isnan(this->current_temperature) || std::isnan(this->target_temperature_low) || + std::isnan(this->target_temperature_high)) { // if any control parameters are nan, go to OFF action (not IDLE!) this->switch_to_action_(climate::CLIMATE_ACTION_OFF); return; diff --git a/esphome/components/bh1750/bh1750.cpp b/esphome/components/bh1750/bh1750.cpp index 43af116c9e..3645a45bf9 100644 --- a/esphome/components/bh1750/bh1750.cpp +++ b/esphome/components/bh1750/bh1750.cpp @@ -71,10 +71,11 @@ void BH1750Sensor::update() { float BH1750Sensor::get_setup_priority() const { return setup_priority::DATA; } void BH1750Sensor::read_data_() { uint16_t raw_value; - if (!this->parent_->raw_receive_16(this->address_, &raw_value, 1)) { + if (!this->read(reinterpret_cast(&raw_value), 2)) { this->status_set_warning(); return; } + raw_value = i2c::i2ctohs(raw_value); float lx = float(raw_value) / 1.2f; lx *= 69.0f / this->measurement_duration_; diff --git a/esphome/components/binary_sensor/automation.h b/esphome/components/binary_sensor/automation.h index 0c1e80afba..31bf1a5565 100644 --- a/esphome/components/binary_sensor/automation.h +++ b/esphome/components/binary_sensor/automation.h @@ -4,6 +4,7 @@ #include "esphome/core/component.h" #include "esphome/core/automation.h" +#include "esphome/core/hal.h" #include "esphome/components/binary_sensor/binary_sensor.h" namespace esphome { diff --git a/esphome/components/ble_client/automation.h b/esphome/components/ble_client/automation.h index 2db609de55..d015d4019c 100644 --- a/esphome/components/ble_client/automation.h +++ b/esphome/components/ble_client/automation.h @@ -3,7 +3,7 @@ #include "esphome/core/automation.h" #include "esphome/components/ble_client/ble_client.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ble_client { diff --git a/esphome/components/ble_client/ble_client.cpp b/esphome/components/ble_client/ble_client.cpp index 0398113f83..f584cdf79e 100644 --- a/esphome/components/ble_client/ble_client.cpp +++ b/esphome/components/ble_client/ble_client.cpp @@ -4,7 +4,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "ble_client.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ble_client { diff --git a/esphome/components/ble_client/ble_client.h b/esphome/components/ble_client/ble_client.h index b9d16ddacd..a69460e8b6 100644 --- a/esphome/components/ble_client/ble_client.h +++ b/esphome/components/ble_client/ble_client.h @@ -4,7 +4,7 @@ #include "esphome/core/helpers.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #include diff --git a/esphome/components/ble_client/sensor/automation.h b/esphome/components/ble_client/sensor/automation.h index a528493947..2255a5ac55 100644 --- a/esphome/components/ble_client/sensor/automation.h +++ b/esphome/components/ble_client/sensor/automation.h @@ -3,7 +3,7 @@ #include "esphome/core/automation.h" #include "esphome/components/ble_client/sensor/ble_sensor.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ble_client { diff --git a/esphome/components/ble_client/sensor/ble_sensor.cpp b/esphome/components/ble_client/sensor/ble_sensor.cpp index 270822be9d..4459163389 100644 --- a/esphome/components/ble_client/sensor/ble_sensor.cpp +++ b/esphome/components/ble_client/sensor/ble_sensor.cpp @@ -4,7 +4,7 @@ #include "esphome/core/helpers.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ble_client { diff --git a/esphome/components/ble_client/sensor/ble_sensor.h b/esphome/components/ble_client/sensor/ble_sensor.h index 25e996b6ee..52c9e9d5ca 100644 --- a/esphome/components/ble_client/sensor/ble_sensor.h +++ b/esphome/components/ble_client/sensor/ble_sensor.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/sensor/sensor.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include namespace esphome { diff --git a/esphome/components/ble_client/switch/ble_switch.cpp b/esphome/components/ble_client/switch/ble_switch.cpp index 669984d705..00593da9d6 100644 --- a/esphome/components/ble_client/switch/ble_switch.cpp +++ b/esphome/components/ble_client/switch/ble_switch.cpp @@ -2,7 +2,7 @@ #include "esphome/core/log.h" #include "esphome/core/application.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ble_client { diff --git a/esphome/components/ble_client/switch/ble_switch.h b/esphome/components/ble_client/switch/ble_switch.h index f91af533f1..2e19c8aeef 100644 --- a/esphome/components/ble_client/switch/ble_switch.h +++ b/esphome/components/ble_client/switch/ble_switch.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/switch/switch.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include namespace esphome { diff --git a/esphome/components/ble_presence/ble_presence_device.cpp b/esphome/components/ble_presence/ble_presence_device.cpp index 1355c2bcc3..e482bb9a78 100644 --- a/esphome/components/ble_presence/ble_presence_device.cpp +++ b/esphome/components/ble_presence/ble_presence_device.cpp @@ -1,7 +1,7 @@ #include "ble_presence_device.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ble_presence { diff --git a/esphome/components/ble_presence/ble_presence_device.h b/esphome/components/ble_presence/ble_presence_device.h index dfc36d68cb..40cda89e62 100644 --- a/esphome/components/ble_presence/ble_presence_device.h +++ b/esphome/components/ble_presence/ble_presence_device.h @@ -4,7 +4,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/binary_sensor/binary_sensor.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ble_presence { diff --git a/esphome/components/ble_rssi/ble_rssi_sensor.cpp b/esphome/components/ble_rssi/ble_rssi_sensor.cpp index 096259d8d1..4b37fcc6ef 100644 --- a/esphome/components/ble_rssi/ble_rssi_sensor.cpp +++ b/esphome/components/ble_rssi/ble_rssi_sensor.cpp @@ -1,7 +1,7 @@ #include "ble_rssi_sensor.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ble_rssi { diff --git a/esphome/components/ble_rssi/ble_rssi_sensor.h b/esphome/components/ble_rssi/ble_rssi_sensor.h index 2082a52469..c6acae2593 100644 --- a/esphome/components/ble_rssi/ble_rssi_sensor.h +++ b/esphome/components/ble_rssi/ble_rssi_sensor.h @@ -4,7 +4,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/sensor/sensor.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ble_rssi { diff --git a/esphome/components/ble_scanner/ble_scanner.cpp b/esphome/components/ble_scanner/ble_scanner.cpp index 798824bb4e..f2cda227bb 100644 --- a/esphome/components/ble_scanner/ble_scanner.cpp +++ b/esphome/components/ble_scanner/ble_scanner.cpp @@ -1,7 +1,7 @@ #include "ble_scanner.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ble_scanner { diff --git a/esphome/components/ble_scanner/ble_scanner.h b/esphome/components/ble_scanner/ble_scanner.h index 194494144c..542d5047ec 100644 --- a/esphome/components/ble_scanner/ble_scanner.h +++ b/esphome/components/ble_scanner/ble_scanner.h @@ -7,7 +7,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/text_sensor/text_sensor.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ble_scanner { diff --git a/esphome/components/bme280/bme280.cpp b/esphome/components/bme280/bme280.cpp index fe11b69945..18386430a2 100644 --- a/esphome/components/bme280/bme280.cpp +++ b/esphome/components/bme280/bme280.cpp @@ -186,7 +186,7 @@ void BME280Component::update() { this->set_timeout("data", uint32_t(ceilf(meas_time)), [this]() { int32_t t_fine = 0; float temperature = this->read_temperature_(&t_fine); - if (isnan(temperature)) { + if (std::isnan(temperature)) { ESP_LOGW(TAG, "Invalid temperature, cannot read pressure & humidity values."); this->status_set_warning(); return; diff --git a/esphome/components/bme680/bme680.cpp b/esphome/components/bme680/bme680.cpp index ee7db3c65f..99e0b6f860 100644 --- a/esphome/components/bme680/bme680.cpp +++ b/esphome/components/bme680/bme680.cpp @@ -1,5 +1,6 @@ #include "bme680.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace bme680 { diff --git a/esphome/components/bme680_bsec/bme680_bsec.cpp b/esphome/components/bme680_bsec/bme680_bsec.cpp index e2cb7491a6..0a8ca7f3c3 100644 --- a/esphome/components/bme680_bsec/bme680_bsec.cpp +++ b/esphome/components/bme680_bsec/bme680_bsec.cpp @@ -381,7 +381,7 @@ void BME680BSECComponent::delay_ms(uint32_t period) { void BME680BSECComponent::load_state_() { uint32_t hash = fnv1_hash("bme680_bsec_state_" + to_string(this->address_)); - this->bsec_state_ = global_preferences.make_preference(hash, true); + this->bsec_state_ = global_preferences->make_preference(hash, true); uint8_t state[BSEC_MAX_STATE_BLOB_SIZE]; if (this->bsec_state_.load(&state)) { diff --git a/esphome/components/bmp280/bmp280.cpp b/esphome/components/bmp280/bmp280.cpp index d04a357ca2..b4348e8a74 100644 --- a/esphome/components/bmp280/bmp280.cpp +++ b/esphome/components/bmp280/bmp280.cpp @@ -139,7 +139,7 @@ void BMP280Component::update() { this->set_timeout("data", uint32_t(ceilf(meas_time)), [this]() { int32_t t_fine = 0; float temperature = this->read_temperature_(&t_fine); - if (isnan(temperature)) { + if (std::isnan(temperature)) { ESP_LOGW(TAG, "Invalid temperature, cannot read pressure values."); this->status_set_warning(); return; diff --git a/esphome/components/captive_portal/__init__.py b/esphome/components/captive_portal/__init__.py index 102bfd370e..a1cc5734c1 100644 --- a/esphome/components/captive_portal/__init__.py +++ b/esphome/components/captive_portal/__init__.py @@ -12,14 +12,17 @@ CODEOWNERS = ["@OttoWinter"] captive_portal_ns = cg.esphome_ns.namespace("captive_portal") CaptivePortal = captive_portal_ns.class_("CaptivePortal", cg.Component) -CONFIG_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.declare_id(CaptivePortal), - cv.GenerateID(CONF_WEB_SERVER_BASE_ID): cv.use_id( - web_server_base.WebServerBase - ), - } -).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(CaptivePortal), + cv.GenerateID(CONF_WEB_SERVER_BASE_ID): cv.use_id( + web_server_base.WebServerBase + ), + } + ).extend(cv.COMPONENT_SCHEMA), + cv.only_with_arduino, +) @coroutine_with_priority(64.0) diff --git a/esphome/components/captive_portal/captive_portal.cpp b/esphome/components/captive_portal/captive_portal.cpp index 3341769956..9e00adae3d 100644 --- a/esphome/components/captive_portal/captive_portal.cpp +++ b/esphome/components/captive_portal/captive_portal.cpp @@ -1,3 +1,5 @@ +#ifdef USE_ARDUINO + #include "captive_portal.h" #include "esphome/core/log.h" #include "esphome/core/application.h" @@ -78,14 +80,14 @@ void CaptivePortal::start() { this->dns_server_ = make_unique(); this->dns_server_->setErrorReplyCode(DNSReplyCode::NoError); - IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip(); - this->dns_server_->start(53, "*", ip); + network::IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip(); + this->dns_server_->start(53, "*", (uint32_t) ip); this->base_->get_server()->onNotFound([this](AsyncWebServerRequest *req) { bool not_found = false; if (!this->active_) { not_found = true; - } else if (req->host() == wifi::global_wifi_component->wifi_soft_ap_ip().toString()) { + } else if (req->host().c_str() == wifi::global_wifi_component->wifi_soft_ap_ip().str()) { not_found = true; } @@ -94,8 +96,8 @@ void CaptivePortal::start() { return; } - auto url = "http://" + wifi::global_wifi_component->wifi_soft_ap_ip().toString(); - req->redirect(url); + auto url = "http://" + wifi::global_wifi_component->wifi_soft_ap_ip().str(); + req->redirect(url.c_str()); }); this->initialized_ = true; @@ -151,3 +153,5 @@ CaptivePortal *global_captive_portal = nullptr; // NOLINT(cppcoreguidelines-avo } // namespace captive_portal } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/captive_portal/captive_portal.h b/esphome/components/captive_portal/captive_portal.h index 399ffaabd3..b308de42b7 100644 --- a/esphome/components/captive_portal/captive_portal.h +++ b/esphome/components/captive_portal/captive_portal.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include #include #include "esphome/core/component.h" @@ -73,3 +75,5 @@ extern CaptivePortal *global_captive_portal; // NOLINT(cppcoreguidelines-avoid- } // namespace captive_portal } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/ccs811/ccs811.cpp b/esphome/components/ccs811/ccs811.cpp index 08df6f7774..6bdb4739cf 100644 --- a/esphome/components/ccs811/ccs811.cpp +++ b/esphome/components/ccs811/ccs811.cpp @@ -1,5 +1,6 @@ #include "ccs811.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace ccs811 { @@ -124,12 +125,12 @@ void CCS811Component::send_env_data_() { float humidity = NAN; if (this->humidity_ != nullptr) humidity = this->humidity_->state; - if (isnan(humidity) || humidity < 0 || humidity > 100) + if (std::isnan(humidity) || humidity < 0 || humidity > 100) humidity = 50; float temperature = NAN; if (this->temperature_ != nullptr) temperature = this->temperature_->state; - if (isnan(temperature) || temperature < -25 || temperature > 50) + if (std::isnan(temperature) || temperature < -25 || temperature > 50) temperature = 25; // temperature has a 25° offset to allow negative temperatures temperature += 25; diff --git a/esphome/components/climate/climate.cpp b/esphome/components/climate/climate.cpp index 383103b014..a365b933bb 100644 --- a/esphome/components/climate/climate.cpp +++ b/esphome/components/climate/climate.cpp @@ -95,7 +95,7 @@ void ClimateCall::validate_() { ESP_LOGW(TAG, " Cannot set target temperature for climate device " "with two-point target temperature!"); this->target_temperature_.reset(); - } else if (isnan(target)) { + } else if (std::isnan(target)) { ESP_LOGW(TAG, " Target temperature must not be NAN!"); this->target_temperature_.reset(); } @@ -107,11 +107,11 @@ void ClimateCall::validate_() { this->target_temperature_high_.reset(); } } - if (this->target_temperature_low_.has_value() && isnan(*this->target_temperature_low_)) { + if (this->target_temperature_low_.has_value() && std::isnan(*this->target_temperature_low_)) { ESP_LOGW(TAG, " Target temperature low must not be NAN!"); this->target_temperature_low_.reset(); } - if (this->target_temperature_high_.has_value() && isnan(*this->target_temperature_high_)) { + if (this->target_temperature_high_.has_value() && std::isnan(*this->target_temperature_high_)) { ESP_LOGW(TAG, " Target temperature low must not be NAN!"); this->target_temperature_high_.reset(); } @@ -318,17 +318,23 @@ void Climate::add_on_state_callback(std::function &&callback) { static const uint32_t RESTORE_STATE_VERSION = 0x848EA6ADUL; optional Climate::restore_state_() { - this->rtc_ = - global_preferences.make_preference(this->get_object_id_hash() ^ RESTORE_STATE_VERSION); + this->rtc_ = global_preferences->make_preference(this->get_object_id_hash() ^ + RESTORE_STATE_VERSION); ClimateDeviceRestoreState recovered{}; if (!this->rtc_.load(&recovered)) return {}; return recovered; } void Climate::save_state_() { +#if defined(USE_ESP_IDF) && !defined(CLANG_TIDY) +#pragma GCC diagnostic ignored "-Wclass-memaccess" +#endif ClimateDeviceRestoreState state{}; // initialize as zero to prevent random data on stack triggering erase memset(&state, 0, sizeof(ClimateDeviceRestoreState)); +#if USE_ESP_IDF && !defined(CLANG_TIDY) +#pragma GCC diagnostic pop +#endif state.mode = this->mode; auto traits = this->get_traits(); diff --git a/esphome/components/climate_ir/climate_ir.cpp b/esphome/components/climate_ir/climate_ir.cpp index 8d5ae6053d..b47d9b0141 100644 --- a/esphome/components/climate_ir/climate_ir.cpp +++ b/esphome/components/climate_ir/climate_ir.cpp @@ -52,7 +52,7 @@ void ClimateIR::setup() { this->swing_mode = climate::CLIMATE_SWING_OFF; } // Never send nan to HA - if (isnan(this->target_temperature)) + if (std::isnan(this->target_temperature)) this->target_temperature = 24; } diff --git a/esphome/components/cover/cover.cpp b/esphome/components/cover/cover.cpp index 2b4452f5b7..e1ce211b64 100644 --- a/esphome/components/cover/cover.cpp +++ b/esphome/components/cover/cover.cpp @@ -180,7 +180,7 @@ void Cover::publish_state(bool save) { } } optional Cover::restore_state_() { - this->rtc_ = global_preferences.make_preference(this->get_object_id_hash()); + this->rtc_ = global_preferences->make_preference(this->get_object_id_hash()); CoverRestoreState recovered{}; if (!this->rtc_.load(&recovered)) return {}; diff --git a/esphome/components/ct_clamp/ct_clamp_sensor.cpp b/esphome/components/ct_clamp/ct_clamp_sensor.cpp index 130cdfefba..0052b1426d 100644 --- a/esphome/components/ct_clamp/ct_clamp_sensor.cpp +++ b/esphome/components/ct_clamp/ct_clamp_sensor.cpp @@ -52,7 +52,7 @@ void CTClampSensor::loop() { // Perform a single sample float value = this->source_->sample(); - if (isnan(value)) + if (std::isnan(value)) return; this->sample_sum_ += value; diff --git a/esphome/components/ct_clamp/ct_clamp_sensor.h b/esphome/components/ct_clamp/ct_clamp_sensor.h index 2f201c11a0..10601ab852 100644 --- a/esphome/components/ct_clamp/ct_clamp_sensor.h +++ b/esphome/components/ct_clamp/ct_clamp_sensor.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/voltage_sampler/voltage_sampler.h" diff --git a/esphome/components/dallas/dallas_component.cpp b/esphome/components/dallas/dallas_component.cpp index 847d630e7c..0fc4108687 100644 --- a/esphome/components/dallas/dallas_component.cpp +++ b/esphome/components/dallas/dallas_component.cpp @@ -161,7 +161,7 @@ const std::string &DallasTemperatureSensor::get_address_name() { return this->address_name_; } -bool ICACHE_RAM_ATTR DallasTemperatureSensor::read_scratch_pad() { +bool IRAM_ATTR DallasTemperatureSensor::read_scratch_pad() { ESPOneWire *wire = this->parent_->one_wire_; if (!wire->reset()) { return false; diff --git a/esphome/components/dallas/esp_one_wire.cpp b/esphome/components/dallas/esp_one_wire.cpp index 702d1eddc2..9278b83f7f 100644 --- a/esphome/components/dallas/esp_one_wire.cpp +++ b/esphome/components/dallas/esp_one_wire.cpp @@ -12,11 +12,11 @@ const int ONE_WIRE_ROM_SEARCH = 0xF0; ESPOneWire::ESPOneWire(GPIOPin *pin) : pin_(pin) {} -bool HOT ICACHE_RAM_ATTR ESPOneWire::reset() { +bool HOT IRAM_ATTR ESPOneWire::reset() { uint8_t retries = 125; // Wait for communication to clear - this->pin_->pin_mode(INPUT_PULLUP); + this->pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); do { if (--retries == 0) return false; @@ -24,12 +24,12 @@ bool HOT ICACHE_RAM_ATTR ESPOneWire::reset() { } while (!this->pin_->digital_read()); // Send 480µs LOW TX reset pulse - this->pin_->pin_mode(OUTPUT); + this->pin_->pin_mode(gpio::FLAG_OUTPUT); this->pin_->digital_write(false); delayMicroseconds(480); // Switch into RX mode, letting the pin float - this->pin_->pin_mode(INPUT_PULLUP); + this->pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); // after 15µs-60µs wait time, responder pulls low for 60µs-240µs // let's have 70µs just in case delayMicroseconds(70); @@ -39,9 +39,9 @@ bool HOT ICACHE_RAM_ATTR ESPOneWire::reset() { return r; } -void HOT ICACHE_RAM_ATTR ESPOneWire::write_bit(bool bit) { +void HOT IRAM_ATTR ESPOneWire::write_bit(bool bit) { // Initiate write/read by pulling low. - this->pin_->pin_mode(OUTPUT); + this->pin_->pin_mode(gpio::FLAG_OUTPUT); this->pin_->digital_write(false); // bus sampled within 15µs and 60µs after pulling LOW. @@ -60,14 +60,14 @@ void HOT ICACHE_RAM_ATTR ESPOneWire::write_bit(bool bit) { } } -bool HOT ICACHE_RAM_ATTR ESPOneWire::read_bit() { +bool HOT IRAM_ATTR ESPOneWire::read_bit() { // Initiate read slot by pulling LOW for at least 1µs - this->pin_->pin_mode(OUTPUT); + this->pin_->pin_mode(gpio::FLAG_OUTPUT); this->pin_->digital_write(false); delayMicroseconds(3); // release bus, we have to sample within 15µs of pulling low - this->pin_->pin_mode(INPUT_PULLUP); + this->pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); delayMicroseconds(10); bool r = this->pin_->digital_read(); @@ -76,43 +76,43 @@ bool HOT ICACHE_RAM_ATTR ESPOneWire::read_bit() { return r; } -void ICACHE_RAM_ATTR ESPOneWire::write8(uint8_t val) { +void IRAM_ATTR ESPOneWire::write8(uint8_t val) { for (uint8_t i = 0; i < 8; i++) { this->write_bit(bool((1u << i) & val)); } } -void ICACHE_RAM_ATTR ESPOneWire::write64(uint64_t val) { +void IRAM_ATTR ESPOneWire::write64(uint64_t val) { for (uint8_t i = 0; i < 64; i++) { this->write_bit(bool((1ULL << i) & val)); } } -uint8_t ICACHE_RAM_ATTR ESPOneWire::read8() { +uint8_t IRAM_ATTR ESPOneWire::read8() { uint8_t ret = 0; for (uint8_t i = 0; i < 8; i++) { ret |= (uint8_t(this->read_bit()) << i); } return ret; } -uint64_t ICACHE_RAM_ATTR ESPOneWire::read64() { +uint64_t IRAM_ATTR ESPOneWire::read64() { uint64_t ret = 0; for (uint8_t i = 0; i < 8; i++) { ret |= (uint64_t(this->read_bit()) << i); } return ret; } -void ICACHE_RAM_ATTR ESPOneWire::select(uint64_t address) { +void IRAM_ATTR ESPOneWire::select(uint64_t address) { this->write8(ONE_WIRE_ROM_SELECT); this->write64(address); } -void ICACHE_RAM_ATTR ESPOneWire::reset_search() { +void IRAM_ATTR ESPOneWire::reset_search() { this->last_discrepancy_ = 0; this->last_device_flag_ = false; this->last_family_discrepancy_ = 0; this->rom_number_ = 0; } -uint64_t HOT ICACHE_RAM_ATTR ESPOneWire::search() { +uint64_t HOT IRAM_ATTR ESPOneWire::search() { if (this->last_device_flag_) { return 0u; } @@ -196,7 +196,7 @@ uint64_t HOT ICACHE_RAM_ATTR ESPOneWire::search() { return this->rom_number_; } -std::vector ICACHE_RAM_ATTR ESPOneWire::search_vec() { +std::vector IRAM_ATTR ESPOneWire::search_vec() { std::vector res; this->reset_search(); @@ -206,12 +206,12 @@ std::vector ICACHE_RAM_ATTR ESPOneWire::search_vec() { return res; } -void ICACHE_RAM_ATTR ESPOneWire::skip() { +void IRAM_ATTR ESPOneWire::skip() { this->write8(0xCC); // skip ROM } GPIOPin *ESPOneWire::get_pin() { return this->pin_; } -uint8_t ICACHE_RAM_ATTR *ESPOneWire::rom_number8_() { return reinterpret_cast(&this->rom_number_); } +uint8_t IRAM_ATTR *ESPOneWire::rom_number8_() { return reinterpret_cast(&this->rom_number_); } } // namespace dallas } // namespace esphome diff --git a/esphome/components/dallas/esp_one_wire.h b/esphome/components/dallas/esp_one_wire.h index 68bcc0c193..728fa127d3 100644 --- a/esphome/components/dallas/esp_one_wire.h +++ b/esphome/components/dallas/esp_one_wire.h @@ -1,7 +1,8 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" +#include namespace esphome { namespace dallas { diff --git a/esphome/components/debug/debug_component.cpp b/esphome/components/debug/debug_component.cpp index 11590a0978..7fd8956148 100644 --- a/esphome/components/debug/debug_component.cpp +++ b/esphome/components/debug/debug_component.cpp @@ -4,8 +4,18 @@ #include "esphome/core/defines.h" #include "esphome/core/version.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include +#include +#endif + +#ifdef USE_ARDUINO +#include +#endif + +#ifdef USE_ESP_IDF +#include +#include #endif namespace esphome { @@ -21,9 +31,14 @@ void DebugComponent::dump_config() { #endif ESP_LOGD(TAG, "ESPHome version %s", ESPHOME_VERSION); +#ifdef USE_ARDUINO this->free_heap_ = ESP.getFreeHeap(); // NOLINT(readability-static-accessed-through-instance) +#elif defined(USE_ESP_IDF) + this->free_heap_ = heap_caps_get_free_size(MALLOC_CAP_INTERNAL); +#endif ESP_LOGD(TAG, "Free Heap Size: %u bytes", this->free_heap_); +#ifdef USE_ARDUINO const char *flash_mode; switch (ESP.getFlashChipMode()) { // NOLINT(readability-static-accessed-through-instance) case FM_QIO: @@ -38,7 +53,7 @@ void DebugComponent::dump_config() { case FM_DOUT: flash_mode = "DOUT"; break; -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 case FM_FAST_READ: flash_mode = "FAST_READ"; break; @@ -52,8 +67,9 @@ void DebugComponent::dump_config() { // NOLINTNEXTLINE(readability-static-accessed-through-instance) ESP_LOGD(TAG, "Flash Chip: Size=%ukB Speed=%uMHz Mode=%s", ESP.getFlashChipSize() / 1024, ESP.getFlashChipSpeed() / 1000000, flash_mode); +#endif // USE_ARDUINO -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 esp_chip_info_t info; esp_chip_info(&info); const char *model; @@ -88,7 +104,9 @@ void DebugComponent::dump_config() { ESP_LOGD(TAG, "ESP-IDF Version: %s", esp_get_idf_version()); - std::string mac = uint64_to_string(ESP.getEfuseMac()); // NOLINT(readability-static-accessed-through-instance) + uint64_t chip_mac = 0LL; + esp_efuse_mac_get_default((uint8_t *) (&chip_mac)); + std::string mac = uint64_to_string(chip_mac); ESP_LOGD(TAG, "EFuse MAC: %s", mac.c_str()); const char *reset_reason; @@ -187,7 +205,7 @@ void DebugComponent::dump_config() { ESP_LOGD(TAG, "Wakeup Reason: %s", wakeup_reason); #endif -#if defined(ARDUINO_ARCH_ESP8266) && !defined(CLANG_TIDY) +#if defined(USE_ESP8266) && !defined(CLANG_TIDY) ESP_LOGD(TAG, "Chip ID: 0x%08X", ESP.getChipId()); ESP_LOGD(TAG, "SDK Version: %s", ESP.getSdkVersion()); ESP_LOGD(TAG, "Core Version: %s", ESP.getCoreVersion().c_str()); @@ -199,7 +217,11 @@ void DebugComponent::dump_config() { #endif } void DebugComponent::loop() { +#ifdef USE_ARDUINO uint32_t new_free_heap = ESP.getFreeHeap(); // NOLINT(readability-static-accessed-through-instance) +#elif defined(USE_ESP_IDF) + uint32_t new_free_heap = heap_caps_get_free_size(MALLOC_CAP_INTERNAL); +#endif if (new_free_heap < this->free_heap_ / 2) { this->free_heap_ = new_free_heap; ESP_LOGD(TAG, "Free Heap Size: %u bytes", this->free_heap_); diff --git a/esphome/components/deep_sleep/__init__.py b/esphome/components/deep_sleep/__init__.py index 1e07b75173..f47888b8eb 100644 --- a/esphome/components/deep_sleep/__init__.py +++ b/esphome/components/deep_sleep/__init__.py @@ -62,7 +62,7 @@ CONFIG_SCHEMA = cv.Schema( cv.Schema( { cv.Required(CONF_PINS): cv.ensure_list( - pins.shorthand_input_pin, validate_pin_number + pins.internal_gpio_input_pin_schema, validate_pin_number ), cv.Required(CONF_MODE): cv.enum(EXT1_WAKEUP_MODES, upper=True), } diff --git a/esphome/components/deep_sleep/deep_sleep_component.cpp b/esphome/components/deep_sleep/deep_sleep_component.cpp index 9c354c8513..e4b1edfb7b 100644 --- a/esphome/components/deep_sleep/deep_sleep_component.cpp +++ b/esphome/components/deep_sleep/deep_sleep_component.cpp @@ -2,6 +2,10 @@ #include "esphome/core/log.h" #include "esphome/core/application.h" +#ifdef USE_ESP8266 +#include +#endif + namespace esphome { namespace deep_sleep { @@ -25,9 +29,9 @@ void DeepSleepComponent::dump_config() { if (this->run_duration_.has_value()) { ESP_LOGCONFIG(TAG, " Run Duration: %u ms", *this->run_duration_); } -#ifdef ARDUINO_ARCH_ESP32 - if (this->wakeup_pin_.has_value()) { - LOG_PIN(" Wakeup Pin: ", *this->wakeup_pin_); +#ifdef USE_ESP32 + if (wakeup_pin_ != nullptr) { + LOG_PIN(" Wakeup Pin: ", this->wakeup_pin_); } #endif } @@ -39,7 +43,7 @@ float DeepSleepComponent::get_loop_priority() const { return -100.0f; // run after everything else is ready } void DeepSleepComponent::set_sleep_duration(uint32_t time_ms) { this->sleep_duration_ = uint64_t(time_ms) * 1000; } -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 void DeepSleepComponent::set_wakeup_pin_mode(WakeupPinMode wakeup_pin_mode) { this->wakeup_pin_mode_ = wakeup_pin_mode; } @@ -52,9 +56,9 @@ void DeepSleepComponent::begin_sleep(bool manual) { this->next_enter_deep_sleep_ = true; return; } -#ifdef ARDUINO_ARCH_ESP32 - if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_KEEP_AWAKE && this->wakeup_pin_.has_value() && - !this->sleep_duration_.has_value() && (*this->wakeup_pin_)->digital_read()) { +#ifdef USE_ESP32 + if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_KEEP_AWAKE && this->wakeup_pin_ != nullptr && + !this->sleep_duration_.has_value() && this->wakeup_pin_->digital_read()) { // Defer deep sleep until inactive if (!this->next_enter_deep_sleep_) { this->status_set_warning(); @@ -69,14 +73,14 @@ void DeepSleepComponent::begin_sleep(bool manual) { App.run_safe_shutdown_hooks(); -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 if (this->sleep_duration_.has_value()) esp_sleep_enable_timer_wakeup(*this->sleep_duration_); - if (this->wakeup_pin_.has_value()) { - bool level = !(*this->wakeup_pin_)->is_inverted(); - if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && (*this->wakeup_pin_)->digital_read()) + if (this->wakeup_pin_ != nullptr) { + bool level = this->wakeup_pin_->is_inverted(); + if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && this->wakeup_pin_->digital_read()) level = !level; - esp_sleep_enable_ext0_wakeup(gpio_num_t((*this->wakeup_pin_)->get_pin()), level); + esp_sleep_enable_ext0_wakeup(gpio_num_t(this->wakeup_pin_->get_pin()), level); } if (this->ext1_wakeup_.has_value()) { esp_sleep_enable_ext1_wakeup(this->ext1_wakeup_->mask, this->ext1_wakeup_->wakeup_mode); @@ -90,7 +94,7 @@ void DeepSleepComponent::begin_sleep(bool manual) { esp_deep_sleep_start(); #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 ESP.deepSleep(*this->sleep_duration_); // NOLINT(readability-static-accessed-through-instance) #endif } diff --git a/esphome/components/deep_sleep/deep_sleep_component.h b/esphome/components/deep_sleep/deep_sleep_component.h index 575f7be72b..d7969ba999 100644 --- a/esphome/components/deep_sleep/deep_sleep_component.h +++ b/esphome/components/deep_sleep/deep_sleep_component.h @@ -3,12 +3,16 @@ #include "esphome/core/component.h" #include "esphome/core/helpers.h" #include "esphome/core/automation.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" + +#ifdef USE_ESP32 +#include +#endif namespace esphome { namespace deep_sleep { -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 /** The values of this enum define what should be done if deep sleep is set up with a wakeup pin on the ESP32 * and the scenario occurs that the wakeup pin is already in the wakeup state. @@ -44,11 +48,11 @@ class DeepSleepComponent : public Component { public: /// Set the duration in ms the component should sleep once it's in deep sleep mode. void set_sleep_duration(uint32_t time_ms); -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 /** Set the pin to wake up to on the ESP32 once it's in deep sleep mode. * Use the inverted property to set the wakeup level. */ - void set_wakeup_pin(GPIOPin *pin) { this->wakeup_pin_ = pin; } + void set_wakeup_pin(InternalGPIOPin *pin) { this->wakeup_pin_ = pin; } void set_wakeup_pin_mode(WakeupPinMode wakeup_pin_mode); @@ -72,8 +76,8 @@ class DeepSleepComponent : public Component { protected: optional sleep_duration_; -#ifdef ARDUINO_ARCH_ESP32 - optional wakeup_pin_; +#ifdef USE_ESP32 + InternalGPIOPin *wakeup_pin_; WakeupPinMode wakeup_pin_mode_{WAKEUP_PIN_MODE_IGNORE}; optional ext1_wakeup_; optional touch_wakeup_; diff --git a/esphome/components/demo/demo_sensor.h b/esphome/components/demo/demo_sensor.h index 9a35674124..b4afa03e11 100644 --- a/esphome/components/demo/demo_sensor.h +++ b/esphome/components/demo/demo_sensor.h @@ -13,7 +13,7 @@ class DemoSensor : public sensor::Sensor, public PollingComponent { float val = random_float(); bool increasing = this->get_state_class() == sensor::STATE_CLASS_TOTAL_INCREASING; if (increasing) { - float base = isnan(this->state) ? 0.0f : this->state; + float base = std::isnan(this->state) ? 0.0f : this->state; this->publish_state(base + val * 10); } else { if (val < 0.1) diff --git a/esphome/components/dht/dht.cpp b/esphome/components/dht/dht.cpp index 734dde20a8..2539bfe5ee 100644 --- a/esphome/components/dht/dht.cpp +++ b/esphome/components/dht/dht.cpp @@ -71,7 +71,7 @@ void DHT::set_dht_model(DHTModel model) { this->model_ = model; this->is_auto_detect_ = model == DHT_MODEL_AUTO_DETECT; } -bool HOT ICACHE_RAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool report_errors) { +bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool report_errors) { *humidity = NAN; *temperature = NAN; @@ -83,7 +83,7 @@ bool HOT ICACHE_RAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, InterruptLock lock; this->pin_->digital_write(false); - this->pin_->pin_mode(OUTPUT); + this->pin_->pin_mode(gpio::FLAG_OUTPUT); this->pin_->digital_write(false); if (this->model_ == DHT_MODEL_DHT11) { @@ -99,7 +99,7 @@ bool HOT ICACHE_RAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, } else { delayMicroseconds(800); } - this->pin_->pin_mode(INPUT_PULLUP); + this->pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); // Host pull up 20-40us then DHT response 80us // Start waiting for initial rising edge at the center when we diff --git a/esphome/components/dht/dht.h b/esphome/components/dht/dht.h index 9b848cb119..f3a29f9ce9 100644 --- a/esphome/components/dht/dht.h +++ b/esphome/components/dht/dht.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" namespace esphome { @@ -36,7 +36,7 @@ class DHT : public PollingComponent { */ void set_dht_model(DHTModel model); - void set_pin(GPIOPin *pin) { pin_ = pin; } + void set_pin(InternalGPIOPin *pin) { pin_ = pin; } void set_model(DHTModel model) { model_ = model; } void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; } void set_humidity_sensor(sensor::Sensor *humidity_sensor) { humidity_sensor_ = humidity_sensor; } @@ -52,7 +52,7 @@ class DHT : public PollingComponent { protected: bool read_sensor_(float *temperature, float *humidity, bool report_errors); - GPIOPin *pin_; + InternalGPIOPin *pin_; DHTModel model_{DHT_MODEL_AUTO_DETECT}; bool is_auto_detect_{false}; sensor::Sensor *temperature_sensor_{nullptr}; diff --git a/esphome/components/display/display_buffer.cpp b/esphome/components/display/display_buffer.cpp index bbb7444cb5..2ee06e379f 100644 --- a/esphome/components/display/display_buffer.cpp +++ b/esphome/components/display/display_buffer.cpp @@ -4,6 +4,7 @@ #include "esphome/core/application.h" #include "esphome/core/color.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace display { @@ -372,7 +373,7 @@ bool Glyph::get_pixel(int x, int y) const { return false; const uint32_t width_8 = ((this->glyph_data_->width + 7u) / 8u) * 8u; const uint32_t pos = x_data + y_data * width_8; - return pgm_read_byte(this->glyph_data_->data + (pos / 8u)) & (0x80 >> (pos % 8u)); + return progmem_read_byte(this->glyph_data_->data + (pos / 8u)) & (0x80 >> (pos % 8u)); } const char *Glyph::get_char() const { return this->glyph_data_->a_char; } bool Glyph::compare_to(const char *str) const { @@ -464,22 +465,22 @@ bool Image::get_pixel(int x, int y) const { return false; const uint32_t width_8 = ((this->width_ + 7u) / 8u) * 8u; const uint32_t pos = x + y * width_8; - return pgm_read_byte(this->data_start_ + (pos / 8u)) & (0x80 >> (pos % 8u)); + return progmem_read_byte(this->data_start_ + (pos / 8u)) & (0x80 >> (pos % 8u)); } Color Image::get_color_pixel(int x, int y) const { if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_) return Color::BLACK; const uint32_t pos = (x + y * this->width_) * 3; - const uint32_t color32 = (pgm_read_byte(this->data_start_ + pos + 2) << 0) | - (pgm_read_byte(this->data_start_ + pos + 1) << 8) | - (pgm_read_byte(this->data_start_ + pos + 0) << 16); + const uint32_t color32 = (progmem_read_byte(this->data_start_ + pos + 2) << 0) | + (progmem_read_byte(this->data_start_ + pos + 1) << 8) | + (progmem_read_byte(this->data_start_ + pos + 0) << 16); return Color(color32); } Color Image::get_grayscale_pixel(int x, int y) const { if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_) return Color::BLACK; const uint32_t pos = (x + y * this->width_); - const uint8_t gray = pgm_read_byte(this->data_start_ + pos); + const uint8_t gray = progmem_read_byte(this->data_start_ + pos); return Color(gray | gray << 8 | gray << 16 | gray << 24); } int Image::get_width() const { return this->width_; } @@ -496,7 +497,7 @@ bool Animation::get_pixel(int x, int y) const { if (frame_index >= this->width_ * this->height_ * this->animation_frame_count_) return false; const uint32_t pos = x + y * width_8 + frame_index; - return pgm_read_byte(this->data_start_ + (pos / 8u)) & (0x80 >> (pos % 8u)); + return progmem_read_byte(this->data_start_ + (pos / 8u)) & (0x80 >> (pos % 8u)); } Color Animation::get_color_pixel(int x, int y) const { if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_) @@ -505,9 +506,9 @@ Color Animation::get_color_pixel(int x, int y) const { if (frame_index >= this->width_ * this->height_ * this->animation_frame_count_) return Color::BLACK; const uint32_t pos = (x + y * this->width_ + frame_index) * 3; - const uint32_t color32 = (pgm_read_byte(this->data_start_ + pos + 2) << 0) | - (pgm_read_byte(this->data_start_ + pos + 1) << 8) | - (pgm_read_byte(this->data_start_ + pos + 0) << 16); + const uint32_t color32 = (progmem_read_byte(this->data_start_ + pos + 2) << 0) | + (progmem_read_byte(this->data_start_ + pos + 1) << 8) | + (progmem_read_byte(this->data_start_ + pos + 0) << 16); return Color(color32); } Color Animation::get_grayscale_pixel(int x, int y) const { @@ -517,7 +518,7 @@ Color Animation::get_grayscale_pixel(int x, int y) const { if (frame_index >= this->width_ * this->height_ * this->animation_frame_count_) return Color::BLACK; const uint32_t pos = (x + y * this->width_ + frame_index); - const uint8_t gray = pgm_read_byte(this->data_start_ + pos); + const uint8_t gray = progmem_read_byte(this->data_start_ + pos); return Color(gray | gray << 8 | gray << 16 | gray << 24); } Animation::Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, ImageType type) diff --git a/esphome/components/display/display_buffer.h b/esphome/components/display/display_buffer.h index 3f89d3f8d2..54488f18f7 100644 --- a/esphome/components/display/display_buffer.h +++ b/esphome/components/display/display_buffer.h @@ -4,6 +4,7 @@ #include "esphome/core/defines.h" #include "esphome/core/automation.h" #include "display_color_utils.h" +#include #ifdef USE_TIME #include "esphome/components/time/real_time_clock.h" diff --git a/esphome/components/dsmr/__init__.py b/esphome/components/dsmr/__init__.py index 1f1a2f980e..dd6e6051aa 100644 --- a/esphome/components/dsmr/__init__.py +++ b/esphome/components/dsmr/__init__.py @@ -39,14 +39,17 @@ def _validate_key(value): return "".join(f"{part:02X}" for part in parts_int) -CONFIG_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.declare_id(Dsmr), - cv.Optional(CONF_DECRYPTION_KEY): _validate_key, - cv.Optional(CONF_CRC_CHECK, default=True): cv.boolean, - cv.Optional(CONF_GAS_MBUS_ID, default=1): cv.int_, - } -).extend(uart.UART_DEVICE_SCHEMA) +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(Dsmr), + cv.Optional(CONF_DECRYPTION_KEY): _validate_key, + cv.Optional(CONF_CRC_CHECK, default=True): cv.boolean, + cv.Optional(CONF_GAS_MBUS_ID, default=1): cv.int_, + } + ).extend(uart.UART_DEVICE_SCHEMA), + cv.only_with_arduino, +) async def to_code(config): diff --git a/esphome/components/dsmr/dsmr.cpp b/esphome/components/dsmr/dsmr.cpp index 7c1b406d42..54c4343cfe 100644 --- a/esphome/components/dsmr/dsmr.cpp +++ b/esphome/components/dsmr/dsmr.cpp @@ -1,3 +1,5 @@ +#ifdef USE_ARDUINO + #include "dsmr.h" #include "esphome/core/log.h" @@ -128,8 +130,9 @@ void Dsmr::receive_encrypted_() { delay(4); // Wait for data } } - if (buffer_length > 0) + if (buffer_length > 0) { ESP_LOGW(TAG, "Timeout while waiting for encrypted data or invalid data received."); + } } bool Dsmr::parse_telegram() { @@ -186,3 +189,5 @@ void Dsmr::set_decryption_key(const std::string &decryption_key) { } // namespace dsmr } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/dsmr/dsmr.h b/esphome/components/dsmr/dsmr.h index dcb8f5f73d..dfee3b338a 100644 --- a/esphome/components/dsmr/dsmr.h +++ b/esphome/components/dsmr/dsmr.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include "esphome/core/component.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/text_sensor/text_sensor.h" @@ -103,3 +105,5 @@ class Dsmr : public Component, public uart::UARTDevice { }; } // namespace dsmr } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/duty_cycle/duty_cycle_sensor.cpp b/esphome/components/duty_cycle/duty_cycle_sensor.cpp index c989421948..3d7f731d5d 100644 --- a/esphome/components/duty_cycle/duty_cycle_sensor.cpp +++ b/esphome/components/duty_cycle/duty_cycle_sensor.cpp @@ -15,7 +15,7 @@ void DutyCycleSensor::setup() { this->last_update_ = micros(); this->store_.last_interrupt = micros(); - this->pin_->attach_interrupt(DutyCycleSensorStore::gpio_intr, &this->store_, CHANGE); + this->pin_->attach_interrupt(DutyCycleSensorStore::gpio_intr, &this->store_, gpio::INTERRUPT_ANY_EDGE); } void DutyCycleSensor::dump_config() { LOG_SENSOR("", "Duty Cycle Sensor", this); @@ -44,8 +44,8 @@ void DutyCycleSensor::update() { float DutyCycleSensor::get_setup_priority() const { return setup_priority::DATA; } -void ICACHE_RAM_ATTR DutyCycleSensorStore::gpio_intr(DutyCycleSensorStore *arg) { - const bool new_level = arg->pin->digital_read(); +void IRAM_ATTR DutyCycleSensorStore::gpio_intr(DutyCycleSensorStore *arg) { + const bool new_level = arg->pin.digital_read(); if (new_level == arg->last_level) return; arg->last_level = new_level; diff --git a/esphome/components/duty_cycle/duty_cycle_sensor.h b/esphome/components/duty_cycle/duty_cycle_sensor.h index e168f20eff..22d3588fb7 100644 --- a/esphome/components/duty_cycle/duty_cycle_sensor.h +++ b/esphome/components/duty_cycle/duty_cycle_sensor.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" namespace esphome { @@ -12,14 +12,14 @@ struct DutyCycleSensorStore { volatile uint32_t last_interrupt{0}; volatile uint32_t on_time{0}; volatile bool last_level{false}; - ISRInternalGPIOPin *pin; + ISRInternalGPIOPin pin; static void gpio_intr(DutyCycleSensorStore *arg); }; class DutyCycleSensor : public sensor::Sensor, public PollingComponent { public: - void set_pin(GPIOPin *pin) { pin_ = pin; } + void set_pin(InternalGPIOPin *pin) { pin_ = pin; } void setup() override; float get_setup_priority() const override; @@ -27,7 +27,7 @@ class DutyCycleSensor : public sensor::Sensor, public PollingComponent { void update() override; protected: - GPIOPin *pin_; + InternalGPIOPin *pin_; DutyCycleSensorStore store_{}; uint32_t last_update_; diff --git a/esphome/components/duty_cycle/sensor.py b/esphome/components/duty_cycle/sensor.py index 3537cb0973..6a367328e6 100644 --- a/esphome/components/duty_cycle/sensor.py +++ b/esphome/components/duty_cycle/sensor.py @@ -25,9 +25,7 @@ CONFIG_SCHEMA = ( .extend( { cv.GenerateID(): cv.declare_id(DutyCycleSensor), - cv.Required(CONF_PIN): cv.All( - pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt - ), + cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pin_schema), } ) .extend(cv.polling_component_schema("60s")) diff --git a/esphome/components/e131/__init__.py b/esphome/components/e131/__init__.py index 5eb823064d..bb662e0989 100644 --- a/esphome/components/e131/__init__.py +++ b/esphome/components/e131/__init__.py @@ -4,6 +4,8 @@ from esphome.components.light.types import AddressableLightEffect from esphome.components.light.effects import register_addressable_effect from esphome.const import CONF_ID, CONF_NAME, CONF_METHOD, CONF_CHANNELS +DEPENDENCIES = ["network"] + e131_ns = cg.esphome_ns.namespace("e131") E131AddressableLightEffect = e131_ns.class_( "E131AddressableLightEffect", AddressableLightEffect @@ -21,11 +23,16 @@ CHANNELS = { CONF_UNIVERSE = "universe" CONF_E131_ID = "e131_id" -CONFIG_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.declare_id(E131Component), - cv.Optional(CONF_METHOD, default="MULTICAST"): cv.one_of(*METHODS, upper=True), - } +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(E131Component), + cv.Optional(CONF_METHOD, default="MULTICAST"): cv.one_of( + *METHODS, upper=True + ), + } + ), + cv.only_with_arduino, ) diff --git a/esphome/components/e131/e131.cpp b/esphome/components/e131/e131.cpp index 7694d039e5..35510fe204 100644 --- a/esphome/components/e131/e131.cpp +++ b/esphome/components/e131/e131.cpp @@ -1,12 +1,14 @@ +#ifdef USE_ARDUINO + #include "e131.h" #include "e131_addressable_light_effect.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #include #include #endif @@ -104,3 +106,5 @@ bool E131Component::process_(int universe, const E131Packet &packet) { } // namespace e131 } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/e131/e131.h b/esphome/components/e131/e131.h index 3f647edbf1..3819e522a5 100644 --- a/esphome/components/e131/e131.h +++ b/esphome/components/e131/e131.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include "esphome/core/component.h" #include @@ -55,3 +57,5 @@ class E131Component : public esphome::Component { } // namespace e131 } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/e131/e131_addressable_light_effect.cpp b/esphome/components/e131/e131_addressable_light_effect.cpp index f0f165b25f..371f3b9cbf 100644 --- a/esphome/components/e131/e131_addressable_light_effect.cpp +++ b/esphome/components/e131/e131_addressable_light_effect.cpp @@ -1,3 +1,5 @@ +#ifdef USE_ARDUINO + #include "e131.h" #include "e131_addressable_light_effect.h" #include "esphome/core/log.h" @@ -90,3 +92,5 @@ bool E131AddressableLightEffect::process_(int universe, const E131Packet &packet } // namespace e131 } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/e131/e131_addressable_light_effect.h b/esphome/components/e131/e131_addressable_light_effect.h index 1ab5d43164..e78f6bb0e0 100644 --- a/esphome/components/e131/e131_addressable_light_effect.h +++ b/esphome/components/e131/e131_addressable_light_effect.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include "esphome/core/component.h" #include "esphome/components/light/addressable_light_effect.h" @@ -46,3 +48,5 @@ class E131AddressableLightEffect : public light::AddressableLightEffect { } // namespace e131 } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/e131/e131_packet.cpp b/esphome/components/e131/e131_packet.cpp index 14fdc084a6..b20eb9f666 100644 --- a/esphome/components/e131/e131_packet.cpp +++ b/esphome/components/e131/e131_packet.cpp @@ -1,8 +1,14 @@ +#ifdef USE_ARDUINO + #include "e131.h" #include "esphome/core/log.h" #include "esphome/core/util.h" +#include "esphome/components/network/ip_address.h" +#include +#include #include +#include #include namespace esphome { @@ -63,8 +69,8 @@ bool E131Component::join_igmp_groups_() { if (!universe.second) continue; - ip4_addr_t multicast_addr = { - static_cast(IPAddress(239, 255, ((universe.first >> 8) & 0xff), ((universe.first >> 0) & 0xff)))}; + ip4_addr_t multicast_addr = {static_cast( + network::IPAddress(239, 255, ((universe.first >> 8) & 0xff), ((universe.first >> 0) & 0xff)))}; auto err = igmp_joingroup(IP4_ADDR_ANY4, &multicast_addr); @@ -98,7 +104,7 @@ void E131Component::leave_(int universe) { if (listen_method_ == E131_MULTICAST) { ip4_addr_t multicast_addr = { - static_cast(IPAddress(239, 255, ((universe >> 8) & 0xff), ((universe >> 0) & 0xff)))}; + static_cast(network::IPAddress(239, 255, ((universe >> 8) & 0xff), ((universe >> 0) & 0xff)))}; igmp_leavegroup(IP4_ADDR_ANY4, &multicast_addr); } @@ -134,3 +140,5 @@ bool E131Component::packet_(const std::vector &data, int &universe, E13 } // namespace e131 } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/endstop/endstop_cover.cpp b/esphome/components/endstop/endstop_cover.cpp index cbc4b334d9..67c6a4ebd3 100644 --- a/esphome/components/endstop/endstop_cover.cpp +++ b/esphome/components/endstop/endstop_cover.cpp @@ -1,5 +1,6 @@ #include "endstop_cover.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace endstop { diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py new file mode 100644 index 0000000000..c86087cc25 --- /dev/null +++ b/esphome/components/esp32/__init__.py @@ -0,0 +1,379 @@ +from dataclasses import dataclass +from typing import Union +from pathlib import Path +import logging + +from esphome.helpers import write_file_if_changed +from esphome.const import ( + CONF_BOARD, + CONF_FRAMEWORK, + CONF_TYPE, + CONF_VARIANT, + CONF_VERSION, + KEY_CORE, + KEY_FRAMEWORK_VERSION, + KEY_TARGET_FRAMEWORK, + KEY_TARGET_PLATFORM, +) +from esphome.core import CORE, HexInt +import esphome.config_validation as cv +import esphome.codegen as cg + +from .const import ( + KEY_BOARD, + KEY_ESP32, + KEY_SDKCONFIG_OPTIONS, + KEY_VARIANT, + VARIANT_ESP32C3, + VARIANTS, +) + +# force import gpio to register pin schema +from .gpio import esp32_pin_to_code # noqa + + +_LOGGER = logging.getLogger(__name__) +CODEOWNERS = ["@esphome/core"] + + +def set_core_data(config): + CORE.data[KEY_ESP32] = {} + CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = "esp32" + conf = config[CONF_FRAMEWORK] + if conf[CONF_TYPE] == FRAMEWORK_ESP_IDF: + CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "esp-idf" + CORE.data[KEY_ESP32][KEY_SDKCONFIG_OPTIONS] = {} + elif conf[CONF_TYPE] == FRAMEWORK_ARDUINO: + CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "arduino" + CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version.parse( + config[CONF_FRAMEWORK][CONF_VERSION_HINT] + ) + CORE.data[KEY_ESP32][KEY_BOARD] = config[CONF_BOARD] + CORE.data[KEY_ESP32][KEY_VARIANT] = config[CONF_VARIANT] + return config + + +def get_esp32_variant(): + return CORE.data[KEY_ESP32][KEY_VARIANT] + + +def is_esp32c3(): + return get_esp32_variant() == VARIANT_ESP32C3 + + +@dataclass +class RawSdkconfigValue: + """An sdkconfig value that won't be auto-formatted""" + + value: str + + +SdkconfigValueType = Union[bool, int, HexInt, str, RawSdkconfigValue] + + +def add_idf_sdkconfig_option(name: str, value: SdkconfigValueType): + """Set an esp-idf sdkconfig value.""" + if not CORE.using_esp_idf: + raise ValueError("Not an esp-idf project") + CORE.data[KEY_ESP32][KEY_SDKCONFIG_OPTIONS][name] = value + + +def _format_framework_arduino_version(ver: cv.Version) -> str: + # format the given arduino (https://github.com/espressif/arduino-esp32/releases) version to + # a PIO platformio/framework-arduinoespressif32 value + # List of package versions: https://api.registry.platformio.org/v3/packages/platformio/tool/framework-arduinoespressif32 + if ver <= cv.Version(1, 0, 3): + return f"~2.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" + return f"~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" + + +# NOTE: Keep this in mind when updating the recommended version: +# * New framework historically have had some regressions, especially for WiFi. +# The new version needs to be thoroughly validated before changing the +# recommended version as otherwise a bunch of devices could be bricked +# * For all constants below, update platformio.ini (in this repo) +# and platformio.ini/platformio-lint.ini in the esphome-docker-base repository + +# The default/recommended arduino framework version +# - https://github.com/espressif/arduino-esp32/releases +# - https://api.registry.platformio.org/v3/packages/platformio/tool/framework-arduinoespressif32 +RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(1, 0, 6) +# The platformio/espressif32 version to use for arduino frameworks +# - https://github.com/platformio/platform-espressif32/releases +# - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32 +ARDUINO_PLATFORM_VERSION = cv.Version(3, 3, 2) + +# The default/recommended esp-idf framework version +# - https://github.com/espressif/esp-idf/releases +# - https://api.registry.platformio.org/v3/packages/platformio/tool/framework-espidf +RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION = cv.Version(4, 3, 0) +# The platformio/espressif32 version to use for esp-idf frameworks +# - https://github.com/platformio/platform-espressif32/releases +# - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32 +ESP_IDF_PLATFORM_VERSION = cv.Version(3, 3, 2) + + +def _arduino_check_versions(value): + value = value.copy() + lookups = { + "dev": ("https://github.com/espressif/arduino-esp32.git", cv.Version(2, 0, 0)), + "latest": ("", cv.Version(1, 0, 3)), + "recommended": ( + _format_framework_arduino_version(RECOMMENDED_ARDUINO_FRAMEWORK_VERSION), + RECOMMENDED_ARDUINO_FRAMEWORK_VERSION, + ), + } + ver_value = value[CONF_VERSION] + default_ver_hint = None + if ver_value.lower() in lookups: + default_ver_hint = str(lookups[ver_value.lower()][1]) + ver_value = lookups[ver_value.lower()][0] + else: + with cv.suppress_invalid(): + ver = cv.Version.parse(cv.version_number(value)) + if ver <= cv.Version(1, 0, 3): + ver_value = f"~2.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" + else: + ver_value = f"~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" + default_ver_hint = str(ver) + value[CONF_VERSION] = ver_value + + if CONF_VERSION_HINT not in value and default_ver_hint is None: + raise cv.Invalid("Needs a version hint to understand the framework version") + + ver_hint_s = value.get(CONF_VERSION_HINT, default_ver_hint) + value[CONF_VERSION_HINT] = ver_hint_s + plat_ver = value.get(CONF_PLATFORM_VERSION, ARDUINO_PLATFORM_VERSION) + value[CONF_PLATFORM_VERSION] = str(plat_ver) + + if cv.Version.parse(ver_hint_s) != RECOMMENDED_ARDUINO_FRAMEWORK_VERSION: + _LOGGER.warning( + "The selected arduino framework version is not the recommended one" + ) + _LOGGER.warning( + "If there are connectivity or build issues please remove the manual version" + ) + + return value + + +def _format_framework_espidf_version(ver: cv.Version) -> str: + # format the given arduino (https://github.com/espressif/esp-idf/releases) version to + # a PIO platformio/framework-espidf value + # List of package versions: https://api.registry.platformio.org/v3/packages/platformio/tool/framework-espidf + return f"~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" + + +def _esp_idf_check_versions(value): + value = value.copy() + lookups = { + "dev": ("https://github.com/espressif/esp-idf.git", cv.Version(4, 3, 1)), + "latest": ("", cv.Version(4, 3, 0)), + "recommended": ( + _format_framework_espidf_version(RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION), + RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION, + ), + } + ver_value = value[CONF_VERSION] + default_ver_hint = None + if ver_value.lower() in lookups: + default_ver_hint = str(lookups[ver_value.lower()][1]) + ver_value = lookups[ver_value.lower()][0] + else: + with cv.suppress_invalid(): + ver = cv.Version.parse(cv.version_number(value)) + ver_value = f"~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" + default_ver_hint = str(ver) + value[CONF_VERSION] = ver_value + + if CONF_VERSION_HINT not in value and default_ver_hint is None: + raise cv.Invalid("Needs a version hint to understand the framework version") + + ver_hint_s = value.get(CONF_VERSION_HINT, default_ver_hint) + value[CONF_VERSION_HINT] = ver_hint_s + if cv.Version.parse(ver_hint_s) < cv.Version(4, 0, 0): + raise cv.Invalid("Only ESP-IDF 4.0+ is supported") + if cv.Version.parse(ver_hint_s) != RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION: + _LOGGER.warning( + "The selected esp-idf framework version is not the recommended one" + ) + _LOGGER.warning( + "If there are connectivity or build issues please remove the manual version" + ) + + plat_ver = value.get(CONF_PLATFORM_VERSION, ESP_IDF_PLATFORM_VERSION) + value[CONF_PLATFORM_VERSION] = str(plat_ver) + + return value + + +CONF_VERSION_HINT = "version_hint" +CONF_PLATFORM_VERSION = "platform_version" +ARDUINO_FRAMEWORK_SCHEMA = cv.All( + cv.Schema( + { + cv.Optional(CONF_VERSION, default="recommended"): cv.string_strict, + cv.Optional(CONF_VERSION_HINT): cv.version_number, + cv.Optional(CONF_PLATFORM_VERSION): cv.string_strict, + } + ), + _arduino_check_versions, +) +CONF_SDKCONFIG_OPTIONS = "sdkconfig_options" +ESP_IDF_FRAMEWORK_SCHEMA = cv.All( + cv.Schema( + { + cv.Optional(CONF_VERSION, default="recommended"): cv.string_strict, + cv.Optional(CONF_VERSION_HINT): cv.version_number, + cv.Optional(CONF_SDKCONFIG_OPTIONS, default={}): { + cv.string_strict: cv.string_strict + }, + cv.Optional(CONF_PLATFORM_VERSION): cv.string_strict, + } + ), + _esp_idf_check_versions, +) + + +FRAMEWORK_ESP_IDF = "esp-idf" +FRAMEWORK_ARDUINO = "arduino" +FRAMEWORK_SCHEMA = cv.typed_schema( + { + FRAMEWORK_ESP_IDF: ESP_IDF_FRAMEWORK_SCHEMA, + FRAMEWORK_ARDUINO: ARDUINO_FRAMEWORK_SCHEMA, + }, + lower=True, + space="-", + default_type=FRAMEWORK_ARDUINO, +) + + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.Required(CONF_BOARD): cv.string_strict, + cv.Optional(CONF_VARIANT, default="ESP32"): cv.one_of( + *VARIANTS, upper=True + ), + cv.Optional(CONF_FRAMEWORK, default={}): FRAMEWORK_SCHEMA, + } + ), + set_core_data, +) + + +async def to_code(config): + cg.add_platformio_option("board", config[CONF_BOARD]) + cg.add_build_flag("-DUSE_ESP32") + cg.add_define("ESPHOME_BOARD", config[CONF_BOARD]) + cg.add_build_flag(f"-DUSE_ESP32_VARIANT_{config[CONF_VARIANT]}") + + conf = config[CONF_FRAMEWORK] + if conf[CONF_TYPE] == FRAMEWORK_ESP_IDF: + cg.add_platformio_option( + "platform", f"espressif32 @ {conf[CONF_PLATFORM_VERSION]}" + ) + cg.add_platformio_option("framework", "espidf") + cg.add_build_flag("-DUSE_ESP_IDF") + cg.add_build_flag("-DUSE_ESP32_FRAMEWORK_ESP_IDF") + cg.add_build_flag("-Wno-nonnull-compare") + cg.add_platformio_option( + "platform_packages", + [f"platformio/framework-espidf @ {conf[CONF_VERSION]}"], + ) + add_idf_sdkconfig_option("CONFIG_PARTITION_TABLE_SINGLE_APP", False) + add_idf_sdkconfig_option("CONFIG_PARTITION_TABLE_CUSTOM", True) + add_idf_sdkconfig_option( + "CONFIG_PARTITION_TABLE_CUSTOM_FILENAME", "partitions.csv" + ) + add_idf_sdkconfig_option("CONFIG_COMPILER_OPTIMIZATION_DEFAULT", False) + add_idf_sdkconfig_option("CONFIG_COMPILER_OPTIMIZATION_SIZE", True) + + cg.add_platformio_option("board_build.partitions", "partitions.csv") + + for name, value in conf[CONF_SDKCONFIG_OPTIONS].items(): + add_idf_sdkconfig_option(name, RawSdkconfigValue(value)) + + elif conf[CONF_TYPE] == FRAMEWORK_ARDUINO: + cg.add_platformio_option( + "platform", f"espressif32 @ {conf[CONF_PLATFORM_VERSION]}" + ) + cg.add_platformio_option("framework", "arduino") + cg.add_build_flag("-DUSE_ARDUINO") + cg.add_build_flag("-DUSE_ESP32_FRAMEWORK_ARDUINO") + cg.add_platformio_option( + "platform_packages", + [f"platformio/framework-arduinoespressif32 @ {conf[CONF_VERSION]}"], + ) + + cg.add_platformio_option("board_build.partitions", "partitions.csv") + + +ARDUINO_PARTITIONS_CSV = """\ +nvs, data, nvs, 0x009000, 0x005000, +otadata, data, ota, 0x00e000, 0x002000, +app0, app, ota_0, 0x010000, 0x1C0000, +app1, app, ota_1, 0x1D0000, 0x1C0000, +eeprom, data, 0x99, 0x390000, 0x001000, +spiffs, data, spiffs, 0x391000, 0x00F000 +""" + + +IDF_PARTITIONS_CSV = """\ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, , 0x4000, +otadata, data, ota, , 0x2000, +phy_init, data, phy, , 0x1000, +app0, app, ota_0, , 0x1C0000, +app1, app, ota_1, , 0x1C0000, +""" + + +def _format_sdkconfig_val(value: SdkconfigValueType) -> str: + if isinstance(value, bool): + return "y" if value else "n" + if isinstance(value, int): + return str(value) + if isinstance(value, str): + return f'"{value}"' + if isinstance(value, RawSdkconfigValue): + return value.value + raise ValueError + + +def _write_sdkconfig(): + # sdkconfig.{name} stores the real sdkconfig (modified by esp-idf with default) + # sdkconfig.{name}.esphomeinternal stores what esphome last wrote + # we use the internal one to detect if there were any changes, and if so write them to the + # real sdkconfig + sdk_path = Path(CORE.relative_build_path(f"sdkconfig.{CORE.name}")) + internal_path = Path( + CORE.relative_build_path(f"sdkconfig.{CORE.name}.esphomeinternal") + ) + + want_opts = CORE.data[KEY_ESP32][KEY_SDKCONFIG_OPTIONS] + contents = ( + "\n".join( + f"{name}={_format_sdkconfig_val(value)}" + for name, value in sorted(want_opts.items()) + ) + + "\n" + ) + if write_file_if_changed(internal_path, contents): + # internal changed, update real one + write_file_if_changed(sdk_path, contents) + + +# Called by writer.py +def copy_files(): + if CORE.using_arduino: + write_file_if_changed( + CORE.relative_build_path("partitions.csv"), + ARDUINO_PARTITIONS_CSV, + ) + if CORE.using_esp_idf: + _write_sdkconfig() + write_file_if_changed( + CORE.relative_build_path("partitions.csv"), + IDF_PARTITIONS_CSV, + ) diff --git a/esphome/boards.py b/esphome/components/esp32/boards.py similarity index 77% rename from esphome/boards.py rename to esphome/components/esp32/boards.py index ba6fe889ea..ddf4bf2026 100644 --- a/esphome/boards.py +++ b/esphome/components/esp32/boards.py @@ -1,212 +1,3 @@ -ESP8266_BASE_PINS = { - "A0": 17, - "SS": 15, - "MOSI": 13, - "MISO": 12, - "SCK": 14, - "SDA": 4, - "SCL": 5, - "RX": 3, - "TX": 1, -} - -ESP8266_BOARD_PINS = { - "d1": { - "D0": 3, - "D1": 1, - "D2": 16, - "D3": 5, - "D4": 4, - "D5": 14, - "D6": 12, - "D7": 13, - "D8": 0, - "D9": 2, - "D10": 15, - "D11": 13, - "D12": 14, - "D13": 14, - "D14": 4, - "D15": 5, - "LED": 2, - }, - "d1_mini": { - "D0": 16, - "D1": 5, - "D2": 4, - "D3": 0, - "D4": 2, - "D5": 14, - "D6": 12, - "D7": 13, - "D8": 15, - "LED": 2, - }, - "d1_mini_lite": "d1_mini", - "d1_mini_pro": "d1_mini", - "esp01": {}, - "esp01_1m": {}, - "esp07": {}, - "esp12e": {}, - "esp210": {}, - "esp8285": {}, - "esp_wroom_02": {}, - "espduino": {"LED": 16}, - "espectro": {"LED": 15, "BUTTON": 2}, - "espino": {"LED": 2, "LED_RED": 2, "LED_GREEN": 4, "LED_BLUE": 5, "BUTTON": 0}, - "espinotee": {"LED": 16}, - "espmxdevkit": {}, - "espresso_lite_v1": {"LED": 16}, - "espresso_lite_v2": {"LED": 2}, - "gen4iod": {}, - "heltec_wifi_kit_8": "d1_mini", - "huzzah": { - "LED": 0, - "LED_RED": 0, - "LED_BLUE": 2, - "D4": 4, - "D5": 5, - "D12": 12, - "D13": 13, - "D14": 14, - "D15": 15, - "D16": 16, - }, - "inventone": {}, - "modwifi": {}, - "nodemcu": { - "D0": 16, - "D1": 5, - "D2": 4, - "D3": 0, - "D4": 2, - "D5": 14, - "D6": 12, - "D7": 13, - "D8": 15, - "D9": 3, - "D10": 1, - "LED": 16, - }, - "nodemcuv2": "nodemcu", - "oak": { - "P0": 2, - "P1": 5, - "P2": 0, - "P3": 3, - "P4": 1, - "P5": 4, - "P6": 15, - "P7": 13, - "P8": 12, - "P9": 14, - "P10": 16, - "P11": 17, - "LED": 5, - }, - "phoenix_v1": {"LED": 16}, - "phoenix_v2": {"LED": 2}, - "sonoff_basic": {}, - "sonoff_s20": {}, - "sonoff_sv": {}, - "sonoff_th": {}, - "sparkfunBlynk": "thing", - "thing": {"LED": 5, "SDA": 2, "SCL": 14}, - "thingdev": "thing", - "wifi_slot": {"LED": 2}, - "wifiduino": { - "D0": 3, - "D1": 1, - "D2": 2, - "D3": 0, - "D4": 4, - "D5": 5, - "D6": 16, - "D7": 14, - "D8": 12, - "D9": 13, - "D10": 15, - "D11": 13, - "D12": 12, - "D13": 14, - }, - "wifinfo": { - "LED": 12, - "D0": 16, - "D1": 5, - "D2": 4, - "D3": 0, - "D4": 2, - "D5": 14, - "D6": 12, - "D7": 13, - "D8": 15, - "D9": 3, - "D10": 1, - }, - "wio_link": {"LED": 2, "GROVE": 15, "D0": 14, "D1": 12, "D2": 13, "BUTTON": 0}, - "wio_node": {"LED": 2, "GROVE": 15, "D0": 3, "D1": 5, "BUTTON": 0}, - "xinabox_cw01": {"SDA": 2, "SCL": 14, "LED": 5, "LED_RED": 12, "LED_GREEN": 13}, -} - -FLASH_SIZE_1_MB = 2 ** 20 -FLASH_SIZE_512_KB = FLASH_SIZE_1_MB // 2 -FLASH_SIZE_2_MB = 2 * FLASH_SIZE_1_MB -FLASH_SIZE_4_MB = 4 * FLASH_SIZE_1_MB -FLASH_SIZE_16_MB = 16 * FLASH_SIZE_1_MB - -ESP8266_FLASH_SIZES = { - "d1": FLASH_SIZE_4_MB, - "d1_mini": FLASH_SIZE_4_MB, - "d1_mini_lite": FLASH_SIZE_1_MB, - "d1_mini_pro": FLASH_SIZE_16_MB, - "esp01": FLASH_SIZE_512_KB, - "esp01_1m": FLASH_SIZE_1_MB, - "esp07": FLASH_SIZE_4_MB, - "esp12e": FLASH_SIZE_4_MB, - "esp210": FLASH_SIZE_4_MB, - "esp8285": FLASH_SIZE_1_MB, - "esp_wroom_02": FLASH_SIZE_2_MB, - "espduino": FLASH_SIZE_4_MB, - "espectro": FLASH_SIZE_4_MB, - "espino": FLASH_SIZE_4_MB, - "espinotee": FLASH_SIZE_4_MB, - "espmxdevkit": FLASH_SIZE_1_MB, - "espresso_lite_v1": FLASH_SIZE_4_MB, - "espresso_lite_v2": FLASH_SIZE_4_MB, - "gen4iod": FLASH_SIZE_512_KB, - "heltec_wifi_kit_8": FLASH_SIZE_4_MB, - "huzzah": FLASH_SIZE_4_MB, - "inventone": FLASH_SIZE_4_MB, - "modwifi": FLASH_SIZE_2_MB, - "nodemcu": FLASH_SIZE_4_MB, - "nodemcuv2": FLASH_SIZE_4_MB, - "oak": FLASH_SIZE_4_MB, - "phoenix_v1": FLASH_SIZE_4_MB, - "phoenix_v2": FLASH_SIZE_4_MB, - "sonoff_basic": FLASH_SIZE_1_MB, - "sonoff_s20": FLASH_SIZE_1_MB, - "sonoff_sv": FLASH_SIZE_1_MB, - "sonoff_th": FLASH_SIZE_1_MB, - "sparkfunBlynk": FLASH_SIZE_4_MB, - "thing": FLASH_SIZE_512_KB, - "thingdev": FLASH_SIZE_512_KB, - "wifi_slot": FLASH_SIZE_1_MB, - "wifiduino": FLASH_SIZE_4_MB, - "wifinfo": FLASH_SIZE_1_MB, - "wio_link": FLASH_SIZE_4_MB, - "wio_node": FLASH_SIZE_4_MB, - "xinabox_cw01": FLASH_SIZE_4_MB, -} - -ESP8266_LD_SCRIPTS = { - FLASH_SIZE_512_KB: ("eagle.flash.512k0.ld", "eagle.flash.512k.ld"), - FLASH_SIZE_1_MB: ("eagle.flash.1m0.ld", "eagle.flash.1m.ld"), - FLASH_SIZE_2_MB: ("eagle.flash.2m.ld", "eagle.flash.2m.ld"), - FLASH_SIZE_4_MB: ("eagle.flash.4m.ld", "eagle.flash.4m.ld"), - FLASH_SIZE_16_MB: ("eagle.flash.16m.ld", "eagle.flash.16m14m.ld"), -} - ESP32_BASE_PINS = { "TX": 1, "RX": 3, @@ -1134,19 +925,3 @@ ESP32_BOARD_PINS = { }, "xinabox_cw02": {"LED": 27}, } - -ESP32_C3_BASE_PINS = { - "TX": 21, - "RX": 20, - "ADC1_0": 0, - "ADC1_1": 1, - "ADC1_2": 2, - "ADC1_3": 3, - "ADC1_4": 4, - "ADC2_0": 5, -} - -ESP32_C3_BOARD_PINS = { - "esp32-c3-devkitm-1": {"LED": 8}, - "esp32-c3-devkitc-02": "esp32-c3-devkitm-1", -} diff --git a/esphome/components/esp32/const.py b/esphome/components/esp32/const.py new file mode 100644 index 0000000000..b82f03bf68 --- /dev/null +++ b/esphome/components/esp32/const.py @@ -0,0 +1,21 @@ +import esphome.codegen as cg + +KEY_ESP32 = "esp32" +KEY_BOARD = "board" +KEY_VARIANT = "variant" +KEY_SDKCONFIG_OPTIONS = "sdkconfig_options" + +VARIANT_ESP32 = "ESP32" +VARIANT_ESP32S2 = "ESP32S2" +VARIANT_ESP32S3 = "ESP32S3" +VARIANT_ESP32C3 = "ESP32C3" +VARIANT_ESP32H2 = "ESP32H2" +VARIANTS = [ + VARIANT_ESP32, + VARIANT_ESP32S2, + VARIANT_ESP32S3, + VARIANT_ESP32C3, + VARIANT_ESP32H2, +] + +esp32_ns = cg.esphome_ns.namespace("esp32") diff --git a/esphome/components/esp32/core.cpp b/esphome/components/esp32/core.cpp new file mode 100644 index 0000000000..96047df535 --- /dev/null +++ b/esphome/components/esp32/core.cpp @@ -0,0 +1,89 @@ +#ifdef USE_ESP32 + +#include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "preferences.h" +#include +#include +#include +#include + +#if ESP_IDF_VERSION_MAJOR >= 4 +#include +#endif + +void setup(); +void loop(); + +namespace esphome { + +void IRAM_ATTR HOT yield() { vPortYield(); } +uint32_t IRAM_ATTR HOT millis() { return (uint32_t)(esp_timer_get_time() / 1000ULL); } +void IRAM_ATTR HOT delay(uint32_t ms) { vTaskDelay(ms / portTICK_PERIOD_MS); } +uint32_t IRAM_ATTR HOT micros() { return (uint32_t) esp_timer_get_time(); } +void IRAM_ATTR HOT delayMicroseconds(uint32_t us) { + auto start = (uint64_t) esp_timer_get_time(); + while (((uint64_t) esp_timer_get_time()) - start < us) + ; +} +void arch_restart() { + esp_restart(); + // restart() doesn't always end execution + while (true) { // NOLINT(clang-diagnostic-unreachable-code) + yield(); + } +} +void IRAM_ATTR HOT arch_feed_wdt() { +#ifdef USE_ARDUINO +#if CONFIG_ARDUINO_RUNNING_CORE == 0 +#ifdef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0 + // ESP32 uses "Task Watchdog" which is hooked to the FreeRTOS idle task. + // To cause the Watchdog to be triggered we need to put the current task + // to sleep to get the idle task scheduled. + delay(1); +#endif +#endif +#endif // USE_ARDUINO + +#ifdef USE_ESP_IDF +#ifdef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0 + delay(1); +#endif +#endif // USE_ESP_IDF +} + +uint8_t progmem_read_byte(const uint8_t *addr) { return *addr; } +uint32_t arch_get_cpu_cycle_count() { +#if ESP_IDF_VERSION_MAJOR >= 4 + return cpu_hal_get_cycle_count(); +#else + uint32_t ccount; + __asm__ __volatile__("esync; rsr %0,ccount" : "=a"(ccount)); + return ccount; +#endif +} +uint32_t arch_get_cpu_freq_hz() { return rtc_clk_apb_freq_get(); } + +#ifdef USE_ESP_IDF +TaskHandle_t loop_task_handle = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + +void loop_task(void *pv_params) { + setup(); + while (true) { + loop(); + } +} + +extern "C" void app_main() { + esp32::setup_preferences(); + xTaskCreate(loop_task, "loopTask", 8192, nullptr, 1, &loop_task_handle); +} +#endif // USE_ESP_IDF + +#ifdef USE_ARDUINO +extern "C" void init() { esp32::setup_preferences(); } +#endif // USE_ARDUINO + +} // namespace esphome + +#endif // USE_ESP32 diff --git a/esphome/components/esp32/gpio.py b/esphome/components/esp32/gpio.py new file mode 100644 index 0000000000..93ab17db22 --- /dev/null +++ b/esphome/components/esp32/gpio.py @@ -0,0 +1,201 @@ +import logging + +from esphome.const import ( + CONF_ID, + CONF_INPUT, + CONF_INVERTED, + CONF_MODE, + CONF_NUMBER, + CONF_OPEN_DRAIN, + CONF_OUTPUT, + CONF_PULLDOWN, + CONF_PULLUP, +) +from esphome import pins +from esphome.core import CORE +import esphome.config_validation as cv +import esphome.codegen as cg + +from . import boards +from .const import KEY_BOARD, KEY_ESP32, esp32_ns + + +_LOGGER = logging.getLogger(__name__) + + +IDFInternalGPIOPin = esp32_ns.class_("IDFInternalGPIOPin", cg.InternalGPIOPin) +ArduinoInternalGPIOPin = esp32_ns.class_("ArduinoInternalGPIOPin", cg.InternalGPIOPin) + + +def _lookup_pin(value): + board = CORE.data[KEY_ESP32][KEY_BOARD] + board_pins = boards.ESP32_BOARD_PINS.get(board, {}) + + # Resolved aliased board pins (shorthand when two boards have the same pin configuration) + while isinstance(board_pins, str): + board_pins = boards.ESP32_BOARD_PINS[board_pins] + + if value in board_pins: + return board_pins[value] + if value in boards.ESP32_BASE_PINS: + return boards.ESP32_BASE_PINS[value] + raise cv.Invalid(f"Cannot resolve pin name '{value}' for board {board}.") + + +def _translate_pin(value): + if isinstance(value, dict) or value is None: + raise cv.Invalid( + "This variable only supports pin numbers, not full pin schemas " + "(with inverted and mode)." + ) + if isinstance(value, int): + return value + try: + return int(value) + except ValueError: + pass + if value.startswith("GPIO"): + return cv.int_(value[len("GPIO") :].strip()) + return _lookup_pin(value) + + +_ESP_SDIO_PINS = { + 6: "Flash Clock", + 7: "Flash Data 0", + 8: "Flash Data 1", + 11: "Flash Command", +} + + +def validate_gpio_pin(value): + value = _translate_pin(value) + if value < 0 or value > 39: + raise cv.Invalid(f"Invalid pin number: {value} (must be 0-39)") + if value in _ESP_SDIO_PINS: + raise cv.Invalid( + f"This pin cannot be used on ESP32s and is already used by the flash interface (function: {_ESP_SDIO_PINS[value]})" + ) + if 9 <= value <= 10: + _LOGGER.warning( + "Pin %s (9-10) might already be used by the " + "flash interface in QUAD IO flash mode.", + value, + ) + if value in (20, 24, 28, 29, 30, 31): + # These pins are not exposed in GPIO mux (reason unknown) + # but they're missing from IO_MUX list in datasheet + raise cv.Invalid(f"The pin GPIO{value} is not usable on ESP32s.") + return value + + +def validate_supports(value): + num = value[CONF_NUMBER] + mode = value[CONF_MODE] + is_input = mode[CONF_INPUT] + is_output = mode[CONF_OUTPUT] + is_open_drain = mode[CONF_OPEN_DRAIN] + is_pullup = mode[CONF_PULLUP] + is_pulldown = mode[CONF_PULLDOWN] + + if is_input: + # All ESP32 pins support input mode + pass + if is_output and 34 <= num <= 39: + raise cv.Invalid( + f"GPIO{num} (34-39) does not support output pin mode.", + [CONF_MODE, CONF_OUTPUT], + ) + if is_open_drain and not is_output: + raise cv.Invalid( + "Open-drain only works with output mode", [CONF_MODE, CONF_OPEN_DRAIN] + ) + if is_pullup and 34 <= num <= 39: + raise cv.Invalid( + f"GPIO{num} (34-39) does not support pullups.", [CONF_MODE, CONF_PULLUP] + ) + if is_pulldown and 34 <= num <= 39: + raise cv.Invalid( + f"GPIO{num} (34-39) does not support pulldowns.", [CONF_MODE, CONF_PULLDOWN] + ) + + if CORE.using_arduino: + # (input, output, open_drain, pullup, pulldown) + supported_modes = { + # INPUT + (True, False, False, False, False), + # OUTPUT + (False, True, False, False, False), + # INPUT_PULLUP + (True, False, False, True, False), + # INPUT_PULLDOWN + (True, False, False, False, True), + # OUTPUT_OPEN_DRAIN + (False, True, True, False, False), + } + key = (is_input, is_output, is_open_drain, is_pullup, is_pulldown) + if key not in supported_modes: + raise cv.Invalid( + "This pin mode is not supported on ESP32 for arduino frameworks", + [CONF_MODE], + ) + + return value + + +# https://docs.espressif.com/projects/esp-idf/en/v3.3.5/api-reference/peripherals/gpio.html#_CPPv416gpio_drive_cap_t +gpio_drive_cap_t = cg.global_ns.enum("gpio_drive_cap_t") +DRIVE_STRENGTHS = { + 5.0: gpio_drive_cap_t.GPIO_DRIVE_CAP_0, + 10.0: gpio_drive_cap_t.GPIO_DRIVE_CAP_1, + 20.0: gpio_drive_cap_t.GPIO_DRIVE_CAP_2, + 40.0: gpio_drive_cap_t.GPIO_DRIVE_CAP_3, +} +gpio_num_t = cg.global_ns.enum("gpio_num_t") + + +def _choose_pin_declaration(value): + if CORE.using_esp_idf: + return cv.declare_id(IDFInternalGPIOPin)(value) + if CORE.using_arduino: + return cv.declare_id(ArduinoInternalGPIOPin)(value) + raise NotImplementedError + + +CONF_DRIVE_STRENGTH = "drive_strength" +ESP32_PIN_SCHEMA = cv.All( + { + cv.GenerateID(): _choose_pin_declaration, + cv.Required(CONF_NUMBER): validate_gpio_pin, + cv.Optional(CONF_MODE, default={}): cv.Schema( + { + cv.Optional(CONF_INPUT, default=False): cv.boolean, + cv.Optional(CONF_OUTPUT, default=False): cv.boolean, + cv.Optional(CONF_OPEN_DRAIN, default=False): cv.boolean, + cv.Optional(CONF_PULLUP, default=False): cv.boolean, + cv.Optional(CONF_PULLDOWN, default=False): cv.boolean, + } + ), + cv.Optional(CONF_INVERTED, default=False): cv.boolean, + cv.SplitDefault(CONF_DRIVE_STRENGTH, esp32_idf="20mA"): cv.All( + cv.only_with_esp_idf, + cv.float_with_unit("current", "mA", optional_unit=True), + cv.enum(DRIVE_STRENGTHS), + ), + }, + validate_supports, +) + + +@pins.PIN_SCHEMA_REGISTRY.register("esp32", ESP32_PIN_SCHEMA) +async def esp32_pin_to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + num = config[CONF_NUMBER] + if CORE.using_esp_idf: + cg.add(var.set_pin(getattr(gpio_num_t, f"GPIO_NUM_{num}"))) + else: + cg.add(var.set_pin(num)) + cg.add(var.set_inverted(config[CONF_INVERTED])) + if CONF_DRIVE_STRENGTH in config: + cg.add(var.set_drive_strength(config[CONF_DRIVE_STRENGTH])) + cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE]))) + return var diff --git a/esphome/components/esp32/gpio_arduino.cpp b/esphome/components/esp32/gpio_arduino.cpp new file mode 100644 index 0000000000..11de1e13e6 --- /dev/null +++ b/esphome/components/esp32/gpio_arduino.cpp @@ -0,0 +1,107 @@ +#ifdef USE_ESP32_FRAMEWORK_ARDUINO + +#include "gpio_arduino.h" +#include "esphome/core/log.h" +#include + +namespace esphome { +namespace esp32 { + +static const char *const TAG = "esp32"; + +struct ISRPinArg { + uint8_t pin; + bool inverted; +}; + +ISRInternalGPIOPin ArduinoInternalGPIOPin::to_isr() const { + auto *arg = new ISRPinArg{}; // NOLINT(cppcoreguidelines-owning-memory) + arg->pin = pin_; + arg->inverted = inverted_; + return ISRInternalGPIOPin((void *) arg); +} + +void ArduinoInternalGPIOPin::attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const { + uint8_t arduino_mode = DISABLED; + switch (type) { + case gpio::INTERRUPT_RISING_EDGE: + arduino_mode = inverted_ ? FALLING : RISING; + break; + case gpio::INTERRUPT_FALLING_EDGE: + arduino_mode = inverted_ ? RISING : FALLING; + break; + case gpio::INTERRUPT_ANY_EDGE: + arduino_mode = CHANGE; + break; + case gpio::INTERRUPT_LOW_LEVEL: + arduino_mode = inverted_ ? ONHIGH : ONLOW; + break; + case gpio::INTERRUPT_HIGH_LEVEL: + arduino_mode = inverted_ ? ONLOW : ONHIGH; + break; + } + + attachInterruptArg(pin_, func, arg, arduino_mode); +} +void ArduinoInternalGPIOPin::pin_mode(gpio::Flags flags) { + uint8_t mode; + if (flags == gpio::FLAG_INPUT) { + mode = INPUT; + } else if (flags == gpio::FLAG_OUTPUT) { + mode = OUTPUT; + } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_PULLUP)) { + mode = INPUT_PULLUP; + } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_PULLDOWN)) { + mode = INPUT_PULLDOWN; + } else if (flags == (gpio::FLAG_OUTPUT | gpio::FLAG_OPEN_DRAIN)) { + mode = OUTPUT_OPEN_DRAIN; + } else { + return; + } + pinMode(pin_, mode); // NOLINT +} + +std::string ArduinoInternalGPIOPin::dump_summary() const { + char buffer[32]; + snprintf(buffer, sizeof(buffer), "GPIO%u", pin_); + return buffer; +} + +bool ArduinoInternalGPIOPin::digital_read() { + return bool(digitalRead(pin_)) != inverted_; // NOLINT +} +void ArduinoInternalGPIOPin::digital_write(bool value) { + digitalWrite(pin_, value != inverted_ ? 1 : 0); // NOLINT +} +void ArduinoInternalGPIOPin::detach_interrupt() const { + detachInterrupt(pin_); // NOLINT +} + +} // namespace esp32 + +using namespace esp32; + +bool IRAM_ATTR ISRInternalGPIOPin::digital_read() { + auto *arg = reinterpret_cast(arg_); + return bool(digitalRead(arg->pin)) != arg->inverted; // NOLINT +} +void IRAM_ATTR ISRInternalGPIOPin::digital_write(bool value) { + auto *arg = reinterpret_cast(arg_); + digitalWrite(arg->pin, value != arg->inverted ? 1 : 0); // NOLINT +} +void IRAM_ATTR ISRInternalGPIOPin::clear_interrupt() { + auto *arg = reinterpret_cast(arg_); +#ifdef CONFIG_IDF_TARGET_ESP32C3 + GPIO.status_w1tc.val = 1UL << arg->pin; +#else + if (arg->pin < 32) { + GPIO.status_w1tc = 1UL << arg->pin; + } else { + GPIO.status1_w1tc.intr_st = 1UL << (arg->pin - 32); + } +#endif +} + +} // namespace esphome + +#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/esp32/gpio_arduino.h b/esphome/components/esp32/gpio_arduino.h new file mode 100644 index 0000000000..a077723075 --- /dev/null +++ b/esphome/components/esp32/gpio_arduino.h @@ -0,0 +1,36 @@ +#pragma once + +#ifdef USE_ESP32_FRAMEWORK_ARDUINO +#include "esphome/core/hal.h" + +namespace esphome { +namespace esp32 { + +class ArduinoInternalGPIOPin : public InternalGPIOPin { + public: + void set_pin(uint8_t pin) { pin_ = pin; } + void set_inverted(bool inverted) { inverted_ = inverted; } + void set_flags(gpio::Flags flags) { flags_ = flags; } + + void setup() override { pin_mode(flags_); } + void pin_mode(gpio::Flags flags) override; + bool digital_read() override; + void digital_write(bool value) override; + std::string dump_summary() const override; + void detach_interrupt() const override; + ISRInternalGPIOPin to_isr() const override; + uint8_t get_pin() const override { return pin_; } + bool is_inverted() const override { return inverted_; } + + protected: + void attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const override; + + uint8_t pin_; + bool inverted_; + gpio::Flags flags_; +}; + +} // namespace esp32 +} // namespace esphome + +#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/esp32/gpio_idf.cpp b/esphome/components/esp32/gpio_idf.cpp new file mode 100644 index 0000000000..d662d5519a --- /dev/null +++ b/esphome/components/esp32/gpio_idf.cpp @@ -0,0 +1,49 @@ +#ifdef USE_ESP32_FRAMEWORK_ESP_IDF + +#include "gpio_idf.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace esp32 { + +static const char *const TAG = "esp32"; + +bool IDFInternalGPIOPin::isr_service_installed_ = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + +struct ISRPinArg { + gpio_num_t pin; + bool inverted; +}; + +ISRInternalGPIOPin IDFInternalGPIOPin::to_isr() const { + auto *arg = new ISRPinArg{}; // NOLINT(cppcoreguidelines-owning-memory) + arg->pin = pin_; + arg->inverted = inverted_; + return ISRInternalGPIOPin((void *) arg); +} + +std::string IDFInternalGPIOPin::dump_summary() const { + char buffer[32]; + snprintf(buffer, sizeof(buffer), "GPIO%u", static_cast(pin_)); + return buffer; +} + +} // namespace esp32 + +using namespace esp32; + +bool IRAM_ATTR ISRInternalGPIOPin::digital_read() { + auto *arg = reinterpret_cast(arg_); + return bool(gpio_get_level(arg->pin)) != arg->inverted; +} +void IRAM_ATTR ISRInternalGPIOPin::digital_write(bool value) { + auto *arg = reinterpret_cast(arg_); + gpio_set_level(arg->pin, value != arg->inverted ? 1 : 0); +} +void IRAM_ATTR ISRInternalGPIOPin::clear_interrupt() { + // not supported +} + +} // namespace esphome + +#endif // USE_ESP32_FRAMEWORK_ESP_IDF diff --git a/esphome/components/esp32/gpio_idf.h b/esphome/components/esp32/gpio_idf.h new file mode 100644 index 0000000000..6a383afcae --- /dev/null +++ b/esphome/components/esp32/gpio_idf.h @@ -0,0 +1,96 @@ +#pragma once + +#ifdef USE_ESP32_FRAMEWORK_ESP_IDF +#include "esphome/core/hal.h" +#include + +namespace esphome { +namespace esp32 { + +class IDFInternalGPIOPin : public InternalGPIOPin { + public: + void set_pin(gpio_num_t pin) { pin_ = pin; } + void set_inverted(bool inverted) { inverted_ = inverted; } + void set_drive_strength(gpio_drive_cap_t drive_strength) { drive_strength_ = drive_strength; } + void set_flags(gpio::Flags flags) { flags_ = flags; } + + void setup() override { + pin_mode(flags_); + gpio_set_drive_capability(pin_, drive_strength_); + } + void pin_mode(gpio::Flags flags) override { + gpio_config_t conf{}; + conf.pin_bit_mask = 1 << static_cast(pin_); + conf.mode = flags_to_mode_(flags); + conf.pull_up_en = flags & gpio::FLAG_PULLUP ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE; + conf.pull_down_en = flags & gpio::FLAG_PULLDOWN ? GPIO_PULLDOWN_ENABLE : GPIO_PULLDOWN_DISABLE; + conf.intr_type = GPIO_INTR_DISABLE; + gpio_config(&conf); + } + bool digital_read() override { return bool(gpio_get_level(pin_)) != inverted_; } + void digital_write(bool value) override { gpio_set_level(pin_, value != inverted_ ? 1 : 0); } + std::string dump_summary() const override; + void detach_interrupt() const override { gpio_intr_disable(pin_); } + ISRInternalGPIOPin to_isr() const override; + uint8_t get_pin() const override { return (uint8_t) pin_; } + bool is_inverted() const override { return inverted_; } + + protected: + static gpio_mode_t flags_to_mode_(gpio::Flags flags) { + flags = (gpio::Flags)(flags & ~(gpio::FLAG_PULLUP | gpio::FLAG_PULLDOWN)); + if (flags == gpio::FLAG_NONE) { + return GPIO_MODE_DISABLE; + } else if (flags == gpio::FLAG_INPUT) { + return GPIO_MODE_INPUT; + } else if (flags == gpio::FLAG_OUTPUT) { + return GPIO_MODE_OUTPUT; + } else if (flags == (gpio::FLAG_OUTPUT | gpio::FLAG_OPEN_DRAIN)) { + return GPIO_MODE_OUTPUT_OD; + } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_OUTPUT | gpio::FLAG_OPEN_DRAIN)) { + return GPIO_MODE_INPUT_OUTPUT_OD; + } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_OUTPUT)) { + return GPIO_MODE_INPUT_OUTPUT; + } else { + // unsupported + return GPIO_MODE_DISABLE; + } + } + void attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const override { + gpio_int_type_t idf_type = GPIO_INTR_ANYEDGE; + switch (type) { + case gpio::INTERRUPT_RISING_EDGE: + idf_type = inverted_ ? GPIO_INTR_NEGEDGE : GPIO_INTR_POSEDGE; + break; + case gpio::INTERRUPT_FALLING_EDGE: + idf_type = inverted_ ? GPIO_INTR_POSEDGE : GPIO_INTR_NEGEDGE; + break; + case gpio::INTERRUPT_ANY_EDGE: + idf_type = GPIO_INTR_ANYEDGE; + break; + case gpio::INTERRUPT_LOW_LEVEL: + idf_type = inverted_ ? GPIO_INTR_HIGH_LEVEL : GPIO_INTR_LOW_LEVEL; + break; + case gpio::INTERRUPT_HIGH_LEVEL: + idf_type = inverted_ ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL; + break; + } + gpio_set_intr_type(pin_, idf_type); + gpio_intr_enable(pin_); + if (!isr_service_installed_) { + gpio_install_isr_service(ESP_INTR_FLAG_LEVEL5); + isr_service_installed_ = true; + } + gpio_isr_handler_add(pin_, func, arg); + } + + gpio_num_t pin_; + bool inverted_; + gpio_drive_cap_t drive_strength_; + gpio::Flags flags_; + static bool isr_service_installed_; +}; + +} // namespace esp32 +} // namespace esphome + +#endif // USE_ESP32_FRAMEWORK_ESP_IDF diff --git a/esphome/components/esp32/preferences.cpp b/esphome/components/esp32/preferences.cpp new file mode 100644 index 0000000000..639e6434d6 --- /dev/null +++ b/esphome/components/esp32/preferences.cpp @@ -0,0 +1,99 @@ +#ifdef USE_ESP32 + +#include "esphome/core/preferences.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" +#include + +namespace esphome { +namespace esp32 { + +static const char *const TAG = "esp32.preferences"; + +class ESP32PreferenceBackend : public ESPPreferenceBackend { + public: + std::string key; + uint32_t nvs_handle; + bool save(const uint8_t *data, size_t len) override { + esp_err_t err = nvs_set_blob(nvs_handle, key.c_str(), data, len); + if (err != 0) { + ESP_LOGV(TAG, "nvs_set_blob('%s', len=%u) failed: %s", key.c_str(), len, esp_err_to_name(err)); + return false; + } + err = nvs_commit(nvs_handle); + if (err != 0) { + ESP_LOGV(TAG, "nvs_commit('%s', len=%u) failed: %s", key.c_str(), len, esp_err_to_name(err)); + return false; + } + return true; + } + bool load(uint8_t *data, size_t len) override { + size_t actual_len; + esp_err_t err = nvs_get_blob(nvs_handle, key.c_str(), nullptr, &actual_len); + if (err != 0) { + ESP_LOGV(TAG, "nvs_get_blob('%s'): %s - the key might not be set yet", key.c_str(), esp_err_to_name(err)); + return false; + } + if (actual_len != len) { + ESP_LOGVV(TAG, "NVS length does not match (%u!=%u)", actual_len, len); + return false; + } + err = nvs_get_blob(nvs_handle, key.c_str(), data, &len); + if (err != 0) { + ESP_LOGV(TAG, "nvs_get_blob('%s') failed: %s", key.c_str(), esp_err_to_name(err)); + return false; + } + return true; + } +}; + +class ESP32Preferences : public ESPPreferences { + public: + uint32_t nvs_handle; + uint32_t current_offset = 0; + + void open() { + esp_err_t err = nvs_open("esphome", NVS_READWRITE, &nvs_handle); + if (err == 0) + return; + + ESP_LOGW(TAG, "nvs_open failed: %s - erasing NVS...", esp_err_to_name(err)); + nvs_flash_deinit(); + nvs_flash_erase(); + nvs_flash_init(); + + err = nvs_open("esphome", NVS_READWRITE, &nvs_handle); + if (err != 0) { + nvs_handle = 0; + } + } + ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash) override { + return make_preference(length, type); + } + ESPPreferenceObject make_preference(size_t length, uint32_t type) override { + auto *pref = new ESP32PreferenceBackend(); // NOLINT(cppcoreguidelines-owning-memory) + pref->nvs_handle = nvs_handle; + current_offset += length; + + uint32_t keyval = current_offset ^ type; + char keybuf[16]; + snprintf(keybuf, sizeof(keybuf), "%d", keyval); + pref->key = keybuf; // copied to std::string + + return ESPPreferenceObject(pref); + } +}; + +void setup_preferences() { + auto *prefs = new ESP32Preferences(); // NOLINT(cppcoreguidelines-owning-memory) + prefs->open(); + global_preferences = prefs; +} + +} // namespace esp32 + +ESPPreferences *global_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + +} // namespace esphome + +#endif // USE_ESP32 diff --git a/esphome/components/esp32/preferences.h b/esphome/components/esp32/preferences.h new file mode 100644 index 0000000000..e44213e4cf --- /dev/null +++ b/esphome/components/esp32/preferences.h @@ -0,0 +1,12 @@ +#pragma once +#ifdef USE_ESP32 + +namespace esphome { +namespace esp32 { + +void setup_preferences(); + +} // namespace esp32 +} // namespace esphome + +#endif // USE_ESP32 diff --git a/esphome/components/esp32_ble/__init__.py b/esphome/components/esp32_ble/__init__.py index ccf1f6cafe..4b5c741ad9 100644 --- a/esphome/components/esp32_ble/__init__.py +++ b/esphome/components/esp32_ble/__init__.py @@ -1,8 +1,10 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.const import CONF_ID, ESP_PLATFORM_ESP32 +from esphome.const import CONF_ID +from esphome.core import CORE +from esphome.components.esp32 import add_idf_sdkconfig_option -ESP_PLATFORMS = [ESP_PLATFORM_ESP32] +DEPENDENCIES = ["esp32"] CODEOWNERS = ["@jesserockz"] CONFLICTS_WITH = ["esp32_ble_tracker", "esp32_ble_beacon"] @@ -20,3 +22,6 @@ CONFIG_SCHEMA = cv.Schema( async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) + + if CORE.using_esp_idf: + add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True) diff --git a/esphome/components/esp32_ble/ble.cpp b/esphome/components/esp32_ble/ble.cpp index 2d710a6ef7..143be06e3b 100644 --- a/esphome/components/esp32_ble/ble.cpp +++ b/esphome/components/esp32_ble/ble.cpp @@ -1,17 +1,21 @@ -#include "ble.h" +#ifdef USE_ESP32 +#include "ble.h" #include "esphome/core/application.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 - #include #include #include #include +#include #include #include +#ifdef USE_ARDUINO +#include +#endif + namespace esphome { namespace esp32_ble { @@ -52,9 +56,29 @@ bool ESP32BLE::ble_setup_() { return false; } - if (!btStart()) { - ESP_LOGE(TAG, "btStart failed: %d", esp_bt_controller_get_status()); - return false; + if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) { + // start bt controller + if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) { + esp_bt_controller_config_t cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + err = esp_bt_controller_init(&cfg); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_bt_controller_init failed: %s", esp_err_to_name(err)); + return false; + } + while (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) + ; + } + if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_INITED) { + err = esp_bt_controller_enable(ESP_BT_MODE_BLE); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_bt_controller_enable failed: %s", esp_err_to_name(err)); + return false; + } + } + if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) { + ESP_LOGE(TAG, "esp bt controller enable failed"); + return false; + } } esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); diff --git a/esphome/components/esp32_ble/ble.h b/esphome/components/esp32_ble/ble.h index 149f0008a4..008eba3235 100644 --- a/esphome/components/esp32_ble/ble.h +++ b/esphome/components/esp32_ble/ble.h @@ -11,7 +11,7 @@ #include "esphome/components/esp32_ble_server/ble_server.h" #endif -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #include diff --git a/esphome/components/esp32_ble/ble_advertising.cpp b/esphome/components/esp32_ble/ble_advertising.cpp index bcfb0d1a1b..31b1f4c383 100644 --- a/esphome/components/esp32_ble/ble_advertising.cpp +++ b/esphome/components/esp32_ble/ble_advertising.cpp @@ -1,8 +1,11 @@ #include "ble_advertising.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include "ble_uuid.h" +#include +#include +#include "esphome/core/log.h" namespace esphome { namespace esp32_ble { diff --git a/esphome/components/esp32_ble/ble_advertising.h b/esphome/components/esp32_ble/ble_advertising.h index d86089f333..01e2ba1295 100644 --- a/esphome/components/esp32_ble/ble_advertising.h +++ b/esphome/components/esp32_ble/ble_advertising.h @@ -2,7 +2,7 @@ #include -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #include diff --git a/esphome/components/esp32_ble/ble_uuid.cpp b/esphome/components/esp32_ble/ble_uuid.cpp index b33275110d..8556aa87df 100644 --- a/esphome/components/esp32_ble/ble_uuid.cpp +++ b/esphome/components/esp32_ble/ble_uuid.cpp @@ -1,6 +1,10 @@ #include "ble_uuid.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 + +#include +#include +#include "esphome/core/log.h" namespace esphome { namespace esp32_ble { diff --git a/esphome/components/esp32_ble/ble_uuid.h b/esphome/components/esp32_ble/ble_uuid.h index 89082e19fa..f953f9fede 100644 --- a/esphome/components/esp32_ble/ble_uuid.h +++ b/esphome/components/esp32_ble/ble_uuid.h @@ -1,9 +1,9 @@ #pragma once #include "esphome/core/helpers.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #include diff --git a/esphome/components/esp32_ble/queue.h b/esphome/components/esp32_ble/queue.h index cd123d5469..8fb2803237 100644 --- a/esphome/components/esp32_ble/queue.h +++ b/esphome/components/esp32_ble/queue.h @@ -1,15 +1,19 @@ #pragma once + +#ifdef USE_ESP32 + #include "esphome/core/component.h" #include "esphome/core/helpers.h" #include #include - -#ifdef ARDUINO_ARCH_ESP32 +#include #include #include #include +#include +#include /* * BLE events come in from a separate Task (thread) in the ESP32 stack. Rather diff --git a/esphome/components/esp32_ble_beacon/__init__.py b/esphome/components/esp32_ble_beacon/__init__.py index cd0cb4bed6..d6cbb15dd2 100644 --- a/esphome/components/esp32_ble_beacon/__init__.py +++ b/esphome/components/esp32_ble_beacon/__init__.py @@ -1,8 +1,10 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.const import CONF_ID, CONF_TYPE, CONF_UUID, ESP_PLATFORM_ESP32 +from esphome.const import CONF_ID, CONF_TYPE, CONF_UUID +from esphome.core import CORE +from esphome.components.esp32 import add_idf_sdkconfig_option -ESP_PLATFORMS = [ESP_PLATFORM_ESP32] +DEPENDENCIES = ["esp32"] CONFLICTS_WITH = ["esp32_ble_tracker"] esp32_ble_beacon_ns = cg.esphome_ns.namespace("esp32_ble_beacon") @@ -29,3 +31,6 @@ async def to_code(config): await cg.register_component(var, config) cg.add(var.set_major(config[CONF_MAJOR])) cg.add(var.set_minor(config[CONF_MINOR])) + + if CORE.using_esp_idf: + add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True) diff --git a/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp b/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp index c9e00a1093..96afadd19a 100644 --- a/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp +++ b/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp @@ -1,14 +1,16 @@ #include "esp32_ble_beacon.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include -#include +#include #include #include #include #include +#include +#include "esphome/core/hal.h" namespace esphome { namespace esp32_ble_beacon { @@ -59,6 +61,7 @@ void ESP32BLEBeacon::ble_core_task(void *params) { delay(1000); // NOLINT } } + void ESP32BLEBeacon::ble_setup() { // Initialize non-volatile storage for the bluetooth controller esp_err_t err = nvs_flash_init(); @@ -67,9 +70,29 @@ void ESP32BLEBeacon::ble_setup() { return; } - if (!btStart()) { - ESP_LOGE(TAG, "btStart failed: %d", esp_bt_controller_get_status()); - return; + if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) { + // start bt controller + if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) { + esp_bt_controller_config_t cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + err = esp_bt_controller_init(&cfg); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_bt_controller_init failed: %s", esp_err_to_name(err)); + return; + } + while (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) + ; + } + if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_INITED) { + err = esp_bt_controller_enable(ESP_BT_MODE_BLE); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_bt_controller_enable failed: %s", esp_err_to_name(err)); + return; + } + } + if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) { + ESP_LOGE(TAG, "esp bt controller enable failed"); + return; + } } esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); diff --git a/esphome/components/esp32_ble_beacon/esp32_ble_beacon.h b/esphome/components/esp32_ble_beacon/esp32_ble_beacon.h index aba02830b3..d0ef73899c 100644 --- a/esphome/components/esp32_ble_beacon/esp32_ble_beacon.h +++ b/esphome/components/esp32_ble_beacon/esp32_ble_beacon.h @@ -2,7 +2,7 @@ #include "esphome/core/component.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include diff --git a/esphome/components/esp32_ble_server/__init__.py b/esphome/components/esp32_ble_server/__init__.py index 0dcbcadc50..2fcc5c7743 100644 --- a/esphome/components/esp32_ble_server/__init__.py +++ b/esphome/components/esp32_ble_server/__init__.py @@ -1,12 +1,14 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.const import CONF_ID, CONF_MODEL, ESP_PLATFORM_ESP32 +from esphome.const import CONF_ID, CONF_MODEL from esphome.components import esp32_ble +from esphome.core import CORE +from esphome.components.esp32 import add_idf_sdkconfig_option AUTO_LOAD = ["esp32_ble"] CODEOWNERS = ["@jesserockz"] CONFLICTS_WITH = ["esp32_ble_tracker", "esp32_ble_beacon"] -ESP_PLATFORMS = [ESP_PLATFORM_ESP32] +DEPENDENCIES = ["esp32"] CONF_MANUFACTURER = "manufacturer" CONF_BLE_ID = "ble_id" @@ -37,3 +39,6 @@ async def to_code(config): cg.add_define("USE_ESP32_BLE_SERVER") cg.add(parent.set_server(var)) + + if CORE.using_esp_idf: + add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True) diff --git a/esphome/components/esp32_ble_server/ble_2901.cpp b/esphome/components/esp32_ble_server/ble_2901.cpp index 962ba3ffbe..ee0808d2c4 100644 --- a/esphome/components/esp32_ble_server/ble_2901.cpp +++ b/esphome/components/esp32_ble_server/ble_2901.cpp @@ -1,7 +1,7 @@ #include "ble_2901.h" #include "esphome/components/esp32_ble/ble_uuid.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace esp32_ble_server { diff --git a/esphome/components/esp32_ble_server/ble_2901.h b/esphome/components/esp32_ble_server/ble_2901.h index 3bb23ae69d..60f53e55b2 100644 --- a/esphome/components/esp32_ble_server/ble_2901.h +++ b/esphome/components/esp32_ble_server/ble_2901.h @@ -2,7 +2,7 @@ #include "ble_descriptor.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace esp32_ble_server { diff --git a/esphome/components/esp32_ble_server/ble_2902.cpp b/esphome/components/esp32_ble_server/ble_2902.cpp index 0a87b239f9..2f34573c37 100644 --- a/esphome/components/esp32_ble_server/ble_2902.cpp +++ b/esphome/components/esp32_ble_server/ble_2902.cpp @@ -1,7 +1,9 @@ #include "ble_2902.h" #include "esphome/components/esp32_ble/ble_uuid.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 + +#include namespace esphome { namespace esp32_ble_server { diff --git a/esphome/components/esp32_ble_server/ble_2902.h b/esphome/components/esp32_ble_server/ble_2902.h index 024eec755e..64605924ad 100644 --- a/esphome/components/esp32_ble_server/ble_2902.h +++ b/esphome/components/esp32_ble_server/ble_2902.h @@ -2,7 +2,7 @@ #include "ble_descriptor.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace esp32_ble_server { diff --git a/esphome/components/esp32_ble_server/ble_characteristic.cpp b/esphome/components/esp32_ble_server/ble_characteristic.cpp index d8ff39cc94..fae8c13934 100644 --- a/esphome/components/esp32_ble_server/ble_characteristic.cpp +++ b/esphome/components/esp32_ble_server/ble_characteristic.cpp @@ -4,7 +4,7 @@ #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace esp32_ble_server { diff --git a/esphome/components/esp32_ble_server/ble_characteristic.h b/esphome/components/esp32_ble_server/ble_characteristic.h index bc5033f2ae..d2467dd176 100644 --- a/esphome/components/esp32_ble_server/ble_characteristic.h +++ b/esphome/components/esp32_ble_server/ble_characteristic.h @@ -5,13 +5,15 @@ #include -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #include #include #include #include +#include +#include namespace esphome { namespace esp32_ble_server { diff --git a/esphome/components/esp32_ble_server/ble_descriptor.cpp b/esphome/components/esp32_ble_server/ble_descriptor.cpp index 5262087ad1..bfb6224335 100644 --- a/esphome/components/esp32_ble_server/ble_descriptor.cpp +++ b/esphome/components/esp32_ble_server/ble_descriptor.cpp @@ -1,10 +1,11 @@ #include "ble_descriptor.h" #include "ble_characteristic.h" #include "ble_service.h" - #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#include + +#ifdef USE_ESP32 namespace esphome { namespace esp32_ble_server { diff --git a/esphome/components/esp32_ble_server/ble_descriptor.h b/esphome/components/esp32_ble_server/ble_descriptor.h index 1a72cb2b54..4b8fb345c3 100644 --- a/esphome/components/esp32_ble_server/ble_descriptor.h +++ b/esphome/components/esp32_ble_server/ble_descriptor.h @@ -2,7 +2,7 @@ #include "esphome/components/esp32_ble/ble_uuid.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #include diff --git a/esphome/components/esp32_ble_server/ble_server.cpp b/esphome/components/esp32_ble_server/ble_server.cpp index 5777e99e23..0b91c238c3 100644 --- a/esphome/components/esp32_ble_server/ble_server.cpp +++ b/esphome/components/esp32_ble_server/ble_server.cpp @@ -5,7 +5,7 @@ #include "esphome/core/application.h" #include "esphome/core/version.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #include diff --git a/esphome/components/esp32_ble_server/ble_server.h b/esphome/components/esp32_ble_server/ble_server.h index 9d955dda79..7df44b4c61 100644 --- a/esphome/components/esp32_ble_server/ble_server.h +++ b/esphome/components/esp32_ble_server/ble_server.h @@ -12,7 +12,7 @@ #include -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #include diff --git a/esphome/components/esp32_ble_server/ble_service.cpp b/esphome/components/esp32_ble_server/ble_service.cpp index 5cfc53a397..281164f0f5 100644 --- a/esphome/components/esp32_ble_server/ble_service.cpp +++ b/esphome/components/esp32_ble_server/ble_service.cpp @@ -2,7 +2,7 @@ #include "ble_server.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace esp32_ble_server { diff --git a/esphome/components/esp32_ble_server/ble_service.h b/esphome/components/esp32_ble_server/ble_service.h index 9fdb95bde5..16cc897238 100644 --- a/esphome/components/esp32_ble_server/ble_service.h +++ b/esphome/components/esp32_ble_server/ble_service.h @@ -3,7 +3,7 @@ #include "ble_characteristic.h" #include "esphome/components/esp32_ble/ble_uuid.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #include diff --git a/esphome/components/esp32_ble_tracker/__init__.py b/esphome/components/esp32_ble_tracker/__init__.py index 162a4dee89..e3d52f345a 100644 --- a/esphome/components/esp32_ble_tracker/__init__.py +++ b/esphome/components/esp32_ble_tracker/__init__.py @@ -4,7 +4,6 @@ import esphome.config_validation as cv from esphome import automation from esphome.const import ( CONF_ID, - ESP_PLATFORM_ESP32, CONF_INTERVAL, CONF_DURATION, CONF_TRIGGER_ID, @@ -15,8 +14,10 @@ from esphome.const import ( CONF_ON_BLE_SERVICE_DATA_ADVERTISE, CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE, ) +from esphome.core import CORE +from esphome.components.esp32 import add_idf_sdkconfig_option -ESP_PLATFORMS = [ESP_PLATFORM_ESP32] +DEPENDENCIES = ["esp32"] AUTO_LOAD = ["xiaomi_ble", "ruuvi_ble"] CONF_ESP32_BLE_ID = "esp32_ble_id" @@ -216,6 +217,9 @@ async def to_code(config): cg.add(trigger.set_address(conf[CONF_MAC_ADDRESS].as_hex)) await automation.build_automation(trigger, [(adv_data_t_const_ref, "x")], conf) + if CORE.using_esp_idf: + add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True) + async def register_ble_device(var, config): paren = await cg.get_variable(config[CONF_ESP32_BLE_ID]) diff --git a/esphome/components/esp32_ble_tracker/automation.h b/esphome/components/esp32_ble_tracker/automation.h index 9df2587ede..3505e9c26d 100644 --- a/esphome/components/esp32_ble_tracker/automation.h +++ b/esphome/components/esp32_ble_tracker/automation.h @@ -3,7 +3,7 @@ #include "esphome/core/automation.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace esp32_ble_tracker { diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index 358fe50605..5568884b9a 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -1,18 +1,24 @@ +#ifdef USE_ESP32 + #include "esp32_ble_tracker.h" #include "esphome/core/log.h" #include "esphome/core/application.h" #include "esphome/core/helpers.h" - -#ifdef ARDUINO_ARCH_ESP32 +#include "esphome/core/hal.h" #include #include #include #include +#include #include #include #include +#ifdef USE_ARDUINO +#include +#endif + // bt_trace.h #undef TAG @@ -126,14 +132,33 @@ bool ESP32BLETracker::ble_setup() { return false; } - esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); - - // Initialize the bluetooth controller with the default configuration - if (!btStart()) { - ESP_LOGE(TAG, "btStart failed: %d", esp_bt_controller_get_status()); - return false; + if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) { + // start bt controller + if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) { + esp_bt_controller_config_t cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + err = esp_bt_controller_init(&cfg); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_bt_controller_init failed: %s", esp_err_to_name(err)); + return false; + } + while (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) + ; + } + if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_INITED) { + err = esp_bt_controller_enable(ESP_BT_MODE_BLE); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_bt_controller_enable failed: %s", esp_err_to_name(err)); + return false; + } + } + if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) { + ESP_LOGE(TAG, "esp bt controller enable failed"); + return false; + } } + esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); + err = esp_bluedroid_init(); if (err != ESP_OK) { ESP_LOGE(TAG, "esp_bluedroid_init failed: %d", err); diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index 0594c4a811..40955d39cf 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -4,7 +4,7 @@ #include "esphome/core/helpers.h" #include "queue.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #include diff --git a/esphome/components/esp32_ble_tracker/queue.h b/esphome/components/esp32_ble_tracker/queue.h index f0f6ab9f17..3d38c17584 100644 --- a/esphome/components/esp32_ble_tracker/queue.h +++ b/esphome/components/esp32_ble_tracker/queue.h @@ -1,14 +1,17 @@ #pragma once + +#ifdef USE_ESP32 #include "esphome/core/component.h" #include "esphome/core/helpers.h" #include #include - -#ifdef ARDUINO_ARCH_ESP32 +#include #include #include +#include +#include /* * BLE events come in from a separate Task (thread) in the ESP32 stack. Rather diff --git a/esphome/components/esp32_camera/__init__.py b/esphome/components/esp32_camera/__init__.py index e3c7383953..de61ab43cf 100644 --- a/esphome/components/esp32_camera/__init__.py +++ b/esphome/components/esp32_camera/__init__.py @@ -9,16 +9,16 @@ from esphome.const import ( CONF_PIN, CONF_SCL, CONF_SDA, - ESP_PLATFORM_ESP32, CONF_DATA_PINS, CONF_RESET_PIN, CONF_RESOLUTION, CONF_BRIGHTNESS, CONF_CONTRAST, ) +from esphome.core import CORE +from esphome.components.esp32 import add_idf_sdkconfig_option -ESP_PLATFORMS = [ESP_PLATFORM_ESP32] -DEPENDENCIES = ["api"] +DEPENDENCIES = ["esp32", "api"] esp32_camera_ns = cg.esphome_ns.namespace("esp32_camera") ESP32Camera = esp32_camera_ns.class_("ESP32Camera", cg.PollingComponent, cg.Nameable) @@ -68,13 +68,15 @@ CONFIG_SCHEMA = cv.Schema( cv.GenerateID(): cv.declare_id(ESP32Camera), cv.Required(CONF_NAME): cv.string, cv.Optional(CONF_DISABLED_BY_DEFAULT, default=False): cv.boolean, - cv.Required(CONF_DATA_PINS): cv.All([pins.input_pin], cv.Length(min=8, max=8)), - cv.Required(CONF_VSYNC_PIN): pins.input_pin, - cv.Required(CONF_HREF_PIN): pins.input_pin, - cv.Required(CONF_PIXEL_CLOCK_PIN): pins.input_pin, + cv.Required(CONF_DATA_PINS): cv.All( + [pins.internal_gpio_input_pin_number], cv.Length(min=8, max=8) + ), + cv.Required(CONF_VSYNC_PIN): pins.internal_gpio_input_pin_number, + cv.Required(CONF_HREF_PIN): pins.internal_gpio_input_pin_number, + cv.Required(CONF_PIXEL_CLOCK_PIN): pins.internal_gpio_input_pin_number, cv.Required(CONF_EXTERNAL_CLOCK): cv.Schema( { - cv.Required(CONF_PIN): pins.output_pin, + cv.Required(CONF_PIN): pins.internal_gpio_input_pin_number, cv.Optional(CONF_FREQUENCY, default="20MHz"): cv.All( cv.frequency, cv.one_of(20e6, 10e6) ), @@ -82,12 +84,12 @@ CONFIG_SCHEMA = cv.Schema( ), cv.Required(CONF_I2C_PINS): cv.Schema( { - cv.Required(CONF_SDA): pins.output_pin, - cv.Required(CONF_SCL): pins.output_pin, + cv.Required(CONF_SDA): pins.internal_gpio_output_pin_number, + cv.Required(CONF_SCL): pins.internal_gpio_output_pin_number, } ), - cv.Optional(CONF_RESET_PIN): pins.output_pin, - cv.Optional(CONF_POWER_DOWN_PIN): pins.output_pin, + cv.Optional(CONF_RESET_PIN): pins.internal_gpio_output_pin_number, + cv.Optional(CONF_POWER_DOWN_PIN): pins.internal_gpio_output_pin_number, cv.Optional(CONF_MAX_FRAMERATE, default="10 fps"): cv.All( cv.framerate, cv.Range(min=0, min_included=False, max=60) ), @@ -146,3 +148,8 @@ async def to_code(config): cg.add_define("USE_ESP32_CAMERA") cg.add_build_flag("-DBOARD_HAS_PSRAM") + + if CORE.using_esp_idf: + cg.add_library("espressif/esp32-camera", "1.0.0") + add_idf_sdkconfig_option("CONFIG_RTCIO_SUPPORT_RTC_GPIO_DESC", True) + add_idf_sdkconfig_option("CONFIG_ESP32_SPIRAM_SUPPORT", True) diff --git a/esphome/components/esp32_camera/esp32_camera.cpp b/esphome/components/esp32_camera/esp32_camera.cpp index 072cf41460..05445f024b 100644 --- a/esphome/components/esp32_camera/esp32_camera.cpp +++ b/esphome/components/esp32_camera/esp32_camera.cpp @@ -1,7 +1,10 @@ +#ifdef USE_ESP32 + #include "esp32_camera.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" -#ifdef ARDUINO_ARCH_ESP32 +#include namespace esphome { namespace esp32_camera { @@ -42,7 +45,9 @@ void ESP32Camera::dump_config() { auto conf = this->config_; ESP_LOGCONFIG(TAG, "ESP32 Camera:"); ESP_LOGCONFIG(TAG, " Name: %s", this->name_.c_str()); +#ifdef USE_ARDUINO ESP_LOGCONFIG(TAG, " Board Has PSRAM: %s", YESNO(psramFound())); +#endif // USE_ARDUINO ESP_LOGCONFIG(TAG, " Data Pins: D0:%d D1:%d D2:%d D3:%d D4:%d D5:%d D6:%d D7:%d", conf.pin_d0, conf.pin_d1, conf.pin_d2, conf.pin_d3, conf.pin_d4, conf.pin_d5, conf.pin_d6, conf.pin_d7); ESP_LOGCONFIG(TAG, " VSYNC Pin: %d", conf.pin_vsync); diff --git a/esphome/components/esp32_camera/esp32_camera.h b/esphome/components/esp32_camera/esp32_camera.h index 03272d3b32..430391aa76 100644 --- a/esphome/components/esp32_camera/esp32_camera.h +++ b/esphome/components/esp32_camera/esp32_camera.h @@ -1,10 +1,12 @@ #pragma once -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include "esphome/core/component.h" #include "esphome/core/helpers.h" #include +#include +#include namespace esphome { namespace esp32_camera { diff --git a/esphome/components/esp32_dac/esp32_dac.cpp b/esphome/components/esp32_dac/esp32_dac.cpp index 20a047f0ba..7f37e2ce47 100644 --- a/esphome/components/esp32_dac/esp32_dac.cpp +++ b/esphome/components/esp32_dac/esp32_dac.cpp @@ -2,9 +2,14 @@ #include "esphome/core/log.h" #include "esphome/core/helpers.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 +#ifdef USE_ARDUINO #include +#endif +#ifdef USE_ESP_IDF +#include +#endif namespace esphome { namespace esp32_dac { @@ -15,6 +20,11 @@ void ESP32DAC::setup() { ESP_LOGCONFIG(TAG, "Setting up ESP32 DAC Output..."); this->pin_->setup(); this->turn_off(); + +#ifdef USE_ESP_IDF + auto channel = pin_->get_pin() == 25 ? DAC_CHANNEL_1 : DAC_CHANNEL_2; + dac_output_enable(channel); +#endif } void ESP32DAC::dump_config() { @@ -28,7 +38,14 @@ void ESP32DAC::write_state(float state) { state = 1.0f - state; state = state * 255; + +#ifdef USE_ESP_IDF + auto channel = pin_->get_pin() == 25 ? DAC_CHANNEL_1 : DAC_CHANNEL_2; + dac_output_voltage(channel, (uint8_t) state); +#endif +#ifdef USE_ARDUINO dacWrite(this->pin_->get_pin(), state); +#endif } } // namespace esp32_dac diff --git a/esphome/components/esp32_dac/esp32_dac.h b/esphome/components/esp32_dac/esp32_dac.h index 648efcfe4f..0fb1ddebf0 100644 --- a/esphome/components/esp32_dac/esp32_dac.h +++ b/esphome/components/esp32_dac/esp32_dac.h @@ -1,18 +1,18 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/core/automation.h" #include "esphome/components/output/float_output.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace esp32_dac { class ESP32DAC : public output::FloatOutput, public Component { public: - void set_pin(GPIOPin *pin) { pin_ = pin; } + void set_pin(InternalGPIOPin *pin) { pin_ = pin; } /// Initialize pin void setup() override; @@ -23,7 +23,7 @@ class ESP32DAC : public output::FloatOutput, public Component { protected: void write_state(float state) override; - GPIOPin *pin_; + InternalGPIOPin *pin_; }; } // namespace esp32_dac diff --git a/esphome/components/esp32_dac/output.py b/esphome/components/esp32_dac/output.py index 8534a1bae1..f119198618 100644 --- a/esphome/components/esp32_dac/output.py +++ b/esphome/components/esp32_dac/output.py @@ -2,9 +2,9 @@ from esphome import pins from esphome.components import output import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_ID, CONF_NUMBER, CONF_PIN, ESP_PLATFORM_ESP32 +from esphome.const import CONF_ID, CONF_NUMBER, CONF_PIN -ESP_PLATFORMS = [ESP_PLATFORM_ESP32] +DEPENDENCIES = ["esp32"] def valid_dac_pin(value): diff --git a/esphome/components/esp32_hall/esp32_hall.cpp b/esphome/components/esp32_hall/esp32_hall.cpp index 4bbf65e048..762497aedc 100644 --- a/esphome/components/esp32_hall/esp32_hall.cpp +++ b/esphome/components/esp32_hall/esp32_hall.cpp @@ -1,8 +1,8 @@ +#ifdef USE_ESP32 #include "esp32_hall.h" #include "esphome/core/log.h" -#include "esphome/core/esphal.h" - -#ifdef ARDUINO_ARCH_ESP32 +#include "esphome/core/hal.h" +#include namespace esphome { namespace esp32_hall { @@ -10,7 +10,9 @@ namespace esp32_hall { static const char *const TAG = "esp32_hall"; void ESP32HallSensor::update() { - float value = (hallRead() / 4095.0f) * 10000.0f; + adc1_config_width(ADC_WIDTH_BIT_12); + int value_int = hall_sensor_read(); + float value = (value_int / 4095.0f) * 10000.0f; ESP_LOGD(TAG, "'%s': Got reading %.0f µT", this->name_.c_str(), value); this->publish_state(value); } diff --git a/esphome/components/esp32_hall/esp32_hall.h b/esphome/components/esp32_hall/esp32_hall.h index 040280fff3..8db50c4667 100644 --- a/esphome/components/esp32_hall/esp32_hall.h +++ b/esphome/components/esp32_hall/esp32_hall.h @@ -3,7 +3,7 @@ #include "esphome/core/component.h" #include "esphome/components/sensor/sensor.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace esp32_hall { diff --git a/esphome/components/esp32_hall/sensor.py b/esphome/components/esp32_hall/sensor.py index 4ba79de714..a752da2c97 100644 --- a/esphome/components/esp32_hall/sensor.py +++ b/esphome/components/esp32_hall/sensor.py @@ -3,13 +3,12 @@ import esphome.config_validation as cv from esphome.components import sensor from esphome.const import ( CONF_ID, - ESP_PLATFORM_ESP32, STATE_CLASS_MEASUREMENT, UNIT_MICROTESLA, ICON_MAGNET, ) -ESP_PLATFORMS = [ESP_PLATFORM_ESP32] +DEPENDENCIES = ["esp32"] esp32_hall_ns = cg.esphome_ns.namespace("esp32_hall") ESP32HallSensor = esp32_hall_ns.class_( diff --git a/esphome/components/esp32_improv/__init__.py b/esphome/components/esp32_improv/__init__.py index 4c337055cb..0b0214c63e 100644 --- a/esphome/components/esp32_improv/__init__.py +++ b/esphome/components/esp32_improv/__init__.py @@ -1,14 +1,13 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import binary_sensor, output, esp32_ble_server -from esphome.const import CONF_ID, ESP_PLATFORM_ESP32 +from esphome.const import CONF_ID AUTO_LOAD = ["binary_sensor", "output", "improv", "esp32_ble_server"] CODEOWNERS = ["@jesserockz"] CONFLICTS_WITH = ["esp32_ble_tracker", "esp32_ble_beacon"] -DEPENDENCIES = ["wifi"] -ESP_PLATFORMS = [ESP_PLATFORM_ESP32] +DEPENDENCIES = ["wifi", "esp32"] CONF_AUTHORIZED_DURATION = "authorized_duration" CONF_AUTHORIZER = "authorizer" diff --git a/esphome/components/esp32_improv/esp32_improv_component.cpp b/esphome/components/esp32_improv/esp32_improv_component.cpp index b584d2e8b9..fc58fbd264 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.cpp +++ b/esphome/components/esp32_improv/esp32_improv_component.cpp @@ -5,7 +5,7 @@ #include "esphome/core/application.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace esp32_improv { diff --git a/esphome/components/esp32_improv/esp32_improv_component.h b/esphome/components/esp32_improv/esp32_improv_component.h index 262124f983..af39ae4748 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.h +++ b/esphome/components/esp32_improv/esp32_improv_component.h @@ -10,7 +10,7 @@ #include "esphome/core/helpers.h" #include "esphome/core/preferences.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace esp32_improv { diff --git a/esphome/components/esp32_touch/__init__.py b/esphome/components/esp32_touch/__init__.py index 1564476ecf..cdf6aa3abd 100644 --- a/esphome/components/esp32_touch/__init__.py +++ b/esphome/components/esp32_touch/__init__.py @@ -9,12 +9,11 @@ from esphome.const import ( CONF_SETUP_MODE, CONF_SLEEP_DURATION, CONF_VOLTAGE_ATTENUATION, - ESP_PLATFORM_ESP32, ) from esphome.core import TimePeriod AUTO_LOAD = ["binary_sensor"] -ESP_PLATFORMS = [ESP_PLATFORM_ESP32] +DEPENDENCIES = ["esp32"] esp32_touch_ns = cg.esphome_ns.namespace("esp32_touch") ESP32TouchComponent = esp32_touch_ns.class_("ESP32TouchComponent", cg.Component) diff --git a/esphome/components/esp32_touch/binary_sensor.py b/esphome/components/esp32_touch/binary_sensor.py index 198a43a738..93640334cd 100644 --- a/esphome/components/esp32_touch/binary_sensor.py +++ b/esphome/components/esp32_touch/binary_sensor.py @@ -5,14 +5,12 @@ from esphome.const import ( CONF_NAME, CONF_PIN, CONF_THRESHOLD, - ESP_PLATFORM_ESP32, CONF_ID, ) -from esphome.pins import validate_gpio_pin +from esphome.components.esp32 import gpio from . import esp32_touch_ns, ESP32TouchComponent -ESP_PLATFORMS = [ESP_PLATFORM_ESP32] -DEPENDENCIES = ["esp32_touch"] +DEPENDENCIES = ["esp32_touch", "esp32"] CONF_ESP32_TOUCH_ID = "esp32_touch_id" CONF_WAKEUP_THRESHOLD = "wakeup_threshold" @@ -32,7 +30,7 @@ TOUCH_PADS = { def validate_touch_pad(value): - value = validate_gpio_pin(value) + value = gpio.validate_gpio_pin(value) if value not in TOUCH_PADS: raise cv.Invalid(f"Pin {value} does not support touch pads.") return value diff --git a/esphome/components/esp32_touch/esp32_touch.cpp b/esphome/components/esp32_touch/esp32_touch.cpp index 9ae671a4a9..801106ab6c 100644 --- a/esphome/components/esp32_touch/esp32_touch.cpp +++ b/esphome/components/esp32_touch/esp32_touch.cpp @@ -1,7 +1,8 @@ +#ifdef USE_ESP32 + #include "esp32_touch.h" #include "esphome/core/log.h" - -#ifdef ARDUINO_ARCH_ESP32 +#include "esphome/core/hal.h" namespace esphome { namespace esp32_touch { diff --git a/esphome/components/esp32_touch/esp32_touch.h b/esphome/components/esp32_touch/esp32_touch.h index 8b92a482c0..c584a6d9bc 100644 --- a/esphome/components/esp32_touch/esp32_touch.h +++ b/esphome/components/esp32_touch/esp32_touch.h @@ -1,9 +1,16 @@ #pragma once +#ifdef USE_ESP32 + #include "esphome/core/component.h" #include "esphome/components/binary_sensor/binary_sensor.h" +#include -#ifdef ARDUINO_ARCH_ESP32 +#if ESP_IDF_VERSION_MAJOR >= 4 +#include +#else +#include +#endif namespace esphome { namespace esp32_touch { diff --git a/esphome/components/esp8266/__init__.py b/esphome/components/esp8266/__init__.py new file mode 100644 index 0000000000..592de06440 --- /dev/null +++ b/esphome/components/esp8266/__init__.py @@ -0,0 +1,213 @@ +import logging + +from esphome.const import ( + CONF_BOARD, + CONF_BOARD_FLASH_MODE, + CONF_FRAMEWORK, + CONF_VERSION, + KEY_CORE, + KEY_FRAMEWORK_VERSION, + KEY_TARGET_FRAMEWORK, + KEY_TARGET_PLATFORM, +) +from esphome.core import CORE +import esphome.config_validation as cv +import esphome.codegen as cg + +from .const import CONF_RESTORE_FROM_FLASH, KEY_BOARD, KEY_ESP8266 +from .boards import ESP8266_FLASH_SIZES, ESP8266_LD_SCRIPTS + +# force import gpio to register pin schema +from .gpio import esp8266_pin_to_code # noqa + + +CODEOWNERS = ["@esphome/core"] +_LOGGER = logging.getLogger(__name__) + + +def set_core_data(config): + CORE.data[KEY_ESP8266] = {} + CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = "esp8266" + CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "arduino" + CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version.parse( + config[CONF_FRAMEWORK][CONF_VERSION_HINT] + ) + CORE.data[KEY_ESP8266][KEY_BOARD] = config[CONF_BOARD] + return config + + +def _format_framework_arduino_version(ver: cv.Version) -> str: + # format the given arduino (https://github.com/esp8266/Arduino/releases) version to + # a PIO platformio/framework-arduinoespressif8266 value + # List of package versions: https://api.registry.platformio.org/v3/packages/platformio/tool/framework-arduinoespressif8266 + if ver <= cv.Version(2, 4, 1): + return f"~1.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" + if ver <= cv.Version(2, 6, 2): + return f"~2.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" + return f"~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" + + +# NOTE: Keep this in mind when updating the recommended version: +# * New framework historically have had some regressions, especially for WiFi. +# The new version needs to be thoroughly validated before changing the +# recommended version as otherwise a bunch of devices could be bricked +# * For all constants below, update platformio.ini (in this repo) +# and platformio.ini/platformio-lint.ini in the esphome-docker-base repository + +# The default/recommended arduino framework version +# - https://github.com/esp8266/Arduino/releases +# - https://api.registry.platformio.org/v3/packages/platformio/tool/framework-arduinoespressif8266 +RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(2, 7, 4) +# The platformio/espressif8266 version to use for arduino 2 framework versions +# - https://github.com/platformio/platform-espressif8266/releases +# - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif8266 +ARDUINO_2_PLATFORM_VERSION = cv.Version(2, 6, 2) +# for arduino 3 framework versions +ARDUINO_3_PLATFORM_VERSION = cv.Version(3, 0, 2) + + +def _arduino_check_versions(value): + value = value.copy() + lookups = { + "dev": ("https://github.com/esp8266/Arduino.git", cv.Version(3, 0, 2)), + "latest": ("", cv.Version(3, 0, 2)), + "recommended": ( + _format_framework_arduino_version(RECOMMENDED_ARDUINO_FRAMEWORK_VERSION), + RECOMMENDED_ARDUINO_FRAMEWORK_VERSION, + ), + } + ver_value = value[CONF_VERSION] + default_ver_hint = None + if ver_value.lower() in lookups: + default_ver_hint = str(lookups[ver_value.lower()][1]) + ver_value = lookups[ver_value.lower()][0] + else: + with cv.suppress_invalid(): + ver = cv.Version.parse(cv.version_number(value)) + if ver <= cv.Version(2, 4, 1): + ver_value = f"~1.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" + elif ver <= cv.Version(2, 6, 2): + ver_value = f"~2.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" + else: + ver_value = f"~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" + default_ver_hint = str(ver) + + value[CONF_VERSION] = ver_value + + if CONF_VERSION_HINT not in value and default_ver_hint is None: + raise cv.Invalid("Needs a version hint to understand the framework version") + + ver_hint_s = value.get(CONF_VERSION_HINT, default_ver_hint) + value[CONF_VERSION_HINT] = ver_hint_s + plat_ver = value.get(CONF_PLATFORM_VERSION) + + if plat_ver is None: + ver_hint = cv.Version.parse(ver_hint_s) + if ver_hint >= cv.Version(3, 0, 0): + plat_ver = ARDUINO_3_PLATFORM_VERSION + elif ver_hint >= cv.Version(2, 5, 0): + plat_ver = ARDUINO_2_PLATFORM_VERSION + else: + plat_ver = cv.Version(1, 8, 0) + value[CONF_PLATFORM_VERSION] = str(plat_ver) + + if cv.Version.parse(ver_hint_s) != RECOMMENDED_ARDUINO_FRAMEWORK_VERSION: + _LOGGER.warning( + "The selected arduino framework version is not the recommended one" + ) + _LOGGER.warning( + "If there are connectivity or build issues please remove the manual version" + ) + + return value + + +CONF_VERSION_HINT = "version_hint" +CONF_PLATFORM_VERSION = "platform_version" +ARDUINO_FRAMEWORK_SCHEMA = cv.All( + cv.Schema( + { + cv.Optional(CONF_VERSION, default="recommended"): cv.string_strict, + cv.Optional(CONF_VERSION_HINT): cv.version_number, + cv.Optional(CONF_PLATFORM_VERSION): cv.string_strict, + } + ), + _arduino_check_versions, +) + + +BUILD_FLASH_MODES = ["qio", "qout", "dio", "dout"] +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.Required(CONF_BOARD): cv.string_strict, + cv.Optional(CONF_FRAMEWORK, default={}): ARDUINO_FRAMEWORK_SCHEMA, + cv.Optional(CONF_RESTORE_FROM_FLASH, default=False): cv.boolean, + cv.Optional(CONF_BOARD_FLASH_MODE, default="dout"): cv.one_of( + *BUILD_FLASH_MODES, lower=True + ), + } + ), + set_core_data, +) + + +async def to_code(config): + cg.add_platformio_option("board", config[CONF_BOARD]) + cg.add_build_flag("-DUSE_ESP8266") + cg.add_define("ESPHOME_BOARD", config[CONF_BOARD]) + + conf = config[CONF_FRAMEWORK] + cg.add_platformio_option("framework", "arduino") + cg.add_build_flag("-DUSE_ARDUINO") + cg.add_build_flag("-DUSE_ESP8266_FRAMEWORK_ARDUINO") + cg.add_platformio_option( + "platform_packages", + [f"platformio/framework-arduinoespressif8266 @ {conf[CONF_VERSION]}"], + ) + cg.add_platformio_option( + "platform", f"platformio/espressif8266 @ {conf[CONF_PLATFORM_VERSION]}" + ) + + # Default for platformio is LWIP2_LOW_MEMORY with: + # - MSS=536 + # - LWIP_FEATURES enabled + # - this only adds some optional features like IP incoming packet reassembly and NAPT + # see also: + # https://github.com/esp8266/Arduino/blob/master/tools/sdk/lwip2/include/lwipopts.h + + # Instead we use LWIP2_HIGHER_BANDWIDTH_LOW_FLASH with: + # - MSS=1460 + # - LWIP_FEATURES disabled (because we don't need them) + # Other projects like Tasmota & ESPEasy also use this + cg.add_build_flag("-DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH") + + if config[CONF_RESTORE_FROM_FLASH]: + cg.add_define("USE_ESP8266_PREFERENCES_FLASH") + + # Arduino 2 has a non-standards conformant new that returns a nullptr instead of failing when + # out of memory and exceptions are disabled. Since Arduino 2.6.0, this flag can be used to make + # new abort instead. Use it so that OOM fails early (on allocation) instead of on dereference of + # a NULL pointer (so the stacktrace makes more sense), and for consistency with Arduino 3, + # which always aborts if exceptions are disabled. + # For cases where nullptrs can be handled, use nothrow: `new (std::nothrow) T;` + cg.add_build_flag("-DNEW_OOM_ABORT") + + cg.add_platformio_option("board_build.flash_mode", config[CONF_BOARD_FLASH_MODE]) + + if config[CONF_BOARD] in ESP8266_FLASH_SIZES: + flash_size = ESP8266_FLASH_SIZES[config[CONF_BOARD]] + ld_scripts = ESP8266_LD_SCRIPTS[flash_size] + ver = CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] + + if ver <= cv.Version(2, 3, 0): + # No ld script support + ld_script = None + if ver <= cv.Version(2, 4, 2): + # Old ld script path + ld_script = ld_scripts[0] + else: + ld_script = ld_scripts[1] + + if ld_script is not None: + cg.add_platformio_option("board_build.ldscript", ld_script) diff --git a/esphome/components/esp8266/boards.py b/esphome/components/esp8266/boards.py new file mode 100644 index 0000000000..c49aae4ffa --- /dev/null +++ b/esphome/components/esp8266/boards.py @@ -0,0 +1,266 @@ +FLASH_SIZE_1_MB = 2 ** 20 +FLASH_SIZE_512_KB = FLASH_SIZE_1_MB // 2 +FLASH_SIZE_2_MB = 2 * FLASH_SIZE_1_MB +FLASH_SIZE_4_MB = 4 * FLASH_SIZE_1_MB +FLASH_SIZE_16_MB = 16 * FLASH_SIZE_1_MB + +ESP8266_FLASH_SIZES = { + "d1": FLASH_SIZE_4_MB, + "d1_mini": FLASH_SIZE_4_MB, + "d1_mini_lite": FLASH_SIZE_1_MB, + "d1_mini_pro": FLASH_SIZE_16_MB, + "esp01": FLASH_SIZE_512_KB, + "esp01_1m": FLASH_SIZE_1_MB, + "esp07": FLASH_SIZE_4_MB, + "esp12e": FLASH_SIZE_4_MB, + "esp210": FLASH_SIZE_4_MB, + "esp8285": FLASH_SIZE_1_MB, + "esp_wroom_02": FLASH_SIZE_2_MB, + "espduino": FLASH_SIZE_4_MB, + "espectro": FLASH_SIZE_4_MB, + "espino": FLASH_SIZE_4_MB, + "espinotee": FLASH_SIZE_4_MB, + "espmxdevkit": FLASH_SIZE_1_MB, + "espresso_lite_v1": FLASH_SIZE_4_MB, + "espresso_lite_v2": FLASH_SIZE_4_MB, + "gen4iod": FLASH_SIZE_512_KB, + "heltec_wifi_kit_8": FLASH_SIZE_4_MB, + "huzzah": FLASH_SIZE_4_MB, + "inventone": FLASH_SIZE_4_MB, + "modwifi": FLASH_SIZE_2_MB, + "nodemcu": FLASH_SIZE_4_MB, + "nodemcuv2": FLASH_SIZE_4_MB, + "oak": FLASH_SIZE_4_MB, + "phoenix_v1": FLASH_SIZE_4_MB, + "phoenix_v2": FLASH_SIZE_4_MB, + "sonoff_basic": FLASH_SIZE_1_MB, + "sonoff_s20": FLASH_SIZE_1_MB, + "sonoff_sv": FLASH_SIZE_1_MB, + "sonoff_th": FLASH_SIZE_1_MB, + "sparkfunBlynk": FLASH_SIZE_4_MB, + "thing": FLASH_SIZE_512_KB, + "thingdev": FLASH_SIZE_512_KB, + "wifi_slot": FLASH_SIZE_1_MB, + "wifiduino": FLASH_SIZE_4_MB, + "wifinfo": FLASH_SIZE_1_MB, + "wio_link": FLASH_SIZE_4_MB, + "wio_node": FLASH_SIZE_4_MB, + "xinabox_cw01": FLASH_SIZE_4_MB, +} + +ESP8266_LD_SCRIPTS = { + FLASH_SIZE_512_KB: ("eagle.flash.512k0.ld", "eagle.flash.512k.ld"), + FLASH_SIZE_1_MB: ("eagle.flash.1m0.ld", "eagle.flash.1m.ld"), + FLASH_SIZE_2_MB: ("eagle.flash.2m.ld", "eagle.flash.2m.ld"), + FLASH_SIZE_4_MB: ("eagle.flash.4m.ld", "eagle.flash.4m.ld"), + FLASH_SIZE_16_MB: ("eagle.flash.16m.ld", "eagle.flash.16m14m.ld"), +} + +ESP8266_BASE_PINS = { + "A0": 17, + "SS": 15, + "MOSI": 13, + "MISO": 12, + "SCK": 14, + "SDA": 4, + "SCL": 5, + "RX": 3, + "TX": 1, +} + +ESP8266_BOARD_PINS = { + "d1": { + "D0": 3, + "D1": 1, + "D2": 16, + "D3": 5, + "D4": 4, + "D5": 14, + "D6": 12, + "D7": 13, + "D8": 0, + "D9": 2, + "D10": 15, + "D11": 13, + "D12": 14, + "D13": 14, + "D14": 4, + "D15": 5, + "LED": 2, + }, + "d1_mini": { + "D0": 16, + "D1": 5, + "D2": 4, + "D3": 0, + "D4": 2, + "D5": 14, + "D6": 12, + "D7": 13, + "D8": 15, + "LED": 2, + }, + "d1_mini_lite": "d1_mini", + "d1_mini_pro": "d1_mini", + "esp01": {}, + "esp01_1m": {}, + "esp07": {}, + "esp12e": {}, + "esp210": {}, + "esp8285": {}, + "esp_wroom_02": {}, + "espduino": {"LED": 16}, + "espectro": {"LED": 15, "BUTTON": 2}, + "espino": {"LED": 2, "LED_RED": 2, "LED_GREEN": 4, "LED_BLUE": 5, "BUTTON": 0}, + "espinotee": {"LED": 16}, + "espmxdevkit": {}, + "espresso_lite_v1": {"LED": 16}, + "espresso_lite_v2": {"LED": 2}, + "gen4iod": {}, + "heltec_wifi_kit_8": "d1_mini", + "huzzah": { + "LED": 0, + "LED_RED": 0, + "LED_BLUE": 2, + "D4": 4, + "D5": 5, + "D12": 12, + "D13": 13, + "D14": 14, + "D15": 15, + "D16": 16, + }, + "inventone": {}, + "modwifi": {}, + "nodemcu": { + "D0": 16, + "D1": 5, + "D2": 4, + "D3": 0, + "D4": 2, + "D5": 14, + "D6": 12, + "D7": 13, + "D8": 15, + "D9": 3, + "D10": 1, + "LED": 16, + }, + "nodemcuv2": "nodemcu", + "oak": { + "P0": 2, + "P1": 5, + "P2": 0, + "P3": 3, + "P4": 1, + "P5": 4, + "P6": 15, + "P7": 13, + "P8": 12, + "P9": 14, + "P10": 16, + "P11": 17, + "LED": 5, + }, + "phoenix_v1": {"LED": 16}, + "phoenix_v2": {"LED": 2}, + "sonoff_basic": {}, + "sonoff_s20": {}, + "sonoff_sv": {}, + "sonoff_th": {}, + "sparkfunBlynk": "thing", + "thing": {"LED": 5, "SDA": 2, "SCL": 14}, + "thingdev": "thing", + "wifi_slot": {"LED": 2}, + "wifiduino": { + "D0": 3, + "D1": 1, + "D2": 2, + "D3": 0, + "D4": 4, + "D5": 5, + "D6": 16, + "D7": 14, + "D8": 12, + "D9": 13, + "D10": 15, + "D11": 13, + "D12": 12, + "D13": 14, + }, + "wifinfo": { + "LED": 12, + "D0": 16, + "D1": 5, + "D2": 4, + "D3": 0, + "D4": 2, + "D5": 14, + "D6": 12, + "D7": 13, + "D8": 15, + "D9": 3, + "D10": 1, + }, + "wio_link": {"LED": 2, "GROVE": 15, "D0": 14, "D1": 12, "D2": 13, "BUTTON": 0}, + "wio_node": {"LED": 2, "GROVE": 15, "D0": 3, "D1": 5, "BUTTON": 0}, + "xinabox_cw01": {"SDA": 2, "SCL": 14, "LED": 5, "LED_RED": 12, "LED_GREEN": 13}, +} + +FLASH_SIZE_1_MB = 2 ** 20 +FLASH_SIZE_512_KB = FLASH_SIZE_1_MB // 2 +FLASH_SIZE_2_MB = 2 * FLASH_SIZE_1_MB +FLASH_SIZE_4_MB = 4 * FLASH_SIZE_1_MB +FLASH_SIZE_16_MB = 16 * FLASH_SIZE_1_MB + +ESP8266_FLASH_SIZES = { + "d1": FLASH_SIZE_4_MB, + "d1_mini": FLASH_SIZE_4_MB, + "d1_mini_lite": FLASH_SIZE_1_MB, + "d1_mini_pro": FLASH_SIZE_16_MB, + "esp01": FLASH_SIZE_512_KB, + "esp01_1m": FLASH_SIZE_1_MB, + "esp07": FLASH_SIZE_4_MB, + "esp12e": FLASH_SIZE_4_MB, + "esp210": FLASH_SIZE_4_MB, + "esp8285": FLASH_SIZE_1_MB, + "esp_wroom_02": FLASH_SIZE_2_MB, + "espduino": FLASH_SIZE_4_MB, + "espectro": FLASH_SIZE_4_MB, + "espino": FLASH_SIZE_4_MB, + "espinotee": FLASH_SIZE_4_MB, + "espmxdevkit": FLASH_SIZE_1_MB, + "espresso_lite_v1": FLASH_SIZE_4_MB, + "espresso_lite_v2": FLASH_SIZE_4_MB, + "gen4iod": FLASH_SIZE_512_KB, + "heltec_wifi_kit_8": FLASH_SIZE_4_MB, + "huzzah": FLASH_SIZE_4_MB, + "inventone": FLASH_SIZE_4_MB, + "modwifi": FLASH_SIZE_2_MB, + "nodemcu": FLASH_SIZE_4_MB, + "nodemcuv2": FLASH_SIZE_4_MB, + "oak": FLASH_SIZE_4_MB, + "phoenix_v1": FLASH_SIZE_4_MB, + "phoenix_v2": FLASH_SIZE_4_MB, + "sonoff_basic": FLASH_SIZE_1_MB, + "sonoff_s20": FLASH_SIZE_1_MB, + "sonoff_sv": FLASH_SIZE_1_MB, + "sonoff_th": FLASH_SIZE_1_MB, + "sparkfunBlynk": FLASH_SIZE_4_MB, + "thing": FLASH_SIZE_512_KB, + "thingdev": FLASH_SIZE_512_KB, + "wifi_slot": FLASH_SIZE_1_MB, + "wifiduino": FLASH_SIZE_4_MB, + "wifinfo": FLASH_SIZE_1_MB, + "wio_link": FLASH_SIZE_4_MB, + "wio_node": FLASH_SIZE_4_MB, + "xinabox_cw01": FLASH_SIZE_4_MB, +} + +ESP8266_LD_SCRIPTS = { + FLASH_SIZE_512_KB: ("eagle.flash.512k0.ld", "eagle.flash.512k.ld"), + FLASH_SIZE_1_MB: ("eagle.flash.1m0.ld", "eagle.flash.1m.ld"), + FLASH_SIZE_2_MB: ("eagle.flash.2m.ld", "eagle.flash.2m.ld"), + FLASH_SIZE_4_MB: ("eagle.flash.4m.ld", "eagle.flash.4m.ld"), + FLASH_SIZE_16_MB: ("eagle.flash.16m.ld", "eagle.flash.16m14m.ld"), +} diff --git a/esphome/components/esp8266/const.py b/esphome/components/esp8266/const.py new file mode 100644 index 0000000000..16a050360c --- /dev/null +++ b/esphome/components/esp8266/const.py @@ -0,0 +1,8 @@ +import esphome.codegen as cg + +KEY_ESP8266 = "esp8266" +KEY_BOARD = "board" +CONF_RESTORE_FROM_FLASH = "restore_from_flash" + +# esp8266 namespace is already defined by arduino, manually prefix esphome +esp8266_ns = cg.global_ns.namespace("esphome").namespace("esp8266") diff --git a/esphome/components/esp8266/core.cpp b/esphome/components/esp8266/core.cpp new file mode 100644 index 0000000000..453f772e09 --- /dev/null +++ b/esphome/components/esp8266/core.cpp @@ -0,0 +1,37 @@ +#ifdef USE_ESP8266 + +#include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "preferences.h" +#include +#include + +namespace esphome { + +void IRAM_ATTR HOT yield() { ::yield(); } +uint32_t IRAM_ATTR HOT millis() { return ::millis(); } +void IRAM_ATTR HOT delay(uint32_t ms) { ::delay(ms); } +uint32_t IRAM_ATTR HOT micros() { return ::micros(); } +void IRAM_ATTR HOT delayMicroseconds(uint32_t us) { ::delayMicroseconds(us); } +void arch_restart() { + ESP.restart(); // NOLINT(readability-static-accessed-through-instance) + // restart() doesn't always end execution + while (true) { // NOLINT(clang-diagnostic-unreachable-code) + yield(); + } +} +void IRAM_ATTR HOT arch_feed_wdt() { + ESP.wdtFeed(); // NOLINT(readability-static-accessed-through-instance) +} + +uint8_t progmem_read_byte(const uint8_t *addr) { + return pgm_read_byte(addr); // NOLINT +} +uint32_t arch_get_cpu_cycle_count() { + return ESP.getCycleCount(); // NOLINT(readability-static-accessed-through-instance) +} +uint32_t arch_get_cpu_freq_hz() { return F_CPU; } + +} // namespace esphome + +#endif // USE_ESP8266 diff --git a/esphome/components/esp8266/gpio.cpp b/esphome/components/esp8266/gpio.cpp new file mode 100644 index 0000000000..4dbffa7f6c --- /dev/null +++ b/esphome/components/esp8266/gpio.cpp @@ -0,0 +1,96 @@ +#ifdef USE_ESP8266 + +#include "gpio.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace esp8266 { + +static const char *const TAG = "esp8266"; + +struct ISRPinArg { + uint8_t pin; + bool inverted; +}; + +ISRInternalGPIOPin ESP8266GPIOPin::to_isr() const { + auto *arg = new ISRPinArg{}; // NOLINT(cppcoreguidelines-owning-memory) + arg->pin = pin_; + arg->inverted = inverted_; + return ISRInternalGPIOPin((void *) arg); +} + +void ESP8266GPIOPin::attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const { + uint8_t arduino_mode = 0; + switch (type) { + case gpio::INTERRUPT_RISING_EDGE: + arduino_mode = inverted_ ? FALLING : RISING; + break; + case gpio::INTERRUPT_FALLING_EDGE: + arduino_mode = inverted_ ? RISING : FALLING; + break; + case gpio::INTERRUPT_ANY_EDGE: + arduino_mode = CHANGE; + break; + case gpio::INTERRUPT_LOW_LEVEL: + arduino_mode = inverted_ ? ONHIGH : ONLOW; + break; + case gpio::INTERRUPT_HIGH_LEVEL: + arduino_mode = inverted_ ? ONLOW : ONHIGH; + break; + } + + attachInterruptArg(pin_, func, arg, arduino_mode); +} +void ESP8266GPIOPin::pin_mode(gpio::Flags flags) { + uint8_t mode; + if (flags == gpio::FLAG_INPUT) { + mode = INPUT; + } else if (flags == gpio::FLAG_OUTPUT) { + mode = OUTPUT; + } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_PULLUP)) { + mode = INPUT_PULLUP; + } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_PULLDOWN)) { + mode = INPUT_PULLDOWN_16; + } else if (flags == (gpio::FLAG_OUTPUT | gpio::FLAG_OPEN_DRAIN)) { + mode = OUTPUT_OPEN_DRAIN; + } else { + return; + } + pinMode(pin_, mode); // NOLINT +} + +std::string ESP8266GPIOPin::dump_summary() const { + char buffer[32]; + snprintf(buffer, sizeof(buffer), "GPIO%u", pin_); + return buffer; +} + +bool ESP8266GPIOPin::digital_read() { + return bool(digitalRead(pin_)) != inverted_; // NOLINT +} +void ESP8266GPIOPin::digital_write(bool value) { + digitalWrite(pin_, value != inverted_ ? 1 : 0); // NOLINT +} +void ESP8266GPIOPin::detach_interrupt() const { detachInterrupt(pin_); } + +} // namespace esp8266 + +using namespace esp8266; + +bool IRAM_ATTR ISRInternalGPIOPin::digital_read() { + auto *arg = reinterpret_cast(arg_); + return bool(digitalRead(arg->pin)) != arg->inverted; // NOLINT +} +void IRAM_ATTR ISRInternalGPIOPin::digital_write(bool value) { + auto *arg = reinterpret_cast(arg_); + digitalWrite(arg->pin, value != arg->inverted ? 1 : 0); // NOLINT +} +void IRAM_ATTR ISRInternalGPIOPin::clear_interrupt() { + auto *arg = reinterpret_cast(arg_); + GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1UL << arg->pin); +} + +} // namespace esphome + +#endif // USE_ESP8266 diff --git a/esphome/components/esp8266/gpio.h b/esphome/components/esp8266/gpio.h new file mode 100644 index 0000000000..465d1099ae --- /dev/null +++ b/esphome/components/esp8266/gpio.h @@ -0,0 +1,38 @@ +#pragma once + +#ifdef USE_ESP8266 + +#include "esphome/core/hal.h" +#include + +namespace esphome { +namespace esp8266 { + +class ESP8266GPIOPin : public InternalGPIOPin { + public: + void set_pin(uint8_t pin) { pin_ = pin; } + void set_inverted(bool inverted) { inverted_ = inverted; } + void set_flags(gpio::Flags flags) { flags_ = flags; } + + void setup() override { pin_mode(flags_); } + void pin_mode(gpio::Flags flags) override; + bool digital_read() override; + void digital_write(bool value) override; + std::string dump_summary() const override; + void detach_interrupt() const override; + ISRInternalGPIOPin to_isr() const override; + uint8_t get_pin() const override { return pin_; } + bool is_inverted() const override { return inverted_; } + + protected: + void attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const override; + + uint8_t pin_; + bool inverted_; + gpio::Flags flags_; +}; + +} // namespace esp8266 +} // namespace esphome + +#endif // USE_ESP8266 diff --git a/esphome/components/esp8266/gpio.py b/esphome/components/esp8266/gpio.py new file mode 100644 index 0000000000..0ebfbd6f69 --- /dev/null +++ b/esphome/components/esp8266/gpio.py @@ -0,0 +1,170 @@ +import logging + +from esphome.const import ( + CONF_ID, + CONF_INPUT, + CONF_INVERTED, + CONF_MODE, + CONF_NUMBER, + CONF_OPEN_DRAIN, + CONF_OUTPUT, + CONF_PULLDOWN, + CONF_PULLUP, +) +from esphome import pins +from esphome.core import CORE +import esphome.config_validation as cv +import esphome.codegen as cg + +from . import boards +from .const import KEY_BOARD, KEY_ESP8266, esp8266_ns + + +_LOGGER = logging.getLogger(__name__) + + +ESP8266GPIOPin = esp8266_ns.class_("ESP8266GPIOPin", cg.InternalGPIOPin) + + +def _lookup_pin(value): + board = CORE.data[KEY_ESP8266][KEY_BOARD] + board_pins = boards.ESP8266_BOARD_PINS.get(board, {}) + + # Resolved aliased board pins (shorthand when two boards have the same pin configuration) + while isinstance(board_pins, str): + board_pins = boards.ESP8266_BOARD_PINS[board_pins] + + if value in board_pins: + return board_pins[value] + if value in boards.ESP8266_BASE_PINS: + return boards.ESP8266_BASE_PINS[value] + raise cv.Invalid(f"Cannot resolve pin name '{value}' for board {board}.") + + +def _translate_pin(value): + if isinstance(value, dict) or value is None: + raise cv.Invalid( + "This variable only supports pin numbers, not full pin schemas " + "(with inverted and mode)." + ) + if isinstance(value, int): + return value + try: + return int(value) + except ValueError: + pass + if value.startswith("GPIO"): + return cv.int_(value[len("GPIO") :].strip()) + return _lookup_pin(value) + + +_ESP_SDIO_PINS = { + 6: "Flash Clock", + 7: "Flash Data 0", + 8: "Flash Data 1", + 11: "Flash Command", +} + + +def validate_gpio_pin(value): + value = _translate_pin(value) + if value < 0 or value > 17: + raise cv.Invalid(f"ESP8266: Invalid pin number: {value}") + if value in _ESP_SDIO_PINS: + raise cv.Invalid( + f"This pin cannot be used on ESP8266s and is already used by the flash interface (function: {_ESP_SDIO_PINS[value]})" + ) + if 9 <= value <= 10: + _LOGGER.warning( + "ESP8266: Pin %s (9-10) might already be used by the " + "flash interface in QUAD IO flash mode.", + value, + ) + return value + + +def validate_supports(value): + num = value[CONF_NUMBER] + mode = value[CONF_MODE] + is_input = mode[CONF_INPUT] + is_output = mode[CONF_OUTPUT] + is_open_drain = mode[CONF_OPEN_DRAIN] + is_pullup = mode[CONF_PULLUP] + is_pulldown = mode[CONF_PULLDOWN] + is_analog = mode[CONF_ANALOG] + + if (not is_analog) and num == 17: + raise cv.Invalid( + "GPIO17 (TOUT) is an analog-only pin on the ESP8266.", + [CONF_MODE], + ) + if is_analog and num != 17: + raise cv.Invalid( + "Only GPIO17 is analog-capable on ESP8266.", + [CONF_MODE, CONF_ANALOG], + ) + if is_open_drain and not is_output: + raise cv.Invalid( + "Open-drain only works with output mode", [CONF_MODE, CONF_OPEN_DRAIN] + ) + if is_pullup and num == 0: + raise cv.Invalid( + "GPIO Pin 0 does not support pullup pin mode. " + "Please choose another pin.", + [CONF_MODE, CONF_PULLUP], + ) + if is_pulldown and num != 16: + raise cv.Invalid("Only GPIO16 supports pulldown.", [CONF_MODE, CONF_PULLDOWN]) + + # (input, output, open_drain, pullup, pulldown) + supported_modes = { + # INPUT + (True, False, False, False, False), + # OUTPUT + (False, True, False, False, False), + # INPUT_PULLUP + (True, False, False, True, False), + # INPUT_PULLDOWN_16 + (True, False, False, False, True), + # OUTPUT_OPEN_DRAIN + (False, True, True, False, False), + } + key = (is_input, is_output, is_open_drain, is_pullup, is_pulldown) + if key not in supported_modes: + raise cv.Invalid( + "This pin mode is not supported on ESP8266", + [CONF_MODE], + ) + + return value + + +CONF_ANALOG = "analog" +ESP8266_PIN_SCHEMA = cv.All( + { + cv.GenerateID(): cv.declare_id(ESP8266GPIOPin), + cv.Required(CONF_NUMBER): validate_gpio_pin, + cv.Optional(CONF_MODE, default={}): cv.Schema( + { + cv.Optional(CONF_ANALOG, default=False): cv.boolean, + cv.Optional(CONF_INPUT, default=False): cv.boolean, + cv.Optional(CONF_OUTPUT, default=False): cv.boolean, + cv.Optional(CONF_OPEN_DRAIN, default=False): cv.boolean, + cv.Optional(CONF_PULLUP, default=False): cv.boolean, + cv.Optional(CONF_PULLDOWN, default=False): cv.boolean, + } + ), + cv.Optional(CONF_INVERTED, default=False): cv.boolean, + }, + validate_supports, +) + + +@pins.PIN_SCHEMA_REGISTRY.register("esp8266", ESP8266_PIN_SCHEMA) +async def esp8266_pin_to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + num = config[CONF_NUMBER] + cg.add(var.set_pin(num)) + cg.add(var.set_inverted(config[CONF_INVERTED])) + cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE]))) + return var diff --git a/esphome/components/esp8266/preferences.cpp b/esphome/components/esp8266/preferences.cpp new file mode 100644 index 0000000000..7a8fdb8289 --- /dev/null +++ b/esphome/components/esp8266/preferences.cpp @@ -0,0 +1,263 @@ +#ifdef USE_ESP8266 + +#include +extern "C" { +#include "spi_flash.h" +} + +#include "preferences.h" +#include +#include "esphome/core/preferences.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" +#include "esphome/core/defines.h" + +namespace esphome { +namespace esp8266 { + +static const char *const TAG = "esp8266.preferences"; + +static bool s_prevent_write = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static uint32_t *s_flash_storage = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_flash_dirty = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + +static const uint32_t ESP_RTC_USER_MEM_START = 0x60001200; +#define ESP_RTC_USER_MEM ((uint32_t *) ESP_RTC_USER_MEM_START) +static const uint32_t ESP_RTC_USER_MEM_SIZE_WORDS = 128; +static const uint32_t ESP_RTC_USER_MEM_SIZE_BYTES = ESP_RTC_USER_MEM_SIZE_WORDS * 4; + +#ifdef USE_ESP8266_PREFERENCES_FLASH +static const uint32_t ESP8266_FLASH_STORAGE_SIZE = 128; +#else +static const uint32_t ESP8266_FLASH_STORAGE_SIZE = 64; +#endif + +static inline bool esp_rtc_user_mem_read(uint32_t index, uint32_t *dest) { + if (index >= ESP_RTC_USER_MEM_SIZE_WORDS) { + return false; + } + *dest = ESP_RTC_USER_MEM[index]; // NOLINT(performance-no-int-to-ptr) + return true; +} + +static inline bool esp_rtc_user_mem_write(uint32_t index, uint32_t value) { + if (index >= ESP_RTC_USER_MEM_SIZE_WORDS) { + return false; + } + if (index < 32 && s_prevent_write) { + return false; + } + + auto *ptr = &ESP_RTC_USER_MEM[index]; // NOLINT(performance-no-int-to-ptr) + *ptr = value; + return true; +} + +extern "C" uint32_t _SPIFFS_end; // NOLINT + +static const uint32_t get_esp8266_flash_sector() { + union { + uint32_t *ptr; + uint32_t uint; + } data{}; + data.ptr = &_SPIFFS_end; + return (data.uint - 0x40200000) / SPI_FLASH_SEC_SIZE; +} +static const uint32_t get_esp8266_flash_address() { return get_esp8266_flash_sector() * SPI_FLASH_SEC_SIZE; } + +template uint32_t calculate_crc(It first, It last, uint32_t type) { + uint32_t crc = type; + while (first != last) { + crc ^= (*first++ * 2654435769UL) >> 1; + } + return crc; +} + +static bool safe_flash() { + if (!s_flash_dirty) + return true; + + ESP_LOGVV(TAG, "Saving preferences to flash..."); + SpiFlashOpResult erase_res, write_res = SPI_FLASH_RESULT_OK; + { + InterruptLock lock; + erase_res = spi_flash_erase_sector(get_esp8266_flash_sector()); + if (erase_res == SPI_FLASH_RESULT_OK) { + write_res = spi_flash_write(get_esp8266_flash_address(), s_flash_storage, ESP8266_FLASH_STORAGE_SIZE * 4); + } + } + if (erase_res != SPI_FLASH_RESULT_OK) { + ESP_LOGV(TAG, "Erase ESP8266 flash failed!"); + return false; + } + if (write_res != SPI_FLASH_RESULT_OK) { + ESP_LOGV(TAG, "Write ESP8266 flash failed!"); + return false; + } + + s_flash_dirty = false; + return true; +} + +static bool safe_to_flash(size_t offset, const uint32_t *data, size_t len) { + for (uint32_t i = 0; i < len; i++) { + uint32_t j = offset + i; + if (j >= ESP8266_FLASH_STORAGE_SIZE) + return false; + uint32_t v = data[i]; + uint32_t *ptr = &s_flash_storage[j]; + if (*ptr != v) + s_flash_dirty = true; + *ptr = v; + } + return safe_flash(); +} + +static bool load_from_flash(size_t offset, uint32_t *data, size_t len) { + for (size_t i = 0; i < len; i++) { + uint32_t j = offset + i; + if (j >= ESP8266_FLASH_STORAGE_SIZE) + return false; + data[i] = s_flash_storage[j]; + } + return true; +} + +static bool safe_to_rtc(size_t offset, const uint32_t *data, size_t len) { + for (uint32_t i = 0; i < len; i++) + if (!esp_rtc_user_mem_write(offset + i, data[i])) + return false; + return true; +} + +static bool load_from_rtc(size_t offset, uint32_t *data, size_t len) { + for (uint32_t i = 0; i < len; i++) + if (!esp_rtc_user_mem_read(offset + i, &data[i])) + return false; + return true; +} + +class ESP8266PreferenceBackend : public ESPPreferenceBackend { + public: + size_t offset = 0; + uint32_t type = 0; + bool in_flash = false; + size_t length_words = 0; + + bool save(const uint8_t *data, size_t len) override { + if ((len + 3) / 4 != length_words) { + return false; + } + std::vector buffer; + buffer.resize(length_words + 1); + memcpy(buffer.data(), data, len); + buffer[buffer.size() - 1] = calculate_crc(buffer.begin(), buffer.end() - 1, type); + + if (in_flash) { + return safe_to_flash(offset, buffer.data(), buffer.size()); + } else { + return safe_to_rtc(offset, buffer.data(), buffer.size()); + } + } + bool load(uint8_t *data, size_t len) override { + if ((len + 3) / 4 != length_words) { + return false; + } + std::vector buffer; + buffer.resize(length_words + 1); + bool ret; + if (in_flash) { + ret = load_from_flash(offset, buffer.data(), buffer.size()); + } else { + ret = load_from_rtc(offset, buffer.data(), buffer.size()); + } + if (!ret) + return false; + + uint32_t crc = calculate_crc(buffer.begin(), buffer.end() - 1, type); + return buffer[buffer.size() - 1] == crc; + } +}; + +class ESP8266Preferences : public ESPPreferences { + public: + uint32_t current_offset = 0; + uint32_t current_flash_offset = 0; // in words + + void setup() { + s_flash_storage = new uint32_t[ESP8266_FLASH_STORAGE_SIZE]; // NOLINT + ESP_LOGVV(TAG, "Loading preferences from flash..."); + + { + InterruptLock lock; + spi_flash_read(get_esp8266_flash_address(), s_flash_storage, ESP8266_FLASH_STORAGE_SIZE * 4); + } + } + + ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash) override { + uint32_t length_words = (length + 3) / 4; + if (in_flash) { + uint32_t start = current_flash_offset; + uint32_t end = start + length_words + 1; + if (end > ESP8266_FLASH_STORAGE_SIZE) + return {}; + auto *pref = new ESP8266PreferenceBackend(); // NOLINT(cppcoreguidelines-owning-memory) + pref->offset = start; + pref->type = type; + pref->length_words = length_words; + pref->in_flash = true; + current_flash_offset = end; + return {pref}; + } + + uint32_t start = current_offset; + uint32_t end = start + length_words + 1; + bool in_normal = start < 96; + // Normal: offset 0-95 maps to RTC offset 32 - 127, + // Eboot: offset 96-127 maps to RTC offset 0 - 31 words + if (in_normal && end > 96) { + // start is in normal but end is not -> switch to Eboot + current_offset = start = 96; + end = start + length_words + 1; + in_normal = false; + } + + if (end > 128) { + // Doesn't fit in data, return uninitialized preference obj. + return {}; + } + + uint32_t rtc_offset = in_normal ? start + 32 : start - 96; + + auto *pref = new ESP8266PreferenceBackend(); // NOLINT(cppcoreguidelines-owning-memory) + pref->offset = rtc_offset; + pref->type = type; + pref->length_words = length_words; + pref->in_flash = false; + current_offset += length_words + 1; + return pref; + } + + ESPPreferenceObject make_preference(size_t length, uint32_t type) override { +#ifdef USE_ESP8266_PREFERENCES_FLASH + return make_preference(length, type, true); +#else + return make_preference(length, type, false); +#endif + } +}; + +void setup_preferences() { + auto *pref = new ESP8266Preferences(); // NOLINT(cppcoreguidelines-owning-memory) + pref->setup(); + global_preferences = pref; +} +void preferences_prevent_write(bool prevent) { s_prevent_write = prevent; } + +} // namespace esp8266 + +ESPPreferences *global_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + +} // namespace esphome + +#endif // USE_ESP8266 diff --git a/esphome/components/esp8266/preferences.h b/esphome/components/esp8266/preferences.h new file mode 100644 index 0000000000..edec915794 --- /dev/null +++ b/esphome/components/esp8266/preferences.h @@ -0,0 +1,14 @@ +#pragma once + +#ifdef USE_ESP8266 + +namespace esphome { +namespace esp8266 { + +void setup_preferences(); +void preferences_prevent_write(bool prevent); + +} // namespace esp8266 +} // namespace esphome + +#endif // USE_ESP8266 diff --git a/esphome/components/esp8266_pwm/esp8266_pwm.cpp b/esphome/components/esp8266_pwm/esp8266_pwm.cpp index 186653acd1..e472edf2a7 100644 --- a/esphome/components/esp8266_pwm/esp8266_pwm.cpp +++ b/esphome/components/esp8266_pwm/esp8266_pwm.cpp @@ -1,11 +1,11 @@ -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #include "esp8266_pwm.h" #include "esphome/core/macros.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" -#if defined(ARDUINO_ARCH_ESP8266) && ARDUINO_VERSION_CODE < VERSION_CODE(2, 4, 0) +#if defined(USE_ESP8266) && ARDUINO_VERSION_CODE < VERSION_CODE(2, 4, 0) #error ESP8266 PWM requires at least arduino_version 2.4.0 #endif diff --git a/esphome/components/esp8266_pwm/esp8266_pwm.h b/esphome/components/esp8266_pwm/esp8266_pwm.h index 1bf875dc43..79530aacd4 100644 --- a/esphome/components/esp8266_pwm/esp8266_pwm.h +++ b/esphome/components/esp8266_pwm/esp8266_pwm.h @@ -1,9 +1,9 @@ #pragma once -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/core/automation.h" #include "esphome/components/output/float_output.h" @@ -12,7 +12,7 @@ namespace esp8266_pwm { class ESP8266PWM : public output::FloatOutput, public Component { public: - void set_pin(GPIOPin *pin) { pin_ = pin; } + void set_pin(InternalGPIOPin *pin) { pin_ = pin; } void set_frequency(float frequency) { this->frequency_ = frequency; } /// Dynamically update frequency void update_frequency(float frequency) override { @@ -29,7 +29,7 @@ class ESP8266PWM : public output::FloatOutput, public Component { protected: void write_state(float state) override; - GPIOPin *pin_; + InternalGPIOPin *pin_; float frequency_{1000.0}; /// Cache last output level for dynamic frequency updating float last_output_{0.0}; diff --git a/esphome/components/esp8266_pwm/output.py b/esphome/components/esp8266_pwm/output.py index 770d400013..3d52e5af16 100644 --- a/esphome/components/esp8266_pwm/output.py +++ b/esphome/components/esp8266_pwm/output.py @@ -7,10 +7,9 @@ from esphome.const import ( CONF_ID, CONF_NUMBER, CONF_PIN, - ESP_PLATFORM_ESP8266, ) -ESP_PLATFORMS = [ESP_PLATFORM_ESP8266] +DEPENDENCIES = ["esp8266"] def valid_pwm_pin(value): diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index 95b7e40151..384fb3dbfb 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -1,7 +1,6 @@ from esphome import pins import esphome.config_validation as cv import esphome.codegen as cg -from esphome.components.network import add_mdns_library from esphome.const import ( CONF_DOMAIN, CONF_ID, @@ -9,17 +8,16 @@ from esphome.const import ( CONF_STATIC_IP, CONF_TYPE, CONF_USE_ADDRESS, - ESP_PLATFORM_ESP32, - CONF_ENABLE_MDNS, CONF_GATEWAY, CONF_SUBNET, CONF_DNS1, CONF_DNS2, ) from esphome.core import CORE, coroutine_with_priority +from esphome.components.network import IPAddress CONFLICTS_WITH = ["wifi"] -ESP_PLATFORMS = [ESP_PLATFORM_ESP32] +DEPENDENCIES = ["esp32"] AUTO_LOAD = ["network"] ethernet_ns = cg.esphome_ns.namespace("ethernet") @@ -55,7 +53,6 @@ MANUAL_IP_SCHEMA = cv.Schema( ) EthernetComponent = ethernet_ns.class_("EthernetComponent", cg.Component) -IPAddress = cg.global_ns.class_("IPAddress") ManualIP = ethernet_ns.struct("ManualIP") @@ -74,20 +71,24 @@ CONFIG_SCHEMA = cv.All( { cv.GenerateID(): cv.declare_id(EthernetComponent), cv.Required(CONF_TYPE): cv.enum(ETHERNET_TYPES, upper=True), - cv.Required(CONF_MDC_PIN): pins.output_pin, - cv.Required(CONF_MDIO_PIN): pins.input_output_pin, + cv.Required(CONF_MDC_PIN): pins.internal_gpio_output_pin_number, + cv.Required(CONF_MDIO_PIN): pins.internal_gpio_output_pin_number, cv.Optional(CONF_CLK_MODE, default="GPIO0_IN"): cv.enum( CLK_MODES, upper=True, space="_" ), cv.Optional(CONF_PHY_ADDR, default=0): cv.int_range(min=0, max=31), cv.Optional(CONF_POWER_PIN): pins.gpio_output_pin_schema, cv.Optional(CONF_MANUAL_IP): MANUAL_IP_SCHEMA, - cv.Optional(CONF_ENABLE_MDNS, default=True): cv.boolean, cv.Optional(CONF_DOMAIN, default=".local"): cv.domain_name, cv.Optional(CONF_USE_ADDRESS): cv.string_strict, + cv.Optional("enable_mdns"): cv.invalid( + "This option has been removed. Please use the [disabled] option under the " + "new mdns component instead." + ), } ).extend(cv.COMPONENT_SCHEMA), _validate, + cv.only_with_arduino, ) @@ -122,6 +123,3 @@ async def to_code(config): cg.add(var.set_manual_ip(manual_ip(config[CONF_MANUAL_IP]))) cg.add_define("USE_ETHERNET") - - if config[CONF_ENABLE_MDNS]: - add_mdns_library() diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index cad76ae476..d03211deaf 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -3,7 +3,7 @@ #include "esphome/core/util.h" #include "esphome/core/application.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32_FRAMEWORK_ARDUINO #include #include @@ -75,10 +75,6 @@ void EthernetComponent::setup() { ESPHL_ERROR_CHECK(err, "ETH init error"); err = esp_eth_enable(); ESPHL_ERROR_CHECK(err, "ETH enable error"); - -#ifdef USE_MDNS - network_setup_mdns(); -#endif } void EthernetComponent::loop() { const uint32_t now = millis(); @@ -118,8 +114,6 @@ void EthernetComponent::loop() { } break; } - - network_tick_mdns(); } void EthernetComponent::dump_config() { ESP_LOGCONFIG(TAG, "Ethernet:"); @@ -131,10 +125,10 @@ void EthernetComponent::dump_config() { } float EthernetComponent::get_setup_priority() const { return setup_priority::WIFI; } bool EthernetComponent::can_proceed() { return this->is_connected(); } -IPAddress EthernetComponent::get_ip_address() { +network::IPAddress EthernetComponent::get_ip_address() { tcpip_adapter_ip_info_t ip; tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_ETH, &ip); - return IPAddress(ip.ip.addr); + return {ip.ip.addr}; } void EthernetComponent::on_wifi_event_(system_event_id_t event, system_event_info_t info) { @@ -229,10 +223,10 @@ bool EthernetComponent::is_connected() { return this->state_ == EthernetComponen void EthernetComponent::dump_connect_params_() { tcpip_adapter_ip_info_t ip; tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_ETH, &ip); - ESP_LOGCONFIG(TAG, " IP Address: %s", IPAddress(ip.ip.addr).toString().c_str()); + ESP_LOGCONFIG(TAG, " IP Address: %s", network::IPAddress(ip.ip.addr).str().c_str()); ESP_LOGCONFIG(TAG, " Hostname: '%s'", App.get_name().c_str()); - ESP_LOGCONFIG(TAG, " Subnet: %s", IPAddress(ip.netmask.addr).toString().c_str()); - ESP_LOGCONFIG(TAG, " Gateway: %s", IPAddress(ip.gw.addr).toString().c_str()); + ESP_LOGCONFIG(TAG, " Subnet: %s", network::IPAddress(ip.netmask.addr).str().c_str()); + ESP_LOGCONFIG(TAG, " Gateway: %s", network::IPAddress(ip.gw.addr).str().c_str()); #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(3, 3, 4) const ip_addr_t *dns_ip1 = dns_getserver(0); @@ -243,8 +237,8 @@ void EthernetComponent::dump_connect_params_() { ip_addr_t tmp_ip2 = dns_getserver(1); const ip_addr_t *dns_ip2 = &tmp_ip2; #endif - ESP_LOGCONFIG(TAG, " DNS1: %s", IPAddress(dns_ip1->u_addr.ip4.addr).toString().c_str()); - ESP_LOGCONFIG(TAG, " DNS2: %s", IPAddress(dns_ip2->u_addr.ip4.addr).toString().c_str()); + ESP_LOGCONFIG(TAG, " DNS1: %s", network::IPAddress(dns_ip1->u_addr.ip4.addr).str().c_str()); + ESP_LOGCONFIG(TAG, " DNS2: %s", network::IPAddress(dns_ip2->u_addr.ip4.addr).str().c_str()); uint8_t mac[6]; esp_eth_get_mac(mac); ESP_LOGCONFIG(TAG, " MAC Address: %02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); @@ -270,4 +264,4 @@ void EthernetComponent::set_use_address(const std::string &use_address) { this-> } // namespace ethernet } // namespace esphome -#endif +#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index dc8816ef86..3e0c798a0c 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -1,9 +1,10 @@ #pragma once -#include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#ifdef USE_ESP32_FRAMEWORK_ARDUINO -#ifdef ARDUINO_ARCH_ESP32 +#include "esphome/core/component.h" +#include "esphome/core/hal.h" +#include "esphome/components/network/ip_address.h" #include "esp_eth.h" #include @@ -19,11 +20,11 @@ enum EthernetType { }; struct ManualIP { - IPAddress static_ip; - IPAddress gateway; - IPAddress subnet; - IPAddress dns1; ///< The first DNS server. 0.0.0.0 for default. - IPAddress dns2; ///< The second DNS server. 0.0.0.0 for default. + network::IPAddress static_ip; + network::IPAddress gateway; + network::IPAddress subnet; + network::IPAddress dns1; ///< The first DNS server. 0.0.0.0 for default. + network::IPAddress dns2; ///< The second DNS server. 0.0.0.0 for default. }; enum class EthernetComponentState { @@ -50,7 +51,7 @@ class EthernetComponent : public Component { void set_clk_mode(eth_clock_mode_t clk_mode); void set_manual_ip(const ManualIP &manual_ip); - IPAddress get_ip_address(); + network::IPAddress get_ip_address(); std::string get_use_address() const; void set_use_address(const std::string &use_address); @@ -84,4 +85,4 @@ extern EthernetComponent *global_eth_component; } // namespace ethernet } // namespace esphome -#endif +#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/exposure_notifications/exposure_notifications.cpp b/esphome/components/exposure_notifications/exposure_notifications.cpp index 9db181fbee..3083cf429c 100644 --- a/esphome/components/exposure_notifications/exposure_notifications.cpp +++ b/esphome/components/exposure_notifications/exposure_notifications.cpp @@ -2,7 +2,7 @@ #include "esphome/core/log.h" #include "esphome/core/helpers.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace exposure_notifications { diff --git a/esphome/components/exposure_notifications/exposure_notifications.h b/esphome/components/exposure_notifications/exposure_notifications.h index 6b9f61b2a0..f7383c28d9 100644 --- a/esphome/components/exposure_notifications/exposure_notifications.h +++ b/esphome/components/exposure_notifications/exposure_notifications.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace exposure_notifications { diff --git a/esphome/components/ezo/ezo.cpp b/esphome/components/ezo/ezo.cpp index 90b1e4ace9..81597f3466 100644 --- a/esphome/components/ezo/ezo.cpp +++ b/esphome/components/ezo/ezo.cpp @@ -1,5 +1,6 @@ #include "ezo.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace ezo { @@ -24,7 +25,7 @@ void EZOSensor::update() { return; } uint8_t c = 'R'; - this->write_bytes_raw(&c, 1); + this->write(&c, 1); this->state_ |= EZO_STATE_WAIT; this->start_time_ = millis(); this->wait_time_ = 900; @@ -35,7 +36,7 @@ void EZOSensor::loop() { if (!(this->state_ & EZO_STATE_WAIT)) { if (this->state_ & EZO_STATE_SEND_TEMP) { int len = sprintf((char *) buf, "T,%0.3f", this->tempcomp_); - this->write_bytes_raw(buf, len); + this->write(buf, len); this->state_ = EZO_STATE_WAIT | EZO_STATE_WAIT_TEMP; this->start_time_ = millis(); this->wait_time_ = 300; diff --git a/esphome/components/fan/fan_state.cpp b/esphome/components/fan/fan_state.cpp index a57115beb4..921b8d57e7 100644 --- a/esphome/components/fan/fan_state.cpp +++ b/esphome/components/fan/fan_state.cpp @@ -27,7 +27,7 @@ struct FanStateRTCState { }; void FanState::setup() { - this->rtc_ = global_preferences.make_preference(this->get_object_id_hash()); + this->rtc_ = global_preferences->make_preference(this->get_object_id_hash()); FanStateRTCState recovered{}; if (!this->rtc_.load(&recovered)) return; diff --git a/esphome/components/fastled_base/fastled_light.cpp b/esphome/components/fastled_base/fastled_light.cpp index 15a5c8984c..486364d0c0 100644 --- a/esphome/components/fastled_base/fastled_light.cpp +++ b/esphome/components/fastled_base/fastled_light.cpp @@ -1,3 +1,5 @@ +#ifdef USE_ARDUINO + #include "fastled_light.h" #include "esphome/core/log.h" @@ -37,3 +39,5 @@ void FastLEDLightOutput::write_state(light::LightState *state) { } // namespace fastled_base } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/fastled_base/fastled_light.h b/esphome/components/fastled_base/fastled_light.h index d1c470599d..80840c3003 100644 --- a/esphome/components/fastled_base/fastled_light.h +++ b/esphome/components/fastled_base/fastled_light.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include "esphome/core/component.h" #include "esphome/core/helpers.h" #include "esphome/components/light/addressable_light.h" @@ -237,3 +239,5 @@ class FastLEDLightOutput : public light::AddressableLight { } // namespace fastled_base } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/fastled_clockless/light.py b/esphome/components/fastled_clockless/light.py index d437d01dcf..acf9488ae3 100644 --- a/esphome/components/fastled_clockless/light.py +++ b/esphome/components/fastled_clockless/light.py @@ -45,10 +45,11 @@ CONFIG_SCHEMA = cv.All( fastled_base.BASE_SCHEMA.extend( { cv.Required(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True), - cv.Required(CONF_PIN): pins.output_pin, + cv.Required(CONF_PIN): pins.internal_gpio_output_pin_number, } ), _validate, + cv.only_with_arduino, ) diff --git a/esphome/components/fastled_spi/light.py b/esphome/components/fastled_spi/light.py index d6ba0e8358..a729fc015a 100644 --- a/esphome/components/fastled_spi/light.py +++ b/esphome/components/fastled_spi/light.py @@ -24,13 +24,16 @@ CHIPSETS = [ "DOTSTAR", ] -CONFIG_SCHEMA = fastled_base.BASE_SCHEMA.extend( - { - cv.Required(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True), - cv.Required(CONF_DATA_PIN): pins.output_pin, - cv.Required(CONF_CLOCK_PIN): pins.output_pin, - cv.Optional(CONF_DATA_RATE): cv.frequency, - } +CONFIG_SCHEMA = cv.All( + fastled_base.BASE_SCHEMA.extend( + { + cv.Required(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True), + cv.Required(CONF_DATA_PIN): pins.internal_gpio_output_pin_number, + cv.Required(CONF_CLOCK_PIN): pins.internal_gpio_output_pin_number, + cv.Optional(CONF_DATA_RATE): cv.frequency, + } + ), + cv.only_with_arduino, ) diff --git a/esphome/components/fingerprint_grow/fingerprint_grow.cpp b/esphome/components/fingerprint_grow/fingerprint_grow.cpp index b0c0be59af..be17e29de3 100644 --- a/esphome/components/fingerprint_grow/fingerprint_grow.cpp +++ b/esphome/components/fingerprint_grow/fingerprint_grow.cpp @@ -15,7 +15,7 @@ void FingerprintGrowComponent::update() { } if (this->sensing_pin_ != nullptr) { - if (this->sensing_pin_->digital_read() == HIGH) { + if (this->sensing_pin_->digital_read()) { ESP_LOGV(TAG, "No touch sensing"); this->waiting_removal_ = false; return; diff --git a/esphome/components/globals/__init__.py b/esphome/components/globals/__init__.py index 9039d0d62e..97a7ba3d54 100644 --- a/esphome/components/globals/__init__.py +++ b/esphome/components/globals/__init__.py @@ -14,6 +14,7 @@ from esphome.core import coroutine_with_priority CODEOWNERS = ["@esphome/core"] globals_ns = cg.esphome_ns.namespace("globals") GlobalsComponent = globals_ns.class_("GlobalsComponent", cg.Component) +RestoringGlobalsComponent = globals_ns.class_("RestoringGlobalsComponent", cg.Component) GlobalVarSetAction = globals_ns.class_("GlobalVarSetAction", automation.Action) MULTI_CONF = True @@ -32,22 +33,25 @@ CONFIG_SCHEMA = cv.Schema( async def to_code(config): type_ = cg.RawExpression(config[CONF_TYPE]) template_args = cg.TemplateArguments(type_) - res_type = GlobalsComponent.template(template_args) + restore = config[CONF_RESTORE_VALUE] + + type = RestoringGlobalsComponent if restore else GlobalsComponent + res_type = type.template(template_args) initial_value = None if CONF_INITIAL_VALUE in config: initial_value = cg.RawExpression(config[CONF_INITIAL_VALUE]) - rhs = GlobalsComponent.new(template_args, initial_value) + rhs = type.new(template_args, initial_value) glob = cg.Pvariable(config[CONF_ID], rhs, res_type) await cg.register_component(glob, config) - if config[CONF_RESTORE_VALUE]: + if restore: value = config[CONF_ID].id if isinstance(value, str): value = value.encode() hash_ = int(hashlib.md5(value).hexdigest()[:8], 16) - cg.add(glob.set_restore_value(hash_)) + cg.add(glob.set_name_hash(hash_)) @automation.register_action( diff --git a/esphome/components/globals/globals_component.h b/esphome/components/globals/globals_component.h index 397c55f6c4..b39c5f404b 100644 --- a/esphome/components/globals/globals_component.h +++ b/esphome/components/globals/globals_component.h @@ -3,6 +3,7 @@ #include "esphome/core/component.h" #include "esphome/core/automation.h" #include "esphome/core/helpers.h" +#include namespace esphome { namespace globals { @@ -16,37 +17,46 @@ template class GlobalsComponent : public Component { memcpy(this->value_, initial_value.data(), sizeof(T)); } + T &value() { return this->value_; } + void setup() override {} + + protected: + T value_{}; +}; + +template class RestoringGlobalsComponent : public Component { + public: + using value_type = T; + explicit RestoringGlobalsComponent() = default; + explicit RestoringGlobalsComponent(T initial_value) : value_(initial_value) {} + explicit RestoringGlobalsComponent( + std::array::type, std::extent::value> initial_value) { + memcpy(this->value_, initial_value.data(), sizeof(T)); + } + T &value() { return this->value_; } void setup() override { - if (this->restore_value_) { - this->rtc_ = global_preferences.make_preference(1944399030U ^ this->name_hash_); - this->rtc_.load(&this->value_); - } + this->rtc_ = global_preferences->make_preference(1944399030U ^ this->name_hash_); + this->rtc_.load(&this->value_); memcpy(&this->prev_value_, &this->value_, sizeof(T)); } float get_setup_priority() const override { return setup_priority::HARDWARE; } void loop() override { - if (this->restore_value_) { - int diff = memcmp(&this->value_, &this->prev_value_, sizeof(T)); - if (diff != 0) { - this->rtc_.save(&this->value_); - memcpy(&this->prev_value_, &this->value_, sizeof(T)); - } + int diff = memcmp(&this->value_, &this->prev_value_, sizeof(T)); + if (diff != 0) { + this->rtc_.save(&this->value_); + memcpy(&this->prev_value_, &this->value_, sizeof(T)); } } - void set_restore_value(uint32_t name_hash) { - this->restore_value_ = true; - this->name_hash_ = name_hash; - } + void set_name_hash(uint32_t name_hash) { this->name_hash_ = name_hash; } protected: T value_{}; T prev_value_{}; - bool restore_value_{false}; uint32_t name_hash_{}; ESPPreferenceObject rtc_; }; diff --git a/esphome/components/gpio/binary_sensor/gpio_binary_sensor.h b/esphome/components/gpio/binary_sensor/gpio_binary_sensor.h index f655207243..33a173fe2e 100644 --- a/esphome/components/gpio/binary_sensor/gpio_binary_sensor.h +++ b/esphome/components/gpio/binary_sensor/gpio_binary_sensor.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/binary_sensor/binary_sensor.h" namespace esphome { diff --git a/esphome/components/gpio/output/gpio_binary_output.h b/esphome/components/gpio/output/gpio_binary_output.h index 0a7dfb46e2..6b72c61c0f 100644 --- a/esphome/components/gpio/output/gpio_binary_output.h +++ b/esphome/components/gpio/output/gpio_binary_output.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/output/binary_output.h" namespace esphome { diff --git a/esphome/components/gpio/switch/gpio_switch.h b/esphome/components/gpio/switch/gpio_switch.h index b39e070c85..99f8060efa 100644 --- a/esphome/components/gpio/switch/gpio_switch.h +++ b/esphome/components/gpio/switch/gpio_switch.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/switch/switch.h" namespace esphome { diff --git a/esphome/components/gps/__init__.py b/esphome/components/gps/__init__.py index 9d323e6e4d..e485373175 100644 --- a/esphome/components/gps/__init__.py +++ b/esphome/components/gps/__init__.py @@ -28,7 +28,7 @@ GPSListener = gps_ns.class_("GPSListener") CONF_GPS_ID = "gps_id" MULTI_CONF = True -CONFIG_SCHEMA = ( +CONFIG_SCHEMA = cv.All( cv.Schema( { cv.GenerateID(): cv.declare_id(GPS), @@ -64,7 +64,8 @@ CONFIG_SCHEMA = ( } ) .extend(cv.polling_component_schema("20s")) - .extend(uart.UART_DEVICE_SCHEMA) + .extend(uart.UART_DEVICE_SCHEMA), + cv.only_with_arduino, ) FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema("gps", require_rx=True) diff --git a/esphome/components/gps/gps.cpp b/esphome/components/gps/gps.cpp index 1e8ca94e9e..8c924d629c 100644 --- a/esphome/components/gps/gps.cpp +++ b/esphome/components/gps/gps.cpp @@ -1,3 +1,5 @@ +#ifdef USE_ARDUINO + #include "gps.h" #include "esphome/core/log.h" @@ -69,3 +71,5 @@ void GPS::loop() { } // namespace gps } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/gps/gps.h b/esphome/components/gps/gps.h index 50dd476ae3..40cda145ca 100644 --- a/esphome/components/gps/gps.h +++ b/esphome/components/gps/gps.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include "esphome/core/component.h" #include "esphome/components/uart/uart.h" #include "esphome/components/sensor/sensor.h" @@ -63,3 +65,5 @@ class GPS : public PollingComponent, public uart::UARTDevice { } // namespace gps } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/gps/time/gps_time.cpp b/esphome/components/gps/time/gps_time.cpp index 5352c7e059..e46f24ba8e 100644 --- a/esphome/components/gps/time/gps_time.cpp +++ b/esphome/components/gps/time/gps_time.cpp @@ -1,3 +1,5 @@ +#ifdef USE_ARDUINO + #include "gps_time.h" #include "esphome/core/log.h" @@ -32,3 +34,5 @@ void GPSTime::from_tiny_gps_(TinyGPSPlus &tiny_gps) { } // namespace gps } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/gps/time/gps_time.h b/esphome/components/gps/time/gps_time.h index a1f69a7130..d0d1db83b5 100644 --- a/esphome/components/gps/time/gps_time.h +++ b/esphome/components/gps/time/gps_time.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include "esphome/core/component.h" #include "esphome/components/time/real_time_clock.h" #include "esphome/components/gps/gps.h" @@ -22,3 +24,5 @@ class GPSTime : public time::RealTimeClock, public GPSListener { } // namespace gps } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/graph/graph.cpp b/esphome/components/graph/graph.cpp index 6ede553fdb..ff736b7cb7 100644 --- a/esphome/components/graph/graph.cpp +++ b/esphome/components/graph/graph.cpp @@ -2,7 +2,7 @@ #include "esphome/components/display/display_buffer.h" #include "esphome/core/color.h" #include "esphome/core/log.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include #include #include // std::cout, std::fixed @@ -34,12 +34,12 @@ void HistoryData::take_sample(float data) { this->count_ = (this->count_ + 1) % this->length_; ESP_LOGV(TAG, "Updating trace with value: %f", data); } - if (!isnan(data)) { + if (!std::isnan(data)) { // Recalc recent max/min this->recent_min_ = data; this->recent_max_ = data; for (int i = 0; i < this->length_; i++) { - if (!isnan(this->samples_[i])) { + if (!std::isnan(this->samples_[i])) { if (this->recent_max_ < this->samples_[i]) this->recent_max_ = this->samples_[i]; if (this->recent_min_ > this->samples_[i]) @@ -70,15 +70,15 @@ void Graph::draw(DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Colo for (auto *trace : traces_) { float mx = trace->get_tracedata()->get_recent_max(); float mn = trace->get_tracedata()->get_recent_min(); - if (isnan(ymax) || (ymax < mx)) + if (std::isnan(ymax) || (ymax < mx)) ymax = mx; - if (isnan(ymin) || (ymin > mn)) + if (std::isnan(ymin) || (ymin > mn)) ymin = mn; } // Adjust if manually overridden - if (!isnan(this->min_value_)) + if (!std::isnan(this->min_value_)) ymin = this->min_value_; - if (!isnan(this->max_value_)) + if (!std::isnan(this->max_value_)) ymax = this->max_value_; float yrange = ymax - ymin; @@ -89,20 +89,20 @@ void Graph::draw(DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Colo for (int16_t i = 0; i < this->width_; i++) { for (auto *trace : traces_) { float v = trace->get_tracedata()->get_value(i); - if (!isnan(v)) { + if (!std::isnan(v)) { if ((v - mn) > this->max_range_) break; if ((mx - v) > this->max_range_) break; - if (isnan(mx) || (v > mx)) + if (std::isnan(mx) || (v > mx)) mx = v; - if (isnan(mn) || (v < mn)) + if (std::isnan(mn) || (v < mn)) mn = v; } } } yrange = this->max_range_; - if (!isnan(mn)) { + if (!std::isnan(mn)) { ymin = mn; ymax = ymin + this->max_range_; } @@ -110,7 +110,7 @@ void Graph::draw(DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Colo } float y_per_div = this->min_range_; - if (!isnan(this->gridspacing_y_)) { + if (!std::isnan(this->gridspacing_y_)) { y_per_div = this->gridspacing_y_; } // Restrict drawing too many gridlines @@ -129,7 +129,7 @@ void Graph::draw(DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Colo yrange = ymax - ymin; /// Draw grid - if (!isnan(this->gridspacing_y_)) { + if (!std::isnan(this->gridspacing_y_)) { for (int y = yn; y <= ym; y++) { int16_t py = (int16_t) roundf((this->height_ - 1) * (1.0 - (float) (y - yn) / (ym - yn))); for (int x = 0; x < this->width_; x += 2) { @@ -137,7 +137,7 @@ void Graph::draw(DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Colo } } } - if (!isnan(this->gridspacing_x_) && (this->gridspacing_x_ > 0)) { + if (!std::isnan(this->gridspacing_x_) && (this->gridspacing_x_ > 0)) { int n = this->duration_ / this->gridspacing_x_; // Restrict drawing too many gridlines if (n > 20) { @@ -160,7 +160,7 @@ void Graph::draw(DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Colo uint16_t thick = trace->get_line_thickness(); for (int16_t i = 0; i < this->width_; i++) { float v = (trace->get_tracedata()->get_value(i) - ymin) / yrange; - if (!isnan(v) && (thick > 0)) { + if (!std::isnan(v) && (thick > 0)) { int16_t x = this->width_ - 1 - i; uint8_t b = (i % (thick * LineType::PATTERN_LENGTH)) / thick; if (((uint8_t) trace->get_line_type() & (1 << b)) == (1 << b)) { diff --git a/esphome/components/hdc1080/hdc1080.cpp b/esphome/components/hdc1080/hdc1080.cpp index 507ac77a28..60e8943e67 100644 --- a/esphome/components/hdc1080/hdc1080.cpp +++ b/esphome/components/hdc1080/hdc1080.cpp @@ -1,5 +1,6 @@ #include "hdc1080.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace hdc1080 { @@ -36,18 +37,30 @@ void HDC1080Component::dump_config() { } void HDC1080Component::update() { uint16_t raw_temp; - if (!this->read_byte_16(HDC1080_CMD_TEMPERATURE, &raw_temp, 20)) { + if (this->write(&HDC1080_CMD_TEMPERATURE, 1) != i2c::ERROR_OK) { this->status_set_warning(); return; } + delay(20); + if (this->read(reinterpret_cast(&raw_temp), 2) != i2c::ERROR_OK) { + this->status_set_warning(); + return; + } + raw_temp = i2c::i2ctohs(raw_temp); float temp = raw_temp * 0.0025177f - 40.0f; // raw * 2^-16 * 165 - 40 this->temperature_->publish_state(temp); uint16_t raw_humidity; - if (!this->read_byte_16(HDC1080_CMD_HUMIDITY, &raw_humidity, 20)) { + if (this->write(&HDC1080_CMD_HUMIDITY, 1) != i2c::ERROR_OK) { this->status_set_warning(); return; } + delay(20); + if (this->read(reinterpret_cast(&raw_humidity), 2) != i2c::ERROR_OK) { + this->status_set_warning(); + return; + } + raw_humidity = i2c::i2ctohs(raw_humidity); float humidity = raw_humidity * 0.001525879f; // raw * 2^-16 * 100 this->humidity_->publish_state(humidity); diff --git a/esphome/components/hlw8012/hlw8012.h b/esphome/components/hlw8012/hlw8012.h index 52fb03c020..5060957cf1 100644 --- a/esphome/components/hlw8012/hlw8012.h +++ b/esphome/components/hlw8012/hlw8012.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/pulse_counter/pulse_counter_sensor.h" @@ -31,8 +31,8 @@ class HLW8012Component : public PollingComponent { void set_current_resistor(float current_resistor) { current_resistor_ = current_resistor; } void set_voltage_divider(float voltage_divider) { voltage_divider_ = voltage_divider; } void set_sel_pin(GPIOPin *sel_pin) { sel_pin_ = sel_pin; } - void set_cf_pin(GPIOPin *cf_pin) { cf_pin_ = cf_pin; } - void set_cf1_pin(GPIOPin *cf1_pin) { cf1_pin_ = cf1_pin; } + void set_cf_pin(InternalGPIOPin *cf_pin) { cf_pin_ = cf_pin; } + void set_cf1_pin(InternalGPIOPin *cf1_pin) { cf1_pin_ = cf1_pin; } void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; } void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; } void set_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; } @@ -48,9 +48,9 @@ class HLW8012Component : public PollingComponent { HLW8012SensorModels sensor_model_{HLW8012_SENSOR_MODEL_HLW8012}; uint64_t cf_total_pulses_{0}; GPIOPin *sel_pin_; - GPIOPin *cf_pin_; + InternalGPIOPin *cf_pin_; pulse_counter::PulseCounterStorage cf_store_; - GPIOPin *cf1_pin_; + InternalGPIOPin *cf1_pin_; pulse_counter::PulseCounterStorage cf1_store_; sensor::Sensor *voltage_sensor_{nullptr}; sensor::Sensor *current_sensor_{nullptr}; diff --git a/esphome/components/hlw8012/sensor.py b/esphome/components/hlw8012/sensor.py index 11e9c8e4d4..033cccc3d4 100644 --- a/esphome/components/hlw8012/sensor.py +++ b/esphome/components/hlw8012/sensor.py @@ -50,12 +50,8 @@ CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(HLW8012Component), cv.Required(CONF_SEL_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_CF_PIN): cv.All( - pins.internal_gpio_input_pullup_pin_schema, pins.validate_has_interrupt - ), - cv.Required(CONF_CF1_PIN): cv.All( - pins.internal_gpio_input_pullup_pin_schema, pins.validate_has_interrupt - ), + cv.Required(CONF_CF_PIN): cv.All(pins.internal_gpio_input_pullup_pin_schema), + cv.Required(CONF_CF1_PIN): cv.All(pins.internal_gpio_input_pullup_pin_schema), cv.Optional(CONF_VOLTAGE): sensor.sensor_schema( unit_of_measurement=UNIT_VOLT, accuracy_decimals=1, diff --git a/esphome/components/hm3301/abstract_aqi_calculator.h b/esphome/components/hm3301/abstract_aqi_calculator.h index f2573ff108..fb41b921d9 100644 --- a/esphome/components/hm3301/abstract_aqi_calculator.h +++ b/esphome/components/hm3301/abstract_aqi_calculator.h @@ -1,5 +1,8 @@ #pragma once +#ifdef USE_ARDUINO +#include + namespace esphome { namespace hm3301 { @@ -10,3 +13,5 @@ class AbstractAQICalculator { } // namespace hm3301 } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/hm3301/aqi_calculator.h b/esphome/components/hm3301/aqi_calculator.h index 627ee686fc..1410eac72b 100644 --- a/esphome/components/hm3301/aqi_calculator.h +++ b/esphome/components/hm3301/aqi_calculator.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include "abstract_aqi_calculator.h" namespace esphome { @@ -46,3 +48,5 @@ class AQICalculator : public AbstractAQICalculator { } // namespace hm3301 } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/hm3301/aqi_calculator_factory.h b/esphome/components/hm3301/aqi_calculator_factory.h index 55608b6e51..3c6f9709b6 100644 --- a/esphome/components/hm3301/aqi_calculator_factory.h +++ b/esphome/components/hm3301/aqi_calculator_factory.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include "caqi_calculator.h" #include "aqi_calculator.h" @@ -27,3 +29,5 @@ class AQICalculatorFactory { } // namespace hm3301 } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/hm3301/caqi_calculator.h b/esphome/components/hm3301/caqi_calculator.h index 403bac2713..51158454d0 100644 --- a/esphome/components/hm3301/caqi_calculator.h +++ b/esphome/components/hm3301/caqi_calculator.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include "esphome/core/log.h" #include "abstract_aqi_calculator.h" @@ -52,3 +54,5 @@ class CAQICalculator : public AbstractAQICalculator { } // namespace hm3301 } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/hm3301/hm3301.cpp b/esphome/components/hm3301/hm3301.cpp index 96c1ec0ee9..759157f330 100644 --- a/esphome/components/hm3301/hm3301.cpp +++ b/esphome/components/hm3301/hm3301.cpp @@ -1,3 +1,5 @@ +#ifdef USE_ARDUINO + #include "esphome/core/log.h" #include "hm3301.h" @@ -102,3 +104,5 @@ uint16_t HM3301Component::get_sensor_value_(const uint8_t *data, uint8_t i) { } // namespace hm3301 } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/hm3301/hm3301.h b/esphome/components/hm3301/hm3301.h index b024f93719..61bbf7e4ab 100644 --- a/esphome/components/hm3301/hm3301.h +++ b/esphome/components/hm3301/hm3301.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include "esphome/core/component.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/i2c/i2c.h" @@ -48,3 +50,5 @@ class HM3301Component : public PollingComponent, public i2c::I2CDevice { } // namespace hm3301 } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/hm3301/sensor.py b/esphome/components/hm3301/sensor.py index 2c3c2f7221..976a0488e1 100644 --- a/esphome/components/hm3301/sensor.py +++ b/esphome/components/hm3301/sensor.py @@ -84,6 +84,7 @@ CONFIG_SCHEMA = cv.All( .extend(cv.polling_component_schema("60s")) .extend(i2c.i2c_device_schema(0x40)), _validate, + cv.only_with_arduino, ) diff --git a/esphome/components/http_request/__init__.py b/esphome/components/http_request/__init__.py index eea2f0d407..a48b3c0acb 100644 --- a/esphome/components/http_request/__init__.py +++ b/esphome/components/http_request/__init__.py @@ -2,7 +2,6 @@ import urllib.parse as urlparse import esphome.codegen as cg import esphome.config_validation as cv -import esphome.final_validate as fv from esphome import automation from esphome.const import ( CONF_ID, @@ -12,7 +11,7 @@ from esphome.const import ( CONF_URL, CONF_ESP8266_DISABLE_SSL_SUPPORT, ) -from esphome.core import CORE, Lambda +from esphome.core import Lambda, CORE DEPENDENCIES = ["network"] AUTO_LOAD = ["json"] @@ -67,35 +66,24 @@ def validate_secure_url(config): return config -CONFIG_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.declare_id(HttpRequestComponent), - cv.Optional(CONF_USERAGENT, "ESPHome"): cv.string, - cv.Optional(CONF_TIMEOUT, default="5s"): cv.positive_time_period_milliseconds, - cv.SplitDefault(CONF_ESP8266_DISABLE_SSL_SUPPORT, esp8266=False): cv.All( - cv.only_on_esp8266, cv.boolean - ), - } -).extend(cv.COMPONENT_SCHEMA) - - -def validate_framework(value): - if not CORE.is_esp8266: - # only for ESP8266 - return - - framework_version = fv.get_arduino_framework_version() - if framework_version is None or framework_version == "dev": - return - - if framework_version < "2.5.1": - raise cv.Invalid( - "This component is not supported on arduino framework version below 2.5.1, ", - "please check esphome->arduino_version", - ) - - -FINAL_VALIDATE_SCHEMA = cv.Schema(validate_framework) +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(HttpRequestComponent), + cv.Optional(CONF_USERAGENT, "ESPHome"): cv.string, + cv.Optional( + CONF_TIMEOUT, default="5s" + ): cv.positive_time_period_milliseconds, + cv.SplitDefault(CONF_ESP8266_DISABLE_SSL_SUPPORT, esp8266=False): cv.All( + cv.only_on_esp8266, cv.boolean + ), + } + ).extend(cv.COMPONENT_SCHEMA), + cv.require_framework_version( + esp8266_arduino=cv.Version(2, 5, 1), + esp32_arduino=cv.Version(0, 0, 0), + ), +) async def to_code(config): diff --git a/esphome/components/http_request/http_request.cpp b/esphome/components/http_request/http_request.cpp index 3f5bf6da87..f88ee19e5c 100644 --- a/esphome/components/http_request/http_request.cpp +++ b/esphome/components/http_request/http_request.cpp @@ -1,3 +1,5 @@ +#ifdef USE_ARDUINO + #include "http_request.h" #include "esphome/core/macros.h" #include "esphome/core/log.h" @@ -28,10 +30,10 @@ void HttpRequestComponent::set_url(std::string url) { void HttpRequestComponent::send(const std::vector &response_triggers) { bool begin_status = false; const String url = this->url_.c_str(); -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 begin_status = this->client_.begin(url); #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #if ARDUINO_VERSION_CODE >= VERSION_CODE(2, 7, 0) this->client_.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); #elif ARDUINO_VERSION_CODE >= VERSION_CODE(2, 6, 0) @@ -79,7 +81,7 @@ void HttpRequestComponent::send(const std::vector ESP_LOGD(TAG, "HTTP Request completed; URL: %s; Code: %d", this->url_.c_str(), http_code); } -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 std::shared_ptr HttpRequestComponent::get_wifi_client_() { #ifdef USE_HTTP_REQUEST_ESP8266_HTTPS if (this->secure_) { @@ -111,3 +113,5 @@ const char *HttpRequestComponent::get_string() { } // namespace http_request } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/http_request/http_request.h b/esphome/components/http_request/http_request.h index fa29f8aa3a..511096e7fa 100644 --- a/esphome/components/http_request/http_request.h +++ b/esphome/components/http_request/http_request.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include "esphome/components/json/json_util.h" #include "esphome/core/automation.h" #include "esphome/core/component.h" @@ -9,10 +11,10 @@ #include #include -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #include #ifdef USE_HTTP_REQUEST_ESP8266_HTTPS #include @@ -54,7 +56,7 @@ class HttpRequestComponent : public Component { uint16_t timeout_{5000}; std::string body_; std::list
headers_; -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 std::shared_ptr wifi_client_; #ifdef USE_HTTP_REQUEST_ESP8266_HTTPS std::shared_ptr wifi_client_secure_; @@ -137,3 +139,5 @@ class HttpRequestResponseTrigger : public Trigger { } // namespace http_request } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/htu21d/htu21d.cpp b/esphome/components/htu21d/htu21d.cpp index a954b2ad59..b53284ae3f 100644 --- a/esphome/components/htu21d/htu21d.cpp +++ b/esphome/components/htu21d/htu21d.cpp @@ -1,5 +1,6 @@ #include "htu21d.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace htu21d { @@ -35,18 +36,30 @@ void HTU21DComponent::dump_config() { } void HTU21DComponent::update() { uint16_t raw_temperature; - if (!this->read_byte_16(HTU21D_REGISTER_TEMPERATURE, &raw_temperature, 50)) { + if (this->write(&HTU21D_REGISTER_TEMPERATURE, 1) != i2c::ERROR_OK) { this->status_set_warning(); return; } + delay(50); // NOLINT + if (this->read(reinterpret_cast(&raw_temperature), 2) != i2c::ERROR_OK) { + this->status_set_warning(); + return; + } + raw_temperature = i2c::i2ctohs(raw_temperature); float temperature = (float(raw_temperature & 0xFFFC)) * 175.72f / 65536.0f - 46.85f; uint16_t raw_humidity; - if (!this->read_byte_16(HTU21D_REGISTER_HUMIDITY, &raw_humidity, 50)) { + if (this->write(&HTU21D_REGISTER_HUMIDITY, 1) != i2c::ERROR_OK) { this->status_set_warning(); return; } + delay(50); // NOLINT + if (this->read(reinterpret_cast(&raw_humidity), 2) != i2c::ERROR_OK) { + this->status_set_warning(); + return; + } + raw_humidity = i2c::i2ctohs(raw_humidity); float humidity = (float(raw_humidity & 0xFFFC)) * 125.0f / 65536.0f - 6.0f; ESP_LOGD(TAG, "Got Temperature=%.1f°C Humidity=%.1f%%", temperature, humidity); diff --git a/esphome/components/hx711/hx711.h b/esphome/components/hx711/hx711.h index 91c8317ee5..9fef649b03 100644 --- a/esphome/components/hx711/hx711.h +++ b/esphome/components/hx711/hx711.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" namespace esphome { diff --git a/esphome/components/i2c/__init__.py b/esphome/components/i2c/__init__.py index f43729066d..46f0abacc6 100644 --- a/esphome/components/i2c/__init__.py +++ b/esphome/components/i2c/__init__.py @@ -2,30 +2,56 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.const import ( - CONF_CHANNEL, CONF_FREQUENCY, CONF_ID, + CONF_INPUT, + CONF_OUTPUT, CONF_SCAN, CONF_SCL, CONF_SDA, CONF_ADDRESS, CONF_I2C_ID, - CONF_MULTIPLEXER, ) -from esphome.core import coroutine_with_priority +from esphome.core import coroutine_with_priority, CORE CODEOWNERS = ["@esphome/core"] i2c_ns = cg.esphome_ns.namespace("i2c") -I2CComponent = i2c_ns.class_("I2CComponent", cg.Component) +I2CBus = i2c_ns.class_("I2CBus") +ArduinoI2CBus = i2c_ns.class_("ArduinoI2CBus", I2CBus, cg.Component) +IDFI2CBus = i2c_ns.class_("IDFI2CBus", I2CBus, cg.Component) I2CDevice = i2c_ns.class_("I2CDevice") -I2CMultiplexer = i2c_ns.class_("I2CMultiplexer", I2CDevice) + +CONF_SDA_PULLUP_ENABLED = "sda_pullup_enabled" +CONF_SCL_PULLUP_ENABLED = "scl_pullup_enabled" MULTI_CONF = True + + +def _bus_declare_type(value): + if CORE.using_arduino: + return cv.declare_id(ArduinoI2CBus)(value) + if CORE.using_esp_idf: + return cv.declare_id(IDFI2CBus)(value) + raise NotImplementedError + + +pin_with_input_and_output_support = cv.All( + pins.internal_gpio_pin_number({CONF_INPUT: True}), + pins.internal_gpio_pin_number({CONF_OUTPUT: True}), +) + + CONFIG_SCHEMA = cv.Schema( { - cv.GenerateID(): cv.declare_id(I2CComponent), - cv.Optional(CONF_SDA, default="SDA"): pins.input_pin, - cv.Optional(CONF_SCL, default="SCL"): pins.input_pin, + cv.GenerateID(): _bus_declare_type, + cv.Optional(CONF_SDA, default="SDA"): pin_with_input_and_output_support, + cv.SplitDefault(CONF_SDA_PULLUP_ENABLED, esp32_idf=True): cv.All( + cv.only_with_esp_idf, cv.boolean + ), + cv.Optional(CONF_SCL, default="SCL"): pin_with_input_and_output_support, + cv.SplitDefault(CONF_SCL_PULLUP_ENABLED, esp32_idf=True): cv.All( + cv.only_with_esp_idf, cv.boolean + ), cv.Optional(CONF_FREQUENCY, default="50kHz"): cv.All( cv.frequency, cv.Range(min=0, min_included=False) ), @@ -33,13 +59,6 @@ CONFIG_SCHEMA = cv.Schema( } ).extend(cv.COMPONENT_SCHEMA) -I2CMULTIPLEXER_SCHEMA = cv.Schema( - { - cv.Required(CONF_ID): cv.use_id(I2CMultiplexer), - cv.Required(CONF_CHANNEL): cv.uint8_t, - } -) - @coroutine_with_priority(1.0) async def to_code(config): @@ -48,10 +67,16 @@ async def to_code(config): await cg.register_component(var, config) cg.add(var.set_sda_pin(config[CONF_SDA])) + if CONF_SDA_PULLUP_ENABLED in config: + cg.add(var.set_sda_pullup_enabled(config[CONF_SDA_PULLUP_ENABLED])) cg.add(var.set_scl_pin(config[CONF_SCL])) + if CONF_SCL_PULLUP_ENABLED in config: + cg.add(var.set_scl_pullup_enabled(config[CONF_SCL_PULLUP_ENABLED])) + cg.add(var.set_frequency(int(config[CONF_FREQUENCY]))) cg.add(var.set_scan(config[CONF_SCAN])) - cg.add_library("Wire", None) + if CORE.using_arduino: + cg.add_library("Wire", None) def i2c_device_schema(default_address): @@ -62,8 +87,11 @@ def i2c_device_schema(default_address): :return: The i2c device schema, `extend` this in your config schema. """ schema = { - cv.GenerateID(CONF_I2C_ID): cv.use_id(I2CComponent), - cv.Optional(CONF_MULTIPLEXER): I2CMULTIPLEXER_SCHEMA, + cv.GenerateID(CONF_I2C_ID): cv.use_id(I2CBus), + cv.Optional("multiplexer"): cv.invalid( + "This option has been removed, please see " + "the tca9584a docs for the updated way to use multiplexers" + ), } if default_address is None: schema[cv.Required(CONF_ADDRESS)] = cv.i2c_address @@ -80,10 +108,5 @@ async def register_i2c_device(var, config): This is a coroutine, you need to await it with a 'yield' expression! """ parent = await cg.get_variable(config[CONF_I2C_ID]) - cg.add(var.set_i2c_parent(parent)) + cg.add(var.set_i2c_bus(parent)) cg.add(var.set_i2c_address(config[CONF_ADDRESS])) - if CONF_MULTIPLEXER in config: - multiplexer = await cg.get_variable(config[CONF_MULTIPLEXER][CONF_ID]) - cg.add( - var.set_i2c_multiplexer(multiplexer, config[CONF_MULTIPLEXER][CONF_CHANNEL]) - ) diff --git a/esphome/components/i2c/i2c.cpp b/esphome/components/i2c/i2c.cpp index a3a3f7f82a..035c483344 100644 --- a/esphome/components/i2c/i2c.cpp +++ b/esphome/components/i2c/i2c.cpp @@ -1,303 +1,40 @@ #include "i2c.h" #include "esphome/core/log.h" -#include "esphome/core/helpers.h" -#include "esphome/core/application.h" +#include namespace esphome { namespace i2c { static const char *const TAG = "i2c"; -I2CComponent::I2CComponent() { -#ifdef ARDUINO_ARCH_ESP32 - static uint8_t next_i2c_bus_num = 0; - if (next_i2c_bus_num == 0) - this->wire_ = &Wire; - else - this->wire_ = new TwoWire(next_i2c_bus_num); // NOLINT(cppcoreguidelines-owning-memory) - next_i2c_bus_num++; -#else - this->wire_ = &Wire; // NOLINT(cppcoreguidelines-prefer-member-initializer) -#endif +bool I2CDevice::write_bytes_16(uint8_t a_register, const uint16_t *data, uint8_t len) { + // we have to copy in order to be able to change byte order + std::unique_ptr temp{new uint16_t[len]}; + for (size_t i = 0; i < len; i++) + temp[i] = htoi2cs(data[i]); + return write_register(a_register, reinterpret_cast(data), len * 2) == ERROR_OK; } -void I2CComponent::setup() { - this->wire_->begin(this->sda_pin_, this->scl_pin_); - this->wire_->setClock(this->frequency_); -} -void I2CComponent::dump_config() { - ESP_LOGCONFIG(TAG, "I2C Bus:"); - ESP_LOGCONFIG(TAG, " SDA Pin: GPIO%u", this->sda_pin_); - ESP_LOGCONFIG(TAG, " SCL Pin: GPIO%u", this->scl_pin_); - ESP_LOGCONFIG(TAG, " Frequency: %u Hz", this->frequency_); - if (this->scan_) { - ESP_LOGI(TAG, "Scanning i2c bus for active devices..."); - uint8_t found = 0; - for (uint8_t address = 1; address < 120; address++) { - this->wire_->beginTransmission(address); - uint8_t error = this->wire_->endTransmission(); - - if (error == 0) { - ESP_LOGI(TAG, "Found i2c device at address 0x%02X", address); - found++; - } else if (error == 4) { - ESP_LOGI(TAG, "Unknown error at address 0x%02X", address); - } - - delay(1); - } - if (found == 0) { - ESP_LOGI(TAG, "Found no i2c devices!"); - } - } -} -float I2CComponent::get_setup_priority() const { return setup_priority::BUS; } - -void I2CComponent::raw_begin_transmission(uint8_t address) { - ESP_LOGVV(TAG, "Beginning Transmission to 0x%02X:", address); - this->wire_->beginTransmission(address); -} -bool I2CComponent::raw_end_transmission(uint8_t address, bool send_stop) { - uint8_t status = this->wire_->endTransmission(send_stop); - ESP_LOGVV(TAG, " Transmission ended. Status code: 0x%02X", status); - - switch (status) { - case 0: - break; - case 1: - ESP_LOGW(TAG, "Too much data to fit in transmitter buffer for address 0x%02X", address); - break; - case 2: - ESP_LOGW(TAG, "Received NACK on transmit of address 0x%02X", address); - break; - case 3: - ESP_LOGW(TAG, "Received NACK on transmit of data for address 0x%02X", address); - break; - default: - ESP_LOGW(TAG, "Unknown transmit error %u for address 0x%02X", status, address); - break; - } - - return status == 0; -} -bool I2CComponent::raw_request_from(uint8_t address, uint8_t len) { - ESP_LOGVV(TAG, "Requesting %u bytes from 0x%02X:", len, address); - uint8_t ret = this->wire_->requestFrom(address, len); - if (ret != len) { - ESP_LOGW(TAG, "Requesting %u bytes from 0x%02X failed!", len, address); - return false; - } - return true; -} -void HOT I2CComponent::raw_write(uint8_t address, const uint8_t *data, uint8_t len) { - for (size_t i = 0; i < len; i++) { - ESP_LOGVV(TAG, " Writing 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]); - this->wire_->write(data[i]); - App.feed_wdt(); - } -} -void HOT I2CComponent::raw_write_16(uint8_t address, const uint16_t *data, uint8_t len) { - for (size_t i = 0; i < len; i++) { - ESP_LOGVV(TAG, " Writing 0b" BYTE_TO_BINARY_PATTERN BYTE_TO_BINARY_PATTERN " (0x%04X)", - BYTE_TO_BINARY(data[i] >> 8), BYTE_TO_BINARY(data[i]), data[i]); - this->wire_->write(data[i] >> 8); - this->wire_->write(data[i]); - App.feed_wdt(); - } -} - -bool I2CComponent::raw_receive(uint8_t address, uint8_t *data, uint8_t len) { - if (!this->raw_request_from(address, len)) - return false; - for (uint8_t i = 0; i < len; i++) { - data[i] = this->wire_->read(); - ESP_LOGVV(TAG, " Received 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]); - App.feed_wdt(); - } - return true; -} -bool I2CComponent::raw_receive_16(uint8_t address, uint16_t *data, uint8_t len) { - if (!this->raw_request_from(address, len * 2)) - return false; - auto *data_8 = reinterpret_cast(data); - for (uint8_t i = 0; i < len; i++) { - data_8[i * 2 + 1] = this->wire_->read(); - data_8[i * 2] = this->wire_->read(); - ESP_LOGVV(TAG, " Received 0b" BYTE_TO_BINARY_PATTERN BYTE_TO_BINARY_PATTERN " (0x%04X)", - BYTE_TO_BINARY(data_8[i * 2 + 1]), BYTE_TO_BINARY(data_8[i * 2]), data[i]); - } - return true; -} -bool I2CComponent::read_bytes(uint8_t address, uint8_t a_register, uint8_t *data, uint8_t len, uint32_t conversion) { - if (!this->write_bytes(address, a_register, nullptr, 0)) - return false; - - if (conversion > 0) - delay(conversion); - return this->raw_receive(address, data, len); -} -bool I2CComponent::read_bytes_raw(uint8_t address, uint8_t *data, uint8_t len) { - return this->raw_receive(address, data, len); -} -bool I2CComponent::read_bytes_16(uint8_t address, uint8_t a_register, uint16_t *data, uint8_t len, - uint32_t conversion) { - if (!this->write_bytes(address, a_register, nullptr, 0)) - return false; - - if (conversion > 0) - delay(conversion); - return this->raw_receive_16(address, data, len); -} -bool I2CComponent::read_byte(uint8_t address, uint8_t a_register, uint8_t *data, uint32_t conversion) { - return this->read_bytes(address, a_register, data, 1, conversion); -} -bool I2CComponent::read_byte_16(uint8_t address, uint8_t a_register, uint16_t *data, uint32_t conversion) { - return this->read_bytes_16(address, a_register, data, 1, conversion); -} -bool I2CComponent::write_bytes(uint8_t address, uint8_t a_register, const uint8_t *data, uint8_t len) { - this->raw_begin_transmission(address); - this->raw_write(address, &a_register, 1); - this->raw_write(address, data, len); - return this->raw_end_transmission(address); -} -bool I2CComponent::write_bytes_raw(uint8_t address, const uint8_t *data, uint8_t len) { - this->raw_begin_transmission(address); - this->raw_write(address, data, len); - return this->raw_end_transmission(address); -} -bool I2CComponent::write_bytes_16(uint8_t address, uint8_t a_register, const uint16_t *data, uint8_t len) { - this->raw_begin_transmission(address); - this->raw_write(address, &a_register, 1); - this->raw_write_16(address, data, len); - return this->raw_end_transmission(address); -} -bool I2CComponent::write_byte(uint8_t address, uint8_t a_register, uint8_t data) { - return this->write_bytes(address, a_register, &data, 1); -} -bool I2CComponent::write_byte_16(uint8_t address, uint8_t a_register, uint16_t data) { - return this->write_bytes_16(address, a_register, &data, 1); -} - -void I2CDevice::set_i2c_address(uint8_t address) { this->address_ = address; } -#ifdef USE_I2C_MULTIPLEXER -void I2CDevice::set_i2c_multiplexer(I2CMultiplexer *multiplexer, uint8_t channel) { - ESP_LOGVV(TAG, " Setting Multiplexer %p for channel %d", multiplexer, channel); - this->multiplexer_ = multiplexer; - this->channel_ = channel; -} - -void I2CDevice::check_multiplexer_() { - if (this->multiplexer_ != nullptr) { - ESP_LOGVV(TAG, "Multiplexer setting channel to %d", this->channel_); - this->multiplexer_->set_channel(this->channel_); - } -} -#endif - -void I2CDevice::raw_begin_transmission() { // NOLINT -#ifdef USE_I2C_MULTIPLEXER - this->check_multiplexer_(); -#endif - this->parent_->raw_begin_transmission(this->address_); -} -bool I2CDevice::raw_end_transmission(bool send_stop) { // NOLINT -#ifdef USE_I2C_MULTIPLEXER - this->check_multiplexer_(); -#endif - return this->parent_->raw_end_transmission(this->address_, send_stop); -} -void I2CDevice::raw_write(const uint8_t *data, uint8_t len) { // NOLINT -#ifdef USE_I2C_MULTIPLEXER - this->check_multiplexer_(); -#endif - this->parent_->raw_write(this->address_, data, len); -} -bool I2CDevice::read_bytes(uint8_t a_register, uint8_t *data, uint8_t len, uint32_t conversion) { // NOLINT -#ifdef USE_I2C_MULTIPLEXER - this->check_multiplexer_(); -#endif - return this->parent_->read_bytes(this->address_, a_register, data, len, conversion); -} -bool I2CDevice::read_bytes_raw(uint8_t *data, uint8_t len) { // NOLINT -#ifdef USE_I2C_MULTIPLEXER - this->check_multiplexer_(); -#endif - return this->parent_->read_bytes_raw(this->address_, data, len); -} -bool I2CDevice::read_byte(uint8_t a_register, uint8_t *data, uint32_t conversion) { // NOLINT -#ifdef USE_I2C_MULTIPLEXER - this->check_multiplexer_(); -#endif - return this->parent_->read_byte(this->address_, a_register, data, conversion); -} -bool I2CDevice::write_bytes(uint8_t a_register, const uint8_t *data, uint8_t len) { // NOLINT -#ifdef USE_I2C_MULTIPLEXER - this->check_multiplexer_(); -#endif - return this->parent_->write_bytes(this->address_, a_register, data, len); -} -bool I2CDevice::write_bytes_raw(const uint8_t *data, uint8_t len) { // NOLINT -#ifdef USE_I2C_MULTIPLEXER - this->check_multiplexer_(); -#endif - return this->parent_->write_bytes_raw(this->address_, data, len); -} -bool I2CDevice::write_byte(uint8_t a_register, uint8_t data) { // NOLINT -#ifdef USE_I2C_MULTIPLEXER - this->check_multiplexer_(); -#endif - return this->parent_->write_byte(this->address_, a_register, data); -} -bool I2CDevice::read_bytes_16(uint8_t a_register, uint16_t *data, uint8_t len, uint32_t conversion) { // NOLINT -#ifdef USE_I2C_MULTIPLEXER - this->check_multiplexer_(); -#endif - return this->parent_->read_bytes_16(this->address_, a_register, data, len, conversion); -} -bool I2CDevice::read_byte_16(uint8_t a_register, uint16_t *data, uint32_t conversion) { // NOLINT -#ifdef USE_I2C_MULTIPLEXER - this->check_multiplexer_(); -#endif - return this->parent_->read_byte_16(this->address_, a_register, data, conversion); -} -bool I2CDevice::write_bytes_16(uint8_t a_register, const uint16_t *data, uint8_t len) { // NOLINT -#ifdef USE_I2C_MULTIPLEXER - this->check_multiplexer_(); -#endif - return this->parent_->write_bytes_16(this->address_, a_register, data, len); -} -bool I2CDevice::write_byte_16(uint8_t a_register, uint16_t data) { // NOLINT -#ifdef USE_I2C_MULTIPLEXER - this->check_multiplexer_(); -#endif - return this->parent_->write_byte_16(this->address_, a_register, data); -} -void I2CDevice::set_i2c_parent(I2CComponent *parent) { this->parent_ = parent; } - I2CRegister &I2CRegister::operator=(uint8_t value) { - this->parent_->write_byte(this->register_, value); + this->parent_->write_register(this->register_, &value, 1); return *this; } - I2CRegister &I2CRegister::operator&=(uint8_t value) { - this->parent_->write_byte(this->register_, this->get() & value); + value &= get(); + this->parent_->write_register(this->register_, &value, 1); return *this; } - I2CRegister &I2CRegister::operator|=(uint8_t value) { - this->parent_->write_byte(this->register_, this->get() | value); + value |= get(); + this->parent_->write_register(this->register_, &value, 1); return *this; } -uint8_t I2CRegister::get() { +uint8_t I2CRegister::get() const { uint8_t value = 0x00; - this->parent_->read_byte(this->register_, &value); + this->parent_->read_register(this->register_, &value, 1); return value; } -I2CRegister &I2CRegister::operator=(const std::vector &value) { - this->parent_->write_bytes(this->register_, value); - return *this; -} } // namespace i2c } // namespace esphome diff --git a/esphome/components/i2c/i2c.h b/esphome/components/i2c/i2c.h index 6f93d4c0e4..71ab650e97 100644 --- a/esphome/components/i2c/i2c.h +++ b/esphome/components/i2c/i2c.h @@ -1,198 +1,79 @@ #pragma once -#include -#include "esphome/core/defines.h" -#include "esphome/core/component.h" -#include "esphome/core/helpers.h" +#include "i2c_bus.h" +#include "esphome/core/optional.h" +#include +#include namespace esphome { namespace i2c { #define LOG_I2C_DEVICE(this) ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_); -/** The I2CComponent is the base of ESPHome's i2c communication. - * - * It handles setting up the bus (with pins, clock frequency) and provides nice helper functions to - * make reading from the i2c bus easier (see read_bytes, write_bytes) and safe (with read timeouts). - * - * For the user, it has a few setters (see set_sda_pin, set_scl_pin, set_frequency) - * to setup some parameters for the bus. Additionally, the i2c component has a scan feature that will - * scan the entire 7-bit i2c address range for devices that respond to transmissions to make finding - * the address of an i2c device easier. - * - * On the ESP32, you can even have multiple I2C bus for communication, simply create multiple - * I2CComponents, each with different SDA and SCL pins and use `set_parent` on all I2CDevices that use - * the non-first I2C bus. - */ -class I2CComponent : public Component { - public: - I2CComponent(); - void set_sda_pin(uint8_t sda_pin) { sda_pin_ = sda_pin; } - void set_scl_pin(uint8_t scl_pin) { scl_pin_ = scl_pin; } - void set_frequency(uint32_t frequency) { frequency_ = frequency; } - void set_scan(bool scan) { scan_ = scan; } - - /** Read len amount of bytes from a register into data. Optionally with a conversion time after - * writing the register value to the bus. - * - * @param address The address to send the request to. - * @param a_register The register number to write to the bus before reading. - * @param data An array to store len amount of 8-bit bytes into. - * @param len The amount of bytes to request and write into data. - * @param conversion The time in ms between writing the register value and reading out the value. - * @return If the operation was successful. - */ - bool read_bytes(uint8_t address, uint8_t a_register, uint8_t *data, uint8_t len, uint32_t conversion = 0); - bool read_bytes_raw(uint8_t address, uint8_t *data, uint8_t len); - - /** Read len amount of 16-bit words (MSB first) from a register into data. - * - * @param address The address to send the request to. - * @param a_register The register number to write to the bus before reading. - * @param data An array to store len amount of 16-bit words into. - * @param len The amount of 16-bit words to request and write into data. - * @param conversion The time in ms between writing the register value and reading out the value. - * @return If the operation was successful. - */ - bool read_bytes_16(uint8_t address, uint8_t a_register, uint16_t *data, uint8_t len, uint32_t conversion = 0); - - /// Read a single byte from a register into the data variable. Return true if successful. - bool read_byte(uint8_t address, uint8_t a_register, uint8_t *data, uint32_t conversion = 0); - - /// Read a single 16-bit words (MSB first) from a register into the data variable. Return true if successful. - bool read_byte_16(uint8_t address, uint8_t a_register, uint16_t *data, uint32_t conversion = 0); - - /** Write len amount of 8-bit bytes to the specified register for address. - * - * @param address The address to use for the transmission. - * @param a_register The register to write the values to. - * @param data An array from which len bytes of data will be written to the bus. - * @param len The amount of bytes to write to the bus. - * @return If the operation was successful. - */ - bool write_bytes(uint8_t address, uint8_t a_register, const uint8_t *data, uint8_t len); - bool write_bytes_raw(uint8_t address, const uint8_t *data, uint8_t len); - - /** Write len amount of 16-bit words (MSB first) to the specified register for address. - * - * @param address The address to use for the transmission. - * @param a_register The register to write the values to. - * @param data An array from which len 16-bit words of data will be written to the bus. - * @param len The amount of bytes to write to the bus. - * @return If the operation was successful. - */ - bool write_bytes_16(uint8_t address, uint8_t a_register, const uint16_t *data, uint8_t len); - - /// Write a single byte of data into the specified register of address. Return true if successful. - bool write_byte(uint8_t address, uint8_t a_register, uint8_t data); - - /// Write a single 16-bit word of data into the specified register of address. Return true if successful. - bool write_byte_16(uint8_t address, uint8_t a_register, uint16_t data); - - // ========== INTERNAL METHODS ========== - // (In most use cases you won't need these) - /// Begin a write transmission to an address. - void raw_begin_transmission(uint8_t address); - - /// End a write transmission to an address, return true if successful. - bool raw_end_transmission(uint8_t address, bool send_stop = true); - - /** Request data from an address with a number of (8-bit) bytes. - * - * @param address The address to request the bytes from. - * @param len The number of bytes to receive, must not be 0. - * @return True if all requested bytes were read, false otherwise. - */ - bool raw_request_from(uint8_t address, uint8_t len); - - /// Write len amount of bytes from data to address. begin_transmission_ must be called before this. - void raw_write(uint8_t address, const uint8_t *data, uint8_t len); - - /// Write len amount of 16-bit words from data to address. begin_transmission_ must be called before this. - void raw_write_16(uint8_t address, const uint16_t *data, uint8_t len); - - /// Request len amount of bytes from address and write the result it into data. Returns true iff was successful. - bool raw_receive(uint8_t address, uint8_t *data, uint8_t len); - - /// Request len amount of 16-bit words from address and write the result into data. Returns true iff was successful. - bool raw_receive_16(uint8_t address, uint16_t *data, uint8_t len); - - /// Setup the i2c. bus - void setup() override; - void dump_config() override; - /// Set a very high setup priority to make sure it's loaded before all other hardware. - float get_setup_priority() const override; - - protected: - TwoWire *wire_; - uint8_t sda_pin_; - uint8_t scl_pin_; - uint32_t frequency_; - bool scan_; -}; - class I2CDevice; -class I2CMultiplexer; class I2CRegister { public: I2CRegister(I2CDevice *parent, uint8_t a_register) : parent_(parent), register_(a_register) {} I2CRegister &operator=(uint8_t value); - I2CRegister &operator=(const std::vector &value); I2CRegister &operator&=(uint8_t value); I2CRegister &operator|=(uint8_t value); - uint8_t get(); + explicit operator uint8_t() const { return get(); } + + uint8_t get() const; protected: I2CDevice *parent_; uint8_t register_; }; -/** All components doing communication on the I2C bus should subclass I2CDevice. - * - * This class stores 1. the address of the i2c device and has a helper function to allow - * users to manually set the address and 2. stores a reference to the "parent" I2CComponent. - * - * - * All this class basically does is to expose all helper functions from I2CComponent. - */ +// like ntohs/htons but without including networking headers. +// ("i2c" byte order is big-endian) +inline uint16_t i2ctohs(uint16_t i2cshort) { + union { + uint16_t x; + uint8_t y[2]; + } conv; + conv.x = i2cshort; + return ((uint16_t) conv.y[0] << 8) | ((uint16_t) conv.y[1] << 0); +} + +inline uint16_t htoi2cs(uint16_t hostshort) { return i2ctohs(hostshort); } + class I2CDevice { public: I2CDevice() = default; - I2CDevice(I2CComponent *parent, uint8_t address) : address_(address), parent_(parent) {} - /// Manually set the i2c address of this device. - void set_i2c_address(uint8_t address); -#ifdef USE_I2C_MULTIPLEXER - /// Manually set the i2c multiplexer of this device. - void set_i2c_multiplexer(I2CMultiplexer *multiplexer, uint8_t channel); -#endif - /// Manually set the parent i2c bus for this device. - void set_i2c_parent(I2CComponent *parent); + void set_i2c_address(uint8_t address) { address_ = address; } + void set_i2c_bus(I2CBus *bus) { bus_ = bus; } I2CRegister reg(uint8_t a_register) { return {this, a_register}; } - /// Begin a write transmission. - void raw_begin_transmission(); + ErrorCode read(uint8_t *data, size_t len) { return bus_->read(address_, data, len); } + ErrorCode read_register(uint8_t a_register, uint8_t *data, size_t len) { + ErrorCode err = this->write(&a_register, 1); + if (err != ERROR_OK) + return err; + return this->read(data, len); + } - /// End a write transmission, return true if successful. - bool raw_end_transmission(bool send_stop = true); + ErrorCode write(const uint8_t *data, uint8_t len) { return bus_->write(address_, data, len); } + ErrorCode write_register(uint8_t a_register, const uint8_t *data, size_t len) { + WriteBuffer buffers[2]; + buffers[0].data = &a_register; + buffers[0].len = 1; + buffers[1].data = data; + buffers[1].len = len; + return bus_->writev(address_, buffers, 2); + } - /// Write len amount of bytes from data. begin_transmission_ must be called before this. - void raw_write(const uint8_t *data, uint8_t len); + // Compat APIs - /** Read len amount of bytes from a register into data. Optionally with a conversion time after - * writing the register value to the bus. - * - * @param a_register The register number to write to the bus before reading. - * @param data An array to store len amount of 8-bit bytes into. - * @param len The amount of bytes to request and write into data. - * @param conversion The time in ms between writing the register value and reading out the value. - * @return If the operation was successful. - */ - bool read_bytes(uint8_t a_register, uint8_t *data, uint8_t len, uint32_t conversion = 0); - bool read_bytes_raw(uint8_t *data, uint8_t len); + bool read_bytes(uint8_t a_register, uint8_t *data, uint8_t len) { + return read_register(a_register, data, len) == ERROR_OK; + } + bool read_bytes_raw(uint8_t *data, uint8_t len) { return read(data, len) == ERROR_OK; } template optional> read_bytes(uint8_t a_register) { std::array res; @@ -209,18 +90,15 @@ class I2CDevice { return res; } - /** Read len amount of 16-bit words (MSB first) from a register into data. - * - * @param a_register The register number to write to the bus before reading. - * @param data An array to store len amount of 16-bit words into. - * @param len The amount of 16-bit words to request and write into data. - * @param conversion The time in ms between writing the register value and reading out the value. - * @return If the operation was successful. - */ - bool read_bytes_16(uint8_t a_register, uint16_t *data, uint8_t len, uint32_t conversion = 0); + bool read_bytes_16(uint8_t a_register, uint16_t *data, uint8_t len) { + if (read_register(a_register, reinterpret_cast(data), len * 2) != ERROR_OK) + return false; + for (size_t i = 0; i < len; i++) + data[i] = i2ctohs(data[i]); + return true; + } - /// Read a single byte from a register into the data variable. Return true if successful. - bool read_byte(uint8_t a_register, uint8_t *data, uint32_t conversion = 0); + bool read_byte(uint8_t a_register, uint8_t *data) { return read_register(a_register, data, 1) == ERROR_OK; } optional read_byte(uint8_t a_register) { uint8_t data; @@ -229,66 +107,30 @@ class I2CDevice { return data; } - /// Read a single 16-bit words (MSB first) from a register into the data variable. Return true if successful. - bool read_byte_16(uint8_t a_register, uint16_t *data, uint32_t conversion = 0); + bool read_byte_16(uint8_t a_register, uint16_t *data) { return read_bytes_16(a_register, data, 1); } - /** Write len amount of 8-bit bytes to the specified register. - * - * @param a_register The register to write the values to. - * @param data An array from which len bytes of data will be written to the bus. - * @param len The amount of bytes to write to the bus. - * @return If the operation was successful. - */ - bool write_bytes(uint8_t a_register, const uint8_t *data, uint8_t len); - bool write_bytes_raw(const uint8_t *data, uint8_t len); - - /** Write a vector of data to a register. - * - * @param a_register The register to write to. - * @param data The data to write. - * @return If the operation was successful. - */ - bool write_bytes(uint8_t a_register, const std::vector &data) { - return this->write_bytes(a_register, data.data(), data.size()); + bool write_bytes(uint8_t a_register, const uint8_t *data, uint8_t len) { + return write_register(a_register, data, len) == ERROR_OK; + } + + bool write_bytes(uint8_t a_register, const std::vector &data) { + return write_bytes(a_register, data.data(), data.size()); } - bool write_bytes_raw(const std::vector &data) { return this->write_bytes_raw(data.data(), data.size()); } template bool write_bytes(uint8_t a_register, const std::array &data) { - return this->write_bytes(a_register, data.data(), data.size()); - } - template bool write_bytes_raw(const std::array &data) { - return this->write_bytes_raw(data.data(), data.size()); + return write_bytes(a_register, data.data(), data.size()); } - /** Write len amount of 16-bit words (MSB first) to the specified register. - * - * @param a_register The register to write the values to. - * @param data An array from which len 16-bit words of data will be written to the bus. - * @param len The amount of bytes to write to the bus. - * @return If the operation was successful. - */ bool write_bytes_16(uint8_t a_register, const uint16_t *data, uint8_t len); - /// Write a single byte of data into the specified register. Return true if successful. - bool write_byte(uint8_t a_register, uint8_t data); + bool write_byte(uint8_t a_register, uint8_t data) { return write_bytes(a_register, &data, 1); } - /// Write a single 16-bit word of data into the specified register. Return true if successful. - bool write_byte_16(uint8_t a_register, uint16_t data); + bool write_byte_16(uint8_t a_register, uint16_t data) { return write_bytes_16(a_register, &data, 1); } protected: - // Checks for multiplexer set and set channel - void check_multiplexer_(); uint8_t address_{0x00}; - I2CComponent *parent_{nullptr}; -#ifdef USE_I2C_MULTIPLEXER - I2CMultiplexer *multiplexer_{nullptr}; - uint8_t channel_; -#endif -}; -class I2CMultiplexer : public I2CDevice { - public: - I2CMultiplexer() = default; - virtual void set_channel(uint8_t channelno); + I2CBus *bus_{nullptr}; }; + } // namespace i2c } // namespace esphome diff --git a/esphome/components/i2c/i2c_bus.h b/esphome/components/i2c/i2c_bus.h new file mode 100644 index 0000000000..cb00260f43 --- /dev/null +++ b/esphome/components/i2c/i2c_bus.h @@ -0,0 +1,46 @@ +#pragma once +#include +#include + +namespace esphome { +namespace i2c { + +enum ErrorCode { + ERROR_OK = 0, + ERROR_INVALID_ARGUMENT = 1, + ERROR_NOT_ACKNOWLEDGED = 2, + ERROR_TIMEOUT = 3, + ERROR_NOT_INITIALIZED = 4, + ERROR_TOO_LARGE = 5, + ERROR_UNKNOWN = 6, +}; + +struct ReadBuffer { + uint8_t *data; + size_t len; +}; +struct WriteBuffer { + const uint8_t *data; + size_t len; +}; + +class I2CBus { + public: + virtual ErrorCode read(uint8_t address, uint8_t *buffer, size_t len) { + ReadBuffer buf; + buf.data = buffer; + buf.len = len; + return readv(address, &buf, 1); + } + virtual ErrorCode readv(uint8_t address, ReadBuffer *buffers, size_t cnt) = 0; + virtual ErrorCode write(uint8_t address, const uint8_t *buffer, size_t len) { + WriteBuffer buf; + buf.data = buffer; + buf.len = len; + return writev(address, &buf, 1); + } + virtual ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t cnt) = 0; +}; + +} // namespace i2c +} // namespace esphome diff --git a/esphome/components/i2c/i2c_bus_arduino.cpp b/esphome/components/i2c/i2c_bus_arduino.cpp new file mode 100644 index 0000000000..aba412c3f7 --- /dev/null +++ b/esphome/components/i2c/i2c_bus_arduino.cpp @@ -0,0 +1,97 @@ +#ifdef USE_ARDUINO + +#include "i2c_bus_arduino.h" +#include "esphome/core/log.h" +#include + +namespace esphome { +namespace i2c { + +static const char *const TAG = "i2c.arduino"; + +void ArduinoI2CBus::setup() { +#ifdef USE_ESP32 + static uint8_t next_bus_num = 0; + if (next_bus_num == 0) + wire_ = &Wire; + else + wire_ = new TwoWire(next_bus_num); // NOLINT(cppcoreguidelines-owning-memory) + next_bus_num++; +#else + wire_ = &Wire; // NOLINT(cppcoreguidelines-prefer-member-initializer) +#endif + + wire_->begin(sda_pin_, scl_pin_); + wire_->setClock(frequency_); + initialized_ = true; +} +void ArduinoI2CBus::dump_config() { + ESP_LOGCONFIG(TAG, "I2C Bus:"); + ESP_LOGCONFIG(TAG, " SDA Pin: GPIO%u", this->sda_pin_); + ESP_LOGCONFIG(TAG, " SCL Pin: GPIO%u", this->scl_pin_); + ESP_LOGCONFIG(TAG, " Frequency: %u Hz", this->frequency_); + if (this->scan_) { + ESP_LOGI(TAG, "Scanning i2c bus for active devices..."); + uint8_t found = 0; + for (uint8_t address = 1; address < 120; address++) { + auto err = readv(address, nullptr, 0); + + if (err == ERROR_OK) { + ESP_LOGI(TAG, "Found i2c device at address 0x%02X", address); + found++; + } else if (err == ERROR_UNKNOWN) { + ESP_LOGI(TAG, "Unknown error at address 0x%02X", address); + } + } + if (found == 0) { + ESP_LOGI(TAG, "Found no i2c devices!"); + } + } +} +ErrorCode ArduinoI2CBus::readv(uint8_t address, ReadBuffer *buffers, size_t cnt) { + if (!initialized_) + return ERROR_NOT_INITIALIZED; + size_t to_request = 0; + for (size_t i = 0; i < cnt; i++) + to_request += buffers[i].len; + size_t ret = wire_->requestFrom((int) address, (int) to_request, 1); + if (ret != to_request) { + return ERROR_TIMEOUT; + } + for (size_t i = 0; i < cnt; i++) { + const auto &buf = buffers[i]; + for (size_t j = 0; j < buf.len; j++) + buf.data[j] = wire_->read(); + } + return ERROR_OK; +} +ErrorCode ArduinoI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cnt) { + if (!initialized_) + return ERROR_NOT_INITIALIZED; + + wire_->beginTransmission(address); + for (size_t i = 0; i < cnt; i++) { + const auto &buf = buffers[i]; + if (buf.len == 0) + continue; + size_t ret = wire_->write(buf.data, buf.len); + if (ret != buf.len) { + return ERROR_UNKNOWN; + } + } + uint8_t status = wire_->endTransmission(true); + if (status == 0) { + return ERROR_OK; + } else if (status == 1) { + // transmit buffer not large enough + return ERROR_UNKNOWN; + } else if (status == 2 || status == 3) { + return ERROR_NOT_ACKNOWLEDGED; + } + return ERROR_UNKNOWN; +} + +} // namespace i2c +} // namespace esphome + +#endif // USE_ESP_IDF diff --git a/esphome/components/i2c/i2c_bus_arduino.h b/esphome/components/i2c/i2c_bus_arduino.h new file mode 100644 index 0000000000..220027b3d4 --- /dev/null +++ b/esphome/components/i2c/i2c_bus_arduino.h @@ -0,0 +1,37 @@ +#pragma once + +#ifdef USE_ARDUINO + +#include "i2c_bus.h" +#include "esphome/core/component.h" +#include + +namespace esphome { +namespace i2c { + +class ArduinoI2CBus : public I2CBus, public Component { + public: + void setup() override; + void dump_config() override; + ErrorCode readv(uint8_t address, ReadBuffer *buffers, size_t cnt) override; + ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t cnt) override; + float get_setup_priority() const override { return setup_priority::BUS; } + + void set_scan(bool scan) { scan_ = scan; } + void set_sda_pin(uint8_t sda_pin) { sda_pin_ = sda_pin; } + void set_scl_pin(uint8_t scl_pin) { scl_pin_ = scl_pin; } + void set_frequency(uint32_t frequency) { frequency_ = frequency; } + + protected: + TwoWire *wire_; + bool scan_; + uint8_t sda_pin_; + uint8_t scl_pin_; + uint32_t frequency_; + bool initialized_ = false; +}; + +} // namespace i2c +} // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/i2c/i2c_bus_esp_idf.cpp b/esphome/components/i2c/i2c_bus_esp_idf.cpp new file mode 100644 index 0000000000..4b93b41877 --- /dev/null +++ b/esphome/components/i2c/i2c_bus_esp_idf.cpp @@ -0,0 +1,147 @@ +#ifdef USE_ESP_IDF + +#include "i2c_bus_esp_idf.h" +#include "esphome/core/log.h" +#include + +namespace esphome { +namespace i2c { + +static const char *const TAG = "i2c.idf"; + +void IDFI2CBus::setup() { + static i2c_port_t next_port = 0; + port_ = next_port++; + + i2c_config_t conf{}; + memset(&conf, 0, sizeof(conf)); + conf.mode = I2C_MODE_MASTER; + conf.sda_io_num = sda_pin_; + conf.sda_pullup_en = sda_pullup_enabled_; + conf.scl_io_num = scl_pin_; + conf.scl_pullup_en = scl_pullup_enabled_; + conf.master.clk_speed = frequency_; + esp_err_t err = i2c_param_config(port_, &conf); + if (err != ESP_OK) { + ESP_LOGW(TAG, "i2c_param_config failed: %s", esp_err_to_name(err)); + this->mark_failed(); + return; + } + err = i2c_driver_install(port_, I2C_MODE_MASTER, 0, 0, ESP_INTR_FLAG_IRAM); + if (err != ESP_OK) { + ESP_LOGW(TAG, "i2c_driver_install failed: %s", esp_err_to_name(err)); + this->mark_failed(); + return; + } + initialized_ = true; +} +void IDFI2CBus::dump_config() { + ESP_LOGCONFIG(TAG, "I2C Bus:"); + ESP_LOGCONFIG(TAG, " SDA Pin: GPIO%u", this->sda_pin_); + ESP_LOGCONFIG(TAG, " SCL Pin: GPIO%u", this->scl_pin_); + ESP_LOGCONFIG(TAG, " Frequency: %u Hz", this->frequency_); + if (this->scan_) { + ESP_LOGI(TAG, "Scanning i2c bus for active devices..."); + uint8_t found = 0; + for (uint8_t address = 1; address < 120; address++) { + auto err = readv(address, nullptr, 0); + + if (err == ERROR_OK) { + ESP_LOGI(TAG, "Found i2c device at address 0x%02X", address); + found++; + } else if (err == ERROR_UNKNOWN) { + ESP_LOGI(TAG, "Unknown error at address 0x%02X", address); + } + } + if (found == 0) { + ESP_LOGI(TAG, "Found no i2c devices!"); + } + } +} +ErrorCode IDFI2CBus::readv(uint8_t address, ReadBuffer *buffers, size_t cnt) { + if (!initialized_) + return ERROR_NOT_INITIALIZED; + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + esp_err_t err = i2c_master_start(cmd); + if (err != ESP_OK) { + i2c_cmd_link_delete(cmd); + return ERROR_UNKNOWN; + } + err = i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_READ, true); + if (err != ESP_OK) { + i2c_cmd_link_delete(cmd); + return ERROR_UNKNOWN; + } + for (size_t i = 0; i < cnt; i++) { + const auto &buf = buffers[i]; + if (buf.len == 0) + continue; + err = i2c_master_read(cmd, buf.data, buf.len, i == cnt - 1 ? I2C_MASTER_LAST_NACK : I2C_MASTER_ACK); + if (err != ESP_OK) { + i2c_cmd_link_delete(cmd); + return ERROR_UNKNOWN; + } + } + err = i2c_master_stop(cmd); + if (err != ESP_OK) { + i2c_cmd_link_delete(cmd); + return ERROR_UNKNOWN; + } + err = i2c_master_cmd_begin(port_, cmd, 20 / portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); + if (err == ESP_FAIL) { + // transfer not acked + return ERROR_NOT_ACKNOWLEDGED; + } else if (err == ESP_ERR_TIMEOUT) { + return ERROR_TIMEOUT; + } else if (err != ESP_OK) { + return ERROR_UNKNOWN; + } + return ERROR_OK; +} +ErrorCode IDFI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cnt) { + if (!initialized_) + return ERROR_NOT_INITIALIZED; + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + esp_err_t err = i2c_master_start(cmd); + if (err != ESP_OK) { + i2c_cmd_link_delete(cmd); + return ERROR_UNKNOWN; + } + err = i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_WRITE, true); + if (err != ESP_OK) { + i2c_cmd_link_delete(cmd); + return ERROR_UNKNOWN; + } + for (size_t i = 0; i < cnt; i++) { + const auto &buf = buffers[i]; + if (buf.len == 0) + continue; + err = i2c_master_write(cmd, buf.data, buf.len, true); + if (err != ESP_OK) { + i2c_cmd_link_delete(cmd); + return ERROR_UNKNOWN; + } + } + err = i2c_master_stop(cmd); + if (err != ESP_OK) { + i2c_cmd_link_delete(cmd); + return ERROR_UNKNOWN; + } + err = i2c_master_cmd_begin(port_, cmd, 20 / portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); + if (err == ESP_FAIL) { + // transfer not acked + return ERROR_NOT_ACKNOWLEDGED; + } else if (err == ESP_ERR_TIMEOUT) { + return ERROR_TIMEOUT; + } else if (err != ESP_OK) { + return ERROR_UNKNOWN; + } + return ERROR_OK; +} + +} // namespace i2c +} // namespace esphome + +#endif // USE_ESP_IDF diff --git a/esphome/components/i2c/i2c_bus_esp_idf.h b/esphome/components/i2c/i2c_bus_esp_idf.h new file mode 100644 index 0000000000..c7e67145a3 --- /dev/null +++ b/esphome/components/i2c/i2c_bus_esp_idf.h @@ -0,0 +1,41 @@ +#pragma once + +#ifdef USE_ESP_IDF + +#include "i2c_bus.h" +#include "esphome/core/component.h" +#include + +namespace esphome { +namespace i2c { + +class IDFI2CBus : public I2CBus, public Component { + public: + void setup() override; + void dump_config() override; + ErrorCode readv(uint8_t address, ReadBuffer *buffers, size_t cnt) override; + ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t cnt) override; + float get_setup_priority() const override { return setup_priority::BUS; } + + void set_scan(bool scan) { scan_ = scan; } + void set_sda_pin(uint8_t sda_pin) { sda_pin_ = sda_pin; } + void set_sda_pullup_enabled(bool sda_pullup_enabled) { sda_pullup_enabled_ = sda_pullup_enabled; } + void set_scl_pin(uint8_t scl_pin) { scl_pin_ = scl_pin; } + void set_scl_pullup_enabled(bool scl_pullup_enabled) { scl_pullup_enabled_ = scl_pullup_enabled; } + void set_frequency(uint32_t frequency) { frequency_ = frequency; } + + protected: + i2c_port_t port_; + bool scan_; + uint8_t sda_pin_; + bool sda_pullup_enabled_; + uint8_t scl_pin_; + bool scl_pullup_enabled_; + uint32_t frequency_; + bool initialized_ = false; +}; + +} // namespace i2c +} // namespace esphome + +#endif // USE_ESP_IDF diff --git a/esphome/components/ili9341/ili9341_display.cpp b/esphome/components/ili9341/ili9341_display.cpp index c06d487e7d..b36d05c864 100644 --- a/esphome/components/ili9341/ili9341_display.cpp +++ b/esphome/components/ili9341/ili9341_display.cpp @@ -2,6 +2,7 @@ #include "esphome/core/log.h" #include "esphome/core/application.h" #include "esphome/core/helpers.h" +#include "esphome/core/hal.h" namespace esphome { namespace ili9341 { @@ -185,8 +186,8 @@ void ILI9341Display::end_data_() { this->disable(); } void ILI9341Display::init_lcd_(const uint8_t *init_cmd) { uint8_t cmd, x, num_args; const uint8_t *addr = init_cmd; - while ((cmd = pgm_read_byte(addr++)) > 0) { - x = pgm_read_byte(addr++); + while ((cmd = progmem_read_byte(addr++)) > 0) { + x = progmem_read_byte(addr++); num_args = x & 0x7F; send_command(cmd, addr, num_args); addr += num_args; diff --git a/esphome/components/improv/improv.cpp b/esphome/components/improv/improv.cpp index d1fee72866..4f6ed7702d 100644 --- a/esphome/components/improv/improv.cpp +++ b/esphome/components/improv/improv.cpp @@ -65,6 +65,7 @@ std::vector build_rpc_response(Command command, const std::vector build_rpc_response(Command command, const std::vector &datum) { std::vector out; uint32_t length = 0; @@ -85,5 +86,6 @@ std::vector build_rpc_response(Command command, const std::vector #include #include @@ -50,6 +53,8 @@ ImprovCommand parse_improv_data(const std::vector &data); ImprovCommand parse_improv_data(const uint8_t *data, size_t length); std::vector build_rpc_response(Command command, const std::vector &datum); +#ifdef USE_ARDUINO std::vector build_rpc_response(Command command, const std::vector &datum); +#endif // USE_ARDUINO } // namespace improv diff --git a/esphome/components/ina219/ina219.cpp b/esphome/components/ina219/ina219.cpp index 506b7e06ed..609f3d0f08 100644 --- a/esphome/components/ina219/ina219.cpp +++ b/esphome/components/ina219/ina219.cpp @@ -1,5 +1,6 @@ #include "ina219.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace ina219 { @@ -149,7 +150,7 @@ float INA219Component::get_setup_priority() const { return setup_priority::DATA; void INA219Component::update() { if (this->bus_voltage_sensor_ != nullptr) { uint16_t raw_bus_voltage; - if (!this->read_byte_16(INA219_REGISTER_BUS_VOLTAGE, &raw_bus_voltage, 1)) { + if (!this->read_byte_16(INA219_REGISTER_BUS_VOLTAGE, &raw_bus_voltage)) { this->status_set_warning(); return; } @@ -160,8 +161,9 @@ void INA219Component::update() { if (this->shunt_voltage_sensor_ != nullptr) { uint16_t raw_shunt_voltage; - if (!this->read_byte_16(INA219_REGISTER_SHUNT_VOLTAGE, &raw_shunt_voltage, 1)) { + if (!this->read_byte_16(INA219_REGISTER_SHUNT_VOLTAGE, &raw_shunt_voltage)) { this->status_set_warning(); + return; } float shunt_voltage_mv = int16_t(raw_shunt_voltage) * 0.01f; this->shunt_voltage_sensor_->publish_state(shunt_voltage_mv / 1000.0f); @@ -169,7 +171,7 @@ void INA219Component::update() { if (this->current_sensor_ != nullptr) { uint16_t raw_current; - if (!this->read_byte_16(INA219_REGISTER_CURRENT, &raw_current, 1)) { + if (!this->read_byte_16(INA219_REGISTER_CURRENT, &raw_current)) { this->status_set_warning(); return; } @@ -179,7 +181,7 @@ void INA219Component::update() { if (this->power_sensor_ != nullptr) { uint16_t raw_power; - if (!this->read_byte_16(INA219_REGISTER_POWER, &raw_power, 1)) { + if (!this->read_byte_16(INA219_REGISTER_POWER, &raw_power)) { this->status_set_warning(); return; } diff --git a/esphome/components/ina226/ina226.cpp b/esphome/components/ina226/ina226.cpp index 701a833041..2e30a5ac01 100644 --- a/esphome/components/ina226/ina226.cpp +++ b/esphome/components/ina226/ina226.cpp @@ -1,5 +1,6 @@ #include "ina226.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace ina226 { @@ -96,7 +97,7 @@ float INA226Component::get_setup_priority() const { return setup_priority::DATA; void INA226Component::update() { if (this->bus_voltage_sensor_ != nullptr) { uint16_t raw_bus_voltage; - if (!this->read_byte_16(INA226_REGISTER_BUS_VOLTAGE, &raw_bus_voltage, 1)) { + if (!this->read_byte_16(INA226_REGISTER_BUS_VOLTAGE, &raw_bus_voltage)) { this->status_set_warning(); return; } @@ -106,8 +107,9 @@ void INA226Component::update() { if (this->shunt_voltage_sensor_ != nullptr) { uint16_t raw_shunt_voltage; - if (!this->read_byte_16(INA226_REGISTER_SHUNT_VOLTAGE, &raw_shunt_voltage, 1)) { + if (!this->read_byte_16(INA226_REGISTER_SHUNT_VOLTAGE, &raw_shunt_voltage)) { this->status_set_warning(); + return; } float shunt_voltage_v = int16_t(raw_shunt_voltage) * 0.0000025f; this->shunt_voltage_sensor_->publish_state(shunt_voltage_v); @@ -115,7 +117,7 @@ void INA226Component::update() { if (this->current_sensor_ != nullptr) { uint16_t raw_current; - if (!this->read_byte_16(INA226_REGISTER_CURRENT, &raw_current, 1)) { + if (!this->read_byte_16(INA226_REGISTER_CURRENT, &raw_current)) { this->status_set_warning(); return; } @@ -125,7 +127,7 @@ void INA226Component::update() { if (this->power_sensor_ != nullptr) { uint16_t raw_power; - if (!this->read_byte_16(INA226_REGISTER_POWER, &raw_power, 1)) { + if (!this->read_byte_16(INA226_REGISTER_POWER, &raw_power)) { this->status_set_warning(); return; } diff --git a/esphome/components/ina3221/ina3221.cpp b/esphome/components/ina3221/ina3221.cpp index f2fcdb21eb..3f8e2d06df 100644 --- a/esphome/components/ina3221/ina3221.cpp +++ b/esphome/components/ina3221/ina3221.cpp @@ -1,5 +1,6 @@ #include "ina3221.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace ina3221 { @@ -87,7 +88,7 @@ void INA3221Component::update() { float bus_voltage_v = NAN, current_a = NAN; uint16_t raw; if (channel.should_measure_bus_voltage()) { - if (!this->read_byte_16(ina3221_bus_voltage_register(i), &raw, 1)) { + if (!this->read_byte_16(ina3221_bus_voltage_register(i), &raw)) { this->status_set_warning(); return; } @@ -96,7 +97,7 @@ void INA3221Component::update() { channel.bus_voltage_sensor_->publish_state(bus_voltage_v); } if (channel.should_measure_shunt_voltage()) { - if (!this->read_byte_16(ina3221_shunt_voltage_register(i), &raw, 1)) { + if (!this->read_byte_16(ina3221_shunt_voltage_register(i), &raw)) { this->status_set_warning(); return; } diff --git a/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp b/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp index 81693c6d96..177bc76072 100644 --- a/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp +++ b/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp @@ -1,7 +1,7 @@ #include "inkbird_ibsth1_mini.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace inkbird_ibsth1_mini { @@ -88,10 +88,10 @@ bool InkbirdIBSTH1_MINI::parse_device(const esp32_ble_tracker::ESPBTDevice &devi auto humidity = ((mnf_data.data[1] << 8) + mnf_data.data[0]) / 100.0f; // Send temperature only if the value is set - if (!isnan(temperature) && this->temperature_ != nullptr) { + if (!std::isnan(temperature) && this->temperature_ != nullptr) { this->temperature_->publish_state(temperature); } - if (!isnan(external_temperature) && this->external_temperature_ != nullptr) { + if (!std::isnan(external_temperature) && this->external_temperature_ != nullptr) { this->external_temperature_->publish_state(external_temperature); } if (this->humidity_ != nullptr) { diff --git a/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.h b/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.h index c3a9f7062d..1b6be7afe0 100644 --- a/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.h +++ b/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.h @@ -4,7 +4,7 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace inkbird_ibsth1_mini { diff --git a/esphome/components/inkplate6/display.py b/esphome/components/inkplate6/display.py index 8e00a69751..e4c71ea717 100644 --- a/esphome/components/inkplate6/display.py +++ b/esphome/components/inkplate6/display.py @@ -8,11 +8,9 @@ from esphome.const import ( CONF_LAMBDA, CONF_PAGES, CONF_WAKEUP_PIN, - ESP_PLATFORM_ESP32, ) -DEPENDENCIES = ["i2c"] -ESP_PLATFORMS = [ESP_PLATFORM_ESP32] +DEPENDENCIES = ["i2c", "esp32"] CONF_DISPLAY_DATA_0_PIN = "display_data_0_pin" CONF_DISPLAY_DATA_1_PIN = "display_data_1_pin" @@ -91,6 +89,7 @@ CONFIG_SCHEMA = cv.All( .extend(cv.polling_component_schema("5s")) .extend(i2c.i2c_device_schema(0x48)), cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), + cv.only_with_arduino, ) diff --git a/esphome/components/inkplate6/inkplate.cpp b/esphome/components/inkplate6/inkplate.cpp index b5cec6cf9e..8a05836db9 100644 --- a/esphome/components/inkplate6/inkplate.cpp +++ b/esphome/components/inkplate6/inkplate.cpp @@ -3,7 +3,9 @@ #include "esphome/core/application.h" #include "esphome/core/helpers.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32_FRAMEWORK_ARDUINO + +#include namespace esphome { namespace inkplate6 { @@ -185,7 +187,7 @@ void Inkplate6::eink_on_() { delay(2); - this->read_byte(0x00, &temperature_, 0); + this->read_register(0x00, nullptr, 0); this->le_pin_->digital_write(false); this->oe_pin_->digital_write(false); @@ -208,7 +210,7 @@ void Inkplate6::fill(Color color) { memset(this->partial_buffer_, fill, this->get_buffer_length_()); } - ESP_LOGV(TAG, "Fill finished (%lums)", millis() - start_time); + ESP_LOGV(TAG, "Fill finished (%ums)", millis() - start_time); } void Inkplate6::display() { ESP_LOGV(TAG, "Display called"); @@ -218,12 +220,12 @@ void Inkplate6::display() { this->display3b_(); } else { if (this->partial_updating_ && this->partial_update_()) { - ESP_LOGV(TAG, "Display finished (partial) (%lums)", millis() - start_time); + ESP_LOGV(TAG, "Display finished (partial) (%ums)", millis() - start_time); return; } this->display1b_(); } - ESP_LOGV(TAG, "Display finished (full) (%lums)", millis() - start_time); + ESP_LOGV(TAG, "Display finished (full) (%ums)", millis() - start_time); } void Inkplate6::display1b_() { ESP_LOGV(TAG, "Display1b called"); @@ -246,32 +248,32 @@ void Inkplate6::display1b_() { clean_fast_(0, 11); uint32_t clock = (1 << this->cl_pin_->get_pin()); - ESP_LOGV(TAG, "Display1b start loops (%lums)", millis() - start_time); + ESP_LOGV(TAG, "Display1b start loops (%ums)", millis() - start_time); for (int k = 0; k < 3; k++) { buffer_ptr = &this->buffer_[this->get_buffer_length_() - 1]; vscan_start_(); for (int i = 0; i < this->get_height_internal(); i++) { buffer_value = *(buffer_ptr--); data = LUTB[(buffer_value >> 4) & 0x0F]; - send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25); + send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) | + (((data & 0b11100000) >> 5) << 25); hscan_start_(send); data = LUTB[buffer_value & 0x0F]; - send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25) | clock; + send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) | + (((data & 0b11100000) >> 5) << 25) | clock; GPIO.out_w1ts = send; GPIO.out_w1tc = send; for (int j = 0, jm = (this->get_width_internal() / 8) - 1; j < jm; j++) { buffer_value = *(buffer_ptr--); data = LUTB[(buffer_value >> 4) & 0x0F]; - send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25) | clock; + send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) | + (((data & 0b11100000) >> 5) << 25) | clock; GPIO.out_w1ts = send; GPIO.out_w1tc = send; data = LUTB[buffer_value & 0x0F]; - send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25) | clock; + send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) | + (((data & 0b11100000) >> 5) << 25) | clock; GPIO.out_w1ts = send; GPIO.out_w1tc = send; } @@ -281,31 +283,31 @@ void Inkplate6::display1b_() { } delayMicroseconds(230); } - ESP_LOGV(TAG, "Display1b first loop x %d (%lums)", 3, millis() - start_time); + ESP_LOGV(TAG, "Display1b first loop x %d (%ums)", 3, millis() - start_time); buffer_ptr = &this->buffer_[this->get_buffer_length_() - 1]; vscan_start_(); for (int i = 0; i < this->get_height_internal(); i++) { buffer_value = *(buffer_ptr--); data = LUT2[(buffer_value >> 4) & 0x0F]; - send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25); + send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) | + (((data & 0b11100000) >> 5) << 25); hscan_start_(send); data = LUT2[buffer_value & 0x0F]; - send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25) | clock; + send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) | + (((data & 0b11100000) >> 5) << 25) | clock; GPIO.out_w1ts = send; GPIO.out_w1tc = send; for (int j = 0, jm = (this->get_width_internal() / 8) - 1; j < jm; j++) { buffer_value = *(buffer_ptr--); data = LUT2[(buffer_value >> 4) & 0x0F]; - send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25) | clock; + send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) | + (((data & 0b11100000) >> 5) << 25) | clock; GPIO.out_w1ts = send; GPIO.out_w1tc = send; data = LUT2[buffer_value & 0x0F]; - send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25) | clock; + send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) | + (((data & 0b11100000) >> 5) << 25) | clock; GPIO.out_w1ts = send; GPIO.out_w1tc = send; } @@ -314,13 +316,13 @@ void Inkplate6::display1b_() { vscan_end_(); } delayMicroseconds(230); - ESP_LOGV(TAG, "Display1b second loop (%lums)", millis() - start_time); + ESP_LOGV(TAG, "Display1b second loop (%ums)", millis() - start_time); vscan_start_(); for (int i = 0; i < this->get_height_internal(); i++) { data = 0b00000000; - send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25); + send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) | + (((data & 0b11100000) >> 5) << 25); hscan_start_(send); send |= clock; GPIO.out_w1ts = send; @@ -336,13 +338,13 @@ void Inkplate6::display1b_() { vscan_end_(); } delayMicroseconds(230); - ESP_LOGV(TAG, "Display1b third loop (%lums)", millis() - start_time); + ESP_LOGV(TAG, "Display1b third loop (%ums)", millis() - start_time); vscan_start_(); eink_off_(); this->block_partial_ = false; this->partial_updates_ = 0; - ESP_LOGV(TAG, "Display1b finished (%lums)", millis() - start_time); + ESP_LOGV(TAG, "Display1b finished (%ums)", millis() - start_time); } void Inkplate6::display3b_() { ESP_LOGV(TAG, "Display3b called"); @@ -380,11 +382,11 @@ void Inkplate6::display3b_() { pixel2 = (waveform3Bit[pix3 & 0x07][k] << 6) | (waveform3Bit[(pix3 >> 4) & 0x07][k] << 4) | (waveform3Bit[pix4 & 0x07][k] << 2) | (waveform3Bit[(pix4 >> 4) & 0x07][k] << 0); - send = ((pixel & B00000011) << 4) | (((pixel & B00001100) >> 2) << 18) | (((pixel & B00010000) >> 4) << 23) | - (((pixel & B11100000) >> 5) << 25); + send = ((pixel & 0b00000011) << 4) | (((pixel & 0b00001100) >> 2) << 18) | (((pixel & 0b00010000) >> 4) << 23) | + (((pixel & 0b11100000) >> 5) << 25); hscan_start_(send); - send = ((pixel2 & B00000011) << 4) | (((pixel2 & B00001100) >> 2) << 18) | (((pixel2 & B00010000) >> 4) << 23) | - (((pixel2 & B11100000) >> 5) << 25) | clock; + send = ((pixel2 & 0b00000011) << 4) | (((pixel2 & 0b00001100) >> 2) << 18) | + (((pixel2 & 0b00010000) >> 4) << 23) | (((pixel2 & 0b11100000) >> 5) << 25) | clock; GPIO.out_w1ts = send; GPIO.out_w1tc = send; @@ -398,13 +400,13 @@ void Inkplate6::display3b_() { pixel2 = (waveform3Bit[pix3 & 0x07][k] << 6) | (waveform3Bit[(pix3 >> 4) & 0x07][k] << 4) | (waveform3Bit[pix4 & 0x07][k] << 2) | (waveform3Bit[(pix4 >> 4) & 0x07][k] << 0); - send = ((pixel & B00000011) << 4) | (((pixel & B00001100) >> 2) << 18) | (((pixel & B00010000) >> 4) << 23) | - (((pixel & B11100000) >> 5) << 25) | clock; + send = ((pixel & 0b00000011) << 4) | (((pixel & 0b00001100) >> 2) << 18) | (((pixel & 0b00010000) >> 4) << 23) | + (((pixel & 0b11100000) >> 5) << 25) | clock; GPIO.out_w1ts = send; GPIO.out_w1tc = send; - send = ((pixel2 & B00000011) << 4) | (((pixel2 & B00001100) >> 2) << 18) | (((pixel2 & B00010000) >> 4) << 23) | - (((pixel2 & B11100000) >> 5) << 25) | clock; + send = ((pixel2 & 0b00000011) << 4) | (((pixel2 & 0b00001100) >> 2) << 18) | + (((pixel2 & 0b00010000) >> 4) << 23) | (((pixel2 & 0b11100000) >> 5) << 25) | clock; GPIO.out_w1ts = send; GPIO.out_w1tc = send; } @@ -418,7 +420,7 @@ void Inkplate6::display3b_() { clean_fast_(3, 1); vscan_start_(); eink_off_(); - ESP_LOGV(TAG, "Display3b finished (%lums)", millis() - start_time); + ESP_LOGV(TAG, "Display3b finished (%ums)", millis() - start_time); } bool Inkplate6::partial_update_() { ESP_LOGV(TAG, "Partial update called"); @@ -445,7 +447,7 @@ bool Inkplate6::partial_update_() { this->partial_buffer_2_[n--] = LUTW[diffw & 0x0F] & LUTB[diffb & 0x0F]; } } - ESP_LOGV(TAG, "Partial update buffer built after (%lums)", millis() - start_time); + ESP_LOGV(TAG, "Partial update buffer built after (%ums)", millis() - start_time); eink_on_(); uint32_t clock = (1 << this->cl_pin_->get_pin()); @@ -454,13 +456,13 @@ bool Inkplate6::partial_update_() { const uint8_t *data_ptr = &this->partial_buffer_2_[(this->get_buffer_length_() * 2) - 1]; for (int i = 0; i < this->get_height_internal(); i++) { data = *(data_ptr--); - send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25); + send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) | + (((data & 0b11100000) >> 5) << 25); hscan_start_(send); for (int j = 0, jm = (this->get_width_internal() / 4) - 1; j < jm; j++) { data = *(data_ptr--); - send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25) | clock; + send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) | + (((data & 0b11100000) >> 5) << 25) | clock; GPIO.out_w1ts = send; GPIO.out_w1tc = send; } @@ -469,7 +471,7 @@ bool Inkplate6::partial_update_() { vscan_end_(); } delayMicroseconds(230); - ESP_LOGV(TAG, "Partial update loop k=%d (%lums)", k, millis() - start_time); + ESP_LOGV(TAG, "Partial update loop k=%d (%ums)", k, millis() - start_time); } clean_fast_(2, 2); clean_fast_(3, 1); @@ -477,7 +479,7 @@ bool Inkplate6::partial_update_() { eink_off_(); memcpy(this->buffer_, this->partial_buffer_, this->get_buffer_length_()); - ESP_LOGV(TAG, "Partial update finished (%lums)", millis() - start_time); + ESP_LOGV(TAG, "Partial update finished (%ums)", millis() - start_time); return true; } void Inkplate6::vscan_start_() { @@ -538,7 +540,7 @@ void Inkplate6::clean() { clean_fast_(0, 8); // Black to Black clean_fast_(2, 1); // Black to White clean_fast_(1, 10); // White to White - ESP_LOGV(TAG, "Clean finished (%lums)", millis() - start_time); + ESP_LOGV(TAG, "Clean finished (%ums)", millis() - start_time); } void Inkplate6::clean_fast_(uint8_t c, uint8_t rep) { ESP_LOGV(TAG, "Clean fast called with: (%d, %d)", c, rep); @@ -547,16 +549,16 @@ void Inkplate6::clean_fast_(uint8_t c, uint8_t rep) { eink_on_(); uint8_t data = 0; if (c == 0) // White - data = B10101010; + data = 0b10101010; else if (c == 1) // Black - data = B01010101; + data = 0b01010101; else if (c == 2) // Discharge - data = B00000000; + data = 0b00000000; else if (c == 3) // Skip - data = B11111111; + data = 0b11111111; - uint32_t send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25); + uint32_t send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) | + (((data & 0b11100000) >> 5) << 25); uint32_t clock = (1 << this->cl_pin_->get_pin()); for (int k = 0; k < rep; k++) { @@ -576,46 +578,46 @@ void Inkplate6::clean_fast_(uint8_t c, uint8_t rep) { vscan_end_(); } delayMicroseconds(230); - ESP_LOGV(TAG, "Clean fast rep loop %d finished (%lums)", k, millis() - start_time); + ESP_LOGV(TAG, "Clean fast rep loop %d finished (%ums)", k, millis() - start_time); } - ESP_LOGV(TAG, "Clean fast finished (%lums)", millis() - start_time); + ESP_LOGV(TAG, "Clean fast finished (%ums)", millis() - start_time); } void Inkplate6::pins_z_state_() { - this->ckv_pin_->pin_mode(INPUT); - this->sph_pin_->pin_mode(INPUT); + this->ckv_pin_->pin_mode(gpio::FLAG_INPUT); + this->sph_pin_->pin_mode(gpio::FLAG_INPUT); - this->oe_pin_->pin_mode(INPUT); - this->gmod_pin_->pin_mode(INPUT); - this->spv_pin_->pin_mode(INPUT); + this->oe_pin_->pin_mode(gpio::FLAG_INPUT); + this->gmod_pin_->pin_mode(gpio::FLAG_INPUT); + this->spv_pin_->pin_mode(gpio::FLAG_INPUT); - this->display_data_0_pin_->pin_mode(INPUT); - this->display_data_1_pin_->pin_mode(INPUT); - this->display_data_2_pin_->pin_mode(INPUT); - this->display_data_3_pin_->pin_mode(INPUT); - this->display_data_4_pin_->pin_mode(INPUT); - this->display_data_5_pin_->pin_mode(INPUT); - this->display_data_6_pin_->pin_mode(INPUT); - this->display_data_7_pin_->pin_mode(INPUT); + this->display_data_0_pin_->pin_mode(gpio::FLAG_INPUT); + this->display_data_1_pin_->pin_mode(gpio::FLAG_INPUT); + this->display_data_2_pin_->pin_mode(gpio::FLAG_INPUT); + this->display_data_3_pin_->pin_mode(gpio::FLAG_INPUT); + this->display_data_4_pin_->pin_mode(gpio::FLAG_INPUT); + this->display_data_5_pin_->pin_mode(gpio::FLAG_INPUT); + this->display_data_6_pin_->pin_mode(gpio::FLAG_INPUT); + this->display_data_7_pin_->pin_mode(gpio::FLAG_INPUT); } void Inkplate6::pins_as_outputs_() { - this->ckv_pin_->pin_mode(OUTPUT); - this->sph_pin_->pin_mode(OUTPUT); + this->ckv_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->sph_pin_->pin_mode(gpio::FLAG_OUTPUT); - this->oe_pin_->pin_mode(OUTPUT); - this->gmod_pin_->pin_mode(OUTPUT); - this->spv_pin_->pin_mode(OUTPUT); + this->oe_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->gmod_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->spv_pin_->pin_mode(gpio::FLAG_OUTPUT); - this->display_data_0_pin_->pin_mode(OUTPUT); - this->display_data_1_pin_->pin_mode(OUTPUT); - this->display_data_2_pin_->pin_mode(OUTPUT); - this->display_data_3_pin_->pin_mode(OUTPUT); - this->display_data_4_pin_->pin_mode(OUTPUT); - this->display_data_5_pin_->pin_mode(OUTPUT); - this->display_data_6_pin_->pin_mode(OUTPUT); - this->display_data_7_pin_->pin_mode(OUTPUT); + this->display_data_0_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->display_data_1_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->display_data_2_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->display_data_3_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->display_data_4_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->display_data_5_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->display_data_6_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->display_data_7_pin_->pin_mode(gpio::FLAG_OUTPUT); } } // namespace inkplate6 } // namespace esphome -#endif +#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/inkplate6/inkplate.h b/esphome/components/inkplate6/inkplate.h index f2821b22a9..56e95e95bb 100644 --- a/esphome/components/inkplate6/inkplate.h +++ b/esphome/components/inkplate6/inkplate.h @@ -1,26 +1,29 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/i2c/i2c.h" #include "esphome/components/display/display_buffer.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32_FRAMEWORK_ARDUINO namespace esphome { namespace inkplate6 { class Inkplate6 : public PollingComponent, public display::DisplayBuffer, public i2c::I2CDevice { public: - const uint8_t LUT2[16] = {B10101010, B10101001, B10100110, B10100101, B10011010, B10011001, B10010110, B10010101, - B01101010, B01101001, B01100110, B01100101, B01011010, B01011001, B01010110, B01010101}; - const uint8_t LUTW[16] = {B11111111, B11111110, B11111011, B11111010, B11101111, B11101110, B11101011, B11101010, - B10111111, B10111110, B10111011, B10111010, B10101111, B10101110, B10101011, B10101010}; - const uint8_t LUTB[16] = {B11111111, B11111101, B11110111, B11110101, B11011111, B11011101, B11010111, B11010101, - B01111111, B01111101, B01110111, B01110101, B01011111, B01011101, B01010111, B01010101}; - const uint8_t pixelMaskLUT[8] = {B00000001, B00000010, B00000100, B00001000, - B00010000, B00100000, B01000000, B10000000}; - const uint8_t pixelMaskGLUT[2] = {B00001111, B11110000}; + const uint8_t LUT2[16] = {0b10101010, 0b10101001, 0b10100110, 0b10100101, 0b10011010, 0b10011001, + 0b10010110, 0b10010101, 0b01101010, 0b01101001, 0b01100110, 0b01100101, + 0b01011010, 0b01011001, 0b01010110, 0b01010101}; + const uint8_t LUTW[16] = {0b11111111, 0b11111110, 0b11111011, 0b11111010, 0b11101111, 0b11101110, + 0b11101011, 0b11101010, 0b10111111, 0b10111110, 0b10111011, 0b10111010, + 0b10101111, 0b10101110, 0b10101011, 0b10101010}; + const uint8_t LUTB[16] = {0b11111111, 0b11111101, 0b11110111, 0b11110101, 0b11011111, 0b11011101, + 0b11010111, 0b11010101, 0b01111111, 0b01111101, 0b01110111, 0b01110101, + 0b01011111, 0b01011101, 0b01010111, 0b01010101}; + const uint8_t pixelMaskLUT[8] = {0b00000001, 0b00000010, 0b00000100, 0b00001000, + 0b00010000, 0b00100000, 0b01000000, 0b10000000}; + const uint8_t pixelMaskGLUT[2] = {0b00001111, 0b11110000}; const uint8_t waveform3Bit[8][8] = {{0, 0, 0, 0, 1, 1, 1, 0}, {1, 2, 2, 2, 1, 1, 1, 0}, {0, 1, 2, 1, 1, 2, 1, 0}, {0, 2, 1, 2, 1, 2, 1, 0}, {0, 0, 0, 1, 1, 1, 2, 0}, {2, 1, 1, 1, 2, 1, 2, 0}, {1, 1, 1, 2, 1, 2, 2, 0}, {0, 0, 0, 0, 0, 0, 2, 0}}; @@ -40,20 +43,20 @@ class Inkplate6 : public PollingComponent, public display::DisplayBuffer, public void set_partial_updating(bool partial_updating) { this->partial_updating_ = partial_updating; } void set_full_update_every(uint32_t full_update_every) { this->full_update_every_ = full_update_every; } - void set_display_data_0_pin(GPIOPin *data) { this->display_data_0_pin_ = data; } - void set_display_data_1_pin(GPIOPin *data) { this->display_data_1_pin_ = data; } - void set_display_data_2_pin(GPIOPin *data) { this->display_data_2_pin_ = data; } - void set_display_data_3_pin(GPIOPin *data) { this->display_data_3_pin_ = data; } - void set_display_data_4_pin(GPIOPin *data) { this->display_data_4_pin_ = data; } - void set_display_data_5_pin(GPIOPin *data) { this->display_data_5_pin_ = data; } - void set_display_data_6_pin(GPIOPin *data) { this->display_data_6_pin_ = data; } - void set_display_data_7_pin(GPIOPin *data) { this->display_data_7_pin_ = data; } + void set_display_data_0_pin(InternalGPIOPin *data) { this->display_data_0_pin_ = data; } + void set_display_data_1_pin(InternalGPIOPin *data) { this->display_data_1_pin_ = data; } + void set_display_data_2_pin(InternalGPIOPin *data) { this->display_data_2_pin_ = data; } + void set_display_data_3_pin(InternalGPIOPin *data) { this->display_data_3_pin_ = data; } + void set_display_data_4_pin(InternalGPIOPin *data) { this->display_data_4_pin_ = data; } + void set_display_data_5_pin(InternalGPIOPin *data) { this->display_data_5_pin_ = data; } + void set_display_data_6_pin(InternalGPIOPin *data) { this->display_data_6_pin_ = data; } + void set_display_data_7_pin(InternalGPIOPin *data) { this->display_data_7_pin_ = data; } void set_ckv_pin(GPIOPin *ckv) { this->ckv_pin_ = ckv; } - void set_cl_pin(GPIOPin *cl) { this->cl_pin_ = cl; } + void set_cl_pin(InternalGPIOPin *cl) { this->cl_pin_ = cl; } void set_gpio0_enable_pin(GPIOPin *gpio0_enable) { this->gpio0_enable_pin_ = gpio0_enable; } void set_gmod_pin(GPIOPin *gmod) { this->gmod_pin_ = gmod; } - void set_le_pin(GPIOPin *le) { this->le_pin_ = le; } + void set_le_pin(InternalGPIOPin *le) { this->le_pin_ = le; } void set_oe_pin(GPIOPin *oe) { this->oe_pin_ = oe; } void set_powerup_pin(GPIOPin *powerup) { this->powerup_pin_ = powerup; } void set_sph_pin(GPIOPin *sph) { this->sph_pin_ = sph; } @@ -130,20 +133,20 @@ class Inkplate6 : public PollingComponent, public display::DisplayBuffer, public bool greyscale_; bool partial_updating_; - GPIOPin *display_data_0_pin_; - GPIOPin *display_data_1_pin_; - GPIOPin *display_data_2_pin_; - GPIOPin *display_data_3_pin_; - GPIOPin *display_data_4_pin_; - GPIOPin *display_data_5_pin_; - GPIOPin *display_data_6_pin_; - GPIOPin *display_data_7_pin_; + InternalGPIOPin *display_data_0_pin_; + InternalGPIOPin *display_data_1_pin_; + InternalGPIOPin *display_data_2_pin_; + InternalGPIOPin *display_data_3_pin_; + InternalGPIOPin *display_data_4_pin_; + InternalGPIOPin *display_data_5_pin_; + InternalGPIOPin *display_data_6_pin_; + InternalGPIOPin *display_data_7_pin_; GPIOPin *ckv_pin_; - GPIOPin *cl_pin_; + InternalGPIOPin *cl_pin_; GPIOPin *gpio0_enable_pin_; GPIOPin *gmod_pin_; - GPIOPin *le_pin_; + InternalGPIOPin *le_pin_; GPIOPin *oe_pin_; GPIOPin *powerup_pin_; GPIOPin *sph_pin_; @@ -155,4 +158,4 @@ class Inkplate6 : public PollingComponent, public display::DisplayBuffer, public } // namespace inkplate6 } // namespace esphome -#endif +#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/integration/integration_sensor.cpp b/esphome/components/integration/integration_sensor.cpp index 9a0f9fc58b..2a398e5240 100644 --- a/esphome/components/integration/integration_sensor.cpp +++ b/esphome/components/integration/integration_sensor.cpp @@ -1,6 +1,7 @@ #include "integration_sensor.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace integration { @@ -9,7 +10,7 @@ static const char *const TAG = "integration"; void IntegrationSensor::setup() { if (this->restore_) { - this->rtc_ = global_preferences.make_preference(this->get_object_id_hash()); + this->rtc_ = global_preferences->make_preference(this->get_object_id_hash()); float preference_value = 0; this->rtc_.load(&preference_value); this->result_ = preference_value; diff --git a/esphome/components/integration/integration_sensor.h b/esphome/components/integration/integration_sensor.h index c575de094c..a3bdfbbb2b 100644 --- a/esphome/components/integration/integration_sensor.h +++ b/esphome/components/integration/integration_sensor.h @@ -3,6 +3,7 @@ #include "esphome/core/component.h" #include "esphome/core/preferences.h" #include "esphome/core/automation.h" +#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" namespace esphome { diff --git a/esphome/components/json/__init__.py b/esphome/components/json/__init__.py index 9879754d9e..fda0a552f1 100644 --- a/esphome/components/json/__init__.py +++ b/esphome/components/json/__init__.py @@ -1,9 +1,15 @@ import esphome.codegen as cg +import esphome.config_validation as cv from esphome.core import coroutine_with_priority CODEOWNERS = ["@OttoWinter"] json_ns = cg.esphome_ns.namespace("json") +CONFIG_SCHEMA = cv.All( + cv.Schema({}), + cv.only_with_arduino, +) + @coroutine_with_priority(1.0) async def to_code(config): diff --git a/esphome/components/json/json_util.cpp b/esphome/components/json/json_util.cpp index aab5b1ce9b..12c5beb73f 100644 --- a/esphome/components/json/json_util.cpp +++ b/esphome/components/json/json_util.cpp @@ -1,3 +1,5 @@ +#ifdef USE_ARDUINO + #include "json_util.h" #include "esphome/core/log.h" @@ -113,3 +115,5 @@ VectorJsonBuffer global_json_buffer; // NOLINT(cppcoreguidelines-avoid-non-cons } // namespace json } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/json/json_util.h b/esphome/components/json/json_util.h index 65b2496b85..577510e63a 100644 --- a/esphome/components/json/json_util.h +++ b/esphome/components/json/json_util.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include #include "esphome/core/helpers.h" @@ -62,3 +64,5 @@ extern VectorJsonBuffer global_json_buffer; // NOLINT(cppcoreguidelines-avoid-n } // namespace json } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/lcd_base/lcd_display.cpp b/esphome/components/lcd_base/lcd_display.cpp index 4c0d0ab3ea..ddd7d6a6b3 100644 --- a/esphome/components/lcd_base/lcd_display.cpp +++ b/esphome/components/lcd_base/lcd_display.cpp @@ -1,6 +1,7 @@ #include "lcd_display.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/hal.h" namespace esphome { namespace lcd_base { diff --git a/esphome/components/lcd_gpio/gpio_lcd_display.cpp b/esphome/components/lcd_gpio/gpio_lcd_display.cpp index 5c1656ec3e..b0344d313c 100644 --- a/esphome/components/lcd_gpio/gpio_lcd_display.cpp +++ b/esphome/components/lcd_gpio/gpio_lcd_display.cpp @@ -30,8 +30,15 @@ void GPIOLCDDisplay::dump_config() { LOG_PIN(" RW Pin: ", this->rw_pin_); LOG_PIN(" Enable Pin: ", this->enable_pin_); - for (uint8_t i = 0; i < (this->is_four_bit_mode() ? 4 : 8); i++) { - ESP_LOGCONFIG(TAG, " Data Pin %u" LOG_PIN_PATTERN, i, LOG_PIN_ARGS(this->data_pins_[i])); + LOG_PIN(" Data Pin 0: ", data_pins_[0]); + LOG_PIN(" Data Pin 1: ", data_pins_[1]); + LOG_PIN(" Data Pin 2: ", data_pins_[2]); + LOG_PIN(" Data Pin 3: ", data_pins_[3]); + if (!is_four_bit_mode()) { + LOG_PIN(" Data Pin 4: ", data_pins_[4]); + LOG_PIN(" Data Pin 5: ", data_pins_[5]); + LOG_PIN(" Data Pin 6: ", data_pins_[6]); + LOG_PIN(" Data Pin 7: ", data_pins_[7]); } LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/lcd_gpio/gpio_lcd_display.h b/esphome/components/lcd_gpio/gpio_lcd_display.h index 01f6f95d9a..aba254a90a 100644 --- a/esphome/components/lcd_gpio/gpio_lcd_display.h +++ b/esphome/components/lcd_gpio/gpio_lcd_display.h @@ -1,6 +1,6 @@ #pragma once -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/lcd_base/lcd_display.h" namespace esphome { diff --git a/esphome/components/lcd_pcf8574/pcf8574_display.cpp b/esphome/components/lcd_pcf8574/pcf8574_display.cpp index 4830b6f223..5b00b08aff 100644 --- a/esphome/components/lcd_pcf8574/pcf8574_display.cpp +++ b/esphome/components/lcd_pcf8574/pcf8574_display.cpp @@ -1,5 +1,6 @@ #include "pcf8574_display.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace lcd_pcf8574 { diff --git a/esphome/components/ledc/ledc_output.cpp b/esphome/components/ledc/ledc_output.cpp index 45bee5b871..77610a476f 100644 --- a/esphome/components/ledc/ledc_output.cpp +++ b/esphome/components/ledc/ledc_output.cpp @@ -1,40 +1,20 @@ #include "ledc_output.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 +#ifdef USE_ARDUINO #include +#endif +#ifdef USE_ESP_IDF +#include +#endif namespace esphome { namespace ledc { static const char *const TAG = "ledc.output"; -void LEDCOutput::write_state(float state) { - if (this->pin_->is_inverted()) - state = 1.0f - state; - - this->duty_ = state; - const uint32_t max_duty = (uint32_t(1) << this->bit_depth_) - 1; - const float duty_rounded = roundf(state * max_duty); - auto duty = static_cast(duty_rounded); - ledcWrite(this->channel_, duty); -} - -void LEDCOutput::setup() { - this->update_frequency(this->frequency_); - this->turn_off(); - // Attach pin after setting default value - ledcAttachPin(this->pin_->get_pin(), this->channel_); -} - -void LEDCOutput::dump_config() { - ESP_LOGCONFIG(TAG, "LEDC Output:"); - LOG_PIN(" Pin ", this->pin_); - ESP_LOGCONFIG(TAG, " LEDC Channel: %u", this->channel_); - ESP_LOGCONFIG(TAG, " Frequency: %.1f Hz", this->frequency_); -} - float ledc_max_frequency_for_bit_depth(uint8_t bit_depth) { return 80e6f / float(1 << bit_depth); } float ledc_min_frequency_for_bit_depth(uint8_t bit_depth) { const float max_div_num = ((1 << 20) - 1) / 256.0f; @@ -50,6 +30,67 @@ optional ledc_bit_depth_for_frequency(float frequency) { return {}; } +void LEDCOutput::write_state(float state) { + if (this->pin_->is_inverted()) + state = 1.0f - state; + + this->duty_ = state; + const uint32_t max_duty = (uint32_t(1) << this->bit_depth_) - 1; + const float duty_rounded = roundf(state * max_duty); + auto duty = static_cast(duty_rounded); + +#ifdef USE_ARDUINO + ledcWrite(this->channel_, duty); +#endif +#ifdef USE_ESP_IDF + auto speed_mode = channel_ < 8 ? LEDC_HIGH_SPEED_MODE : LEDC_LOW_SPEED_MODE; + auto chan_num = static_cast(channel_ % 8); + ledc_set_duty(speed_mode, chan_num, duty); + ledc_update_duty(speed_mode, chan_num); +#endif +} + +void LEDCOutput::setup() { +#ifdef USE_ARDUINO + this->update_frequency(this->frequency_); + this->turn_off(); + // Attach pin after setting default value + ledcAttachPin(this->pin_->get_pin(), this->channel_); +#endif +#ifdef USE_ESP_IDF + auto speed_mode = channel_ < 8 ? LEDC_HIGH_SPEED_MODE : LEDC_LOW_SPEED_MODE; + auto timer_num = static_cast((channel_ % 8) / 2); + auto chan_num = static_cast(channel_ % 8); + + bit_depth_ = *ledc_bit_depth_for_frequency(frequency_); + + ledc_timer_config_t timer_conf{}; + timer_conf.speed_mode = speed_mode; + timer_conf.duty_resolution = static_cast(bit_depth_); + timer_conf.timer_num = timer_num; + timer_conf.freq_hz = (uint32_t) frequency_; + timer_conf.clk_cfg = LEDC_AUTO_CLK; + ledc_timer_config(&timer_conf); + + ledc_channel_config_t chan_conf{}; + chan_conf.gpio_num = pin_->get_pin(); + chan_conf.speed_mode = speed_mode; + chan_conf.channel = chan_num; + chan_conf.intr_type = LEDC_INTR_DISABLE; + chan_conf.timer_sel = timer_num; + chan_conf.duty = inverted_ == pin_->is_inverted() ? 0 : (1U << bit_depth_); + chan_conf.hpoint = 0; + ledc_channel_config(&chan_conf); +#endif +} + +void LEDCOutput::dump_config() { + ESP_LOGCONFIG(TAG, "LEDC Output:"); + LOG_PIN(" Pin ", this->pin_); + ESP_LOGCONFIG(TAG, " LEDC Channel: %u", this->channel_); + ESP_LOGCONFIG(TAG, " Frequency: %.1f Hz", this->frequency_); +} + void LEDCOutput::update_frequency(float frequency) { auto bit_depth_opt = ledc_bit_depth_for_frequency(frequency); if (!bit_depth_opt.has_value()) { @@ -58,7 +99,21 @@ void LEDCOutput::update_frequency(float frequency) { } this->bit_depth_ = bit_depth_opt.value_or(8); this->frequency_ = frequency; +#ifdef USE_ARDUINO ledcSetup(this->channel_, frequency, this->bit_depth_); +#endif // USE_ARDUINO +#ifdef USE_ESP_IDF + auto speed_mode = channel_ < 8 ? LEDC_HIGH_SPEED_MODE : LEDC_LOW_SPEED_MODE; + auto timer_num = static_cast((channel_ % 8) / 2); + + ledc_timer_config_t timer_conf{}; + timer_conf.speed_mode = speed_mode; + timer_conf.duty_resolution = static_cast(bit_depth_); + timer_conf.timer_num = timer_num; + timer_conf.freq_hz = (uint32_t) frequency_; + timer_conf.clk_cfg = LEDC_AUTO_CLK; + ledc_timer_config(&timer_conf); +#endif // re-apply duty this->write_state(this->duty_); } diff --git a/esphome/components/ledc/ledc_output.h b/esphome/components/ledc/ledc_output.h index b3b14fe855..f810ce1e35 100644 --- a/esphome/components/ledc/ledc_output.h +++ b/esphome/components/ledc/ledc_output.h @@ -1,11 +1,11 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/core/automation.h" #include "esphome/components/output/float_output.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ledc { @@ -14,7 +14,7 @@ extern uint8_t next_ledc_channel; class LEDCOutput : public output::FloatOutput, public Component { public: - explicit LEDCOutput(GPIOPin *pin) : pin_(pin) { this->channel_ = next_ledc_channel++; } + explicit LEDCOutput(InternalGPIOPin *pin) : pin_(pin) { this->channel_ = next_ledc_channel++; } void set_channel(uint8_t channel) { this->channel_ = channel; } void set_frequency(float frequency) { this->frequency_ = frequency; } @@ -31,7 +31,7 @@ class LEDCOutput : public output::FloatOutput, public Component { void write_state(float state) override; protected: - GPIOPin *pin_; + InternalGPIOPin *pin_; uint8_t channel_{}; uint8_t bit_depth_{}; float frequency_{}; diff --git a/esphome/components/ledc/output.py b/esphome/components/ledc/output.py index 0f5f186ff5..895dcc998b 100644 --- a/esphome/components/ledc/output.py +++ b/esphome/components/ledc/output.py @@ -7,10 +7,9 @@ from esphome.const import ( CONF_FREQUENCY, CONF_ID, CONF_PIN, - ESP_PLATFORM_ESP32, ) -ESP_PLATFORMS = [ESP_PLATFORM_ESP32] +DEPENDENCIES = ["esp32"] def calc_max_frequency(bit_depth): diff --git a/esphome/components/light/light_state.cpp b/esphome/components/light/light_state.cpp index 398546a09f..f888006b17 100644 --- a/esphome/components/light/light_state.cpp +++ b/esphome/components/light/light_state.cpp @@ -53,7 +53,7 @@ void LightState::setup() { case LIGHT_RESTORE_DEFAULT_ON: case LIGHT_RESTORE_INVERTED_DEFAULT_OFF: case LIGHT_RESTORE_INVERTED_DEFAULT_ON: - this->rtc_ = global_preferences.make_preference(this->get_object_id_hash()); + this->rtc_ = global_preferences->make_preference(this->get_object_id_hash()); // Attempt to load from preferences, else fall back to default values if (!this->rtc_.load(&recovered)) { recovered.state = false; diff --git a/esphome/components/light/light_transformer.h b/esphome/components/light/light_transformer.h index c5181abd4f..dd904d0eed 100644 --- a/esphome/components/light/light_transformer.h +++ b/esphome/components/light/light_transformer.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/helpers.h" +#include "esphome/core/hal.h" #include "light_color_values.h" namespace esphome { @@ -9,6 +10,8 @@ namespace light { /// Base class for all light color transformers, such as transitions or flashes. class LightTransformer { public: + virtual ~LightTransformer() = default; + void setup(const LightColorValues &start_values, const LightColorValues &target_values, uint32_t length) { this->start_time_ = millis(); this->length_ = length; diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index b821e8c5d8..bc1bc6bb41 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -214,7 +214,7 @@ def validate_printf(value): (?:\.(?:\d+|\*))? # precision (?:h|l|ll|w|I|I32|I64)? # size [cCdiouxXeEfgGaAnpsSZ] # type - ) + ) """ # noqa matches = re.findall(cfmt, value[CONF_FORMAT], flags=re.X) if len(matches) != len(value[CONF_ARGS]): diff --git a/esphome/components/logger/logger.cpp b/esphome/components/logger/logger.cpp index 59dfa93429..045f7059a9 100644 --- a/esphome/components/logger/logger.cpp +++ b/esphome/components/logger/logger.cpp @@ -1,9 +1,15 @@ #include "logger.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP_IDF +#include "freertos/FreeRTOS.h" +#include +#endif + +#if defined(USE_ESP32_FRAMEWORK_ARDUINO) || defined(USE_ESP_IDF) #include #endif -#include +#include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace logger { @@ -63,11 +69,11 @@ void Logger::log_vprintf_(int level, const char *tag, int line, const __FlashStr recursion_guard_ = true; this->reset_buffer_(); // copy format string - const char *format_pgm_p = (PGM_P) format; + auto *format_pgm_p = reinterpret_cast(format); size_t len = 0; char ch = '.'; while (!this->is_buffer_full_() && ch != '\0') { - this->tx_buffer_[this->tx_buffer_at_++] = ch = pgm_read_byte(format_pgm_p++); + this->tx_buffer_[this->tx_buffer_at_++] = ch = (char) progmem_read_byte(format_pgm_p++); } // Buffer full form copying format if (this->is_buffer_full_()) @@ -105,19 +111,26 @@ void HOT Logger::log_message_(int level, const char *tag, int offset) { this->set_null_terminator_(); const char *msg = this->tx_buffer_ + offset; +#ifdef USE_ARDUINO if (this->baud_rate_ > 0) this->hw_serial_->println(msg); -#ifdef ARDUINO_ARCH_ESP32 +#endif // USE_ARDUINO +#ifdef USE_ESP_IDF + uart_write_bytes(uart_num_, msg, strlen(msg)); + uart_write_bytes(uart_num_, "\n", 1); +#endif + +#ifdef USE_ESP32 // Suppress network-logging if memory constrained, but still log to serial // ports. In some configurations (eg BLE enabled) there may be some transient // memory exhaustion, and trying to log when OOM can lead to a crash. Skipping // here usually allows the stack to recover instead. // See issue #1234 for analysis. - if (xPortGetFreeHeapSize() > 2048) - this->log_callback_.call(level, tag, msg); -#else - this->log_callback_.call(level, tag, msg); + if (xPortGetFreeHeapSize() < 2048) + return; #endif + + this->log_callback_.call(level, tag, msg); } Logger::Logger(uint32_t baud_rate, size_t tx_buffer_size, UARTSelection uart) @@ -128,9 +141,10 @@ Logger::Logger(uint32_t baud_rate, size_t tx_buffer_size, UARTSelection uart) void Logger::pre_setup() { if (this->baud_rate_ > 0) { +#ifdef USE_ARDUINO switch (this->uart_) { case UART_SELECTION_UART0: -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 case UART_SELECTION_UART0_SWAP: #endif this->hw_serial_ = &Serial; @@ -138,7 +152,7 @@ void Logger::pre_setup() { case UART_SELECTION_UART1: this->hw_serial_ = &Serial1; break; -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 case UART_SELECTION_UART2: #if !CONFIG_IDF_TARGET_ESP32S2 && !CONFIG_IDF_TARGET_ESP32C3 // FIXME: Validate in config that UART2 can't be set for ESP32-S2 (only has @@ -148,23 +162,51 @@ void Logger::pre_setup() { break; #endif } +#endif // USE_ARDUINO +#ifdef USE_ESP_IDF + uart_num_ = UART_NUM_0; + switch (uart_) { + case UART_SELECTION_UART0: + uart_num_ = UART_NUM_0; + break; + case UART_SELECTION_UART1: + uart_num_ = UART_NUM_1; + break; + case UART_SELECTION_UART2: + uart_num_ = UART_NUM_2; + break; + } + uart_config_t uart_config = { + .baud_rate = (int) baud_rate_, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + }; + uart_param_config(uart_num_, &uart_config); + const int uart_buffer_size = tx_buffer_size_; + // Install UART driver using an event queue here + uart_driver_install(uart_num_, uart_buffer_size, uart_buffer_size, 10, nullptr, 0); +#endif +#ifdef USE_ARDUINO this->hw_serial_->begin(this->baud_rate_); -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 if (this->uart_ == UART_SELECTION_UART0_SWAP) { this->hw_serial_->swap(); } this->hw_serial_->setDebugOutput(ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE); #endif +#endif // USE_ARDUINO } -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 else { uart_set_debug(UART_NO); } #endif global_logger = this; -#ifdef ARDUINO_ARCH_ESP32 +#if defined(USE_ESP_IDF) || defined(USE_ESP32_FRAMEWORK_ARDUINO) esp_log_set_vprintf(esp_idf_log_vprintf_); if (ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE) { esp_log_level_set("*", ESP_LOG_VERBOSE); @@ -183,10 +225,10 @@ void Logger::add_on_log_callback(std::function + +#ifdef USE_ARDUINO +#include +#endif +#ifdef USE_ESP_IDF +#include +#endif namespace esphome { @@ -17,11 +24,11 @@ namespace logger { enum UARTSelection { UART_SELECTION_UART0 = 0, UART_SELECTION_UART1, -#ifdef ARDUINO_ARCH_ESP32 - UART_SELECTION_UART2 +#ifdef USE_ESP32 + UART_SELECTION_UART2, #endif -#ifdef ARDUINO_ARCH_ESP8266 - UART_SELECTION_UART0_SWAP +#ifdef USE_ESP8266 + UART_SELECTION_UART0_SWAP, #endif }; @@ -32,7 +39,12 @@ class Logger : public Component { /// Manually set the baud rate for serial, set to 0 to disable. void set_baud_rate(uint32_t baud_rate); uint32_t get_baud_rate() const { return baud_rate_; } +#ifdef USE_ARDUINO HardwareSerial *get_hw_serial() const { return hw_serial_; } +#endif +#ifdef USE_ESP_IDF + uart_port_t get_uart_num() const { return uart_num_; } +#endif /// Get the UART used by the logger. UARTSelection get_uart() const; @@ -106,7 +118,12 @@ class Logger : public Component { int tx_buffer_at_{0}; int tx_buffer_size_{0}; UARTSelection uart_{UART_SELECTION_UART0}; +#ifdef USE_ARDUINO HardwareSerial *hw_serial_{nullptr}; +#endif +#ifdef USE_ESP_IDF + uart_port_t uart_num_; +#endif struct LogLevelOverride { std::string tag; int level; diff --git a/esphome/components/max31856/max31856.cpp b/esphome/components/max31856/max31856.cpp index 9b627e21a2..9300916fdc 100644 --- a/esphome/components/max31856/max31856.cpp +++ b/esphome/components/max31856/max31856.cpp @@ -163,7 +163,7 @@ void MAX31856Sensor::write_register_(uint8_t reg, uint8_t value) { ESP_LOGV(TAG, "write_register_ 0x%02X: 0x%02X", reg, value); } -const uint8_t MAX31856Sensor::read_register_(uint8_t reg) { +uint8_t MAX31856Sensor::read_register_(uint8_t reg) { ESP_LOGVV(TAG, "read_register_ 0x%02X", reg); this->enable(); ESP_LOGVV(TAG, "write_byte reg=0x%02X", reg); @@ -175,7 +175,7 @@ const uint8_t MAX31856Sensor::read_register_(uint8_t reg) { return value; } -const uint32_t MAX31856Sensor::read_register24_(uint8_t reg) { +uint32_t MAX31856Sensor::read_register24_(uint8_t reg) { ESP_LOGVV(TAG, "read_register_24_ 0x%02X", reg); this->enable(); ESP_LOGVV(TAG, "write_byte reg=0x%02X", reg); diff --git a/esphome/components/max31856/max31856.h b/esphome/components/max31856/max31856.h index 779eb52c8e..157aad433c 100644 --- a/esphome/components/max31856/max31856.h +++ b/esphome/components/max31856/max31856.h @@ -82,8 +82,8 @@ class MAX31856Sensor : public sensor::Sensor, protected: MAX31856ConfigFilter filter_; - const uint8_t read_register_(uint8_t reg); - const uint32_t read_register24_(uint8_t reg); + uint8_t read_register_(uint8_t reg); + uint32_t read_register24_(uint8_t reg); void write_register_(uint8_t reg, uint8_t value); void one_shot_temperature_(); diff --git a/esphome/components/max31865/max31865.cpp b/esphome/components/max31865/max31865.cpp index daadc26cdc..91946cde2c 100644 --- a/esphome/components/max31865/max31865.cpp +++ b/esphome/components/max31865/max31865.cpp @@ -156,7 +156,7 @@ void MAX31865Sensor::write_register_(uint8_t reg, uint8_t value) { ESP_LOGVV(TAG, "write_register_ 0x%02X: 0x%02X", reg, value); } -const uint8_t MAX31865Sensor::read_register_(uint8_t reg) { +uint8_t MAX31865Sensor::read_register_(uint8_t reg) { this->enable(); this->write_byte(reg); const uint8_t value(this->read_byte()); @@ -165,7 +165,7 @@ const uint8_t MAX31865Sensor::read_register_(uint8_t reg) { return value; } -const uint16_t MAX31865Sensor::read_register_16_(uint8_t reg) { +uint16_t MAX31865Sensor::read_register_16_(uint8_t reg) { this->enable(); this->write_byte(reg); const uint8_t msb(this->read_byte()); @@ -176,7 +176,7 @@ const uint16_t MAX31865Sensor::read_register_16_(uint8_t reg) { return value; } -float MAX31865Sensor::calc_temperature_(const float &rtd_ratio) { +float MAX31865Sensor::calc_temperature_(float rtd_ratio) { // Based loosely on Adafruit's library: https://github.com/adafruit/Adafruit_MAX31865 // Mainly based on formulas provided by Analog: // http://www.analog.com/media/en/technical-documentation/application-notes/AN709_0.pdf diff --git a/esphome/components/max31865/max31865.h b/esphome/components/max31865/max31865.h index 393dd4b434..b83753a678 100644 --- a/esphome/components/max31865/max31865.h +++ b/esphome/components/max31865/max31865.h @@ -49,9 +49,9 @@ class MAX31865Sensor : public sensor::Sensor, void read_data_(); void write_config_(uint8_t mask, uint8_t bits, uint8_t start_position = 0); void write_register_(uint8_t reg, uint8_t value); - const uint8_t read_register_(uint8_t reg); - const uint16_t read_register_16_(uint8_t reg); - float calc_temperature_(const float &rtd_ratio); + uint8_t read_register_(uint8_t reg); + uint16_t read_register_16_(uint8_t reg); + float calc_temperature_(float rtd_ratio); }; } // namespace max31865 diff --git a/esphome/components/max7219/max7219.cpp b/esphome/components/max7219/max7219.cpp index 97886e53fa..960ac58071 100644 --- a/esphome/components/max7219/max7219.cpp +++ b/esphome/components/max7219/max7219.cpp @@ -1,6 +1,7 @@ #include "max7219.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/hal.h" namespace esphome { namespace max7219 { @@ -172,7 +173,7 @@ uint8_t MAX7219Component::print(uint8_t start_pos, const char *str) { for (; *str != '\0'; str++) { uint8_t data = MAX7219_UNKNOWN_CHAR; if (*str >= ' ' && *str <= '~') - data = pgm_read_byte(&MAX7219_ASCII_TO_RAW[*str - ' ']); + data = progmem_read_byte(&MAX7219_ASCII_TO_RAW[*str - ' ']); if (data == MAX7219_UNKNOWN_CHAR) { ESP_LOGW(TAG, "Encountered character '%c' with no MAX7219 representation while translating string!", *str); diff --git a/esphome/components/max7219digit/max7219digit.cpp b/esphome/components/max7219digit/max7219digit.cpp index 520694af9d..4fedd3d312 100644 --- a/esphome/components/max7219digit/max7219digit.cpp +++ b/esphome/components/max7219digit/max7219digit.cpp @@ -1,6 +1,7 @@ #include "max7219digit.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/hal.h" #include "max7219font.h" namespace esphome { @@ -212,7 +213,7 @@ void MAX7219Component::scroll_left() { void MAX7219Component::send_char(uint8_t chip, uint8_t data) { // get this character from PROGMEM for (uint8_t i = 0; i < 8; i++) - this->max_displaybuffer_[chip * 8 + i] = pgm_read_byte(&MAX7219_DOT_MATRIX_FONT[data][i]); + this->max_displaybuffer_[chip * 8 + i] = progmem_read_byte(&MAX7219_DOT_MATRIX_FONT[data][i]); } // end of send_char // send one character (data) to position (chip) diff --git a/esphome/components/max7219digit/max7219digit.h b/esphome/components/max7219digit/max7219digit.h index 83f45e3a00..02fe8b6f42 100644 --- a/esphome/components/max7219digit/max7219digit.h +++ b/esphome/components/max7219digit/max7219digit.h @@ -54,8 +54,8 @@ class MAX7219Component : public PollingComponent, void set_scroll_mode(uint8_t mode) { this->scroll_mode_ = mode; }; void set_reverse(bool on_off) { this->reverse_ = on_off; }; - void send_char(byte chip, byte data); - void send64pixels(byte chip, const byte pixels[8]); + void send_char(uint8_t chip, uint8_t data); + void send64pixels(uint8_t chip, const uint8_t pixels[8]); void scroll_left(); void scroll(bool on_off, uint8_t mode, uint16_t speed, uint16_t delay, uint16_t dwell); diff --git a/esphome/components/max7219digit/max7219font.h b/esphome/components/max7219digit/max7219font.h index 3d42d1cc2d..22d64d1ecd 100644 --- a/esphome/components/max7219digit/max7219font.h +++ b/esphome/components/max7219digit/max7219font.h @@ -1,11 +1,13 @@ #pragma once +#include "esphome/core/hal.h" + namespace esphome { namespace max7219digit { // bit patterns for the CP437 font -const byte MAX7219_DOT_MATRIX_FONT[256][8] PROGMEM = { +const uint8_t MAX7219_DOT_MATRIX_FONT[256][8] PROGMEM = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // 0x00 {0x7E, 0x81, 0x95, 0xB1, 0xB1, 0x95, 0x81, 0x7E}, // 0x01 {0x7E, 0xFF, 0xEB, 0xCF, 0xCF, 0xEB, 0xFF, 0x7E}, // 0x02 diff --git a/esphome/components/mcp23008/mcp23008.h b/esphome/components/mcp23008/mcp23008.h index 42c8e497fa..406ce0b419 100644 --- a/esphome/components/mcp23008/mcp23008.h +++ b/esphome/components/mcp23008/mcp23008.h @@ -2,7 +2,7 @@ #include "esphome/core/component.h" #include "esphome/components/mcp23x08_base/mcp23x08_base.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/i2c/i2c.h" namespace esphome { diff --git a/esphome/components/mcp23016/__init__.py b/esphome/components/mcp23016/__init__.py index 4d9657e794..c1209a9627 100644 --- a/esphome/components/mcp23016/__init__.py +++ b/esphome/components/mcp23016/__init__.py @@ -2,17 +2,19 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import i2c -from esphome.const import CONF_ID, CONF_NUMBER, CONF_MODE, CONF_INVERTED +from esphome.const import ( + CONF_ID, + CONF_INPUT, + CONF_NUMBER, + CONF_MODE, + CONF_INVERTED, + CONF_OUTPUT, +) DEPENDENCIES = ["i2c"] MULTI_CONF = True mcp23016_ns = cg.esphome_ns.namespace("mcp23016") -MCP23016GPIOMode = mcp23016_ns.enum("MCP23016GPIOMode") -MCP23016_GPIO_MODES = { - "INPUT": MCP23016GPIOMode.MCP23016_INPUT, - "OUTPUT": MCP23016GPIOMode.MCP23016_OUTPUT, -} MCP23016 = mcp23016_ns.class_("MCP23016", cg.Component, i2c.I2CDevice) MCP23016GPIOPin = mcp23016_ns.class_("MCP23016GPIOPin", cg.GPIOPin) @@ -34,34 +36,41 @@ async def to_code(config): await i2c.register_i2c_device(var, config) +def validate_mode(value): + if not (value[CONF_INPUT] or value[CONF_OUTPUT]): + raise cv.Invalid("Mode must be either input or output") + if value[CONF_INPUT] and value[CONF_OUTPUT]: + raise cv.Invalid("Mode must be either input or output") + return value + + CONF_MCP23016 = "mcp23016" -MCP23016_OUTPUT_PIN_SCHEMA = cv.Schema( +MCP23016_PIN_SCHEMA = cv.All( { + cv.GenerateID(): cv.declare_id(MCP23016GPIOPin), cv.Required(CONF_MCP23016): cv.use_id(MCP23016), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum( - MCP23016_GPIO_MODES, upper=True - ), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - } -) -MCP23016_INPUT_PIN_SCHEMA = cv.Schema( - { - cv.Required(CONF_MCP23016): cv.use_id(MCP23016), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="INPUT"): cv.enum( - MCP23016_GPIO_MODES, upper=True + cv.Required(CONF_NUMBER): cv.int_range(min=0, max=15), + cv.Optional(CONF_MODE, default={}): cv.All( + { + cv.Optional(CONF_INPUT, default=False): cv.boolean, + cv.Optional(CONF_OUTPUT, default=False): cv.boolean, + }, + validate_mode, ), cv.Optional(CONF_INVERTED, default=False): cv.boolean, } ) -@pins.PIN_SCHEMA_REGISTRY.register( - CONF_MCP23016, (MCP23016_OUTPUT_PIN_SCHEMA, MCP23016_INPUT_PIN_SCHEMA) -) +@pins.PIN_SCHEMA_REGISTRY.register(CONF_MCP23016, MCP23016_PIN_SCHEMA) async def mcp23016_pin_to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) parent = await cg.get_variable(config[CONF_MCP23016]) - return MCP23016GPIOPin.new( - parent, config[CONF_NUMBER], config[CONF_MODE], config[CONF_INVERTED] - ) + + cg.add(var.set_parent(parent)) + + num = config[CONF_NUMBER] + cg.add(var.set_pin(num)) + cg.add(var.set_inverted(config[CONF_INVERTED])) + cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE]))) + return var diff --git a/esphome/components/mcp23016/mcp23016.cpp b/esphome/components/mcp23016/mcp23016.cpp index f2b55fe2e2..a8df4e1745 100644 --- a/esphome/components/mcp23016/mcp23016.cpp +++ b/esphome/components/mcp23016/mcp23016.cpp @@ -1,5 +1,6 @@ #include "mcp23016.h" #include "esphome/core/log.h" +#include namespace esphome { namespace mcp23016 { @@ -29,17 +30,12 @@ void MCP23016::digital_write(uint8_t pin, bool value) { uint8_t reg_addr = pin < 8 ? MCP23016_OLAT0 : MCP23016_OLAT1; this->update_reg_(pin, value, reg_addr); } -void MCP23016::pin_mode(uint8_t pin, uint8_t mode) { +void MCP23016::pin_mode(uint8_t pin, gpio::Flags flags) { uint8_t iodir = pin < 8 ? MCP23016_IODIR0 : MCP23016_IODIR1; - switch (mode) { - case MCP23016_INPUT: - this->update_reg_(pin, true, iodir); - break; - case MCP23016_OUTPUT: - this->update_reg_(pin, false, iodir); - break; - default: - break; + if (flags == gpio::FLAG_INPUT) { + this->update_reg_(pin, true, iodir); + } else if (flags == gpio::FLAG_OUTPUT) { + this->update_reg_(pin, false, iodir); } } float MCP23016::get_setup_priority() const { return setup_priority::HARDWARE; } @@ -80,12 +76,15 @@ void MCP23016::update_reg_(uint8_t pin, bool pin_value, uint8_t reg_addr) { } } -MCP23016GPIOPin::MCP23016GPIOPin(MCP23016 *parent, uint8_t pin, uint8_t mode, bool inverted) - : GPIOPin(pin, mode, inverted), parent_(parent) {} -void MCP23016GPIOPin::setup() { this->pin_mode(this->mode_); } -void MCP23016GPIOPin::pin_mode(uint8_t mode) { this->parent_->pin_mode(this->pin_, mode); } +void MCP23016GPIOPin::setup() { pin_mode(flags_); } +void MCP23016GPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); } bool MCP23016GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; } void MCP23016GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); } +std::string MCP23016GPIOPin::dump_summary() const { + char buffer[32]; + snprintf(buffer, sizeof(buffer), "%u via MCP23016", pin_); + return buffer; +} } // namespace mcp23016 } // namespace esphome diff --git a/esphome/components/mcp23016/mcp23016.h b/esphome/components/mcp23016/mcp23016.h index 53502f80eb..a4890b4120 100644 --- a/esphome/components/mcp23016/mcp23016.h +++ b/esphome/components/mcp23016/mcp23016.h @@ -1,18 +1,12 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/i2c/i2c.h" namespace esphome { namespace mcp23016 { -/// Modes for MCP23016 pins -enum MCP23016GPIOMode : uint8_t { - MCP23016_INPUT = INPUT, // 0x00 - MCP23016_OUTPUT = OUTPUT // 0x01 -}; - enum MCP23016GPIORegisters { // 0 side MCP23016_GP0 = 0x00, @@ -38,7 +32,7 @@ class MCP23016 : public Component, public i2c::I2CDevice { bool digital_read(uint8_t pin); void digital_write(uint8_t pin, bool value); - void pin_mode(uint8_t pin, uint8_t mode); + void pin_mode(uint8_t pin, gpio::Flags flags); float get_setup_priority() const override; @@ -56,15 +50,22 @@ class MCP23016 : public Component, public i2c::I2CDevice { class MCP23016GPIOPin : public GPIOPin { public: - MCP23016GPIOPin(MCP23016 *parent, uint8_t pin, uint8_t mode, bool inverted = false); - void setup() override; - void pin_mode(uint8_t mode) override; + void pin_mode(gpio::Flags flags) override; bool digital_read() override; void digital_write(bool value) override; + std::string dump_summary() const override; + + void set_parent(MCP23016 *parent) { parent_ = parent; } + void set_pin(uint8_t pin) { pin_ = pin; } + void set_inverted(bool inverted) { inverted_ = inverted; } + void set_flags(gpio::Flags flags) { flags_ = flags; } protected: MCP23016 *parent_; + uint8_t pin_; + bool inverted_; + gpio::Flags flags_; }; } // namespace mcp23016 diff --git a/esphome/components/mcp23017/mcp23017.h b/esphome/components/mcp23017/mcp23017.h index fd9086a492..8959e06a41 100644 --- a/esphome/components/mcp23017/mcp23017.h +++ b/esphome/components/mcp23017/mcp23017.h @@ -2,7 +2,7 @@ #include "esphome/core/component.h" #include "esphome/components/mcp23x17_base/mcp23x17_base.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/i2c/i2c.h" namespace esphome { diff --git a/esphome/components/mcp23s08/mcp23s08.h b/esphome/components/mcp23s08/mcp23s08.h index 4ca02c54fc..a2a6be880a 100644 --- a/esphome/components/mcp23s08/mcp23s08.h +++ b/esphome/components/mcp23s08/mcp23s08.h @@ -2,7 +2,7 @@ #include "esphome/core/component.h" #include "esphome/components/mcp23x08_base/mcp23x08_base.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/spi/spi.h" namespace esphome { diff --git a/esphome/components/mcp23s17/mcp23s17.h b/esphome/components/mcp23s17/mcp23s17.h index 1ced144c23..cb5d6cfcd8 100644 --- a/esphome/components/mcp23s17/mcp23s17.h +++ b/esphome/components/mcp23s17/mcp23s17.h @@ -2,7 +2,7 @@ #include "esphome/core/component.h" #include "esphome/components/mcp23x17_base/mcp23x17_base.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/spi/spi.h" namespace esphome { diff --git a/esphome/components/mcp23x08_base/mcp23x08_base.cpp b/esphome/components/mcp23x08_base/mcp23x08_base.cpp index 2b047fa288..2137b36921 100644 --- a/esphome/components/mcp23x08_base/mcp23x08_base.cpp +++ b/esphome/components/mcp23x08_base/mcp23x08_base.cpp @@ -19,22 +19,16 @@ void MCP23X08Base::digital_write(uint8_t pin, bool value) { this->update_reg(pin, value, reg_addr); } -void MCP23X08Base::pin_mode(uint8_t pin, uint8_t mode) { +void MCP23X08Base::pin_mode(uint8_t pin, gpio::Flags flags) { uint8_t iodir = mcp23x08_base::MCP23X08_IODIR; uint8_t gppu = mcp23x08_base::MCP23X08_GPPU; - switch (mode) { - case mcp23xxx_base::MCP23XXX_INPUT: - this->update_reg(pin, true, iodir); - break; - case mcp23xxx_base::MCP23XXX_INPUT_PULLUP: - this->update_reg(pin, true, iodir); - this->update_reg(pin, true, gppu); - break; - case mcp23xxx_base::MCP23XXX_OUTPUT: - this->update_reg(pin, false, iodir); - break; - default: - break; + if (flags == gpio::FLAG_INPUT) { + this->update_reg(pin, true, iodir); + } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_PULLUP)) { + this->update_reg(pin, true, iodir); + this->update_reg(pin, true, gppu); + } else if (flags == gpio::FLAG_OUTPUT) { + this->update_reg(pin, false, iodir); } } diff --git a/esphome/components/mcp23x08_base/mcp23x08_base.h b/esphome/components/mcp23x08_base/mcp23x08_base.h index 5e2c1a047f..910519119b 100644 --- a/esphome/components/mcp23x08_base/mcp23x08_base.h +++ b/esphome/components/mcp23x08_base/mcp23x08_base.h @@ -2,7 +2,7 @@ #include "esphome/core/component.h" #include "esphome/components/mcp23xxx_base/mcp23xxx_base.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" namespace esphome { namespace mcp23x08_base { @@ -26,7 +26,7 @@ class MCP23X08Base : public mcp23xxx_base::MCP23XXXBase { public: bool digital_read(uint8_t pin) override; void digital_write(uint8_t pin, bool value) override; - void pin_mode(uint8_t pin, uint8_t mode) override; + void pin_mode(uint8_t pin, gpio::Flags flags) override; void pin_interrupt_mode(uint8_t pin, mcp23xxx_base::MCP23XXXInterruptMode interrupt_mode) override; protected: diff --git a/esphome/components/mcp23x17_base/mcp23x17_base.cpp b/esphome/components/mcp23x17_base/mcp23x17_base.cpp index 72dec2d457..e975670faa 100644 --- a/esphome/components/mcp23x17_base/mcp23x17_base.cpp +++ b/esphome/components/mcp23x17_base/mcp23x17_base.cpp @@ -19,22 +19,16 @@ void MCP23X17Base::digital_write(uint8_t pin, bool value) { this->update_reg(pin, value, reg_addr); } -void MCP23X17Base::pin_mode(uint8_t pin, uint8_t mode) { +void MCP23X17Base::pin_mode(uint8_t pin, gpio::Flags flags) { uint8_t iodir = pin < 8 ? mcp23x17_base::MCP23X17_IODIRA : mcp23x17_base::MCP23X17_IODIRB; uint8_t gppu = pin < 8 ? mcp23x17_base::MCP23X17_GPPUA : mcp23x17_base::MCP23X17_GPPUB; - switch (mode) { - case mcp23xxx_base::MCP23XXX_INPUT: - this->update_reg(pin, true, iodir); - break; - case mcp23xxx_base::MCP23XXX_INPUT_PULLUP: - this->update_reg(pin, true, iodir); - this->update_reg(pin, true, gppu); - break; - case mcp23xxx_base::MCP23XXX_OUTPUT: - this->update_reg(pin, false, iodir); - break; - default: - break; + if (flags == gpio::FLAG_INPUT) { + this->update_reg(pin, true, iodir); + } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_PULLUP)) { + this->update_reg(pin, true, iodir); + this->update_reg(pin, true, gppu); + } else if (flags == gpio::FLAG_OUTPUT) { + this->update_reg(pin, false, iodir); } } diff --git a/esphome/components/mcp23x17_base/mcp23x17_base.h b/esphome/components/mcp23x17_base/mcp23x17_base.h index 1bbcb97041..3d50ee8c03 100644 --- a/esphome/components/mcp23x17_base/mcp23x17_base.h +++ b/esphome/components/mcp23x17_base/mcp23x17_base.h @@ -2,7 +2,7 @@ #include "esphome/core/component.h" #include "esphome/components/mcp23xxx_base/mcp23xxx_base.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" namespace esphome { namespace mcp23x17_base { @@ -38,7 +38,7 @@ class MCP23X17Base : public mcp23xxx_base::MCP23XXXBase { public: bool digital_read(uint8_t pin) override; void digital_write(uint8_t pin, bool value) override; - void pin_mode(uint8_t pin, uint8_t mode) override; + void pin_mode(uint8_t pin, gpio::Flags flags) override; void pin_interrupt_mode(uint8_t pin, mcp23xxx_base::MCP23XXXInterruptMode interrupt_mode) override; protected: diff --git a/esphome/components/mcp23xxx_base/__init__.py b/esphome/components/mcp23xxx_base/__init__.py index c22d377b3c..f2c2706416 100644 --- a/esphome/components/mcp23xxx_base/__init__.py +++ b/esphome/components/mcp23xxx_base/__init__.py @@ -3,11 +3,14 @@ import esphome.config_validation as cv from esphome import pins from esphome.const import ( CONF_ID, + CONF_INPUT, CONF_NUMBER, CONF_MODE, CONF_INVERTED, CONF_INTERRUPT, CONF_OPEN_DRAIN_INTERRUPT, + CONF_OUTPUT, + CONF_PULLUP, ) from esphome.core import coroutine @@ -47,26 +50,29 @@ async def register_mcp23xxx(config): return var +def validate_mode(value): + if not (value[CONF_INPUT] or value[CONF_OUTPUT]): + raise cv.Invalid("Mode must be either input or output") + if value[CONF_INPUT] and value[CONF_OUTPUT]: + raise cv.Invalid("Mode must be either input or output") + if value[CONF_PULLUP] and not value[CONF_INPUT]: + raise cv.Invalid("Pullup only available with input") + return value + + CONF_MCP23XXX = "mcp23xxx" -MCP23XXX_OUTPUT_PIN_SCHEMA = cv.Schema( +MCP23XXX_PIN_SCHEMA = cv.All( { + cv.GenerateID(): cv.declare_id(MCP23XXXGPIOPin), cv.Required(CONF_MCP23XXX): cv.use_id(MCP23XXXBase), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum( - MCP23XXX_GPIO_MODES, upper=True - ), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - cv.Optional(CONF_INTERRUPT, default="NO_INTERRUPT"): cv.enum( - MCP23XXX_INTERRUPT_MODES, upper=True - ), - } -) -MCP23XXX_INPUT_PIN_SCHEMA = cv.Schema( - { - cv.Required(CONF_MCP23XXX): cv.use_id(MCP23XXXBase), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="INPUT"): cv.enum( - MCP23XXX_GPIO_MODES, upper=True + cv.Required(CONF_NUMBER): cv.int_range(min=0, max=15), + cv.Optional(CONF_MODE, default={}): cv.All( + { + cv.Optional(CONF_INPUT, default=False): cv.boolean, + cv.Optional(CONF_PULLUP, default=False): cv.boolean, + cv.Optional(CONF_OUTPUT, default=False): cv.boolean, + }, + validate_mode, ), cv.Optional(CONF_INVERTED, default=False): cv.boolean, cv.Optional(CONF_INTERRUPT, default="NO_INTERRUPT"): cv.enum( @@ -76,42 +82,31 @@ MCP23XXX_INPUT_PIN_SCHEMA = cv.Schema( ) -@pins.PIN_SCHEMA_REGISTRY.register( - CONF_MCP23XXX, (MCP23XXX_OUTPUT_PIN_SCHEMA, MCP23XXX_INPUT_PIN_SCHEMA) -) +@pins.PIN_SCHEMA_REGISTRY.register(CONF_MCP23XXX, MCP23XXX_PIN_SCHEMA) async def mcp23xxx_pin_to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) parent = await cg.get_variable(config[CONF_MCP23XXX]) - return MCP23XXXGPIOPin.new( - parent, - config[CONF_NUMBER], - config[CONF_MODE], - config[CONF_INVERTED], - config[CONF_INTERRUPT], - ) + + cg.add(var.set_parent(parent)) + + num = config[CONF_NUMBER] + cg.add(var.set_pin(num)) + cg.add(var.set_inverted(config[CONF_INVERTED])) + cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE]))) + cg.add(var.set_interrupt_mode(config[CONF_INTERRUPT])) + return var # BEGIN Removed pin schemas below to show error in configuration # TODO remove in 2022.5.0 for id in ["mcp23008", "mcp23s08", "mcp23017", "mcp23s17"]: - PIN_SCHEMA = cv.Schema( - { - cv.Required(id): cv.invalid( - f"'{id}:' has been removed from the pin schema in 1.17.0, please use 'mcp23xxx:'" - ), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="INPUT"): cv.enum( - MCP23XXX_GPIO_MODES, upper=True - ), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - cv.Optional(CONF_INTERRUPT, default="NO_INTERRUPT"): cv.enum( - MCP23XXX_INTERRUPT_MODES, upper=True - ), - } + invalid_schema = cv.invalid( + f"'{id}:' has been removed from the pin schema in 1.17.0, please use 'mcp23xxx:'" ) # pylint: disable=cell-var-from-loop - @pins.PIN_SCHEMA_REGISTRY.register(id, (PIN_SCHEMA, PIN_SCHEMA)) + @pins.PIN_SCHEMA_REGISTRY.register(id, invalid_schema) def pin_to_code(config): pass diff --git a/esphome/components/mcp23xxx_base/mcp23xxx_base.cpp b/esphome/components/mcp23xxx_base/mcp23xxx_base.cpp index 37c55fceaf..14a703fb9f 100644 --- a/esphome/components/mcp23xxx_base/mcp23xxx_base.cpp +++ b/esphome/components/mcp23xxx_base/mcp23xxx_base.cpp @@ -6,16 +6,15 @@ namespace mcp23xxx_base { float MCP23XXXBase::get_setup_priority() const { return setup_priority::IO; } -MCP23XXXGPIOPin::MCP23XXXGPIOPin(MCP23XXXBase *parent, uint8_t pin, uint8_t mode, bool inverted, - MCP23XXXInterruptMode interrupt_mode) - : GPIOPin(pin, mode, inverted), parent_(parent), interrupt_mode_(interrupt_mode) {} -void MCP23XXXGPIOPin::setup() { this->pin_mode(this->mode_); } -void MCP23XXXGPIOPin::pin_mode(uint8_t mode) { - this->parent_->pin_mode(this->pin_, mode); - this->parent_->pin_interrupt_mode(this->pin_, this->interrupt_mode_); -} +void MCP23XXXGPIOPin::setup() { pin_mode(flags_); } +void MCP23XXXGPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); } bool MCP23XXXGPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; } void MCP23XXXGPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); } +std::string MCP23XXXGPIOPin::dump_summary() const { + char buffer[32]; + snprintf(buffer, sizeof(buffer), "%u via MCP23XXX", pin_); + return buffer; +} } // namespace mcp23xxx_base } // namespace esphome diff --git a/esphome/components/mcp23xxx_base/mcp23xxx_base.h b/esphome/components/mcp23xxx_base/mcp23xxx_base.h index bf01320264..a522ea28c5 100644 --- a/esphome/components/mcp23xxx_base/mcp23xxx_base.h +++ b/esphome/components/mcp23xxx_base/mcp23xxx_base.h @@ -1,25 +1,18 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" namespace esphome { namespace mcp23xxx_base { enum MCP23XXXInterruptMode : uint8_t { MCP23XXX_NO_INTERRUPT = 0, MCP23XXX_CHANGE, MCP23XXX_RISING, MCP23XXX_FALLING }; -/// Modes for MCP23XXX pins -enum MCP23XXXGPIOMode : uint8_t { - MCP23XXX_INPUT = INPUT, // 0x00 - MCP23XXX_INPUT_PULLUP = INPUT_PULLUP, // 0x02 - MCP23XXX_OUTPUT = OUTPUT // 0x01 -}; - class MCP23XXXBase : public Component { public: virtual bool digital_read(uint8_t pin); virtual void digital_write(uint8_t pin, bool value); - virtual void pin_mode(uint8_t pin, uint8_t mode); + virtual void pin_mode(uint8_t pin, gpio::Flags flags); virtual void pin_interrupt_mode(uint8_t pin, MCP23XXXInterruptMode interrupt_mode); void set_open_drain_ints(const bool value) { this->open_drain_ints_ = value; } @@ -38,16 +31,23 @@ class MCP23XXXBase : public Component { class MCP23XXXGPIOPin : public GPIOPin { public: - MCP23XXXGPIOPin(MCP23XXXBase *parent, uint8_t pin, uint8_t mode, bool inverted = false, - MCP23XXXInterruptMode interrupt_mode = MCP23XXX_NO_INTERRUPT); - void setup() override; - void pin_mode(uint8_t mode) override; + void pin_mode(gpio::Flags flags) override; bool digital_read() override; void digital_write(bool value) override; + std::string dump_summary() const override; + + void set_parent(MCP23XXXBase *parent) { parent_ = parent; } + void set_pin(uint8_t pin) { pin_ = pin; } + void set_inverted(bool inverted) { inverted_ = inverted; } + void set_flags(gpio::Flags flags) { flags_ = flags; } + void set_interrupt_mode(MCP23XXXInterruptMode interrupt_mode) { interrupt_mode_ = interrupt_mode; } protected: MCP23XXXBase *parent_; + uint8_t pin_; + bool inverted_; + gpio::Flags flags_; MCP23XXXInterruptMode interrupt_mode_; }; diff --git a/esphome/components/mcp3008/mcp3008.h b/esphome/components/mcp3008/mcp3008.h index 16f1c14fcb..6f3dc576ea 100644 --- a/esphome/components/mcp3008/mcp3008.h +++ b/esphome/components/mcp3008/mcp3008.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/spi/spi.h" #include "esphome/components/voltage_sampler/voltage_sampler.h" diff --git a/esphome/components/mcp4725/mcp4725.cpp b/esphome/components/mcp4725/mcp4725.cpp index a8b130208e..2cb19282b6 100644 --- a/esphome/components/mcp4725/mcp4725.cpp +++ b/esphome/components/mcp4725/mcp4725.cpp @@ -8,13 +8,10 @@ static const char *const TAG = "mcp4725"; void MCP4725::setup() { ESP_LOGCONFIG(TAG, "Setting up MCP4725 (0x%02X)...", this->address_); - - this->raw_begin_transmission(); - - if (!this->raw_end_transmission()) { + auto err = this->write(nullptr, 0); + if (err != i2c::ERROR_OK) { this->error_code_ = COMMUNICATION_FAILED; this->mark_failed(); - return; } } diff --git a/esphome/components/mcp9808/mcp9808.cpp b/esphome/components/mcp9808/mcp9808.cpp index 8f60df1d88..fca1331fc3 100644 --- a/esphome/components/mcp9808/mcp9808.cpp +++ b/esphome/components/mcp9808/mcp9808.cpp @@ -20,14 +20,14 @@ static const char *const TAG = "mcp9808"; void MCP9808Sensor::setup() { ESP_LOGCONFIG(TAG, "Setting up %s...", this->name_.c_str()); - uint16_t manu; - if (!this->read_byte_16(MCP9808_REG_MANUF_ID, &manu, 0) || manu != MCP9808_MANUF_ID) { + uint16_t manu = 0; + if (!this->read_byte_16(MCP9808_REG_MANUF_ID, &manu) || manu != MCP9808_MANUF_ID) { this->mark_failed(); ESP_LOGE(TAG, "%s manufacuturer id failed, device returned %X", this->name_.c_str(), manu); return; } - uint16_t dev_id; - if (!this->read_byte_16(MCP9808_REG_DEVICE_ID, &dev_id, 0) || dev_id != MCP9808_DEV_ID) { + uint16_t dev_id = 0; + if (!this->read_byte_16(MCP9808_REG_DEVICE_ID, &dev_id) || dev_id != MCP9808_DEV_ID) { this->mark_failed(); ESP_LOGE(TAG, "%s device id failed, device returned %X", this->name_.c_str(), dev_id); return; @@ -66,7 +66,7 @@ void MCP9808Sensor::update() { temp = (uint16_t)(msb) *16 + lsb / 16.0f; } - if (isnan(temp)) { + if (std::isnan(temp)) { this->status_set_warning(); return; } diff --git a/esphome/components/mdns/__init__.py b/esphome/components/mdns/__init__.py new file mode 100644 index 0000000000..ec568ae2d5 --- /dev/null +++ b/esphome/components/mdns/__init__.py @@ -0,0 +1,25 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.core import CORE + +CODEOWNERS = ["@esphome/core"] +DEPENDENCIES = ["network"] + +CONF_DISABLED = "disabled" +CONFIG_SCHEMA = cv.Schema( + { + cv.Optional(CONF_DISABLED, default=False): cv.boolean, + } +) + + +async def to_code(config): + if config[CONF_DISABLED]: + return + + cg.add_define("USE_MDNS") + if CORE.using_arduino: + if CORE.is_esp32: + cg.add_library("ESPmDNS", None) + elif CORE.is_esp8266: + cg.add_library("ESP8266mDNS", None) diff --git a/esphome/components/mdns/mdns_component.cpp b/esphome/components/mdns/mdns_component.cpp new file mode 100644 index 0000000000..c742fe3948 --- /dev/null +++ b/esphome/components/mdns/mdns_component.cpp @@ -0,0 +1,74 @@ +#include "mdns_component.h" +#include "esphome/core/defines.h" +#include "esphome/core/version.h" +#include "esphome/core/application.h" + +#ifdef USE_API +#include "esphome/components/api/api_server.h" +#endif + +namespace esphome { +namespace mdns { + +#ifndef WEBSERVER_PORT +#define WEBSERVER_PORT 80 // NOLINT +#endif + +std::vector MDNSComponent::compile_services_() { + std::vector res; + +#ifdef USE_API + if (api::global_api_server != nullptr) { + MDNSService service{}; + service.service_type = "esphomelib"; + service.proto = "_tcp"; + service.port = api::global_api_server->get_port(); + service.txt_records.push_back({"version", ESPHOME_VERSION}); + service.txt_records.push_back({"mac", get_mac_address()}); + const char *platform = nullptr; +#ifdef USE_ESP8266 + platform = "ESP8266"; +#endif +#ifdef USE_ESP32 + platform = "ESP32"; +#endif + if (platform != nullptr) { + service.txt_records.push_back({"platform", platform}); + } + + service.txt_records.push_back({"board", ESPHOME_BOARD}); + +#ifdef ESPHOME_PROJECT_NAME + service.txt_records.push_back({"project_name", ESPHOME_PROJECT_NAME}); + service.txt_records.push_back({"project_version", ESPHOME_PROJECT_VERSION}); +#endif // ESPHOME_PROJECT_NAME + res.push_back(service); + } +#endif // USE_API + +#ifdef USE_PROMETHEUS + { + MDNSService service{}; + service.service_type = "prometheus-http"; + service.proto = "_tcp"; + service.port = WEBSERVER_PORT; + res.push_back(service); + } +#endif + + if (res.empty()) { + // Publish "http" service if not using native API + // This is just to have *some* mDNS service so that .local resolution works + MDNSService service{}; + service.service_type = "http"; + service.proto = "_tcp"; + service.port = WEBSERVER_PORT; + service.txt_records.push_back({"version", ESPHOME_VERSION}); + res.push_back(service); + } + return res; +} +std::string MDNSComponent::compile_hostname_() { return App.get_name(); } + +} // namespace mdns +} // namespace esphome diff --git a/esphome/components/mdns/mdns_component.h b/esphome/components/mdns/mdns_component.h new file mode 100644 index 0000000000..985947d99c --- /dev/null +++ b/esphome/components/mdns/mdns_component.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include "esphome/core/component.h" + +namespace esphome { +namespace mdns { + +struct MDNSTXTRecord { + std::string key; + std::string value; +}; + +struct MDNSService { + std::string service_type; + std::string proto; + uint16_t port; + std::vector txt_records; +}; + +class MDNSComponent : public Component { + public: + void setup() override; + +#if defined(USE_ESP8266) && defined(USE_ARDUINO) + void loop() override; +#endif + float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + + protected: + std::vector compile_services_(); + std::string compile_hostname_(); +}; + +} // namespace mdns +} // namespace esphome diff --git a/esphome/components/mdns/mdns_esp32_arduino.cpp b/esphome/components/mdns/mdns_esp32_arduino.cpp new file mode 100644 index 0000000000..4d13b7321a --- /dev/null +++ b/esphome/components/mdns/mdns_esp32_arduino.cpp @@ -0,0 +1,27 @@ +#ifdef USE_ESP32_FRAMEWORK_ARDUINO + +#include "mdns_component.h" +#include "esphome/core/log.h" +#include + +namespace esphome { +namespace mdns { + +static const char *const TAG = "mdns"; + +void MDNSComponent::setup() { + MDNS.begin(compile_hostname_().c_str()); + + auto services = compile_services_(); + for (const auto &service : services) { + MDNS.addService(service.service_type.c_str(), service.proto.c_str(), service.port); + for (const auto &record : service.txt_records) { + MDNS.addServiceTxt(service.service_type.c_str(), service.proto.c_str(), record.key.c_str(), record.value.c_str()); + } + } +} + +} // namespace mdns +} // namespace esphome + +#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/mdns/mdns_esp8266.cpp b/esphome/components/mdns/mdns_esp8266.cpp new file mode 100644 index 0000000000..48f31f1bbf --- /dev/null +++ b/esphome/components/mdns/mdns_esp8266.cpp @@ -0,0 +1,32 @@ +#if defined(USE_ESP8266) && defined(USE_ARDUINO) + +#include "mdns_component.h" +#include "esphome/core/log.h" +#include "esphome/components/network/ip_address.h" +#include "esphome/components/network/util.h" +#include + +namespace esphome { +namespace mdns { + +static const char *const TAG = "mdns"; + +void MDNSComponent::setup() { + network::IPAddress addr = network::get_ip_address(); + MDNS.begin(compile_hostname_().c_str(), (uint32_t) addr); + + auto services = compile_services_(); + for (const auto &service : services) { + MDNS.addService(service.service_type.c_str(), service.proto.c_str(), service.port); + for (const auto &record : service.txt_records) { + MDNS.addServiceTxt(service.service_type.c_str(), service.proto.c_str(), record.key.c_str(), record.value.c_str()); + } + } +} + +void MDNSComponent::loop() { MDNS.update(); } + +} // namespace mdns +} // namespace esphome + +#endif diff --git a/esphome/components/mdns/mdns_esp_idf.cpp b/esphome/components/mdns/mdns_esp_idf.cpp new file mode 100644 index 0000000000..17874f1ffe --- /dev/null +++ b/esphome/components/mdns/mdns_esp_idf.cpp @@ -0,0 +1,52 @@ +#ifdef USE_ESP_IDF + +#include "mdns_component.h" +#include "esphome/core/log.h" +#include +#include + +namespace esphome { +namespace mdns { + +static const char *const TAG = "mdns"; + +void MDNSComponent::setup() { + esp_err_t err = mdns_init(); + if (err != ESP_OK) { + ESP_LOGW(TAG, "MDNS init failed: %s", esp_err_to_name(err)); + this->mark_failed(); + return; + } + + mdns_hostname_set(compile_hostname_().c_str()); + mdns_instance_name_set(compile_hostname_().c_str()); + + auto services = compile_services_(); + for (const auto &service : services) { + std::vector txt_records; + for (const auto &record : service.txt_records) { + mdns_txt_item_t it{}; + // dup strings to ensure the pointer is valid even after the record loop + it.key = strdup(record.key.c_str()); + it.value = strdup(record.value.c_str()); + txt_records.push_back(it); + } + err = mdns_service_add(nullptr, service.service_type.c_str(), service.proto.c_str(), service.port, + txt_records.data(), txt_records.size()); + + // free records + for (const auto &it : txt_records) { + delete it.key; // NOLINT(cppcoreguidelines-owning-memory) + delete it.value; // NOLINT(cppcoreguidelines-owning-memory) + } + + if (err != ESP_OK) { + ESP_LOGW(TAG, "Failed to register mDNS service %s: %s", service.service_type.c_str(), esp_err_to_name(err)); + } + } +} + +} // namespace mdns +} // namespace esphome + +#endif diff --git a/esphome/components/midea/adapter.cpp b/esphome/components/midea/adapter.cpp index bd5b289095..a3f19dbda8 100644 --- a/esphome/components/midea/adapter.cpp +++ b/esphome/components/midea/adapter.cpp @@ -1,3 +1,5 @@ +#ifdef USE_ARDUINO + #include "esphome/core/log.h" #include "adapter.h" @@ -171,3 +173,5 @@ void Converters::to_climate_traits(ClimateTraits &traits, const dudanov::midea:: } // namespace midea } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/midea/adapter.h b/esphome/components/midea/adapter.h index 8d8d57e8f9..2497cbbe5b 100644 --- a/esphome/components/midea/adapter.h +++ b/esphome/components/midea/adapter.h @@ -1,4 +1,7 @@ #pragma once + +#ifdef USE_ARDUINO + #include #include "esphome/components/climate/climate_traits.h" #include "appliance_base.h" @@ -40,3 +43,5 @@ class Converters { } // namespace midea } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/midea/air_conditioner.cpp b/esphome/components/midea/air_conditioner.cpp index a71f1dbdfb..103b852936 100644 --- a/esphome/components/midea/air_conditioner.cpp +++ b/esphome/components/midea/air_conditioner.cpp @@ -1,3 +1,5 @@ +#ifdef USE_ARDUINO + #include "esphome/core/log.h" #include "air_conditioner.h" #include "adapter.h" @@ -150,3 +152,5 @@ void AirConditioner::do_display_toggle() { } // namespace midea } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/midea/air_conditioner.h b/esphome/components/midea/air_conditioner.h index 895b6412f3..8dfb9dcb3d 100644 --- a/esphome/components/midea/air_conditioner.h +++ b/esphome/components/midea/air_conditioner.h @@ -1,4 +1,7 @@ #pragma once + +#ifdef USE_ARDUINO + #include #include "appliance_base.h" #include "esphome/components/sensor/sensor.h" @@ -39,3 +42,5 @@ class AirConditioner : public ApplianceBase } // namespace midea } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/midea/appliance_base.h b/esphome/components/midea/appliance_base.h index aa616ced36..43dd2ffb32 100644 --- a/esphome/components/midea/appliance_base.h +++ b/esphome/components/midea/appliance_base.h @@ -1,4 +1,7 @@ #pragma once + +#ifdef USE_ARDUINO + #include "esphome/core/component.h" #include "esphome/core/log.h" #include "esphome/components/uart/uart.h" @@ -19,7 +22,8 @@ using climate::ClimateMode; using climate::ClimateSwingMode; using climate::ClimateFanMode; -template class ApplianceBase : public Component, public uart::UARTDevice, public climate::Climate { +template +class ApplianceBase : public Component, public uart::UARTDevice, public climate::Climate, public Stream { static_assert(std::is_base_of::value, "T must derive from dudanov::midea::ApplianceBase class"); @@ -60,6 +64,12 @@ template class ApplianceBase : public Component, public uart::UARTDe } #endif + int available() override { return uart::UARTDevice::available(); } + int read() override { return uart::UARTDevice::read(); } + int peek() override { return uart::UARTDevice::peek(); } + void flush() override { uart::UARTDevice::flush(); } + size_t write(uint8_t data) override { return uart::UARTDevice::write(data); } + protected: T base_; std::set supported_modes_{}; @@ -74,3 +84,5 @@ template class ApplianceBase : public Component, public uart::UARTDe } // namespace midea } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/midea/automations.h b/esphome/components/midea/automations.h index 1f026c0c15..5b638286ac 100644 --- a/esphome/components/midea/automations.h +++ b/esphome/components/midea/automations.h @@ -1,4 +1,7 @@ #pragma once + +#ifdef USE_ARDUINO + #include "esphome/core/automation.h" #include "air_conditioner.h" @@ -54,3 +57,5 @@ template class PowerOffAction : public MideaActionBase { } // namespace midea } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/midea/climate.py b/esphome/components/midea/climate.py index 137fcdd607..0d0bdce471 100644 --- a/esphome/components/midea/climate.py +++ b/esphome/components/midea/climate.py @@ -151,7 +151,8 @@ CONFIG_SCHEMA = cv.All( } ) .extend(uart.UART_DEVICE_SCHEMA) - .extend(cv.COMPONENT_SCHEMA) + .extend(cv.COMPONENT_SCHEMA), + cv.only_with_arduino, ) # Actions diff --git a/esphome/components/midea/midea_ir.h b/esphome/components/midea/midea_ir.h index 2459d844a1..abd4324bcc 100644 --- a/esphome/components/midea/midea_ir.h +++ b/esphome/components/midea/midea_ir.h @@ -1,4 +1,6 @@ #pragma once + +#ifdef USE_ARDUINO #ifdef USE_REMOTE_TRANSMITTER #include "esphome/components/remote_base/midea_protocol.h" @@ -40,3 +42,4 @@ class IrSpecialData : public IrData { } // namespace esphome #endif +#endif // USE_ARDUINO diff --git a/esphome/components/mpr121/mpr121.cpp b/esphome/components/mpr121/mpr121.cpp index 274ed6dfec..7ba3da7b4d 100644 --- a/esphome/components/mpr121/mpr121.cpp +++ b/esphome/components/mpr121/mpr121.cpp @@ -1,5 +1,6 @@ #include "mpr121.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace mpr121 { diff --git a/esphome/components/mqtt/__init__.py b/esphome/components/mqtt/__init__.py index 73ee50ee65..8f02f8d437 100644 --- a/esphome/components/mqtt/__init__.py +++ b/esphome/components/mqtt/__init__.py @@ -192,6 +192,7 @@ CONFIG_SCHEMA = cv.All( } ), validate_config, + cv.only_with_arduino, ) diff --git a/esphome/components/mqtt/custom_mqtt_device.cpp b/esphome/components/mqtt/custom_mqtt_device.cpp index 9dec9498ad..787cc1153f 100644 --- a/esphome/components/mqtt/custom_mqtt_device.cpp +++ b/esphome/components/mqtt/custom_mqtt_device.cpp @@ -1,4 +1,7 @@ #include "custom_mqtt_device.h" + +#ifdef USE_MQTT + #include "esphome/core/log.h" namespace esphome { @@ -28,3 +31,5 @@ bool CustomMQTTDevice::is_connected() { return global_mqtt_client != nullptr && } // namespace mqtt } // namespace esphome + +#endif // USE_MQTT diff --git a/esphome/components/mqtt/custom_mqtt_device.h b/esphome/components/mqtt/custom_mqtt_device.h index 1c8b2e916e..9795d69304 100644 --- a/esphome/components/mqtt/custom_mqtt_device.h +++ b/esphome/components/mqtt/custom_mqtt_device.h @@ -1,5 +1,8 @@ #pragma once +#include "esphome/core/defines.h" +#ifdef USE_MQTT + #include "esphome/core/component.h" #include "mqtt_client.h" @@ -215,3 +218,5 @@ void CustomMQTTDevice::subscribe_json(const std::string &topic, void (T::*callba } // namespace mqtt } // namespace esphome + +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_binary_sensor.cpp b/esphome/components/mqtt/mqtt_binary_sensor.cpp index 53a49c6844..d7322298bb 100644 --- a/esphome/components/mqtt/mqtt_binary_sensor.cpp +++ b/esphome/components/mqtt/mqtt_binary_sensor.cpp @@ -1,6 +1,7 @@ #include "mqtt_binary_sensor.h" #include "esphome/core/log.h" +#ifdef USE_MQTT #ifdef USE_BINARY_SENSOR namespace esphome { @@ -55,3 +56,4 @@ bool MQTTBinarySensorComponent::publish_state(bool state) { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_binary_sensor.h b/esphome/components/mqtt/mqtt_binary_sensor.h index 1ca82a947e..c459bea1f7 100644 --- a/esphome/components/mqtt/mqtt_binary_sensor.h +++ b/esphome/components/mqtt/mqtt_binary_sensor.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/defines.h" - +#ifdef USE_MQTT #ifdef USE_BINARY_SENSOR #include "mqtt_component.h" @@ -41,3 +41,4 @@ class MQTTBinarySensorComponent : public mqtt::MQTTComponent { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_client.cpp b/esphome/components/mqtt/mqtt_client.cpp index 237a8146e6..040b0001fe 100644 --- a/esphome/components/mqtt/mqtt_client.cpp +++ b/esphome/components/mqtt/mqtt_client.cpp @@ -1,9 +1,11 @@ #include "mqtt_client.h" +#ifdef USE_MQTT + #include "esphome/core/application.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" -#include "esphome/core/util.h" +#include "esphome/components/network/util.h" #include #ifdef USE_LOGGER #include "esphome/components/logger/logger.h" @@ -60,7 +62,7 @@ void MQTTClientComponent::setup() { void MQTTClientComponent::dump_config() { ESP_LOGCONFIG(TAG, "MQTT:"); ESP_LOGCONFIG(TAG, " Server Address: %s:%u (%s)", this->credentials_.address.c_str(), this->credentials_.port, - this->ip_.toString().c_str()); + this->ip_.str().c_str()); ESP_LOGCONFIG(TAG, " Username: " LOG_SECRET("'%s'"), this->credentials_.username.c_str()); ESP_LOGCONFIG(TAG, " Client ID: " LOG_SECRET("'%s'"), this->credentials_.client_id.c_str()); if (!this->discovery_info_.prefix.empty()) { @@ -87,11 +89,11 @@ void MQTTClientComponent::start_dnslookup_() { this->dns_resolve_error_ = false; this->dns_resolved_ = false; ip_addr_t addr; -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 err_t err = dns_gethostbyname_addrtype(this->credentials_.address.c_str(), &addr, MQTTClientComponent::dns_found_callback, this, LWIP_DNS_ADDRTYPE_IPV4); #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 err_t err = dns_gethostbyname(this->credentials_.address.c_str(), &addr, esphome::mqtt::MQTTClientComponent::dns_found_callback, this); #endif @@ -99,11 +101,11 @@ void MQTTClientComponent::start_dnslookup_() { case ERR_OK: { // Got IP immediately this->dns_resolved_ = true; -#ifdef ARDUINO_ARCH_ESP32 - this->ip_ = IPAddress(addr.u_addr.ip4.addr); +#ifdef USE_ESP32 + this->ip_ = addr.u_addr.ip4.addr; #endif -#ifdef ARDUINO_ARCH_ESP8266 - this->ip_ = IPAddress(addr.addr); +#ifdef USE_ESP8266 + this->ip_ = addr.addr; #endif this->start_connect_(); return; @@ -116,7 +118,7 @@ void MQTTClientComponent::start_dnslookup_() { default: case ERR_ARG: { // error -#if defined(ARDUINO_ARCH_ESP8266) +#if defined(USE_ESP8266) ESP_LOGW(TAG, "Error resolving MQTT broker IP address: %ld", err); #else ESP_LOGW(TAG, "Error resolving MQTT broker IP address: %d", err); @@ -143,10 +145,10 @@ void MQTTClientComponent::check_dnslookup_() { return; } - ESP_LOGD(TAG, "Resolved broker IP address to %s", this->ip_.toString().c_str()); + ESP_LOGD(TAG, "Resolved broker IP address to %s", this->ip_.str().c_str()); this->start_connect_(); } -#if defined(ARDUINO_ARCH_ESP8266) && LWIP_VERSION_MAJOR == 1 +#if defined(USE_ESP8266) && LWIP_VERSION_MAJOR == 1 void MQTTClientComponent::dns_found_callback(const char *name, ip_addr_t *ipaddr, void *callback_arg) { #else void MQTTClientComponent::dns_found_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg) { @@ -155,18 +157,18 @@ void MQTTClientComponent::dns_found_callback(const char *name, const ip_addr_t * if (ipaddr == nullptr) { a_this->dns_resolve_error_ = true; } else { -#ifdef ARDUINO_ARCH_ESP32 - a_this->ip_ = IPAddress(ipaddr->u_addr.ip4.addr); +#ifdef USE_ESP32 + a_this->ip_ = ipaddr->u_addr.ip4.addr; #endif -#ifdef ARDUINO_ARCH_ESP8266 - a_this->ip_ = IPAddress(ipaddr->addr); +#ifdef USE_ESP8266 + a_this->ip_ = ipaddr->addr; #endif a_this->dns_resolved_ = true; } } void MQTTClientComponent::start_connect_() { - if (!network_is_connected()) + if (!network::is_connected()) return; ESP_LOGI(TAG, "Connecting to MQTT..."); @@ -183,7 +185,7 @@ void MQTTClientComponent::start_connect_() { this->mqtt_client_.setCredentials(username, password); - this->mqtt_client_.setServer(this->ip_, this->credentials_.port); + this->mqtt_client_.setServer((uint32_t) this->ip_, this->credentials_.port); if (!this->last_will_.topic.empty()) { this->mqtt_client_.setWill(this->last_will_.topic.c_str(), this->last_will_.qos, this->last_will_.retain, this->last_will_.payload.c_str(), this->last_will_.payload.length()); @@ -251,7 +253,7 @@ void MQTTClientComponent::loop() { reason_s = LOG_STR("Unknown"); break; } - if (!network_is_connected()) { + if (!network::is_connected()) { reason_s = LOG_STR("WiFi disconnected"); } ESP_LOGW(TAG, "MQTT Disconnected: %s.", LOG_STR_ARG(reason_s)); @@ -477,7 +479,7 @@ static bool topic_match(const char *message, const char *subscription) { } void MQTTClientComponent::on_message(const std::string &topic, const std::string &payload) { -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 // on ESP8266, this is called in LWiP thread; some components do not like running // in an ISR. this->defer([this, topic, payload]() { @@ -485,7 +487,7 @@ void MQTTClientComponent::on_message(const std::string &topic, const std::string for (auto &subscription : this->subscriptions_) if (topic_match(topic.c_str(), subscription.topic.c_str())) subscription.callback(topic, payload); -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 }); #endif } @@ -587,3 +589,5 @@ float MQTTMessageTrigger::get_setup_priority() const { return setup_priority::AF } // namespace mqtt } // namespace esphome + +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_client.h b/esphome/components/mqtt/mqtt_client.h index 119e61db2b..fa689eaa04 100644 --- a/esphome/components/mqtt/mqtt_client.h +++ b/esphome/components/mqtt/mqtt_client.h @@ -1,10 +1,14 @@ #pragma once -#include "esphome/core/component.h" #include "esphome/core/defines.h" + +#ifdef USE_MQTT + +#include "esphome/core/component.h" #include "esphome/core/automation.h" #include "esphome/core/log.h" #include "esphome/components/json/json_util.h" +#include "esphome/components/network/ip_address.h" #include #include "lwip/ip_addr.h" @@ -226,7 +230,7 @@ class MQTTClientComponent : public Component { void start_connect_(); void start_dnslookup_(); void check_dnslookup_(); -#if defined(ARDUINO_ARCH_ESP8266) && LWIP_VERSION_MAJOR == 1 +#if defined(USE_ESP8266) && LWIP_VERSION_MAJOR == 1 static void dns_found_callback(const char *name, ip_addr_t *ipaddr, void *callback_arg); #else static void dns_found_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg); @@ -265,7 +269,7 @@ class MQTTClientComponent : public Component { std::vector subscriptions_; AsyncMqttClient mqtt_client_; MQTTClientState state_{MQTT_CLIENT_DISCONNECTED}; - IPAddress ip_; + network::IPAddress ip_; bool dns_resolved_{false}; bool dns_resolve_error_{false}; std::vector children_; @@ -352,3 +356,5 @@ template class MQTTConnectedCondition : public Condition } // namespace mqtt } // namespace esphome + +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_climate.cpp b/esphome/components/mqtt/mqtt_climate.cpp index be9dbb0a08..8519e296b0 100644 --- a/esphome/components/mqtt/mqtt_climate.cpp +++ b/esphome/components/mqtt/mqtt_climate.cpp @@ -1,6 +1,7 @@ #include "mqtt_climate.h" #include "esphome/core/log.h" +#ifdef USE_MQTT #ifdef USE_CLIMATE namespace esphome { @@ -245,7 +246,7 @@ bool MQTTClimateComponent::publish_state_() { if (!this->publish(this->get_mode_state_topic(), mode_s)) success = false; int8_t accuracy = traits.get_temperature_accuracy_decimals(); - if (traits.get_supports_current_temperature() && !isnan(this->device_->current_temperature)) { + if (traits.get_supports_current_temperature() && !std::isnan(this->device_->current_temperature)) { std::string payload = value_accuracy_to_string(this->device_->current_temperature, accuracy); if (!this->publish(this->get_current_temperature_state_topic(), payload)) success = false; @@ -359,3 +360,4 @@ bool MQTTClimateComponent::publish_state_() { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_climate.h b/esphome/components/mqtt/mqtt_climate.h index 8aea4feb26..8b8a8e866e 100644 --- a/esphome/components/mqtt/mqtt_climate.h +++ b/esphome/components/mqtt/mqtt_climate.h @@ -2,6 +2,7 @@ #include "esphome/core/defines.h" +#ifdef USE_MQTT #ifdef USE_CLIMATE #include "esphome/components/climate/climate.h" @@ -48,3 +49,4 @@ class MQTTClimateComponent : public mqtt::MQTTComponent { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_component.cpp b/esphome/components/mqtt/mqtt_component.cpp index 3ed9aafb42..96cda57914 100644 --- a/esphome/components/mqtt/mqtt_component.cpp +++ b/esphome/components/mqtt/mqtt_component.cpp @@ -1,4 +1,7 @@ #include "mqtt_component.h" + +#ifdef USE_MQTT + #include "esphome/core/log.h" #include "esphome/core/application.h" #include "esphome/core/helpers.h" @@ -198,3 +201,5 @@ bool MQTTComponent::is_connected_() const { return global_mqtt_client->is_connec } // namespace mqtt } // namespace esphome + +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_component.h b/esphome/components/mqtt/mqtt_component.h index 668162da5a..f07e752e02 100644 --- a/esphome/components/mqtt/mqtt_component.h +++ b/esphome/components/mqtt/mqtt_component.h @@ -1,5 +1,9 @@ #pragma once +#include "esphome/core/defines.h" + +#ifdef USE_MQTT + #include #include "esphome/core/component.h" @@ -181,3 +185,5 @@ class MQTTComponent : public Component { } // namespace mqtt } // namespace esphome + +#endif // USE_MQTt diff --git a/esphome/components/mqtt/mqtt_cover.cpp b/esphome/components/mqtt/mqtt_cover.cpp index b11ae1fb93..6a0d2d1bc5 100644 --- a/esphome/components/mqtt/mqtt_cover.cpp +++ b/esphome/components/mqtt/mqtt_cover.cpp @@ -1,6 +1,7 @@ #include "mqtt_cover.h" #include "esphome/core/log.h" +#ifdef USE_MQTT #ifdef USE_COVER namespace esphome { @@ -115,3 +116,4 @@ bool MQTTCoverComponent::publish_state() { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_cover.h b/esphome/components/mqtt/mqtt_cover.h index 5c2ce93987..b8c9f2617c 100644 --- a/esphome/components/mqtt/mqtt_cover.h +++ b/esphome/components/mqtt/mqtt_cover.h @@ -3,6 +3,7 @@ #include "esphome/core/defines.h" #include "mqtt_component.h" +#ifdef USE_MQTT #ifdef USE_COVER #include "esphome/components/cover/cover.h" @@ -40,3 +41,4 @@ class MQTTCoverComponent : public mqtt::MQTTComponent { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_fan.cpp b/esphome/components/mqtt/mqtt_fan.cpp index b8eecf0ff3..c8db5ecece 100644 --- a/esphome/components/mqtt/mqtt_fan.cpp +++ b/esphome/components/mqtt/mqtt_fan.cpp @@ -1,6 +1,7 @@ #include "mqtt_fan.h" #include "esphome/core/log.h" +#ifdef USE_MQTT #ifdef USE_FAN #include "esphome/components/fan/fan_helpers.h" @@ -127,3 +128,4 @@ bool MQTTFanComponent::publish_state() { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_fan.h b/esphome/components/mqtt/mqtt_fan.h index 5495780a27..99d9c055cf 100644 --- a/esphome/components/mqtt/mqtt_fan.h +++ b/esphome/components/mqtt/mqtt_fan.h @@ -2,6 +2,7 @@ #include "esphome/core/defines.h" +#ifdef USE_MQTT #ifdef USE_FAN #include "esphome/components/fan/fan_state.h" @@ -45,3 +46,4 @@ class MQTTFanComponent : public mqtt::MQTTComponent { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_light.cpp b/esphome/components/mqtt/mqtt_light.cpp index f53be9c010..d028b1b037 100644 --- a/esphome/components/mqtt/mqtt_light.cpp +++ b/esphome/components/mqtt/mqtt_light.cpp @@ -1,6 +1,7 @@ #include "mqtt_light.h" #include "esphome/core/log.h" +#ifdef USE_MQTT #ifdef USE_LIGHT #include "esphome/components/light/light_json_schema.h" @@ -79,3 +80,4 @@ void MQTTJSONLightComponent::dump_config() { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_light.h b/esphome/components/mqtt/mqtt_light.h index 9417e71ddd..b0f3145900 100644 --- a/esphome/components/mqtt/mqtt_light.h +++ b/esphome/components/mqtt/mqtt_light.h @@ -2,6 +2,7 @@ #include "esphome/core/defines.h" +#ifdef USE_MQTT #ifdef USE_LIGHT #include "mqtt_component.h" @@ -39,3 +40,4 @@ class MQTTJSONLightComponent : public mqtt::MQTTComponent { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_number.cpp b/esphome/components/mqtt/mqtt_number.cpp index f209f4fe20..faa38056a9 100644 --- a/esphome/components/mqtt/mqtt_number.cpp +++ b/esphome/components/mqtt/mqtt_number.cpp @@ -1,6 +1,7 @@ #include "mqtt_number.h" #include "esphome/core/log.h" +#ifdef USE_MQTT #ifdef USE_NUMBER namespace esphome { @@ -63,3 +64,4 @@ bool MQTTNumberComponent::publish_state(float value) { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_number.h b/esphome/components/mqtt/mqtt_number.h index f44de91435..46dc221e9e 100644 --- a/esphome/components/mqtt/mqtt_number.h +++ b/esphome/components/mqtt/mqtt_number.h @@ -2,6 +2,7 @@ #include "esphome/core/defines.h" +#ifdef USE_MQTT #ifdef USE_NUMBER #include "esphome/components/number/number.h" @@ -44,3 +45,4 @@ class MQTTNumberComponent : public mqtt::MQTTComponent { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_select.cpp b/esphome/components/mqtt/mqtt_select.cpp index c0ac472d46..467a0cd84c 100644 --- a/esphome/components/mqtt/mqtt_select.cpp +++ b/esphome/components/mqtt/mqtt_select.cpp @@ -1,6 +1,7 @@ #include "mqtt_select.h" #include "esphome/core/log.h" +#ifdef USE_MQTT #ifdef USE_SELECT namespace esphome { @@ -56,3 +57,4 @@ bool MQTTSelectComponent::publish_state(const std::string &value) { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_select.h b/esphome/components/mqtt/mqtt_select.h index 013e905ead..0115c0a41e 100644 --- a/esphome/components/mqtt/mqtt_select.h +++ b/esphome/components/mqtt/mqtt_select.h @@ -2,6 +2,7 @@ #include "esphome/core/defines.h" +#ifdef USE_MQTT #ifdef USE_SELECT #include "esphome/components/select/select.h" @@ -44,3 +45,4 @@ class MQTTSelectComponent : public mqtt::MQTTComponent { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_sensor.cpp b/esphome/components/mqtt/mqtt_sensor.cpp index e921056167..72ec7c54ee 100644 --- a/esphome/components/mqtt/mqtt_sensor.cpp +++ b/esphome/components/mqtt/mqtt_sensor.cpp @@ -1,6 +1,7 @@ #include "mqtt_sensor.h" #include "esphome/core/log.h" +#ifdef USE_MQTT #ifdef USE_SENSOR #ifdef USE_DEEP_SLEEP @@ -77,3 +78,4 @@ std::string MQTTSensorComponent::unique_id() { return this->sensor_->unique_id() } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_sensor.h b/esphome/components/mqtt/mqtt_sensor.h index 8d8fa83531..2385529f4f 100644 --- a/esphome/components/mqtt/mqtt_sensor.h +++ b/esphome/components/mqtt/mqtt_sensor.h @@ -2,6 +2,7 @@ #include "esphome/core/defines.h" +#ifdef USE_MQTT #ifdef USE_SENSOR #include "esphome/components/sensor/sensor.h" @@ -58,3 +59,4 @@ class MQTTSensorComponent : public mqtt::MQTTComponent { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_switch.cpp b/esphome/components/mqtt/mqtt_switch.cpp index b73e1ab8dc..b13ddd5d9d 100644 --- a/esphome/components/mqtt/mqtt_switch.cpp +++ b/esphome/components/mqtt/mqtt_switch.cpp @@ -1,6 +1,7 @@ #include "mqtt_switch.h" #include "esphome/core/log.h" +#ifdef USE_MQTT #ifdef USE_SWITCH namespace esphome { @@ -58,3 +59,4 @@ bool MQTTSwitchComponent::publish_state(bool state) { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_switch.h b/esphome/components/mqtt/mqtt_switch.h index 33b829c856..9959d21872 100644 --- a/esphome/components/mqtt/mqtt_switch.h +++ b/esphome/components/mqtt/mqtt_switch.h @@ -2,6 +2,7 @@ #include "esphome/core/defines.h" +#ifdef USE_MQTT #ifdef USE_SWITCH #include "esphome/components/switch/switch.h" @@ -39,3 +40,4 @@ class MQTTSwitchComponent : public mqtt::MQTTComponent { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_text_sensor.cpp b/esphome/components/mqtt/mqtt_text_sensor.cpp index 8bc11d954c..fd96cd0902 100644 --- a/esphome/components/mqtt/mqtt_text_sensor.cpp +++ b/esphome/components/mqtt/mqtt_text_sensor.cpp @@ -1,6 +1,7 @@ #include "mqtt_text_sensor.h" #include "esphome/core/log.h" +#ifdef USE_MQTT #ifdef USE_TEXT_SENSOR namespace esphome { @@ -43,3 +44,4 @@ std::string MQTTTextSensor::unique_id() { return this->sensor_->unique_id(); } } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_text_sensor.h b/esphome/components/mqtt/mqtt_text_sensor.h index a5ce0658c7..1d3f95b894 100644 --- a/esphome/components/mqtt/mqtt_text_sensor.h +++ b/esphome/components/mqtt/mqtt_text_sensor.h @@ -2,6 +2,7 @@ #include "esphome/core/defines.h" +#ifdef USE_MQTT #ifdef USE_TEXT_SENSOR #include "esphome/components/text_sensor/text_sensor.h" @@ -40,3 +41,4 @@ class MQTTTextSensor : public mqtt::MQTTComponent { } // namespace esphome #endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt_subscribe/sensor/mqtt_subscribe_sensor.cpp b/esphome/components/mqtt_subscribe/sensor/mqtt_subscribe_sensor.cpp index 06251bd7c8..e1accf3c70 100644 --- a/esphome/components/mqtt_subscribe/sensor/mqtt_subscribe_sensor.cpp +++ b/esphome/components/mqtt_subscribe/sensor/mqtt_subscribe_sensor.cpp @@ -1,4 +1,7 @@ #include "mqtt_subscribe_sensor.h" + +#ifdef USE_MQTT + #include "esphome/core/log.h" namespace esphome { @@ -31,3 +34,5 @@ void MQTTSubscribeSensor::dump_config() { } // namespace mqtt_subscribe } // namespace esphome + +#endif // USE_MQTT diff --git a/esphome/components/mqtt_subscribe/sensor/mqtt_subscribe_sensor.h b/esphome/components/mqtt_subscribe/sensor/mqtt_subscribe_sensor.h index a303ccad89..0619326ac9 100644 --- a/esphome/components/mqtt_subscribe/sensor/mqtt_subscribe_sensor.h +++ b/esphome/components/mqtt_subscribe/sensor/mqtt_subscribe_sensor.h @@ -1,5 +1,9 @@ #pragma once +#include "esphome/core/defines.h" + +#ifdef USE_MQTT + #include "esphome/core/component.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/mqtt/mqtt_client.h" @@ -25,3 +29,5 @@ class MQTTSubscribeSensor : public sensor::Sensor, public Component { } // namespace mqtt_subscribe } // namespace esphome + +#endif // USE_MQTT diff --git a/esphome/components/mqtt_subscribe/text_sensor/mqtt_subscribe_text_sensor.cpp b/esphome/components/mqtt_subscribe/text_sensor/mqtt_subscribe_text_sensor.cpp index 2b0908979c..8aa094a2d4 100644 --- a/esphome/components/mqtt_subscribe/text_sensor/mqtt_subscribe_text_sensor.cpp +++ b/esphome/components/mqtt_subscribe/text_sensor/mqtt_subscribe_text_sensor.cpp @@ -1,5 +1,7 @@ #include "mqtt_subscribe_text_sensor.h" +#ifdef USE_MQTT + #include "esphome/core/log.h" #include @@ -22,3 +24,5 @@ void MQTTSubscribeTextSensor::dump_config() { } // namespace mqtt_subscribe } // namespace esphome + +#endif // USE_MQTT diff --git a/esphome/components/mqtt_subscribe/text_sensor/mqtt_subscribe_text_sensor.h b/esphome/components/mqtt_subscribe/text_sensor/mqtt_subscribe_text_sensor.h index 69409f6348..9f8e5c63cc 100644 --- a/esphome/components/mqtt_subscribe/text_sensor/mqtt_subscribe_text_sensor.h +++ b/esphome/components/mqtt_subscribe/text_sensor/mqtt_subscribe_text_sensor.h @@ -1,5 +1,9 @@ #pragma once +#include "esphome/core/defines.h" + +#ifdef USE_MQTT + #include "esphome/core/component.h" #include "esphome/components/text_sensor/text_sensor.h" #include "esphome/components/mqtt/mqtt_client.h" @@ -24,3 +28,5 @@ class MQTTSubscribeTextSensor : public text_sensor::TextSensor, public Component } // namespace mqtt_subscribe } // namespace esphome + +#endif // USE_MQTT diff --git a/esphome/components/ms5611/ms5611.cpp b/esphome/components/ms5611/ms5611.cpp index 51dc569240..1d7516dbe8 100644 --- a/esphome/components/ms5611/ms5611.cpp +++ b/esphome/components/ms5611/ms5611.cpp @@ -1,5 +1,6 @@ #include "ms5611.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace ms5611 { diff --git a/esphome/components/my9231/my9231.h b/esphome/components/my9231/my9231.h index ee15f9743c..a777dcc960 100644 --- a/esphome/components/my9231/my9231.h +++ b/esphome/components/my9231/my9231.h @@ -1,8 +1,9 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/output/float_output.h" +#include namespace esphome { namespace my9231 { diff --git a/esphome/components/neopixelbus/light.py b/esphome/components/neopixelbus/light.py index a86b4e4588..0117f1b063 100644 --- a/esphome/components/neopixelbus/light.py +++ b/esphome/components/neopixelbus/light.py @@ -168,14 +168,15 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_VARIANT, default="800KBPS"): validate_variant, cv.Optional(CONF_METHOD, default=None): validate_method, cv.Optional(CONF_INVERT, default="no"): cv.boolean, - cv.Optional(CONF_PIN): pins.output_pin, - cv.Optional(CONF_CLOCK_PIN): pins.output_pin, - cv.Optional(CONF_DATA_PIN): pins.output_pin, + cv.Optional(CONF_PIN): pins.internal_gpio_output_pin_number, + cv.Optional(CONF_CLOCK_PIN): pins.internal_gpio_output_pin_number, + cv.Optional(CONF_DATA_PIN): pins.internal_gpio_output_pin_number, cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int, } ).extend(cv.COMPONENT_SCHEMA), _validate, validate_method_pin, + cv.only_with_arduino, ) diff --git a/esphome/components/neopixelbus/neopixelbus_light.h b/esphome/components/neopixelbus/neopixelbus_light.h index 5359bac61b..34e10f2cfe 100644 --- a/esphome/components/neopixelbus/neopixelbus_light.h +++ b/esphome/components/neopixelbus/neopixelbus_light.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include "esphome/core/macros.h" #include "esphome/core/component.h" #include "esphome/core/helpers.h" @@ -7,7 +9,7 @@ #include "esphome/components/light/light_output.h" #include "esphome/components/light/addressable_light.h" -#if defined(ARDUINO_ARCH_ESP8266) && ARDUINO_VERSION_CODE < VERSION_CODE(2, 4, 0) +#if defined(USE_ESP8266) && ARDUINO_VERSION_CODE < VERSION_CODE(2, 4, 0) #error The NeoPixelBus library requires at least arduino_version 2.4.x #endif @@ -144,3 +146,5 @@ class NeoPixelRGBWLightOutput : public NeoPixelBusLightOutputBase +#include +#include +#include + +namespace esphome { +namespace network { + +struct IPAddress { + public: + IPAddress() : addr_({0, 0, 0, 0}) {} + IPAddress(uint8_t first, uint8_t second, uint8_t third, uint8_t fourth) : addr_({first, second, third, fourth}) {} + IPAddress(uint32_t raw) { + addr_[0] = (uint8_t)(raw >> 0); + addr_[1] = (uint8_t)(raw >> 8); + addr_[2] = (uint8_t)(raw >> 16); + addr_[3] = (uint8_t)(raw >> 24); + } + operator uint32_t() const { + uint32_t res = 0; + res |= ((uint32_t) addr_[0]) << 0; + res |= ((uint32_t) addr_[1]) << 8; + res |= ((uint32_t) addr_[2]) << 16; + res |= ((uint32_t) addr_[3]) << 24; + return res; + } + std::string str() const { + char buffer[24]; + snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d", addr_[0], addr_[1], addr_[2], addr_[3]); + return buffer; + } + bool operator==(const IPAddress &other) const { + return addr_[0] == other.addr_[0] && addr_[1] == other.addr_[1] && addr_[2] == other.addr_[2] && + addr_[3] == other.addr_[3]; + } + uint8_t operator[](int index) const { return addr_[index]; } + uint8_t &operator[](int index) { return addr_[index]; } + + protected: + std::array addr_; +}; + +} // namespace network +} // namespace esphome diff --git a/esphome/components/network/util.cpp b/esphome/components/network/util.cpp new file mode 100644 index 0000000000..f7ac6b543e --- /dev/null +++ b/esphome/components/network/util.cpp @@ -0,0 +1,54 @@ +#include "util.h" +#include "esphome/core/defines.h" + +#ifdef USE_WIFI +#include "esphome/components/wifi/wifi_component.h" +#endif + +#ifdef USE_ETHERNET +#include "esphome/components/ethernet/ethernet_component.h" +#endif + +namespace esphome { +namespace network { + +bool is_connected() { +#ifdef USE_ETHERNET + if (ethernet::global_eth_component != nullptr && ethernet::global_eth_component->is_connected()) + return true; +#endif + +#ifdef USE_WIFI + if (wifi::global_wifi_component != nullptr) + return wifi::global_wifi_component->is_connected(); +#endif + + return false; +} + +network::IPAddress get_ip_address() { +#ifdef USE_ETHERNET + if (ethernet::global_eth_component != nullptr) + return ethernet::global_eth_component->get_ip_address(); +#endif +#ifdef USE_WIFI + if (wifi::global_wifi_component != nullptr) + return wifi::global_wifi_component->get_ip_address(); +#endif + return {}; +} + +std::string get_use_address() { +#ifdef USE_ETHERNET + if (ethernet::global_eth_component != nullptr) + return ethernet::global_eth_component->get_use_address(); +#endif +#ifdef USE_WIFI + if (wifi::global_wifi_component != nullptr) + return wifi::global_wifi_component->get_use_address(); +#endif + return ""; +} + +} // namespace network +} // namespace esphome diff --git a/esphome/components/network/util.h b/esphome/components/network/util.h new file mode 100644 index 0000000000..f248d5cbf4 --- /dev/null +++ b/esphome/components/network/util.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include "ip_address.h" + +namespace esphome { +namespace network { + +/// Return whether the node is connected to the network (through wifi, eth, ...) +bool is_connected(); +/// Get the active network hostname +std::string get_use_address(); +IPAddress get_ip_address(); + +} // namespace network +} // namespace esphome diff --git a/esphome/components/nextion/display.py b/esphome/components/nextion/display.py index e693b2f1ec..f4b35fd56f 100644 --- a/esphome/components/nextion/display.py +++ b/esphome/components/nextion/display.py @@ -33,7 +33,7 @@ CONFIG_SCHEMA = ( display.BASIC_DISPLAY_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(Nextion), - cv.Optional(CONF_TFT_URL): cv.string, + cv.Optional(CONF_TFT_URL): cv.All(cv.string, cv.only_with_arduino), cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage, cv.Optional(CONF_ON_SETUP): automation.validate_automation( { @@ -74,7 +74,7 @@ async def to_code(config): cg.add(var.set_writer(lambda_)) if CONF_TFT_URL in config: - cg.add_define("USE_TFT_UPLOAD") + cg.add_define("USE_NEXTION_TFT_UPLOAD") cg.add(var.set_tft_url(config[CONF_TFT_URL])) if CONF_TOUCH_SLEEP_TIMEOUT in config: diff --git a/esphome/components/nextion/nextion.h b/esphome/components/nextion/nextion.h index 45c4a629c4..1bee41f6cf 100644 --- a/esphome/components/nextion/nextion.h +++ b/esphome/components/nextion/nextion.h @@ -7,11 +7,11 @@ #include "nextion_component.h" #include "esphome/components/display/display_color_utils.h" -#if defined(USE_ETHERNET) || defined(USE_WIFI) -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_NEXTION_TFT_UPLOAD +#ifdef USE_ESP32 #include #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #include #include #endif @@ -652,7 +652,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe */ bool send_command_printf(const char *format, ...) __attribute__((format(printf, 2, 3))); -#ifdef USE_TFT_UPLOAD +#ifdef USE_NEXTION_TFT_UPLOAD /** * Set the tft file URL. https seems problamtic with arduino.. */ @@ -770,9 +770,8 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe const std::string &variable_name_to_send, const std::string &state_value, bool is_sleep_safe = false); -#ifdef USE_TFT_UPLOAD -#if defined(USE_ETHERNET) || defined(USE_WIFI) -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_NEXTION_TFT_UPLOAD +#ifdef USE_ESP8266 WiFiClient *wifi_client_{nullptr}; BearSSL::WiFiClientSecure *wifi_client_secure_{nullptr}; WiFiClient *get_wifi_client_(); @@ -801,9 +800,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe bool upload_from_buffer_(const uint8_t *file_buf, size_t buf_size); void upload_end_(); -#endif - -#endif +#endif // USE_NEXTION_TFT_UPLOAD bool get_is_connected_() { return this->is_connected_; } @@ -828,7 +825,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe void remove_front_no_sensors_(); -#ifdef USE_TFT_UPLOAD +#ifdef USE_NEXTION_TFT_UPLOAD std::string tft_url_; uint8_t *transfer_buffer_{nullptr}; size_t transfer_buffer_size_; diff --git a/esphome/components/nextion/nextion_upload.cpp b/esphome/components/nextion/nextion_upload.cpp index f864a397cc..9a748277d8 100644 --- a/esphome/components/nextion/nextion_upload.cpp +++ b/esphome/components/nextion/nextion_upload.cpp @@ -1,16 +1,16 @@ +#ifdef USE_NEXTION_TFT_UPLOAD #include "nextion.h" #include "esphome/core/application.h" #include "esphome/core/macros.h" #include "esphome/core/util.h" #include "esphome/core/log.h" +#include "esphome/components/network/util.h" namespace esphome { namespace nextion { static const char *const TAG = "nextion_upload"; -#if defined(USE_TFT_UPLOAD) && (defined(USE_ETHERNET) || defined(USE_WIFI)) - // Followed guide // https://unofficialnextion.com/t/nextion-upload-protocol-v1-2-the-fast-one/1044/2 @@ -26,7 +26,7 @@ int Nextion::upload_by_chunks_(HTTPClient *http, int range_start) { if (range_end > this->tft_size_) range_end = this->tft_size_; -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #if ARDUINO_VERSION_CODE >= VERSION_CODE(2, 7, 0) http->setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); #elif ARDUINO_VERSION_CODE >= VERSION_CODE(2, 6, 0) @@ -46,10 +46,10 @@ int Nextion::upload_by_chunks_(HTTPClient *http, int range_start) { int code = 0; bool begin_status = false; while (tries <= 5) { -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 begin_status = http->begin(this->tft_url_.c_str()); #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 begin_status = http->begin(*this->get_wifi_client_(), this->tft_url_.c_str()); #endif @@ -129,7 +129,7 @@ void Nextion::upload_tft() { return; } - if (!network_is_connected()) { + if (!network::is_connected()) { ESP_LOGD(TAG, "network is not connected"); return; } @@ -139,10 +139,10 @@ void Nextion::upload_tft() { HTTPClient http; http.setTimeout(15000); // Yes 15 seconds.... Helps 8266s along bool begin_status = false; -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 begin_status = http.begin(this->tft_url_.c_str()); #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #if ARDUINO_VERSION_CODE >= VERSION_CODE(2, 7, 0) http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); #elif ARDUINO_VERSION_CODE >= VERSION_CODE(2, 6, 0) @@ -157,7 +157,7 @@ void Nextion::upload_tft() { if (!begin_status) { this->is_updating_ = false; ESP_LOGD(TAG, "connection failed"); -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 if (psramFound()) free(this->transfer_buffer_); // NOLINT else @@ -249,7 +249,7 @@ void Nextion::upload_tft() { } // Nextion wants 4096 bytes at a time. Make chunk_size a multiple of 4096 -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 uint32_t chunk_size = 8192; if (psramFound()) { chunk_size = this->content_length_; @@ -268,7 +268,7 @@ void Nextion::upload_tft() { #endif if (this->transfer_buffer_ == nullptr) { -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 if (psramFound()) { ESP_LOGD(TAG, "Allocating PSRAM buffer size %d, Free PSRAM size is %u", chunk_size, ESP.getFreePsram()); this->transfer_buffer_ = (uint8_t *) ps_malloc(chunk_size); @@ -289,7 +289,7 @@ void Nextion::upload_tft() { if (!this->transfer_buffer_) this->upload_end_(); -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 } #endif } @@ -325,7 +325,7 @@ void Nextion::upload_end_() { ESP.restart(); // NOLINT(readability-static-accessed-through-instance) } -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 WiFiClient *Nextion::get_wifi_client_() { if (this->tft_url_.compare(0, 6, "https:") == 0) { if (this->wifi_client_secure_ == nullptr) { @@ -342,9 +342,7 @@ WiFiClient *Nextion::get_wifi_client_() { return this->wifi_client_; } #endif - -#else -void Nextion::upload_tft() { ESP_LOGW(TAG, "tft_url, WIFI or Ethernet components are needed. Cannot upload."); } -#endif } // namespace nextion } // namespace esphome + +#endif // USE_NEXTION_TFT_UPLOAD diff --git a/esphome/components/nextion/sensor/nextion_sensor.cpp b/esphome/components/nextion/sensor/nextion_sensor.cpp index 1e79164546..e983ebcc6f 100644 --- a/esphome/components/nextion/sensor/nextion_sensor.cpp +++ b/esphome/components/nextion/sensor/nextion_sensor.cpp @@ -48,7 +48,7 @@ void NextionSensor::set_state(float state, bool publish, bool send_to_nextion) { if (!this->nextion_->is_setup()) return; - if (isnan(state)) + if (std::isnan(state)) return; if (this->wave_chan_id_ == UINT8_MAX) { diff --git a/esphome/components/ntc/ntc.cpp b/esphome/components/ntc/ntc.cpp index 80a11384b9..333dbc5a75 100644 --- a/esphome/components/ntc/ntc.cpp +++ b/esphome/components/ntc/ntc.cpp @@ -14,7 +14,7 @@ void NTC::setup() { void NTC::dump_config() { LOG_SENSOR("", "NTC Sensor", this) } float NTC::get_setup_priority() const { return setup_priority::DATA; } void NTC::process_(float value) { - if (isnan(value)) { + if (std::isnan(value)) { this->publish_state(NAN); return; } diff --git a/esphome/components/number/automation.cpp b/esphome/components/number/automation.cpp index a0b169427f..c75d272660 100644 --- a/esphome/components/number/automation.cpp +++ b/esphome/components/number/automation.cpp @@ -7,7 +7,7 @@ namespace number { static const char *const TAG = "number.automation"; void ValueRangeTrigger::setup() { - this->rtc_ = global_preferences.make_preference(this->parent_->get_object_id_hash()); + this->rtc_ = global_preferences->make_preference(this->parent_->get_object_id_hash()); bool initial_state; if (this->rtc_.load(&initial_state)) { this->previous_in_range_ = initial_state; @@ -18,18 +18,18 @@ void ValueRangeTrigger::setup() { float ValueRangeTrigger::get_setup_priority() const { return setup_priority::HARDWARE; } void ValueRangeTrigger::on_state_(float state) { - if (isnan(state)) + if (std::isnan(state)) return; float local_min = this->min_.value(state); float local_max = this->max_.value(state); bool in_range; - if (isnan(local_min) && isnan(local_max)) { + if (std::isnan(local_min) && std::isnan(local_max)) { in_range = this->previous_in_range_; - } else if (isnan(local_min)) { + } else if (std::isnan(local_min)) { in_range = state <= local_max; - } else if (isnan(local_max)) { + } else if (std::isnan(local_max)) { in_range = state >= local_min; } else { in_range = local_min <= state && state <= local_max; diff --git a/esphome/components/number/automation.h b/esphome/components/number/automation.h index 9e812f8c49..98554a346a 100644 --- a/esphome/components/number/automation.h +++ b/esphome/components/number/automation.h @@ -57,9 +57,9 @@ template class NumberInRangeCondition : public Condition void set_max(float max) { this->max_ = max; } bool check(Ts... x) override { const float state = this->parent_->state; - if (isnan(this->min_)) { + if (std::isnan(this->min_)) { return state <= this->max_; - } else if (isnan(this->max_)) { + } else if (std::isnan(this->max_)) { return state >= this->min_; } else { return this->min_ <= state && state <= this->max_; diff --git a/esphome/components/number/number.cpp b/esphome/components/number/number.cpp index dbc1c88a5d..57a5c7c4bd 100644 --- a/esphome/components/number/number.cpp +++ b/esphome/components/number/number.cpp @@ -8,7 +8,7 @@ static const char *const TAG = "number"; void NumberCall::perform() { ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str()); - if (!this->value_.has_value() || isnan(*this->value_)) { + if (!this->value_.has_value() || std::isnan(*this->value_)) { ESP_LOGW(TAG, "No value set for NumberCall"); return; } diff --git a/esphome/components/ota/__init__.py b/esphome/components/ota/__init__.py index 75641ad399..59ab22056b 100644 --- a/esphome/components/ota/__init__.py +++ b/esphome/components/ota/__init__.py @@ -15,6 +15,7 @@ from esphome.core import CORE, coroutine_with_priority CODEOWNERS = ["@esphome/core"] DEPENDENCIES = ["network"] +AUTO_LOAD = ["socket"] CONF_ON_STATE_CHANGE = "on_state_change" CONF_ON_BEGIN = "on_begin" @@ -33,12 +34,21 @@ OTAProgressTrigger = ota_ns.class_("OTAProgressTrigger", automation.Trigger.temp OTAEndTrigger = ota_ns.class_("OTAEndTrigger", automation.Trigger.template()) OTAErrorTrigger = ota_ns.class_("OTAErrorTrigger", automation.Trigger.template()) + +def validate_password_support(value): + if CORE.using_arduino: + return value + if CORE.using_esp_idf: + raise cv.Invalid("Password support is not implemented yet for ESP-IDF") + raise NotImplementedError + + CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(OTAComponent), cv.Optional(CONF_SAFE_MODE, default=True): cv.boolean, cv.SplitDefault(CONF_PORT, esp8266=8266, esp32=3232): cv.port, - cv.Optional(CONF_PASSWORD, default=""): cv.string, + cv.Optional(CONF_PASSWORD): cv.All(cv.string, validate_password_support), cv.Optional( CONF_REBOOT_TIMEOUT, default="5min" ): cv.positive_time_period_milliseconds, @@ -76,7 +86,9 @@ CONFIG_SCHEMA = cv.Schema( async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) cg.add(var.set_port(config[CONF_PORT])) - cg.add(var.set_auth_password(config[CONF_PASSWORD])) + if CONF_PASSWORD in config: + cg.add(var.set_auth_password(config[CONF_PASSWORD])) + cg.add_define("USE_OTA_PASSWORD") await cg.register_component(var, config) @@ -88,7 +100,7 @@ async def to_code(config): if CORE.is_esp8266: cg.add_library("Update", None) - elif CORE.is_esp32: + elif CORE.is_esp32 and CORE.using_arduino: cg.add_library("Hash", None) use_state_callback = False diff --git a/esphome/components/ota/ota_component.cpp b/esphome/components/ota/ota_component.cpp index e8ac1b9bcd..7ee3ed2866 100644 --- a/esphome/components/ota/ota_component.cpp +++ b/esphome/components/ota/ota_component.cpp @@ -2,14 +2,31 @@ #include "esphome/core/log.h" #include "esphome/core/application.h" +#include "esphome/core/hal.h" #include "esphome/core/util.h" +#include "esphome/components/network/util.h" +#include #include + +#ifdef USE_ARDUINO +#ifdef USE_OTA_PASSWORD #include -#ifdef ARDUINO_ARCH_ESP32 +#endif // USE_OTA_PASSWORD + +#ifdef USE_ESP32 #include +#endif // USE_ESP32 +#endif // USE_ARDUINO + +#ifdef USE_ESP8266 +#include +#include "esphome/components/esp8266/preferences.h" +#endif // USE_ESP8266 + +#ifdef USE_ESP_IDF +#include #endif -#include namespace esphome { namespace ota { @@ -18,19 +35,177 @@ static const char *const TAG = "ota"; static const uint8_t OTA_VERSION_1_0 = 1; +class OTABackend { + public: + virtual ~OTABackend() = default; + virtual OTAResponseTypes begin(size_t image_size) = 0; + virtual void set_update_md5(const char *md5) = 0; + virtual OTAResponseTypes write(uint8_t *data, size_t len) = 0; + virtual OTAResponseTypes end() = 0; + virtual void abort() = 0; +}; + +#ifdef USE_ARDUINO +class ArduinoOTABackend : public OTABackend { + public: + OTAResponseTypes begin(size_t image_size) override { + bool ret = Update.begin(image_size, U_FLASH); + if (ret) { +#ifdef USE_ESP8266 + esp8266::preferences_prevent_write(true); +#endif + return OTA_RESPONSE_OK; + } + + uint8_t error = Update.getError(); +#ifdef USE_ESP8266 + if (error == UPDATE_ERROR_BOOTSTRAP) + return OTA_RESPONSE_ERROR_INVALID_BOOTSTRAPPING; + if (error == UPDATE_ERROR_NEW_FLASH_CONFIG) + return OTA_RESPONSE_ERROR_WRONG_NEW_FLASH_CONFIG; + if (error == UPDATE_ERROR_FLASH_CONFIG) + return OTA_RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG; + if (error == UPDATE_ERROR_SPACE) + return OTA_RESPONSE_ERROR_ESP8266_NOT_ENOUGH_SPACE; +#endif +#ifdef USE_ESP32 + if (error == UPDATE_ERROR_SIZE) + return OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE; +#endif + return OTA_RESPONSE_ERROR_UNKNOWN; + } + void set_update_md5(const char *md5) override { Update.setMD5(md5); } + OTAResponseTypes write(uint8_t *data, size_t len) override { + size_t written = Update.write(data, len); + if (written != len) { + return OTA_RESPONSE_ERROR_WRITING_FLASH; + } + return OTA_RESPONSE_OK; + } + OTAResponseTypes end() override { + if (!Update.end()) + return OTA_RESPONSE_ERROR_UPDATE_END; + return OTA_RESPONSE_OK; + } + void abort() override { +#ifdef USE_ESP32 + Update.abort(); +#endif + +#ifdef USE_ESP8266 + Update.end(); + esp8266::preferences_prevent_write(false); +#endif + } +}; +std::unique_ptr make_ota_backend() { return make_unique(); } +#endif // USE_ARDUINO + +#ifdef USE_ESP_IDF +class IDFOTABackend : public OTABackend { + public: + esp_ota_handle_t update_handle = 0; + + OTAResponseTypes begin(size_t image_size) override { + const esp_partition_t *update_partition = esp_ota_get_next_update_partition(nullptr); + if (update_partition == nullptr) { + return OTA_RESPONSE_ERROR_NO_UPDATE_PARTITION; + } + esp_err_t err = esp_ota_begin(update_partition, image_size, &update_handle); + if (err != ESP_OK) { + esp_ota_abort(update_handle); + update_handle = 0; + if (err == ESP_ERR_INVALID_SIZE) { + return OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE; + } else if (err == ESP_ERR_FLASH_OP_TIMEOUT || err == ESP_ERR_FLASH_OP_FAIL) { + return OTA_RESPONSE_ERROR_WRITING_FLASH; + } + return OTA_RESPONSE_ERROR_UNKNOWN; + } + return OTA_RESPONSE_OK; + } + void set_update_md5(const char *md5) override { + // pass + } + OTAResponseTypes write(uint8_t *data, size_t len) override { + esp_err_t err = esp_ota_write(update_handle, data, len); + if (err != ESP_OK) { + if (err == ESP_ERR_OTA_VALIDATE_FAILED) { + return OTA_RESPONSE_ERROR_MAGIC; + } else if (err == ESP_ERR_FLASH_OP_TIMEOUT || err == ESP_ERR_FLASH_OP_FAIL) { + return OTA_RESPONSE_ERROR_WRITING_FLASH; + } + return OTA_RESPONSE_ERROR_UNKNOWN; + } + return OTA_RESPONSE_OK; + } + OTAResponseTypes end() override { + esp_err_t err = esp_ota_end(update_handle); + update_handle = 0; + if (err != ESP_OK) { + if (err == ESP_ERR_OTA_VALIDATE_FAILED) { + return OTA_RESPONSE_ERROR_UPDATE_END; + } + return OTA_RESPONSE_ERROR_UNKNOWN; + } + return OTA_RESPONSE_OK; + } + void abort() override { esp_ota_abort(update_handle); } +}; +std::unique_ptr make_ota_backend() { return make_unique(); } +#endif // USE_ESP_IDF + void OTAComponent::setup() { - this->server_ = make_unique(this->port_); - this->server_->begin(); + server_ = socket::socket(AF_INET, SOCK_STREAM, 0); + if (server_ == nullptr) { + ESP_LOGW(TAG, "Could not create socket."); + this->mark_failed(); + return; + } + int enable = 1; + int err = server_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)); + if (err != 0) { + ESP_LOGW(TAG, "Socket unable to set reuseaddr: errno %d", err); + // we can still continue + } + err = server_->setblocking(false); + if (err != 0) { + ESP_LOGW(TAG, "Socket unable to set nonblocking mode: errno %d", err); + this->mark_failed(); + return; + } + + struct sockaddr_in server; + memset(&server, 0, sizeof(server)); + server.sin_family = AF_INET; + server.sin_addr.s_addr = ESPHOME_INADDR_ANY; + server.sin_port = htons(this->port_); + + err = server_->bind((struct sockaddr *) &server, sizeof(server)); + if (err != 0) { + ESP_LOGW(TAG, "Socket unable to bind: errno %d", errno); + this->mark_failed(); + return; + } + + err = server_->listen(4); + if (err != 0) { + ESP_LOGW(TAG, "Socket unable to listen: errno %d", errno); + this->mark_failed(); + return; + } this->dump_config(); } void OTAComponent::dump_config() { ESP_LOGCONFIG(TAG, "Over-The-Air Updates:"); - ESP_LOGCONFIG(TAG, " Address: %s:%u", network_get_address().c_str(), this->port_); + ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->port_); +#ifdef USE_OTA_PASSWORD if (!this->password_.empty()) { ESP_LOGCONFIG(TAG, " Using Password."); } +#endif if (this->has_safe_mode_ && this->safe_mode_rtc_value_ > 1) { ESP_LOGW(TAG, "Last Boot was an unhandled reset, will proceed to safe mode in %d restarts", this->safe_mode_num_attempts_ - this->safe_mode_rtc_value_); @@ -57,25 +232,31 @@ void OTAComponent::handle_() { char *sbuf = reinterpret_cast(buf); uint32_t ota_size; uint8_t ota_features; + std::unique_ptr backend; (void) ota_features; - if (!this->client_.connected()) { - this->client_ = this->server_->available(); + if (client_ == nullptr) { + struct sockaddr_storage source_addr; + socklen_t addr_len = sizeof(source_addr); + client_ = server_->accept((struct sockaddr *) &source_addr, &addr_len); + } + if (client_ == nullptr) + return; - if (!this->client_.connected()) - return; + int enable = 1; + int err = client_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int)); + if (err != 0) { + ESP_LOGW(TAG, "Socket could not enable tcp nodelay, errno: %d", errno); + return; } - // enable nodelay for outgoing data - this->client_.setNoDelay(true); - - ESP_LOGD(TAG, "Starting OTA Update from %s...", this->client_.remoteIP().toString().c_str()); + ESP_LOGD(TAG, "Starting OTA Update from %s...", this->client_->getpeername().c_str()); this->status_set_warning(); #ifdef USE_OTA_STATE_CALLBACK this->state_callback_.call(OTA_STARTED, 0.0f, 0); #endif - if (!this->wait_receive_(buf, 5)) { + if (!this->readall_(buf, 5)) { ESP_LOGW(TAG, "Reading magic bytes failed!"); goto error; } @@ -88,11 +269,12 @@ void OTAComponent::handle_() { } // Send OK and version - 2 bytes - this->client_.write(OTA_RESPONSE_OK); - this->client_.write(OTA_VERSION_1_0); + buf[0] = OTA_RESPONSE_OK; + buf[1] = OTA_VERSION_1_0; + this->writeall_(buf, 2); // Read features - 1 byte - if (!this->wait_receive_(buf, 1)) { + if (!this->readall_(buf, 1)) { ESP_LOGW(TAG, "Reading features failed!"); goto error; } @@ -100,10 +282,13 @@ void OTAComponent::handle_() { ESP_LOGV(TAG, "OTA features is 0x%02X", ota_features); // Acknowledge header - 1 byte - this->client_.write(OTA_RESPONSE_HEADER_OK); + buf[0] = OTA_RESPONSE_HEADER_OK; + this->writeall_(buf, 1); +#ifdef USE_OTA_PASSWORD if (!this->password_.empty()) { - this->client_.write(OTA_RESPONSE_REQUEST_AUTH); + buf[0] = OTA_RESPONSE_REQUEST_AUTH; + this->writeall_(buf, 1); MD5Builder md5_builder{}; md5_builder.begin(); sprintf(sbuf, "%08X", random_uint32()); @@ -113,7 +298,7 @@ void OTAComponent::handle_() { ESP_LOGV(TAG, "Auth: Nonce is %s", sbuf); // Send nonce, 32 bytes hex MD5 - if (this->client_.write(reinterpret_cast(sbuf), 32) != 32) { + if (!this->writeall_(reinterpret_cast(sbuf), 32)) { ESP_LOGW(TAG, "Auth: Writing nonce failed!"); goto error; } @@ -125,7 +310,7 @@ void OTAComponent::handle_() { md5_builder.add(sbuf); // Receive cnonce, 32 bytes hex MD5 - if (!this->wait_receive_(buf, 32)) { + if (!this->readall_(buf, 32)) { ESP_LOGW(TAG, "Auth: Reading cnonce failed!"); goto error; } @@ -140,7 +325,7 @@ void OTAComponent::handle_() { ESP_LOGV(TAG, "Auth: Result is %s", sbuf); // Receive result, 32 bytes hex MD5 - if (!this->wait_receive_(buf + 64, 32)) { + if (!this->writeall_(buf + 64, 32)) { ESP_LOGW(TAG, "Auth: Reading response failed!"); goto error; } @@ -157,12 +342,14 @@ void OTAComponent::handle_() { goto error; } } +#endif // USE_OTA_PASSWORD // Acknowledge auth OK - 1 byte - this->client_.write(OTA_RESPONSE_AUTH_OK); + buf[0] = OTA_RESPONSE_AUTH_OK; + this->writeall_(buf, 1); // Read size, 4 bytes MSB first - if (!this->wait_receive_(buf, 4)) { + if (!this->readall_(buf, 4)) { ESP_LOGW(TAG, "Reading size failed!"); goto error; } @@ -173,72 +360,46 @@ void OTAComponent::handle_() { } ESP_LOGV(TAG, "OTA size is %u bytes", ota_size); -#ifdef ARDUINO_ARCH_ESP8266 - global_preferences.prevent_write(true); -#endif - - if (!Update.begin(ota_size, U_FLASH)) { - uint8_t error = Update.getError(); - StreamString ss; - Update.printError(ss); -#ifdef ARDUINO_ARCH_ESP8266 - if (error == UPDATE_ERROR_BOOTSTRAP) { - error_code = OTA_RESPONSE_ERROR_INVALID_BOOTSTRAPPING; - goto error; - } - if (error == UPDATE_ERROR_NEW_FLASH_CONFIG) { - error_code = OTA_RESPONSE_ERROR_WRONG_NEW_FLASH_CONFIG; - goto error; - } - if (error == UPDATE_ERROR_FLASH_CONFIG) { - error_code = OTA_RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG; - goto error; - } - if (error == UPDATE_ERROR_SPACE) { - error_code = OTA_RESPONSE_ERROR_ESP8266_NOT_ENOUGH_SPACE; - goto error; - } -#endif -#ifdef ARDUINO_ARCH_ESP32 - if (error == UPDATE_ERROR_SIZE) { - error_code = OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE; - goto error; - } -#endif - ESP_LOGW(TAG, "Preparing OTA partition failed! '%s'", ss.c_str()); - error_code = OTA_RESPONSE_ERROR_UPDATE_PREPARE; + backend = make_ota_backend(); + error_code = backend->begin(ota_size); + if (error_code != OTA_RESPONSE_OK) goto error; - } update_started = true; // Acknowledge prepare OK - 1 byte - this->client_.write(OTA_RESPONSE_UPDATE_PREPARE_OK); + buf[0] = OTA_RESPONSE_UPDATE_PREPARE_OK; + this->writeall_(buf, 1); // Read binary MD5, 32 bytes - if (!this->wait_receive_(buf, 32)) { + if (!this->readall_(buf, 32)) { ESP_LOGW(TAG, "Reading binary MD5 checksum failed!"); goto error; } sbuf[32] = '\0'; ESP_LOGV(TAG, "Update: Binary MD5 is %s", sbuf); - Update.setMD5(sbuf); + backend->set_update_md5(sbuf); // Acknowledge MD5 OK - 1 byte - this->client_.write(OTA_RESPONSE_BIN_MD5_OK); + buf[0] = OTA_RESPONSE_BIN_MD5_OK; + this->writeall_(buf, 1); - while (!Update.isFinished()) { - size_t available = this->wait_receive_(buf, 0); - if (!available) { + while (total < ota_size) { + // TODO: timeout check + size_t requested = std::min(sizeof(buf), ota_size - total); + ssize_t read = this->client_->read(buf, requested); + if (read == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) + continue; + ESP_LOGW(TAG, "Error receiving data for update, errno: %d", errno); goto error; } - uint32_t written = Update.write(buf, available); - if (written != available) { - ESP_LOGW(TAG, "Error writing binary data to flash: %u != %u!", written, available); // NOLINT - error_code = OTA_RESPONSE_ERROR_WRITING_FLASH; + error_code = backend->write(buf, read); + if (error_code != OTA_RESPONSE_OK) { + ESP_LOGW(TAG, "Error writing binary data to flash!"); goto error; } - total += written; + total += read; uint32_t now = millis(); if (now - last_progress > 1000) { @@ -254,24 +415,27 @@ void OTAComponent::handle_() { } // Acknowledge receive OK - 1 byte - this->client_.write(OTA_RESPONSE_RECEIVE_OK); + buf[0] = OTA_RESPONSE_RECEIVE_OK; + this->writeall_(buf, 1); - if (!Update.end()) { - error_code = OTA_RESPONSE_ERROR_UPDATE_END; + error_code = backend->end(); + if (error_code != OTA_RESPONSE_OK) { + ESP_LOGW(TAG, "Error ending OTA!"); goto error; } // Acknowledge Update end OK - 1 byte - this->client_.write(OTA_RESPONSE_UPDATE_END_OK); + buf[0] = OTA_RESPONSE_UPDATE_END_OK; + this->writeall_(buf, 1); // Read ACK - if (!this->wait_receive_(buf, 1, false) || buf[0] != OTA_RESPONSE_OK) { + if (!this->readall_(buf, 1) || buf[0] != OTA_RESPONSE_OK) { ESP_LOGW(TAG, "Reading back acknowledgement failed!"); // do not go to error, this is not fatal } - this->client_.flush(); - this->client_.stop(); + this->client_->close(); + this->client_ = nullptr; delay(10); ESP_LOGI(TAG, "OTA update finished!"); this->status_clear_warning(); @@ -282,88 +446,72 @@ void OTAComponent::handle_() { App.safe_reboot(); error: - if (update_started) { - StreamString ss; - Update.printError(ss); - ESP_LOGW(TAG, "Update end failed! Error: %s", ss.c_str()); - } - if (this->client_.connected()) { - this->client_.write(static_cast(error_code)); - this->client_.flush(); - } - this->client_.stop(); + buf[0] = static_cast(error_code); + this->writeall_(buf, 1); + this->client_->close(); + this->client_ = nullptr; -#ifdef ARDUINO_ARCH_ESP32 - if (update_started) { - Update.abort(); + if (backend != nullptr && update_started) { + backend->abort(); } -#endif - -#ifdef ARDUINO_ARCH_ESP8266 - if (update_started) { - Update.end(); - } -#endif this->status_momentary_error("onerror", 5000); #ifdef USE_OTA_STATE_CALLBACK this->state_callback_.call(OTA_ERROR, 0.0f, static_cast(error_code)); #endif - -#ifdef ARDUINO_ARCH_ESP8266 - global_preferences.prevent_write(false); -#endif } -size_t OTAComponent::wait_receive_(uint8_t *buf, size_t bytes, bool check_disconnected) { - size_t available = 0; +bool OTAComponent::readall_(uint8_t *buf, size_t len) { uint32_t start = millis(); - do { - App.feed_wdt(); - if (check_disconnected && !this->client_.connected()) { - ESP_LOGW(TAG, "Error client disconnected while receiving data!"); - return 0; - } - int availi = this->client_.available(); - if (availi < 0) { - ESP_LOGW(TAG, "Error reading data!"); - return 0; - } + uint32_t at = 0; + while (len - at > 0) { uint32_t now = millis(); - if (availi == 0 && now - start > 10000) { - ESP_LOGW(TAG, "Timeout waiting for data!"); - return 0; + if (now - start > 1000) { + ESP_LOGW(TAG, "Timed out reading %d bytes of data", len); + return false; } - available = size_t(availi); - yield(); - } while (bytes == 0 ? available == 0 : available < bytes); - if (bytes == 0) - bytes = std::min(available, size_t(1024)); - - bool success = false; - for (uint32_t i = 0; !success && i < 100; i++) { - int res = this->client_.read(buf, bytes); - - if (res != int(bytes)) { - // ESP32 implementation has an issue where calling read can fail with EAGAIN (race condition) - // so just re-try it until it works (with generous timeout of 1s) - // because we check with available() first this should not cause us any trouble in all other cases - delay(10); + ssize_t read = this->client_->read(buf + at, len - at); + if (read == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + delay(1); + continue; + } + ESP_LOGW(TAG, "Failed to read %d bytes of data, errno: %d", len, errno); + return false; } else { - success = true; + at += read; } + delay(1); } - if (!success) { - ESP_LOGW(TAG, "Reading %u bytes of binary data failed!", bytes); // NOLINT - return 0; - } - - return bytes; + return true; } +bool OTAComponent::writeall_(const uint8_t *buf, size_t len) { + uint32_t start = millis(); + uint32_t at = 0; + while (len - at > 0) { + uint32_t now = millis(); + if (now - start > 1000) { + ESP_LOGW(TAG, "Timed out writing %d bytes of data", len); + return false; + } -void OTAComponent::set_auth_password(const std::string &password) { this->password_ = password; } + ssize_t written = this->client_->write(buf + at, len - at); + if (written == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + delay(1); + continue; + } + ESP_LOGW(TAG, "Failed to write %d bytes of data, errno: %d", len, errno); + return false; + } else { + at += written; + } + delay(1); + } + return true; +} float OTAComponent::get_setup_priority() const { return setup_priority::AFTER_WIFI; } uint16_t OTAComponent::get_port() const { return this->port_; } @@ -373,7 +521,7 @@ bool OTAComponent::should_enter_safe_mode(uint8_t num_attempts, uint32_t enable_ this->safe_mode_start_time_ = millis(); this->safe_mode_enable_time_ = enable_time; this->safe_mode_num_attempts_ = num_attempts; - this->rtc_ = global_preferences.make_preference(233825507UL, false); + this->rtc_ = global_preferences->make_preference(233825507UL, false); this->safe_mode_rtc_value_ = this->read_rtc_(); ESP_LOGCONFIG(TAG, "There have been %u suspected unsuccessful boot attempts.", this->safe_mode_rtc_value_); diff --git a/esphome/components/ota/ota_component.h b/esphome/components/ota/ota_component.h index 04cea56ec2..f76295735e 100644 --- a/esphome/components/ota/ota_component.h +++ b/esphome/components/ota/ota_component.h @@ -1,12 +1,10 @@ #pragma once -#include - +#include "esphome/components/socket/socket.h" #include "esphome/core/component.h" #include "esphome/core/preferences.h" #include "esphome/core/helpers.h" -#include -#include +#include "esphome/core/defines.h" namespace esphome { namespace ota { @@ -32,6 +30,7 @@ enum OTAResponseTypes { OTA_RESPONSE_ERROR_WRONG_NEW_FLASH_CONFIG = 135, OTA_RESPONSE_ERROR_ESP8266_NOT_ENOUGH_SPACE = 136, OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE = 137, + OTA_RESPONSE_ERROR_NO_UPDATE_PARTITION = 138, OTA_RESPONSE_ERROR_UNKNOWN = 255, }; @@ -40,14 +39,9 @@ enum OTAState { OTA_COMPLETED = 0, OTA_STARTED, OTA_IN_PROGRESS, OTA_ERROR }; /// OTAComponent provides a simple way to integrate Over-the-Air updates into your app using ArduinoOTA. class OTAComponent : public Component { public: - /** Set a plaintext password that OTA will use for authentication. - * - * Warning: This password will be stored in plaintext in the ROM and can be read - * by intruders. - * - * @param password The plaintext password. - */ - void set_auth_password(const std::string &password); +#ifdef USE_OTA_PASSWORD + void set_auth_password(const std::string &password) { password_ = password; } +#endif // USE_OTA_PASSWORD /// Manually set the port OTA should listen on. void set_port(uint16_t port); @@ -76,14 +70,17 @@ class OTAComponent : public Component { uint32_t read_rtc_(); void handle_(); - size_t wait_receive_(uint8_t *buf, size_t bytes, bool check_disconnected = true); + bool readall_(uint8_t *buf, size_t len); + bool writeall_(const uint8_t *buf, size_t len); +#ifdef USE_OTA_PASSWORD std::string password_; +#endif // USE_OTA_PASSWORD uint16_t port_; - std::unique_ptr server_{nullptr}; - WiFiClient client_{}; + std::unique_ptr server_; + std::unique_ptr client_; bool has_safe_mode_{false}; ///< stores whether safe mode can be enabled. uint32_t safe_mode_start_time_; ///< stores when safe mode was enabled. diff --git a/esphome/components/pca9685/pca9685_output.h b/esphome/components/pca9685/pca9685_output.h index bd3f7f15f9..5dd52b5510 100644 --- a/esphome/components/pca9685/pca9685_output.h +++ b/esphome/components/pca9685/pca9685_output.h @@ -20,9 +20,10 @@ extern const uint8_t PCA9685_MODE_OUTNE_LOW; class PCA9685Output; -class PCA9685Channel : public output::FloatOutput, public Parented { +class PCA9685Channel : public output::FloatOutput { public: void set_channel(uint8_t channel) { channel_ = channel; } + void set_parent(PCA9685Output *parent) { parent_ = parent; } protected: friend class PCA9685Output; @@ -30,6 +31,7 @@ class PCA9685Channel : public output::FloatOutput, public Parentedwrite_gpio_(); } -void PCF8574Component::pin_mode(uint8_t pin, uint8_t mode) { - switch (mode) { - case PCF8574_INPUT: - // Clear mode mask bit - this->mode_mask_ &= ~(1 << pin); - // Write GPIO to enable input mode - this->write_gpio_(); - break; - case PCF8574_OUTPUT: - // Set mode mask bit - this->mode_mask_ |= 1 << pin; - break; - default: - break; +void PCF8574Component::pin_mode(uint8_t pin, gpio::Flags flags) { + if (flags == gpio::FLAG_INPUT) { + // Clear mode mask bit + this->mode_mask_ &= ~(1 << pin); + // Write GPIO to enable input mode + this->write_gpio_(); + } else if (flags == gpio::FLAG_OUTPUT) { + // Set mode mask bit + this->mode_mask_ |= 1 << pin; } } bool PCF8574Component::read_gpio_() { @@ -87,7 +82,7 @@ bool PCF8574Component::write_gpio_() { uint8_t data[2]; data[0] = value; data[1] = value >> 8; - if (!this->write_bytes_raw(data, this->pcf8575_ ? 2 : 1)) { + if (this->write(data, this->pcf8575_ ? 2 : 1) != i2c::ERROR_OK) { this->status_set_warning(); return false; } @@ -97,12 +92,15 @@ bool PCF8574Component::write_gpio_() { } float PCF8574Component::get_setup_priority() const { return setup_priority::IO; } -void PCF8574GPIOPin::setup() { this->pin_mode(this->mode_); } +void PCF8574GPIOPin::setup() { pin_mode(flags_); } +void PCF8574GPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); } bool PCF8574GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; } void PCF8574GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); } -void PCF8574GPIOPin::pin_mode(uint8_t mode) { this->parent_->pin_mode(this->pin_, mode); } -PCF8574GPIOPin::PCF8574GPIOPin(PCF8574Component *parent, uint8_t pin, uint8_t mode, bool inverted) - : GPIOPin(pin, mode, inverted), parent_(parent) {} +std::string PCF8574GPIOPin::dump_summary() const { + char buffer[32]; + snprintf(buffer, sizeof(buffer), "%u via PCF8574", pin_); + return buffer; +} } // namespace pcf8574 } // namespace esphome diff --git a/esphome/components/pcf8574/pcf8574.h b/esphome/components/pcf8574/pcf8574.h index 925fa30899..c201e0615f 100644 --- a/esphome/components/pcf8574/pcf8574.h +++ b/esphome/components/pcf8574/pcf8574.h @@ -1,18 +1,12 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/i2c/i2c.h" namespace esphome { namespace pcf8574 { -/// Modes for PCF8574 pins -enum PCF8574GPIOMode : uint8_t { - PCF8574_INPUT = INPUT, - PCF8574_OUTPUT = OUTPUT, -}; - class PCF8574Component : public Component, public i2c::I2CDevice { public: PCF8574Component() = default; @@ -26,7 +20,7 @@ class PCF8574Component : public Component, public i2c::I2CDevice { /// Helper function to write the value of a pin. void digital_write(uint8_t pin, bool value); /// Helper function to set the pin mode of a pin. - void pin_mode(uint8_t pin, uint8_t mode); + void pin_mode(uint8_t pin, gpio::Flags flags); float get_setup_priority() const override; @@ -49,15 +43,22 @@ class PCF8574Component : public Component, public i2c::I2CDevice { /// Helper class to expose a PCF8574 pin as an internal input GPIO pin. class PCF8574GPIOPin : public GPIOPin { public: - PCF8574GPIOPin(PCF8574Component *parent, uint8_t pin, uint8_t mode, bool inverted = false); - void setup() override; - void pin_mode(uint8_t mode) override; + void pin_mode(gpio::Flags flags) override; bool digital_read() override; void digital_write(bool value) override; + std::string dump_summary() const override; + + void set_parent(PCF8574Component *parent) { parent_ = parent; } + void set_pin(uint8_t pin) { pin_ = pin; } + void set_inverted(bool inverted) { inverted_ = inverted; } + void set_flags(gpio::Flags flags) { flags_ = flags; } protected: PCF8574Component *parent_; + uint8_t pin_; + bool inverted_; + gpio::Flags flags_; }; } // namespace pcf8574 diff --git a/esphome/components/pid/pid_autotuner.cpp b/esphome/components/pid/pid_autotuner.cpp index 83e4b6be32..15c1c5f076 100644 --- a/esphome/components/pid/pid_autotuner.cpp +++ b/esphome/components/pid/pid_autotuner.cpp @@ -72,7 +72,7 @@ PIDAutotuner::PIDAutotuneResult PIDAutotuner::update(float setpoint, float proce return res; } - if (!isnan(this->setpoint_) && this->setpoint_ != setpoint) { + if (!std::isnan(this->setpoint_) && this->setpoint_ != setpoint) { ESP_LOGW(TAG, "Setpoint changed during autotune! The result will not be accurate!"); } this->setpoint_ = setpoint; diff --git a/esphome/components/pid/pid_climate.cpp b/esphome/components/pid/pid_climate.cpp index 4c7d92e26d..f5c7792782 100644 --- a/esphome/components/pid/pid_climate.cpp +++ b/esphome/components/pid/pid_climate.cpp @@ -100,7 +100,7 @@ void PIDClimate::write_output_(float value) { } void PIDClimate::update_pid_() { float value; - if (isnan(this->current_temperature) || isnan(this->target_temperature)) { + if (std::isnan(this->current_temperature) || std::isnan(this->target_temperature)) { // if any control parameters are nan, turn off all outputs value = 0.0; } else { diff --git a/esphome/components/pid/pid_controller.h b/esphome/components/pid/pid_controller.h index 4caad8dd8b..35e3eb9fc0 100644 --- a/esphome/components/pid/pid_controller.h +++ b/esphome/components/pid/pid_controller.h @@ -1,6 +1,6 @@ #pragma once -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" namespace esphome { namespace pid { @@ -23,9 +23,9 @@ struct PIDController { // i(t) := K_i * \int_{0}^{t} e(t) dt accumulated_integral_ += error * dt * ki; // constrain accumulated integral value - if (!isnan(min_integral) && accumulated_integral_ < min_integral) + if (!std::isnan(min_integral) && accumulated_integral_ < min_integral) accumulated_integral_ = min_integral; - if (!isnan(max_integral) && accumulated_integral_ > max_integral) + if (!std::isnan(max_integral) && accumulated_integral_ > max_integral) accumulated_integral_ = max_integral; integral_term = accumulated_integral_; diff --git a/esphome/components/pipsolar/pipsolar.cpp b/esphome/components/pipsolar/pipsolar.cpp index 8603adfbf4..7dbbd798ad 100644 --- a/esphome/components/pipsolar/pipsolar.cpp +++ b/esphome/components/pipsolar/pipsolar.cpp @@ -840,7 +840,7 @@ void Pipsolar::send_next_poll_() { this->used_polling_commands_[this->last_polling_command_].length); } -void Pipsolar::queue_command_(const char *command, byte length) { +void Pipsolar::queue_command_(const char *command, uint8_t length) { uint8_t next_position = command_queue_position_; for (uint8_t i = 0; i < COMMAND_QUEUE_LENGTH; i++) { uint8_t testposition = (next_position + i) % COMMAND_QUEUE_LENGTH; diff --git a/esphome/components/pipsolar/pipsolar.h b/esphome/components/pipsolar/pipsolar.h index 508036c7de..fe2a80d1d5 100644 --- a/esphome/components/pipsolar/pipsolar.h +++ b/esphome/components/pipsolar/pipsolar.h @@ -197,7 +197,7 @@ class Pipsolar : public uart::UARTDevice, public PollingComponent { uint16_t crc_xmodem_update_(uint16_t crc, uint8_t data); uint8_t send_next_command_(); void send_next_poll_(); - void queue_command_(const char *command, byte length); + void queue_command_(const char *command, uint8_t length); std::string command_queue_[COMMAND_QUEUE_LENGTH]; uint8_t command_queue_position_ = 0; uint8_t read_buffer_[PIPSOLAR_READ_BUFFER_LENGTH]; diff --git a/esphome/components/pmsa003i/pmsa003i.cpp b/esphome/components/pmsa003i/pmsa003i.cpp index 1396c9f3d4..ca3d28367a 100644 --- a/esphome/components/pmsa003i/pmsa003i.cpp +++ b/esphome/components/pmsa003i/pmsa003i.cpp @@ -1,5 +1,6 @@ #include "pmsa003i.h" #include "esphome/core/log.h" +#include namespace esphome { namespace pmsa003i { diff --git a/esphome/components/pn532/pn532.cpp b/esphome/components/pn532/pn532.cpp index 1c4160539a..ed2a2c1e35 100644 --- a/esphome/components/pn532/pn532.cpp +++ b/esphome/components/pn532/pn532.cpp @@ -2,6 +2,7 @@ #include #include "esphome/core/log.h" +#include "esphome/core/hal.h" // Based on: // - https://cdn-shop.adafruit.com/datasheets/PN532C106_Application+Note_v1.2.pdf diff --git a/esphome/components/pn532_i2c/pn532_i2c.cpp b/esphome/components/pn532_i2c/pn532_i2c.cpp index 25f24758bf..ef7480ec25 100644 --- a/esphome/components/pn532_i2c/pn532_i2c.cpp +++ b/esphome/components/pn532_i2c/pn532_i2c.cpp @@ -1,5 +1,6 @@ #include "pn532_i2c.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" // Based on: // - https://cdn-shop.adafruit.com/datasheets/PN532C106_Application+Note_v1.2.pdf @@ -11,7 +12,7 @@ namespace pn532_i2c { static const char *const TAG = "pn532_i2c"; -bool PN532I2C::write_data(const std::vector &data) { return this->write_bytes_raw(data.data(), data.size()); } +bool PN532I2C::write_data(const std::vector &data) { return this->write(data.data(), data.size()); } bool PN532I2C::read_data(std::vector &data, uint8_t len) { delay(1); diff --git a/esphome/components/power_supply/power_supply.h b/esphome/components/power_supply/power_supply.h index 12d2a9984f..66e4a7565a 100644 --- a/esphome/components/power_supply/power_supply.h +++ b/esphome/components/power_supply/power_supply.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" namespace esphome { namespace power_supply { diff --git a/esphome/components/prometheus/prometheus_handler.cpp b/esphome/components/prometheus/prometheus_handler.cpp index 06a0e39e2c..fa7b4fe132 100644 --- a/esphome/components/prometheus/prometheus_handler.cpp +++ b/esphome/components/prometheus/prometheus_handler.cpp @@ -1,3 +1,5 @@ +#ifdef USE_ARDUINO + #include "prometheus_handler.h" #include "esphome/core/application.h" @@ -55,7 +57,7 @@ void PrometheusHandler::sensor_type_(AsyncResponseStream *stream) { void PrometheusHandler::sensor_row_(AsyncResponseStream *stream, sensor::Sensor *obj) { if (obj->is_internal()) return; - if (!isnan(obj->state)) { + if (!std::isnan(obj->state)) { // We have a valid value, output this value stream->print(F("esphome_sensor_failed{id=\"")); stream->print(obj->get_object_id().c_str()); @@ -249,7 +251,7 @@ void PrometheusHandler::cover_type_(AsyncResponseStream *stream) { void PrometheusHandler::cover_row_(AsyncResponseStream *stream, cover::Cover *obj) { if (obj->is_internal()) return; - if (!isnan(obj->position)) { + if (!std::isnan(obj->position)) { // We have a valid value, output this value stream->print(F("esphome_cover_failed{id=\"")); stream->print(obj->get_object_id().c_str()); @@ -310,3 +312,5 @@ void PrometheusHandler::switch_row_(AsyncResponseStream *stream, switch_::Switch } // namespace prometheus } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/prometheus/prometheus_handler.h b/esphome/components/prometheus/prometheus_handler.h index 6abd406556..5076883ba6 100644 --- a/esphome/components/prometheus/prometheus_handler.h +++ b/esphome/components/prometheus/prometheus_handler.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include "esphome/components/web_server_base/web_server_base.h" #include "esphome/core/controller.h" #include "esphome/core/component.h" @@ -79,3 +81,5 @@ class PrometheusHandler : public AsyncWebHandler, public Component { } // namespace prometheus } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/pulse_counter/pulse_counter_sensor.cpp b/esphome/components/pulse_counter/pulse_counter_sensor.cpp index 602c0bdd67..f538a4c905 100644 --- a/esphome/components/pulse_counter/pulse_counter_sensor.cpp +++ b/esphome/components/pulse_counter/pulse_counter_sensor.cpp @@ -8,15 +8,15 @@ static const char *const TAG = "pulse_counter"; const char *const EDGE_MODE_TO_STRING[] = {"DISABLE", "INCREMENT", "DECREMENT"}; -#ifdef ARDUINO_ARCH_ESP8266 -void ICACHE_RAM_ATTR PulseCounterStorage::gpio_intr(PulseCounterStorage *arg) { +#ifdef USE_ESP8266 +void IRAM_ATTR PulseCounterStorage::gpio_intr(PulseCounterStorage *arg) { const uint32_t now = micros(); const bool discard = now - arg->last_pulse < arg->filter_us; arg->last_pulse = now; if (discard) return; - PulseCounterCountMode mode = arg->isr_pin->digital_read() ? arg->rising_edge_mode : arg->falling_edge_mode; + PulseCounterCountMode mode = arg->isr_pin.digital_read() ? arg->rising_edge_mode : arg->falling_edge_mode; switch (mode) { case PULSE_COUNTER_DISABLE: break; @@ -28,11 +28,11 @@ void ICACHE_RAM_ATTR PulseCounterStorage::gpio_intr(PulseCounterStorage *arg) { break; } } -bool PulseCounterStorage::pulse_counter_setup(GPIOPin *pin) { +bool PulseCounterStorage::pulse_counter_setup(InternalGPIOPin *pin) { this->pin = pin; this->pin->setup(); this->isr_pin = this->pin->to_isr(); - this->pin->attach_interrupt(PulseCounterStorage::gpio_intr, this, CHANGE); + this->pin->attach_interrupt(PulseCounterStorage::gpio_intr, this, gpio::INTERRUPT_ANY_EDGE); return true; } pulse_counter_t PulseCounterStorage::read_raw_value() { @@ -43,8 +43,8 @@ pulse_counter_t PulseCounterStorage::read_raw_value() { } #endif -#ifdef ARDUINO_ARCH_ESP32 -bool PulseCounterStorage::pulse_counter_setup(GPIOPin *pin) { +#ifdef USE_ESP32 +bool PulseCounterStorage::pulse_counter_setup(InternalGPIOPin *pin) { static pcnt_unit_t next_pcnt_unit = PCNT_UNIT_0; this->pin = pin; this->pin->setup(); diff --git a/esphome/components/pulse_counter/pulse_counter_sensor.h b/esphome/components/pulse_counter/pulse_counter_sensor.h index 3203ab81fd..94e37bc232 100644 --- a/esphome/components/pulse_counter/pulse_counter_sensor.h +++ b/esphome/components/pulse_counter/pulse_counter_sensor.h @@ -1,10 +1,10 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #endif @@ -17,30 +17,30 @@ enum PulseCounterCountMode { PULSE_COUNTER_DECREMENT, }; -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 using pulse_counter_t = int16_t; #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 using pulse_counter_t = int32_t; #endif struct PulseCounterStorage { - bool pulse_counter_setup(GPIOPin *pin); + bool pulse_counter_setup(InternalGPIOPin *pin); pulse_counter_t read_raw_value(); static void gpio_intr(PulseCounterStorage *arg); -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 volatile pulse_counter_t counter{0}; volatile uint32_t last_pulse{0}; #endif - GPIOPin *pin; -#ifdef ARDUINO_ARCH_ESP32 + InternalGPIOPin *pin; +#ifdef USE_ESP32 pcnt_unit_t pcnt_unit; #endif -#ifdef ARDUINO_ARCH_ESP8266 - ISRInternalGPIOPin *isr_pin; +#ifdef USE_ESP8266 + ISRInternalGPIOPin isr_pin; #endif PulseCounterCountMode rising_edge_mode{PULSE_COUNTER_INCREMENT}; PulseCounterCountMode falling_edge_mode{PULSE_COUNTER_DISABLE}; @@ -50,7 +50,7 @@ struct PulseCounterStorage { class PulseCounterSensor : public sensor::Sensor, public PollingComponent { public: - void set_pin(GPIOPin *pin) { pin_ = pin; } + void set_pin(InternalGPIOPin *pin) { pin_ = pin; } void set_rising_edge_mode(PulseCounterCountMode mode) { storage_.rising_edge_mode = mode; } void set_falling_edge_mode(PulseCounterCountMode mode) { storage_.falling_edge_mode = mode; } void set_filter_us(uint32_t filter) { storage_.filter_us = filter; } @@ -63,7 +63,7 @@ class PulseCounterSensor : public sensor::Sensor, public PollingComponent { void dump_config() override; protected: - GPIOPin *pin_; + InternalGPIOPin *pin_; PulseCounterStorage storage_; uint32_t current_total_ = 0; sensor::Sensor *total_sensor_; diff --git a/esphome/components/pulse_meter/pulse_meter_sensor.cpp b/esphome/components/pulse_meter/pulse_meter_sensor.cpp index 1a35deba2f..fd1403b4fd 100644 --- a/esphome/components/pulse_meter/pulse_meter_sensor.cpp +++ b/esphome/components/pulse_meter/pulse_meter_sensor.cpp @@ -9,7 +9,7 @@ static const char *const TAG = "pulse_meter"; void PulseMeterSensor::setup() { this->pin_->setup(); this->isr_pin_ = pin_->to_isr(); - this->pin_->attach_interrupt(PulseMeterSensor::gpio_intr, this, CHANGE); + this->pin_->attach_interrupt(PulseMeterSensor::gpio_intr, this, gpio::INTERRUPT_ANY_EDGE); this->last_detected_edge_us_ = 0; this->last_valid_edge_us_ = 0; @@ -56,14 +56,14 @@ void PulseMeterSensor::dump_config() { ESP_LOGCONFIG(TAG, " Assuming 0 pulses/min after not receiving a pulse for %us", this->timeout_us_ / 1000000); } -void ICACHE_RAM_ATTR PulseMeterSensor::gpio_intr(PulseMeterSensor *sensor) { +void IRAM_ATTR PulseMeterSensor::gpio_intr(PulseMeterSensor *sensor) { // This is an interrupt handler - we can't call any virtual method from this method // Get the current time before we do anything else so the measurements are consistent const uint32_t now = micros(); // We only look at rising edges - if (!sensor->isr_pin_->digital_read()) { + if (!sensor->isr_pin_.digital_read()) { return; } diff --git a/esphome/components/pulse_meter/pulse_meter_sensor.h b/esphome/components/pulse_meter/pulse_meter_sensor.h index d02cff6123..1cebc1748e 100644 --- a/esphome/components/pulse_meter/pulse_meter_sensor.h +++ b/esphome/components/pulse_meter/pulse_meter_sensor.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" #include "esphome/core/helpers.h" @@ -10,7 +10,7 @@ namespace pulse_meter { class PulseMeterSensor : public sensor::Sensor, public Component { public: - void set_pin(GPIOPin *pin) { this->pin_ = pin; } + void set_pin(InternalGPIOPin *pin) { this->pin_ = pin; } void set_filter_us(uint32_t filter) { this->filter_us_ = filter; } void set_timeout_us(uint32_t timeout) { this->timeout_us_ = timeout; } void set_total_sensor(sensor::Sensor *sensor) { this->total_sensor_ = sensor; } @@ -25,8 +25,8 @@ class PulseMeterSensor : public sensor::Sensor, public Component { protected: static void gpio_intr(PulseMeterSensor *sensor); - GPIOPin *pin_ = nullptr; - ISRInternalGPIOPin *isr_pin_; + InternalGPIOPin *pin_ = nullptr; + ISRInternalGPIOPin isr_pin_; uint32_t filter_us_ = 0; uint32_t timeout_us_ = 1000000UL * 60UL * 5UL; sensor::Sensor *total_sensor_ = nullptr; diff --git a/esphome/components/pulse_width/pulse_width.cpp b/esphome/components/pulse_width/pulse_width.cpp index fb998ef4e1..8d66861049 100644 --- a/esphome/components/pulse_width/pulse_width.cpp +++ b/esphome/components/pulse_width/pulse_width.cpp @@ -6,8 +6,8 @@ namespace pulse_width { static const char *const TAG = "pulse_width"; -void ICACHE_RAM_ATTR PulseWidthSensorStore::gpio_intr(PulseWidthSensorStore *arg) { - const bool new_level = arg->pin_->digital_read(); +void IRAM_ATTR PulseWidthSensorStore::gpio_intr(PulseWidthSensorStore *arg) { + const bool new_level = arg->pin_.digital_read(); const uint32_t now = micros(); if (new_level) { arg->last_rise_ = now; diff --git a/esphome/components/pulse_width/pulse_width.h b/esphome/components/pulse_width/pulse_width.h index 9d32ce99b1..822688ec88 100644 --- a/esphome/components/pulse_width/pulse_width.h +++ b/esphome/components/pulse_width/pulse_width.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" namespace esphome { @@ -10,11 +10,11 @@ namespace pulse_width { /// Store data in a class that doesn't use multiple-inheritance (vtables in flash) class PulseWidthSensorStore { public: - void setup(GPIOPin *pin) { + void setup(InternalGPIOPin *pin) { pin->setup(); this->pin_ = pin->to_isr(); this->last_rise_ = micros(); - pin->attach_interrupt(&PulseWidthSensorStore::gpio_intr, this, CHANGE); + pin->attach_interrupt(&PulseWidthSensorStore::gpio_intr, this, gpio::INTERRUPT_ANY_EDGE); } static void gpio_intr(PulseWidthSensorStore *arg); uint32_t get_pulse_width_us() const { return this->last_width_; } @@ -22,14 +22,14 @@ class PulseWidthSensorStore { uint32_t get_last_rise() const { return last_rise_; } protected: - ISRInternalGPIOPin *pin_; + ISRInternalGPIOPin pin_; volatile uint32_t last_width_{0}; volatile uint32_t last_rise_{0}; }; class PulseWidthSensor : public sensor::Sensor, public PollingComponent { public: - void set_pin(GPIOPin *pin) { pin_ = pin; } + void set_pin(InternalGPIOPin *pin) { pin_ = pin; } void setup() override { this->store_.setup(this->pin_); } void dump_config() override; float get_setup_priority() const override { return setup_priority::DATA; } @@ -37,7 +37,7 @@ class PulseWidthSensor : public sensor::Sensor, public PollingComponent { protected: PulseWidthSensorStore store_; - GPIOPin *pin_; + InternalGPIOPin *pin_; }; } // namespace pulse_width diff --git a/esphome/components/pulse_width/sensor.py b/esphome/components/pulse_width/sensor.py index 6c91104036..b090647627 100644 --- a/esphome/components/pulse_width/sensor.py +++ b/esphome/components/pulse_width/sensor.py @@ -26,9 +26,7 @@ CONFIG_SCHEMA = ( .extend( { cv.GenerateID(): cv.declare_id(PulseWidthSensor), - cv.Required(CONF_PIN): cv.All( - pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt - ), + cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pin_schema), } ) .extend(cv.polling_component_schema("60s")) diff --git a/esphome/components/pvvx_mithermometer/pvvx_mithermometer.cpp b/esphome/components/pvvx_mithermometer/pvvx_mithermometer.cpp index 9edcd9166c..ff9723ab2f 100644 --- a/esphome/components/pvvx_mithermometer/pvvx_mithermometer.cpp +++ b/esphome/components/pvvx_mithermometer/pvvx_mithermometer.cpp @@ -1,7 +1,7 @@ #include "pvvx_mithermometer.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace pvvx_mithermometer { diff --git a/esphome/components/pvvx_mithermometer/pvvx_mithermometer.h b/esphome/components/pvvx_mithermometer/pvvx_mithermometer.h index 4132954983..bb67769d4f 100644 --- a/esphome/components/pvvx_mithermometer/pvvx_mithermometer.h +++ b/esphome/components/pvvx_mithermometer/pvvx_mithermometer.h @@ -4,7 +4,7 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace pvvx_mithermometer { diff --git a/esphome/components/qmc5883l/qmc5883l.cpp b/esphome/components/qmc5883l/qmc5883l.cpp index 9e80cdbd88..f03b6af191 100644 --- a/esphome/components/qmc5883l/qmc5883l.cpp +++ b/esphome/components/qmc5883l/qmc5883l.cpp @@ -1,5 +1,7 @@ #include "qmc5883l.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" +#include namespace esphome { namespace qmc5883l { @@ -115,9 +117,10 @@ void QMC5883LComponent::update() { } bool QMC5883LComponent::read_byte_16_(uint8_t a_register, uint16_t *data) { - bool success = this->read_byte_16(a_register, data); + if (!this->read_byte_16(a_register, data)) + return false; *data = (*data & 0x00FF) << 8 | (*data & 0xFF00) >> 8; // Flip Byte order, LSB first; - return success; + return true; } } // namespace qmc5883l diff --git a/esphome/components/rc522/rc522.cpp b/esphome/components/rc522/rc522.cpp index 6261f63132..385641fea0 100644 --- a/esphome/components/rc522/rc522.cpp +++ b/esphome/components/rc522/rc522.cpp @@ -43,14 +43,14 @@ void RC522::setup() { // First set the resetPowerDownPin as digital input, to check the MFRC522 power down mode. if (reset_pin_ != nullptr) { - reset_pin_->pin_mode(INPUT); + reset_pin_->pin_mode(gpio::FLAG_INPUT); - if (reset_pin_->digital_read() == LOW) { // The MFRC522 chip is in power down mode. + if (!reset_pin_->digital_read()) { // The MFRC522 chip is in power down mode. ESP_LOGV(TAG, "Power down mode detected. Hard resetting..."); - reset_pin_->pin_mode(OUTPUT); // Now set the resetPowerDownPin as digital output. - reset_pin_->digital_write(LOW); // Make sure we have a clean LOW state. + reset_pin_->pin_mode(gpio::FLAG_OUTPUT); // Now set the resetPowerDownPin as digital output. + reset_pin_->digital_write(false); // Make sure we have a clean LOW state. delayMicroseconds(2); // 8.8.1 Reset timing requirements says about 100ns. Let us be generous: 2μsl - reset_pin_->digital_write(HIGH); // Exit power down mode. This triggers a hard reset. + reset_pin_->digital_write(true); // Exit power down mode. This triggers a hard reset. // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74μs. // Let us be generous: 50ms. reset_timeout_ = millis(); diff --git a/esphome/components/rc522/rc522.h b/esphome/components/rc522/rc522.h index e4e3387ff6..d853d2f5ff 100644 --- a/esphome/components/rc522/rc522.h +++ b/esphome/components/rc522/rc522.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/core/automation.h" #include "esphome/components/binary_sensor/binary_sensor.h" diff --git a/esphome/components/rc522_i2c/rc522_i2c.cpp b/esphome/components/rc522_i2c/rc522_i2c.cpp index 896e27214a..6a3d8d2486 100644 --- a/esphome/components/rc522_i2c/rc522_i2c.cpp +++ b/esphome/components/rc522_i2c/rc522_i2c.cpp @@ -18,8 +18,9 @@ void RC522I2C::dump_config() { uint8_t RC522I2C::pcd_read_register(PcdRegister reg ///< The register to read from. One of the PCD_Register enums. ) { uint8_t value; - read_byte(reg >> 1, &value); - ESP_LOGVV(TAG, "read_register_(%x) -> %x", reg, value); + if (!read_byte(reg >> 1, &value)) + return 0; + ESP_LOGVV(TAG, "read_register_(%x) -> %u", reg, value); return value; } diff --git a/esphome/components/remote_base/__init__.py b/esphome/components/remote_base/__init__.py index 537ae2283c..d2b848600d 100644 --- a/esphome/components/remote_base/__init__.py +++ b/esphome/components/remote_base/__init__.py @@ -1092,15 +1092,13 @@ MIDEA_SCHEMA = cv.Schema( [cv.Any(cv.hex_uint8_t, cv.uint8_t)], cv.Length(min=5, max=5), ), - cv.GenerateID(CONF_CODE_STORAGE_ID): cv.declare_id(cg.uint8), } ) @register_binary_sensor("midea", MideaBinarySensor, MIDEA_SCHEMA) def midea_binary_sensor(var, config): - arr_ = cg.progmem_array(config[CONF_CODE_STORAGE_ID], config[CONF_CODE]) - cg.add(var.set_code(arr_)) + cg.add(var.set_code(config[CONF_CODE])) @register_trigger("midea", MideaTrigger, MideaData) @@ -1119,5 +1117,4 @@ def midea_dumper(var, config): MIDEA_SCHEMA, ) async def midea_action(var, config, args): - arr_ = cg.progmem_array(config[CONF_CODE_STORAGE_ID], config[CONF_CODE]) - cg.add(var.set_code(arr_)) + cg.add(var.set_code(config[CONF_CODE])) diff --git a/esphome/components/remote_base/midea_protocol.h b/esphome/components/remote_base/midea_protocol.h index 9b0d156617..12916bd44d 100644 --- a/esphome/components/remote_base/midea_protocol.h +++ b/esphome/components/remote_base/midea_protocol.h @@ -17,8 +17,6 @@ class MideaData { MideaData(const std::vector &data) { memcpy(this->data_, data.data(), std::min(data.size(), sizeof(this->data_))); } - // Make 40-bit copy from PROGMEM array - MideaData(const uint8_t *data) { memcpy_P(this->data_, data, OFFSET_CS); } // Default copy constructor MideaData(const MideaData &) = default; @@ -83,7 +81,7 @@ class MideaBinarySensor : public RemoteReceiverBinarySensorBase { auto data = MideaProtocol().decode(src); return data.has_value() && data.value() == this->data_; } - void set_code(const uint8_t *code) { this->data_ = code; } + void set_code(const std::vector &code) { this->data_ = code; } protected: MideaData data_; @@ -93,7 +91,8 @@ using MideaTrigger = RemoteReceiverTrigger; using MideaDumper = RemoteReceiverDumper; template class MideaAction : public RemoteTransmitterActionBase { - TEMPLATABLE_VALUE(const uint8_t *, code) + TEMPLATABLE_VALUE(std::vector, code) + void set_code(std::vector code) { code_ = code; } void encode(RemoteTransmitData *dst, Ts... x) override { MideaData data = this->code_.value(x...); data.finalize(); diff --git a/esphome/components/remote_base/remote_base.cpp b/esphome/components/remote_base/remote_base.cpp index d36c0d7ebe..a43f743ab6 100644 --- a/esphome/components/remote_base/remote_base.cpp +++ b/esphome/components/remote_base/remote_base.cpp @@ -6,7 +6,7 @@ namespace remote_base { static const char *const TAG = "remote_base"; -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 RemoteRMTChannel::RemoteRMTChannel(uint8_t mem_block_num) : mem_block_num_(mem_block_num) { static rmt_channel_t next_rmt_channel = RMT_CHANNEL_0; this->channel_ = next_rmt_channel; diff --git a/esphome/components/remote_base/remote_base.h b/esphome/components/remote_base/remote_base.h index 16c732df83..68cf67d175 100644 --- a/esphome/components/remote_base/remote_base.h +++ b/esphome/components/remote_base/remote_base.h @@ -3,11 +3,11 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/core/automation.h" #include "esphome/components/binary_sensor/binary_sensor.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #endif @@ -146,13 +146,13 @@ template class RemoteProtocol { class RemoteComponentBase { public: - explicit RemoteComponentBase(GPIOPin *pin) : pin_(pin){}; + explicit RemoteComponentBase(InternalGPIOPin *pin) : pin_(pin){}; protected: - GPIOPin *pin_; + InternalGPIOPin *pin_; }; -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 class RemoteRMTChannel { public: explicit RemoteRMTChannel(uint8_t mem_block_num = 1); @@ -178,7 +178,7 @@ class RemoteRMTChannel { class RemoteTransmitterBase : public RemoteComponentBase { public: - RemoteTransmitterBase(GPIOPin *pin) : RemoteComponentBase(pin) {} + RemoteTransmitterBase(InternalGPIOPin *pin) : RemoteComponentBase(pin) {} class TransmitCall { public: explicit TransmitCall(RemoteTransmitterBase *parent) : parent_(parent) {} @@ -221,7 +221,7 @@ class RemoteReceiverDumperBase { class RemoteReceiverBase : public RemoteComponentBase { public: - RemoteReceiverBase(GPIOPin *pin) : RemoteComponentBase(pin) {} + RemoteReceiverBase(InternalGPIOPin *pin) : RemoteComponentBase(pin) {} void register_listener(RemoteReceiverListener *listener) { this->listeners_.push_back(listener); } void register_dumper(RemoteReceiverDumperBase *dumper) { if (dumper->is_secondary()) { diff --git a/esphome/components/remote_base/samsung_protocol.cpp b/esphome/components/remote_base/samsung_protocol.cpp index 0f2605d865..4571f332b3 100644 --- a/esphome/components/remote_base/samsung_protocol.cpp +++ b/esphome/components/remote_base/samsung_protocol.cpp @@ -1,5 +1,6 @@ #include "samsung_protocol.h" #include "esphome/core/log.h" +#include namespace esphome { namespace remote_base { diff --git a/esphome/components/remote_base/toshiba_ac_protocol.cpp b/esphome/components/remote_base/toshiba_ac_protocol.cpp index 27d042ace0..bd1d2a8f5b 100644 --- a/esphome/components/remote_base/toshiba_ac_protocol.cpp +++ b/esphome/components/remote_base/toshiba_ac_protocol.cpp @@ -1,5 +1,6 @@ #include "toshiba_ac_protocol.h" #include "esphome/core/log.h" +#include namespace esphome { namespace remote_base { diff --git a/esphome/components/remote_receiver/__init__.py b/esphome/components/remote_receiver/__init__.py index 7158368ed8..253204bd1a 100644 --- a/esphome/components/remote_receiver/__init__.py +++ b/esphome/components/remote_receiver/__init__.py @@ -25,9 +25,7 @@ CONFIG_SCHEMA = remote_base.validate_triggers( cv.Schema( { cv.GenerateID(): cv.declare_id(RemoteReceiverComponent), - cv.Required(CONF_PIN): cv.All( - pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt - ), + cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pin_schema), cv.Optional(CONF_DUMP, default=[]): remote_base.validate_dumpers, cv.Optional(CONF_TOLERANCE, default=25): cv.All( cv.percentage_int, cv.Range(min=0) diff --git a/esphome/components/remote_receiver/remote_receiver.h b/esphome/components/remote_receiver/remote_receiver.h index 25262e3366..50153c105d 100644 --- a/esphome/components/remote_receiver/remote_receiver.h +++ b/esphome/components/remote_receiver/remote_receiver.h @@ -6,7 +6,7 @@ namespace esphome { namespace remote_receiver { -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 struct RemoteReceiverComponentStore { static void gpio_intr(RemoteReceiverComponentStore *arg); @@ -21,23 +21,23 @@ struct RemoteReceiverComponentStore { bool overflow{false}; uint32_t buffer_size{1000}; uint8_t filter_us{10}; - ISRInternalGPIOPin *pin; + ISRInternalGPIOPin pin; }; #endif class RemoteReceiverComponent : public remote_base::RemoteReceiverBase, public Component -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 , public remote_base::RemoteRMTChannel #endif { public: -#ifdef ARDUINO_ARCH_ESP32 - RemoteReceiverComponent(GPIOPin *pin, uint8_t mem_block_num = 1) +#ifdef USE_ESP32 + RemoteReceiverComponent(InternalGPIOPin *pin, uint8_t mem_block_num = 1) : RemoteReceiverBase(pin), remote_base::RemoteRMTChannel(mem_block_num) {} #else - RemoteReceiverComponent(GPIOPin *pin) : RemoteReceiverBase(pin) {} + RemoteReceiverComponent(InternalGPIOPin *pin) : RemoteReceiverBase(pin) {} #endif void setup() override; void dump_config() override; @@ -49,13 +49,13 @@ class RemoteReceiverComponent : public remote_base::RemoteReceiverBase, void set_idle_us(uint32_t idle_us) { this->idle_us_ = idle_us; } protected: -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 void decode_rmt_(rmt_item32_t *item, size_t len); RingbufHandle_t ringbuf_; esp_err_t error_code_{ESP_OK}; #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 RemoteReceiverComponentStore store_; HighFrequencyLoopRequester high_freq_; #endif diff --git a/esphome/components/remote_receiver/remote_receiver_esp32.cpp b/esphome/components/remote_receiver/remote_receiver_esp32.cpp index b2ddc69b1c..bec2af6718 100644 --- a/esphome/components/remote_receiver/remote_receiver_esp32.cpp +++ b/esphome/components/remote_receiver/remote_receiver_esp32.cpp @@ -1,7 +1,7 @@ #include "remote_receiver.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include namespace esphome { diff --git a/esphome/components/remote_receiver/remote_receiver_esp8266.cpp b/esphome/components/remote_receiver/remote_receiver_esp8266.cpp index d509eea925..cf2c15402e 100644 --- a/esphome/components/remote_receiver/remote_receiver_esp8266.cpp +++ b/esphome/components/remote_receiver/remote_receiver_esp8266.cpp @@ -1,20 +1,20 @@ #include "remote_receiver.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 namespace esphome { namespace remote_receiver { static const char *const TAG = "remote_receiver.esp8266"; -void ICACHE_RAM_ATTR HOT RemoteReceiverComponentStore::gpio_intr(RemoteReceiverComponentStore *arg) { +void IRAM_ATTR HOT RemoteReceiverComponentStore::gpio_intr(RemoteReceiverComponentStore *arg) { const uint32_t now = micros(); // If the lhs is 1 (rising edge) we should write to an uneven index and vice versa const uint32_t next = (arg->buffer_write_at + 1) % arg->buffer_size; - const bool level = arg->pin->digital_read(); + const bool level = arg->pin.digital_read(); if (level != next % 2) return; @@ -54,7 +54,7 @@ void RemoteReceiverComponent::setup() { } else { s.buffer_write_at = s.buffer_read_at = 0; } - this->pin_->attach_interrupt(RemoteReceiverComponentStore::gpio_intr, &this->store_, CHANGE); + this->pin_->attach_interrupt(RemoteReceiverComponentStore::gpio_intr, &this->store_, gpio::INTERRUPT_ANY_EDGE); } void RemoteReceiverComponent::dump_config() { ESP_LOGCONFIG(TAG, "Remote Receiver:"); diff --git a/esphome/components/remote_transmitter/remote_transmitter.h b/esphome/components/remote_transmitter/remote_transmitter.h index 853b5b6289..d05942de3b 100644 --- a/esphome/components/remote_transmitter/remote_transmitter.h +++ b/esphome/components/remote_transmitter/remote_transmitter.h @@ -8,13 +8,13 @@ namespace remote_transmitter { class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, public Component -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 , public remote_base::RemoteRMTChannel #endif { public: - explicit RemoteTransmitterComponent(GPIOPin *pin) : remote_base::RemoteTransmitterBase(pin) {} + explicit RemoteTransmitterComponent(InternalGPIOPin *pin) : remote_base::RemoteTransmitterBase(pin) {} void setup() override; @@ -26,7 +26,7 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, protected: void send_internal(uint32_t send_times, uint32_t send_wait) override; -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 void calculate_on_off_time_(uint32_t carrier_frequency, uint32_t *on_time_period, uint32_t *off_time_period); void mark_(uint32_t on_time, uint32_t off_time, uint32_t usec); @@ -34,7 +34,7 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, void space_(uint32_t usec); #endif -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 void configure_rmt(); uint32_t current_carrier_frequency_{UINT32_MAX}; diff --git a/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp b/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp index 90166d2741..ff53d0be84 100644 --- a/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +++ b/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp @@ -2,7 +2,7 @@ #include "esphome/core/log.h" #include "esphome/core/application.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace remote_transmitter { diff --git a/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp b/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp index f8735fe763..33c01985d7 100644 --- a/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +++ b/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp @@ -2,7 +2,7 @@ #include "esphome/core/log.h" #include "esphome/core/application.h" -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 namespace esphome { namespace remote_transmitter { diff --git a/esphome/components/resistance/resistance_sensor.cpp b/esphome/components/resistance/resistance_sensor.cpp index 1380354a5f..4d3dfa5928 100644 --- a/esphome/components/resistance/resistance_sensor.cpp +++ b/esphome/components/resistance/resistance_sensor.cpp @@ -13,7 +13,7 @@ void ResistanceSensor::dump_config() { ESP_LOGCONFIG(TAG, " Reference Voltage: %.1fV", this->reference_voltage_); } void ResistanceSensor::process_(float value) { - if (isnan(value)) { + if (std::isnan(value)) { this->publish_state(NAN); return; } diff --git a/esphome/components/restart/restart_switch.cpp b/esphome/components/restart/restart_switch.cpp index ea46c5f910..3076fde99e 100644 --- a/esphome/components/restart/restart_switch.cpp +++ b/esphome/components/restart/restart_switch.cpp @@ -1,4 +1,5 @@ #include "restart_switch.h" +#include "esphome/core/hal.h" #include "esphome/core/log.h" #include "esphome/core/application.h" diff --git a/esphome/components/rotary_encoder/rotary_encoder.cpp b/esphome/components/rotary_encoder/rotary_encoder.cpp index 8ef6f932c5..7c95fac98e 100644 --- a/esphome/components/rotary_encoder/rotary_encoder.cpp +++ b/esphome/components/rotary_encoder/rotary_encoder.cpp @@ -82,12 +82,12 @@ static const uint16_t DRAM_ATTR STATE_LOOKUP_TABLE[32] = { STATE_CW | STATE_S3 // 0x1F: stay here }; -void ICACHE_RAM_ATTR HOT RotaryEncoderSensorStore::gpio_intr(RotaryEncoderSensorStore *arg) { +void IRAM_ATTR HOT RotaryEncoderSensorStore::gpio_intr(RotaryEncoderSensorStore *arg) { // Forget upper bits and add pin states uint8_t input_state = arg->state & STATE_LUT_MASK; - if (arg->pin_a->digital_read()) + if (arg->pin_a.digital_read()) input_state |= STATE_PIN_A_HIGH; - if (arg->pin_b->digital_read()) + if (arg->pin_b.digital_read()) input_state |= STATE_PIN_B_HIGH; int8_t rotation_dir = 0; @@ -134,8 +134,8 @@ void RotaryEncoderSensor::setup() { this->pin_i_->setup(); } - this->pin_a_->attach_interrupt(RotaryEncoderSensorStore::gpio_intr, &this->store_, CHANGE); - this->pin_b_->attach_interrupt(RotaryEncoderSensorStore::gpio_intr, &this->store_, CHANGE); + this->pin_a_->attach_interrupt(RotaryEncoderSensorStore::gpio_intr, &this->store_, gpio::INTERRUPT_ANY_EDGE); + this->pin_b_->attach_interrupt(RotaryEncoderSensorStore::gpio_intr, &this->store_, gpio::INTERRUPT_ANY_EDGE); } void RotaryEncoderSensor::dump_config() { LOG_SENSOR("", "Rotary Encoder", this); @@ -157,13 +157,14 @@ void RotaryEncoderSensor::dump_config() { void RotaryEncoderSensor::loop() { std::array rotation_events; bool rotation_events_overflow; - ets_intr_lock(); - rotation_events = this->store_.rotation_events; - rotation_events_overflow = this->store_.rotation_events_overflow; + { + InterruptLock lock; + rotation_events = this->store_.rotation_events; + rotation_events_overflow = this->store_.rotation_events_overflow; - this->store_.rotation_events.fill(0); - this->store_.rotation_events_overflow = false; - ets_intr_unlock(); + this->store_.rotation_events.fill(0); + this->store_.rotation_events_overflow = false; + } if (rotation_events_overflow) { ESP_LOGW(TAG, "Captured more rotation events than expected"); diff --git a/esphome/components/rotary_encoder/rotary_encoder.h b/esphome/components/rotary_encoder/rotary_encoder.h index 000350d66c..4825e472a1 100644 --- a/esphome/components/rotary_encoder/rotary_encoder.h +++ b/esphome/components/rotary_encoder/rotary_encoder.h @@ -3,7 +3,7 @@ #include #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/core/automation.h" #include "esphome/components/sensor/sensor.h" @@ -19,8 +19,8 @@ enum RotaryEncoderResolution { }; struct RotaryEncoderSensorStore { - ISRInternalGPIOPin *pin_a; - ISRInternalGPIOPin *pin_b; + ISRInternalGPIOPin pin_a; + ISRInternalGPIOPin pin_b; volatile int32_t counter{0}; RotaryEncoderResolution resolution{ROTARY_ENCODER_1_PULSE_PER_CYCLE}; @@ -37,8 +37,8 @@ struct RotaryEncoderSensorStore { class RotaryEncoderSensor : public sensor::Sensor, public Component { public: - void set_pin_a(GPIOPin *pin_a) { pin_a_ = pin_a; } - void set_pin_b(GPIOPin *pin_b) { pin_b_ = pin_b; } + void set_pin_a(InternalGPIOPin *pin_a) { pin_a_ = pin_a; } + void set_pin_b(InternalGPIOPin *pin_b) { pin_b_ = pin_b; } /** Set the resolution of the rotary encoder. * @@ -76,8 +76,8 @@ class RotaryEncoderSensor : public sensor::Sensor, public Component { } protected: - GPIOPin *pin_a_; - GPIOPin *pin_b_; + InternalGPIOPin *pin_a_; + InternalGPIOPin *pin_b_; GPIOPin *pin_i_{nullptr}; /// Index pin, if this is not nullptr, the counter will reset to 0 once this pin is HIGH. RotaryEncoderSensorStore store_{}; diff --git a/esphome/components/rotary_encoder/sensor.py b/esphome/components/rotary_encoder/sensor.py index e82b9d5f13..ef1110c6d8 100644 --- a/esphome/components/rotary_encoder/sensor.py +++ b/esphome/components/rotary_encoder/sensor.py @@ -64,12 +64,8 @@ CONFIG_SCHEMA = cv.All( .extend( { cv.GenerateID(): cv.declare_id(RotaryEncoderSensor), - cv.Required(CONF_PIN_A): cv.All( - pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt - ), - cv.Required(CONF_PIN_B): cv.All( - pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt - ), + cv.Required(CONF_PIN_A): cv.All(pins.internal_gpio_input_pin_schema), + cv.Required(CONF_PIN_B): cv.All(pins.internal_gpio_input_pin_schema), cv.Optional(CONF_PIN_RESET): pins.internal_gpio_output_pin_schema, cv.Optional(CONF_RESOLUTION, default=1): cv.enum(RESOLUTIONS, int=True), cv.Optional(CONF_MIN_VALUE): cv.int_, diff --git a/esphome/components/ruuvi_ble/ruuvi_ble.cpp b/esphome/components/ruuvi_ble/ruuvi_ble.cpp index e3e9f42305..bdd012cf5c 100644 --- a/esphome/components/ruuvi_ble/ruuvi_ble.cpp +++ b/esphome/components/ruuvi_ble/ruuvi_ble.cpp @@ -1,7 +1,7 @@ #include "ruuvi_ble.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ruuvi_ble { diff --git a/esphome/components/ruuvi_ble/ruuvi_ble.h b/esphome/components/ruuvi_ble/ruuvi_ble.h index 848004f3d7..add431ce42 100644 --- a/esphome/components/ruuvi_ble/ruuvi_ble.h +++ b/esphome/components/ruuvi_ble/ruuvi_ble.h @@ -3,7 +3,7 @@ #include "esphome/core/component.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ruuvi_ble { diff --git a/esphome/components/ruuvitag/ruuvitag.cpp b/esphome/components/ruuvitag/ruuvitag.cpp index f4e4a72270..9b462b4794 100644 --- a/esphome/components/ruuvitag/ruuvitag.cpp +++ b/esphome/components/ruuvitag/ruuvitag.cpp @@ -1,7 +1,7 @@ #include "ruuvitag.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ruuvitag { diff --git a/esphome/components/ruuvitag/ruuvitag.h b/esphome/components/ruuvitag/ruuvitag.h index 863c5775c2..63029ebb4d 100644 --- a/esphome/components/ruuvitag/ruuvitag.h +++ b/esphome/components/ruuvitag/ruuvitag.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/ruuvi_ble/ruuvi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace ruuvitag { diff --git a/esphome/components/scd30/scd30.cpp b/esphome/components/scd30/scd30.cpp index 8dfd992abe..30775fdea4 100644 --- a/esphome/components/scd30/scd30.cpp +++ b/esphome/components/scd30/scd30.cpp @@ -1,5 +1,10 @@ #include "scd30.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" + +#ifdef USE_ESP8266 +#include +#endif namespace esphome { namespace scd30 { @@ -23,7 +28,7 @@ static const uint16_t SCD30_CMD_SOFT_RESET = 0xD304; void SCD30Component::setup() { ESP_LOGCONFIG(TAG, "Setting up scd30..."); -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 Wire.setClockStretchLimit(150000); #endif @@ -51,7 +56,7 @@ void SCD30Component::setup() { return; } } -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 // According ESP32 clock stretching is typically 30ms and up to 150ms "due to // internal calibration processes". The I2C peripheral only supports 13ms (at // least when running at 80MHz). @@ -73,7 +78,7 @@ void SCD30Component::setup() { return; } } -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 delay(30); #endif @@ -83,7 +88,7 @@ void SCD30Component::setup() { this->mark_failed(); return; } -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 delay(30); #endif @@ -193,7 +198,7 @@ bool SCD30Component::write_command_(uint16_t command, uint16_t data) { raw[2] = data >> 8; raw[3] = data & 0xFF; raw[4] = sht_crc_(raw[2], raw[3]); - return this->write_bytes_raw(raw, 5); + return this->write(raw, 5); } uint8_t SCD30Component::sht_crc_(uint8_t data1, uint8_t data2) { @@ -223,7 +228,7 @@ bool SCD30Component::read_data_(uint16_t *data, uint8_t len) { const uint8_t num_bytes = len * 3; std::vector buf(num_bytes); - if (!this->parent_->raw_receive(this->address_, buf.data(), num_bytes)) { + if (this->read(buf.data(), num_bytes) != i2c::ERROR_OK) { return false; } diff --git a/esphome/components/sdp3x/sdp3x.cpp b/esphome/components/sdp3x/sdp3x.cpp index 5e6c5887ac..ba7a028f8e 100644 --- a/esphome/components/sdp3x/sdp3x.cpp +++ b/esphome/components/sdp3x/sdp3x.cpp @@ -17,29 +17,29 @@ void SDP3XComponent::update() { this->read_pressure_(); } void SDP3XComponent::setup() { ESP_LOGD(TAG, "Setting up SDP3X..."); - if (!this->write_bytes_raw(SDP3X_STOP_MEAS, 2)) { + if (this->write(SDP3X_STOP_MEAS, 2) != i2c::ERROR_OK) { ESP_LOGW(TAG, "Stop SDP3X failed!"); // This sometimes fails for no good reason } - if (!this->write_bytes_raw(SDP3X_SOFT_RESET, 2)) { + if (this->write(SDP3X_SOFT_RESET, 2) != i2c::ERROR_OK) { ESP_LOGW(TAG, "Soft Reset SDP3X failed!"); // This sometimes fails for no good reason } delay_microseconds_accurate(20000); - if (!this->write_bytes_raw(SDP3X_READ_ID1, 2)) { + if (this->write(SDP3X_READ_ID1, 2) != i2c::ERROR_OK) { ESP_LOGE(TAG, "Read ID1 SDP3X failed!"); this->mark_failed(); return; } - if (!this->write_bytes_raw(SDP3X_READ_ID2, 2)) { + if (this->write(SDP3X_READ_ID2, 2) != i2c::ERROR_OK) { ESP_LOGE(TAG, "Read ID2 SDP3X failed!"); this->mark_failed(); return; } uint8_t data[18]; - if (!this->read_bytes_raw(data, 18)) { + if (this->read(data, 18) != i2c::ERROR_OK) { ESP_LOGE(TAG, "Read ID SDP3X failed!"); this->mark_failed(); return; @@ -59,7 +59,7 @@ void SDP3XComponent::setup() { pressure_scale_factor_ = 240.0f * 100.0f; } - if (!this->write_bytes_raw(SDP3X_START_DP_AVG, 2)) { + if (this->write(SDP3X_START_DP_AVG, 2) != i2c::ERROR_OK) { ESP_LOGE(TAG, "Start Measurements SDP3X failed!"); this->mark_failed(); return; @@ -77,7 +77,7 @@ void SDP3XComponent::dump_config() { void SDP3XComponent::read_pressure_() { uint8_t data[9]; - if (!this->read_bytes_raw(data, 9)) { + if (this->read(data, 9) != i2c::ERROR_OK) { ESP_LOGW(TAG, "Couldn't read SDP3X data!"); this->status_set_warning(); return; diff --git a/esphome/components/sensor/automation.h b/esphome/components/sensor/automation.h index c70fb93963..8cd0adbeb2 100644 --- a/esphome/components/sensor/automation.h +++ b/esphome/components/sensor/automation.h @@ -40,7 +40,7 @@ class ValueRangeTrigger : public Trigger, public Component { template void set_max(V max) { this->max_ = max; } void setup() override { - this->rtc_ = global_preferences.make_preference(this->parent_->get_object_id_hash()); + this->rtc_ = global_preferences->make_preference(this->parent_->get_object_id_hash()); bool initial_state; if (this->rtc_.load(&initial_state)) { this->previous_in_range_ = initial_state; @@ -52,18 +52,18 @@ class ValueRangeTrigger : public Trigger, public Component { protected: void on_state_(float state) { - if (isnan(state)) + if (std::isnan(state)) return; float local_min = this->min_.value(state); float local_max = this->max_.value(state); bool in_range; - if (isnan(local_min) && isnan(local_max)) { + if (std::isnan(local_min) && std::isnan(local_max)) { in_range = this->previous_in_range_; - } else if (isnan(local_min)) { + } else if (std::isnan(local_min)) { in_range = state <= local_max; - } else if (isnan(local_max)) { + } else if (std::isnan(local_max)) { in_range = state >= local_min; } else { in_range = local_min <= state && state <= local_max; @@ -92,9 +92,9 @@ template class SensorInRangeCondition : public Condition void set_max(float max) { this->max_ = max; } bool check(Ts... x) override { const float state = this->parent_->state; - if (isnan(this->min_)) { + if (std::isnan(this->min_)) { return state <= this->max_; - } else if (isnan(this->max_)) { + } else if (std::isnan(this->max_)) { return state >= this->min_; } else { return this->min_ <= state && state <= this->max_; diff --git a/esphome/components/sensor/filter.cpp b/esphome/components/sensor/filter.cpp index f048189959..63801e7996 100644 --- a/esphome/components/sensor/filter.cpp +++ b/esphome/components/sensor/filter.cpp @@ -1,6 +1,7 @@ #include "filter.h" #include "sensor.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace sensor { @@ -35,7 +36,7 @@ MedianFilter::MedianFilter(size_t window_size, size_t send_every, size_t send_fi void MedianFilter::set_send_every(size_t send_every) { this->send_every_ = send_every; } void MedianFilter::set_window_size(size_t window_size) { this->window_size_ = window_size; } optional MedianFilter::new_value(float value) { - if (!isnan(value)) { + if (!std::isnan(value)) { while (this->queue_.size() >= this->window_size_) { this->queue_.pop_front(); } @@ -71,7 +72,7 @@ MinFilter::MinFilter(size_t window_size, size_t send_every, size_t send_first_at void MinFilter::set_send_every(size_t send_every) { this->send_every_ = send_every; } void MinFilter::set_window_size(size_t window_size) { this->window_size_ = window_size; } optional MinFilter::new_value(float value) { - if (!isnan(value)) { + if (!std::isnan(value)) { while (this->queue_.size() >= this->window_size_) { this->queue_.pop_front(); } @@ -100,7 +101,7 @@ MaxFilter::MaxFilter(size_t window_size, size_t send_every, size_t send_first_at void MaxFilter::set_send_every(size_t send_every) { this->send_every_ = send_every; } void MaxFilter::set_window_size(size_t window_size) { this->window_size_ = window_size; } optional MaxFilter::new_value(float value) { - if (!isnan(value)) { + if (!std::isnan(value)) { while (this->queue_.size() >= this->window_size_) { this->queue_.pop_front(); } @@ -130,7 +131,7 @@ SlidingWindowMovingAverageFilter::SlidingWindowMovingAverageFilter(size_t window void SlidingWindowMovingAverageFilter::set_send_every(size_t send_every) { this->send_every_ = send_every; } void SlidingWindowMovingAverageFilter::set_window_size(size_t window_size) { this->window_size_ = window_size; } optional SlidingWindowMovingAverageFilter::new_value(float value) { - if (!isnan(value)) { + if (!std::isnan(value)) { if (this->queue_.size() == this->window_size_) { this->sum_ -= this->queue_[0]; this->queue_.pop_front(); @@ -165,7 +166,7 @@ optional SlidingWindowMovingAverageFilter::new_value(float value) { ExponentialMovingAverageFilter::ExponentialMovingAverageFilter(float alpha, size_t send_every) : send_every_(send_every), send_at_(send_every - 1), alpha_(alpha) {} optional ExponentialMovingAverageFilter::new_value(float value) { - if (!isnan(value)) { + if (!std::isnan(value)) { if (this->first_value_) this->accumulator_ = value; else @@ -211,8 +212,8 @@ optional MultiplyFilter::new_value(float value) { return value * this->mu FilterOutValueFilter::FilterOutValueFilter(float value_to_filter_out) : value_to_filter_out_(value_to_filter_out) {} optional FilterOutValueFilter::new_value(float value) { - if (isnan(this->value_to_filter_out_)) { - if (isnan(value)) + if (std::isnan(this->value_to_filter_out_)) { + if (std::isnan(value)) return {}; else return value; @@ -243,9 +244,9 @@ optional ThrottleFilter::new_value(float value) { // DeltaFilter DeltaFilter::DeltaFilter(float min_delta) : min_delta_(min_delta), last_value_(NAN) {} optional DeltaFilter::new_value(float value) { - if (isnan(value)) + if (std::isnan(value)) return {}; - if (isnan(this->last_value_)) { + if (std::isnan(this->last_value_)) { return this->last_value_ = value; } if (fabsf(value - this->last_value_) >= this->min_delta_) { diff --git a/esphome/components/servo/servo.cpp b/esphome/components/servo/servo.cpp index 0b018ddb2e..2e1ba587a2 100644 --- a/esphome/components/servo/servo.cpp +++ b/esphome/components/servo/servo.cpp @@ -1,5 +1,6 @@ #include "servo.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace servo { diff --git a/esphome/components/servo/servo.h b/esphome/components/servo/servo.h index d95a524a8b..e2e3823158 100644 --- a/esphome/components/servo/servo.h +++ b/esphome/components/servo/servo.h @@ -24,7 +24,7 @@ class Servo : public Component { void setup() override { float v; if (this->restore_) { - this->rtc_ = global_preferences.make_preference(global_servo_id); + this->rtc_ = global_preferences->make_preference(global_servo_id); global_servo_id++; if (this->rtc_.load(&v)) { this->output_->set_level(v); diff --git a/esphome/components/sgp30/sgp30.cpp b/esphome/components/sgp30/sgp30.cpp index 5a6f438429..1a64a12907 100644 --- a/esphome/components/sgp30/sgp30.cpp +++ b/esphome/components/sgp30/sgp30.cpp @@ -1,6 +1,7 @@ #include "sgp30.h" #include "esphome/core/log.h" #include "esphome/core/application.h" +#include namespace esphome { namespace sgp30 { @@ -84,7 +85,7 @@ void SGP30Component::setup() { // Hash with compilation time // This ensures the baseline storage is cleared after OTA uint32_t hash = fnv1_hash(App.get_compilation_time()); - this->pref_ = global_preferences.make_preference(hash, true); + this->pref_ = global_preferences->make_preference(hash, true); if (this->pref_.load(&this->baselines_storage_)) { ESP_LOGI(TAG, "Loaded eCO2 baseline: 0x%04X, TVOC baseline: 0x%04X", this->baselines_storage_.eco2, @@ -172,7 +173,7 @@ void SGP30Component::send_env_data_() { float humidity = NAN; if (this->humidity_sensor_ != nullptr) humidity = this->humidity_sensor_->state; - if (isnan(humidity) || humidity < 0.0f || humidity > 100.0f) { + if (std::isnan(humidity) || humidity < 0.0f || humidity > 100.0f) { ESP_LOGW(TAG, "Compensation not possible yet: bad humidity data."); return; } else { @@ -182,7 +183,7 @@ void SGP30Component::send_env_data_() { if (this->temperature_sensor_ != nullptr) { temperature = float(this->temperature_sensor_->state); } - if (isnan(temperature) || temperature < -40.0f || temperature > 85.0f) { + if (std::isnan(temperature) || temperature < -40.0f || temperature > 85.0f) { ESP_LOGW(TAG, "Compensation not possible yet: bad temperature value data."); return; } else { @@ -334,7 +335,7 @@ bool SGP30Component::read_data_(uint16_t *data, uint8_t len) { const uint8_t num_bytes = len * 3; std::vector buf(num_bytes); - if (!this->parent_->raw_receive(this->address_, buf.data(), num_bytes)) { + if (this->read(buf.data(), num_bytes) != i2c::ERROR_OK) { return false; } diff --git a/esphome/components/sgp40/sgp40.cpp b/esphome/components/sgp40/sgp40.cpp index 1a1909eeb0..fddd0255b8 100644 --- a/esphome/components/sgp40/sgp40.cpp +++ b/esphome/components/sgp40/sgp40.cpp @@ -1,5 +1,6 @@ -#include "esphome/core/log.h" #include "sgp40.h" +#include "esphome/core/log.h" +#include "esphome/core/hal.h" #include namespace esphome { @@ -55,7 +56,7 @@ void SGP40Component::setup() { // Hash with compilation time // This ensures the baseline storage is cleared after OTA uint32_t hash = fnv1_hash(App.get_compilation_time()); - this->pref_ = global_preferences.make_preference(hash, true); + this->pref_ = global_preferences->make_preference(hash, true); if (this->pref_.load(&this->baselines_storage_)) { this->state0_ = this->baselines_storage_.state0; @@ -165,7 +166,7 @@ uint16_t SGP40Component::measure_raw_() { if (this->humidity_sensor_ != nullptr) { humidity = this->humidity_sensor_->state; } - if (isnan(humidity) || humidity < 0.0f || humidity > 100.0f) { + if (std::isnan(humidity) || humidity < 0.0f || humidity > 100.0f) { humidity = 50; } @@ -173,7 +174,7 @@ uint16_t SGP40Component::measure_raw_() { if (this->temperature_sensor_ != nullptr) { temperature = float(this->temperature_sensor_->state); } - if (isnan(temperature) || temperature < -40.0f || temperature > 85.0f) { + if (std::isnan(temperature) || temperature < -40.0f || temperature > 85.0f) { temperature = 25; } @@ -191,9 +192,9 @@ uint16_t SGP40Component::measure_raw_() { command[6] = tempticks & 0xFF; command[7] = generate_crc_(command + 5, 2); - if (!this->write_bytes_raw(command, 8)) { + if (this->write(command, 8) != i2c::ERROR_OK) { this->status_set_warning(); - ESP_LOGD(TAG, "write_bytes_raw error"); + ESP_LOGD(TAG, "write error"); return UINT16_MAX; } delay(250); // NOLINT @@ -302,7 +303,7 @@ bool SGP40Component::read_data_(uint16_t *data, uint8_t len) { const uint8_t num_bytes = len * 3; std::vector buf(num_bytes); - if (!this->parent_->raw_receive(this->address_, buf.data(), num_bytes)) { + if (this->read(buf.data(), num_bytes) != i2c::ERROR_OK) { return false; } diff --git a/esphome/components/sht3xd/sht3xd.cpp b/esphome/components/sht3xd/sht3xd.cpp index be5b93c124..56a43d5161 100644 --- a/esphome/components/sht3xd/sht3xd.cpp +++ b/esphome/components/sht3xd/sht3xd.cpp @@ -103,7 +103,7 @@ bool SHT3XDComponent::read_data_(uint16_t *data, uint8_t len) { const uint8_t num_bytes = len * 3; std::vector buf(num_bytes); - if (!this->parent_->raw_receive(this->address_, buf.data(), num_bytes)) { + if (this->read(buf.data(), num_bytes) != i2c::ERROR_OK) { return false; } diff --git a/esphome/components/sht4x/sht4x.cpp b/esphome/components/sht4x/sht4x.cpp index 6d7c917b57..248f32c4de 100644 --- a/esphome/components/sht4x/sht4x.cpp +++ b/esphome/components/sht4x/sht4x.cpp @@ -12,7 +12,7 @@ void SHT4XComponent::start_heater_() { uint8_t cmd[] = {MEASURECOMMANDS[this->heater_command_]}; ESP_LOGD(TAG, "Heater turning on"); - this->write_bytes_raw(cmd, 1); + this->write(cmd, 1); } void SHT4XComponent::setup() { @@ -53,7 +53,7 @@ void SHT4XComponent::update() { uint8_t cmd[] = {MEASURECOMMANDS[this->precision_]}; // Send command - this->write_bytes_raw(cmd, 1); + this->write(cmd, 1); this->set_timeout(10, [this]() { const uint8_t num_bytes = 6; diff --git a/esphome/components/shtcx/shtcx.cpp b/esphome/components/shtcx/shtcx.cpp index db3a3bf803..f2fb6bd5c3 100644 --- a/esphome/components/shtcx/shtcx.cpp +++ b/esphome/components/shtcx/shtcx.cpp @@ -1,5 +1,6 @@ #include "shtcx.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace shtcx { @@ -132,7 +133,7 @@ bool SHTCXComponent::read_data_(uint16_t *data, uint8_t len) { const uint8_t num_bytes = len * 3; std::vector buf(num_bytes); - if (!this->parent_->raw_receive(this->address_, buf.data(), num_bytes)) { + if (this->read(buf.data(), num_bytes) != i2c::ERROR_OK) { return false; } diff --git a/esphome/components/shutdown/shutdown_switch.cpp b/esphome/components/shutdown/shutdown_switch.cpp index 87b755cab6..0e5853cc46 100644 --- a/esphome/components/shutdown/shutdown_switch.cpp +++ b/esphome/components/shutdown/shutdown_switch.cpp @@ -2,6 +2,13 @@ #include "esphome/core/log.h" #include "esphome/core/application.h" +#ifdef USE_ESP32 +#include +#endif +#ifdef USE_ESP8266 +#include +#endif + namespace esphome { namespace shutdown { @@ -17,10 +24,10 @@ void ShutdownSwitch::write_state(bool state) { delay(100); // NOLINT App.run_safe_shutdown_hooks(); -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 ESP.deepSleep(0); // NOLINT(readability-static-accessed-through-instance) #endif -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 esp_deep_sleep_start(); #endif } diff --git a/esphome/components/slow_pwm/slow_pwm_output.h b/esphome/components/slow_pwm/slow_pwm_output.h index 4a2c1d0a14..f0524f36d8 100644 --- a/esphome/components/slow_pwm/slow_pwm_output.h +++ b/esphome/components/slow_pwm/slow_pwm_output.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/output/float_output.h" namespace esphome { diff --git a/esphome/components/sm16716/sm16716.h b/esphome/components/sm16716/sm16716.h index 85f78c8cf5..73414c0003 100644 --- a/esphome/components/sm16716/sm16716.h +++ b/esphome/components/sm16716/sm16716.h @@ -1,8 +1,9 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/output/float_output.h" +#include namespace esphome { namespace sm16716 { diff --git a/esphome/components/sm2135/sm2135.h b/esphome/components/sm2135/sm2135.h index e39730579f..0277e9ba1c 100644 --- a/esphome/components/sm2135/sm2135.h +++ b/esphome/components/sm2135/sm2135.h @@ -1,8 +1,9 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/output/float_output.h" +#include namespace esphome { namespace sm2135 { diff --git a/esphome/components/sn74hc595/__init__.py b/esphome/components/sn74hc595/__init__.py index 4437878970..0d1ff6ecba 100644 --- a/esphome/components/sn74hc595/__init__.py +++ b/esphome/components/sn74hc595/__init__.py @@ -3,10 +3,12 @@ import esphome.config_validation as cv from esphome import pins from esphome.const import ( CONF_ID, + CONF_MODE, CONF_NUMBER, CONF_INVERTED, CONF_DATA_PIN, CONF_CLOCK_PIN, + CONF_OUTPUT, ) DEPENDENCIES = [] @@ -48,19 +50,36 @@ async def to_code(config): cg.add(var.set_sr_count(config[CONF_SR_COUNT])) -SN74HC595_OUTPUT_PIN_SCHEMA = cv.Schema( +def _validate_output_mode(value): + if value is not True: + raise cv.Invalid("Only output mode is supported") + return value + + +SN74HC595_PIN_SCHEMA = cv.All( { + cv.GenerateID(): cv.declare_id(SN74HC595GPIOPin), cv.Required(CONF_SN74HC595): cv.use_id(SN74HC595Component), - cv.Required(CONF_NUMBER): cv.int_, + cv.Required(CONF_NUMBER): cv.int_range(min=0, max=7), + cv.Optional(CONF_MODE, default={}): cv.All( + { + cv.Optional(CONF_OUTPUT, default=True): cv.All( + cv.boolean, _validate_output_mode + ), + }, + ), cv.Optional(CONF_INVERTED, default=False): cv.boolean, } ) -SN74HC595_INPUT_PIN_SCHEMA = cv.Schema({}) -@pins.PIN_SCHEMA_REGISTRY.register( - CONF_SN74HC595, (SN74HC595_OUTPUT_PIN_SCHEMA, SN74HC595_INPUT_PIN_SCHEMA) -) +@pins.PIN_SCHEMA_REGISTRY.register(CONF_SN74HC595, SN74HC595_PIN_SCHEMA) async def sn74hc595_pin_to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) parent = await cg.get_variable(config[CONF_SN74HC595]) - return SN74HC595GPIOPin.new(parent, config[CONF_NUMBER], config[CONF_INVERTED]) + cg.add(var.set_parent(parent)) + + num = config[CONF_NUMBER] + cg.add(var.set_pin(num)) + cg.add(var.set_inverted(config[CONF_INVERTED])) + return var diff --git a/esphome/components/sn74hc595/sn74hc595.cpp b/esphome/components/sn74hc595/sn74hc595.cpp index 596ebf755d..5ebf50e5cb 100644 --- a/esphome/components/sn74hc595/sn74hc595.cpp +++ b/esphome/components/sn74hc595/sn74hc595.cpp @@ -10,17 +10,17 @@ void SN74HC595Component::setup() { ESP_LOGCONFIG(TAG, "Setting up SN74HC595..."); if (this->have_oe_pin_) { // disable output - this->oe_pin_->pin_mode(OUTPUT); + this->oe_pin_->setup(); this->oe_pin_->digital_write(true); } // initialize output pins - this->clock_pin_->pin_mode(OUTPUT); - this->data_pin_->pin_mode(OUTPUT); - this->latch_pin_->pin_mode(OUTPUT); - this->clock_pin_->digital_write(LOW); - this->data_pin_->digital_write(LOW); - this->latch_pin_->digital_write(LOW); + this->clock_pin_->setup(); + this->data_pin_->setup(); + this->latch_pin_->setup(); + this->clock_pin_->digital_write(false); + this->data_pin_->digital_write(false); + this->latch_pin_->digital_write(false); // send state to shift register this->write_gpio_(); @@ -62,16 +62,14 @@ bool SN74HC595Component::write_gpio_() { float SN74HC595Component::get_setup_priority() const { return setup_priority::IO; } -void SN74HC595GPIOPin::setup() {} - -bool SN74HC595GPIOPin::digital_read() { return this->parent_->digital_read_(this->pin_) != this->inverted_; } - void SN74HC595GPIOPin::digital_write(bool value) { this->parent_->digital_write_(this->pin_, value != this->inverted_); } - -SN74HC595GPIOPin::SN74HC595GPIOPin(SN74HC595Component *parent, uint8_t pin, bool inverted) - : GPIOPin(pin, OUTPUT, inverted), parent_(parent) {} +std::string SN74HC595GPIOPin::dump_summary() const { + char buffer[32]; + snprintf(buffer, sizeof(buffer), "%u via SN74HC595", pin_); + return buffer; +} } // namespace sn74hc595 } // namespace esphome diff --git a/esphome/components/sn74hc595/sn74hc595.h b/esphome/components/sn74hc595/sn74hc595.h index d6f9a68bc8..784019c3a6 100644 --- a/esphome/components/sn74hc595/sn74hc595.h +++ b/esphome/components/sn74hc595/sn74hc595.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" namespace esphome { namespace sn74hc595 { @@ -41,14 +41,20 @@ class SN74HC595Component : public Component { /// Helper class to expose a SC74HC595 pin as an internal output GPIO pin. class SN74HC595GPIOPin : public GPIOPin { public: - SN74HC595GPIOPin(SN74HC595Component *parent, uint8_t pin, bool inverted = false); - - void setup() override; - bool digital_read() override; + void setup() override {} + void pin_mode(gpio::Flags flags) override {} + bool digital_read() override { return false; } void digital_write(bool value) override; + std::string dump_summary() const override; + + void set_parent(SN74HC595Component *parent) { parent_ = parent; } + void set_pin(uint8_t pin) { pin_ = pin; } + void set_inverted(bool inverted) { inverted_ = inverted; } protected: SN74HC595Component *parent_; + uint8_t pin_; + bool inverted_; }; } // namespace sn74hc595 diff --git a/esphome/components/sntp/sntp_component.cpp b/esphome/components/sntp/sntp_component.cpp index 895c775b19..2b6cd10e80 100644 --- a/esphome/components/sntp/sntp_component.cpp +++ b/esphome/components/sntp/sntp_component.cpp @@ -1,10 +1,10 @@ #include "sntp_component.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include "lwip/apps/sntp.h" #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #include "sntp.h" #endif @@ -20,13 +20,13 @@ static const char *const TAG = "sntp"; void SNTPComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up SNTP..."); -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 if (sntp_enabled()) { sntp_stop(); } sntp_setoperatingmode(SNTP_OPMODE_POLL); #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 sntp_stop(); #endif diff --git a/esphome/components/socket/bsd_sockets_impl.cpp b/esphome/components/socket/bsd_sockets_impl.cpp index aa1bcd3b3c..1db24973e7 100644 --- a/esphome/components/socket/bsd_sockets_impl.cpp +++ b/esphome/components/socket/bsd_sockets_impl.cpp @@ -6,8 +6,9 @@ #include -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include +#include #endif namespace esphome { @@ -81,7 +82,7 @@ class BSDSocketImpl : public Socket { int listen(int backlog) override { return ::listen(fd_, backlog); } ssize_t read(void *buf, size_t len) override { return ::read(fd_, buf, len); } ssize_t readv(const struct iovec *iov, int iovcnt) override { -#if defined(ARDUINO_ARCH_ESP32) && ESP_IDF_VERSION_MAJOR < 4 +#if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR < 4 // esp-idf v3 doesn't have readv, emulate it ssize_t ret = 0; for (int i = 0; i < iovcnt; i++) { @@ -97,6 +98,9 @@ class BSDSocketImpl : public Socket { break; } return ret; +#elif defined(USE_ESP32) + // ESP-IDF v4 only has symbol lwip_readv + return ::lwip_readv(fd_, iov, iovcnt); #else return ::readv(fd_, iov, iovcnt); #endif @@ -104,7 +108,7 @@ class BSDSocketImpl : public Socket { ssize_t write(const void *buf, size_t len) override { return ::write(fd_, buf, len); } ssize_t send(void *buf, size_t len, int flags) { return ::send(fd_, buf, len, flags); } ssize_t writev(const struct iovec *iov, int iovcnt) override { -#if defined(ARDUINO_ARCH_ESP32) && ESP_IDF_VERSION_MAJOR < 4 +#if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR < 4 // esp-idf v3 doesn't have writev, emulate it ssize_t ret = 0; for (int i = 0; i < iovcnt; i++) { @@ -121,6 +125,9 @@ class BSDSocketImpl : public Socket { break; } return ret; +#elif defined(USE_ESP32) + // ESP-IDF v4 only has symbol lwip_writev + return ::lwip_writev(fd_, iov, iovcnt); #else return ::writev(fd_, iov, iovcnt); #endif diff --git a/esphome/components/socket/headers.h b/esphome/components/socket/headers.h index fbe8f929a0..f9697fd421 100644 --- a/esphome/components/socket/headers.h +++ b/esphome/components/socket/headers.h @@ -86,7 +86,7 @@ struct iovec { size_t iov_len; }; -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 // arduino-esp8266 declares a global vars called INADDR_NONE/ANY which are invalid with the define #ifdef INADDR_ANY #undef INADDR_ANY @@ -97,7 +97,7 @@ struct iovec { #define ESPHOME_INADDR_ANY ((uint32_t) 0x00000000UL) #define ESPHOME_INADDR_NONE ((uint32_t) 0xFFFFFFFFUL) -#else // !ARDUINO_ARCH_ESP8266 +#else // !USE_ESP8266 #define ESPHOME_INADDR_ANY INADDR_ANY #define ESPHOME_INADDR_NONE INADDR_NONE #endif @@ -114,7 +114,7 @@ struct iovec { #include #include -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ARDUINO // arduino-esp32 declares a global var called INADDR_NONE which is replaced // by the define #ifdef INADDR_NONE @@ -125,7 +125,7 @@ typedef uint32_t socklen_t; #define ESPHOME_INADDR_ANY ((uint32_t) 0x00000000UL) #define ESPHOME_INADDR_NONE ((uint32_t) 0xFFFFFFFFUL) -#else // !ARDUINO_ARCH_ESP32 +#else // !USE_ESP32 #define ESPHOME_INADDR_ANY INADDR_ANY #define ESPHOME_INADDR_NONE INADDR_NONE #endif diff --git a/esphome/components/socket/lwip_raw_tcp_impl.cpp b/esphome/components/socket/lwip_raw_tcp_impl.cpp index 366f0972ef..7521d1339f 100644 --- a/esphome/components/socket/lwip_raw_tcp_impl.cpp +++ b/esphome/components/socket/lwip_raw_tcp_impl.cpp @@ -11,6 +11,7 @@ #include #include +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { @@ -492,7 +493,7 @@ class LWIPRawImpl : public Socket { // nothing to do here, we just don't push it to the queue return ERR_OK; } - auto sock = std::unique_ptr(new LWIPRawImpl(newpcb)); + auto sock = make_unique(newpcb); sock->init(); accepted_sockets_.push(std::move(sock)); return ERR_OK; diff --git a/esphome/components/spi/spi.cpp b/esphome/components/spi/spi.cpp index 3180447711..d883142c81 100644 --- a/esphome/components/spi/spi.cpp +++ b/esphome/components/spi/spi.cpp @@ -8,12 +8,13 @@ namespace spi { static const char *const TAG = "spi"; -void ICACHE_RAM_ATTR HOT SPIComponent::disable() { +void IRAM_ATTR HOT SPIComponent::disable() { +#ifdef USE_SPI_ARDUINO_BACKEND if (this->hw_spi_ != nullptr) { this->hw_spi_->endTransaction(); } +#endif // USE_SPI_ARDUINO_BACKEND if (this->active_cs_) { - ESP_LOGVV(TAG, "Disabling SPI Chip on pin %u...", this->active_cs_->get_pin()); this->active_cs_->digital_write(true); this->active_cs_ = nullptr; } @@ -23,19 +24,37 @@ void SPIComponent::setup() { this->clk_->setup(); this->clk_->digital_write(true); +#ifdef USE_SPI_ARDUINO_BACKEND bool use_hw_spi = true; - if (this->clk_->is_inverted()) - use_hw_spi = false; const bool has_miso = this->miso_ != nullptr; const bool has_mosi = this->mosi_ != nullptr; - if (has_miso && this->miso_->is_inverted()) + int8_t clk_pin = -1, miso_pin = -1, mosi_pin = -1; + + if (!this->clk_->is_internal()) use_hw_spi = false; - if (has_mosi && this->mosi_->is_inverted()) + if (has_miso && !miso_->is_internal()) use_hw_spi = false; - int8_t clk_pin = this->clk_->get_pin(); - int8_t miso_pin = has_miso ? this->miso_->get_pin() : -1; - int8_t mosi_pin = has_mosi ? this->mosi_->get_pin() : -1; -#ifdef ARDUINO_ARCH_ESP8266 + if (has_mosi && !mosi_->is_internal()) + use_hw_spi = false; + if (use_hw_spi) { + auto *clk_internal = (InternalGPIOPin *) clk_; + auto *miso_internal = (InternalGPIOPin *) miso_; + auto *mosi_internal = (InternalGPIOPin *) mosi_; + + if (clk_internal->is_inverted()) + use_hw_spi = false; + if (has_miso && miso_internal->is_inverted()) + use_hw_spi = false; + if (has_mosi && mosi_internal->is_inverted()) + use_hw_spi = false; + + if (use_hw_spi) { + clk_pin = clk_internal->get_pin(); + miso_pin = has_miso ? miso_internal->get_pin() : -1; + mosi_pin = has_mosi ? mosi_internal->get_pin() : -1; + } + } +#ifdef USE_ESP8266 if (clk_pin == 6 && miso_pin == 7 && mosi_pin == 8) { // pass } else if (clk_pin == 14 && (!has_miso || miso_pin == 12) && (!has_mosi || mosi_pin == 13)) { @@ -50,8 +69,8 @@ void SPIComponent::setup() { this->hw_spi_->begin(); return; } -#endif -#ifdef ARDUINO_ARCH_ESP32 +#endif // USE_ESP8266 +#ifdef USE_ESP32 static uint8_t spi_bus_num = 0; if (spi_bus_num >= 2) { use_hw_spi = false; @@ -67,7 +86,8 @@ void SPIComponent::setup() { this->hw_spi_->begin(clk_pin, miso_pin, mosi_pin); return; } -#endif +#endif // USE_ESP32 +#endif // USE_SPI_ARDUINO_BACKEND if (this->miso_ != nullptr) { this->miso_->setup(); @@ -82,32 +102,28 @@ void SPIComponent::dump_config() { LOG_PIN(" CLK Pin: ", this->clk_); LOG_PIN(" MISO Pin: ", this->miso_); LOG_PIN(" MOSI Pin: ", this->mosi_); +#ifdef USE_SPI_ARDUINO_BACKEND ESP_LOGCONFIG(TAG, " Using HW SPI: %s", YESNO(this->hw_spi_ != nullptr)); +#endif // USE_SPI_ARDUINO_BACKEND } float SPIComponent::get_setup_priority() const { return setup_priority::BUS; } -void SPIComponent::debug_tx(uint8_t value) { - ESP_LOGVV(TAG, " TX 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(value), value); -} -void SPIComponent::debug_rx(uint8_t value) { - ESP_LOGVV(TAG, " RX 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(value), value); -} -void SPIComponent::debug_enable(uint8_t pin) { ESP_LOGVV(TAG, "Enabling SPI Chip on pin %u...", pin); } - void SPIComponent::cycle_clock_(bool value) { - uint32_t start = ESP.getCycleCount(); // NOLINT(readability-static-accessed-through-instance) - while (start - ESP.getCycleCount() < this->wait_cycle_) // NOLINT(readability-static-accessed-through-instance) + uint32_t start = arch_get_cpu_cycle_count(); + while (start - arch_get_cpu_cycle_count() < this->wait_cycle_) ; this->clk_->digital_write(value); start += this->wait_cycle_; - while (start - ESP.getCycleCount() < this->wait_cycle_) // NOLINT(readability-static-accessed-through-instance) + while (start - arch_get_cpu_cycle_count() < this->wait_cycle_) ; } // NOLINTNEXTLINE +#ifndef CLANG_TIDY #pragma GCC optimize("unroll-loops") // NOLINTNEXTLINE #pragma GCC optimize("O2") +#endif // CLANG_TIDY template uint8_t HOT SPIComponent::transfer_(uint8_t data) { @@ -153,15 +169,6 @@ uint8_t HOT SPIComponent::transfer_(uint8_t data) { } } -#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE - if (WRITE) { - SPIComponent::debug_tx(data); - } - if (READ) { - SPIComponent::debug_rx(out_data); - } -#endif - App.feed_wdt(); return out_data; diff --git a/esphome/components/spi/spi.h b/esphome/components/spi/spi.h index eb8f9ce7ce..601a5c5a7e 100644 --- a/esphome/components/spi/spi.h +++ b/esphome/components/spi/spi.h @@ -1,8 +1,16 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" +#include + +#ifdef USE_ARDUINO +#define USE_SPI_ARDUINO_BACKEND +#endif + +#ifdef USE_SPI_ARDUINO_BACKEND #include +#endif namespace esphome { namespace spi { @@ -72,18 +80,22 @@ class SPIComponent : public Component { void dump_config() override; template uint8_t read_byte() { +#ifdef USE_SPI_ARDUINO_BACKEND if (this->hw_spi_ != nullptr) { return this->hw_spi_->transfer(0x00); } +#endif // USE_SPI_ARDUINO_BACKEND return this->transfer_(0x00); } template void read_array(uint8_t *data, size_t length) { +#ifdef USE_SPI_ARDUINO_BACKEND if (this->hw_spi_ != nullptr) { this->hw_spi_->transfer(data, length); return; } +#endif // USE_SPI_ARDUINO_BACKEND for (size_t i = 0; i < length; i++) { data[i] = this->read_byte(); } @@ -91,19 +103,23 @@ class SPIComponent : public Component { template void write_byte(uint8_t data) { +#ifdef USE_SPI_ARDUINO_BACKEND if (this->hw_spi_ != nullptr) { this->hw_spi_->write(data); return; } +#endif // USE_SPI_ARDUINO_BACKEND this->transfer_(data); } template void write_byte16(const uint16_t data) { +#ifdef USE_SPI_ARDUINO_BACKEND if (this->hw_spi_ != nullptr) { this->hw_spi_->write16(data); return; } +#endif // USE_SPI_ARDUINO_BACKEND this->write_byte(data >> 8); this->write_byte(data); @@ -111,12 +127,14 @@ class SPIComponent : public Component { template void write_array16(const uint16_t *data, size_t length) { +#ifdef USE_SPI_ARDUINO_BACKEND if (this->hw_spi_ != nullptr) { for (size_t i = 0; i < length; i++) { this->hw_spi_->write16(data[i]); } return; } +#endif // USE_SPI_ARDUINO_BACKEND for (size_t i = 0; i < length; i++) { this->write_byte16(data[i]); } @@ -124,11 +142,13 @@ class SPIComponent : public Component { template void write_array(const uint8_t *data, size_t length) { +#ifdef USE_SPI_ARDUINO_BACKEND if (this->hw_spi_ != nullptr) { auto *data_c = const_cast(data); this->hw_spi_->writeBytes(data_c, length); return; } +#endif // USE_SPI_ARDUINO_BACKEND for (size_t i = 0; i < length; i++) { this->write_byte(data[i]); } @@ -136,6 +156,7 @@ class SPIComponent : public Component { template uint8_t transfer_byte(uint8_t data) { +#ifdef USE_SPI_ARDUINO_BACKEND if (this->miso_ != nullptr) { if (this->hw_spi_ != nullptr) { return this->hw_spi_->transfer(data); @@ -143,12 +164,14 @@ class SPIComponent : public Component { return this->transfer_(data); } } +#endif // USE_SPI_ARDUINO_BACKEND this->write_byte(data); return 0; } template void transfer_array(uint8_t *data, size_t length) { +#ifdef USE_SPI_ARDUINO_BACKEND if (this->hw_spi_ != nullptr) { if (this->miso_ != nullptr) { this->hw_spi_->transfer(data, length); @@ -157,6 +180,7 @@ class SPIComponent : public Component { } return; } +#endif // USE_SPI_ARDUINO_BACKEND if (this->miso_ != nullptr) { for (size_t i = 0; i < length; i++) { @@ -169,18 +193,19 @@ class SPIComponent : public Component { template void enable(GPIOPin *cs) { - if (cs != nullptr) { - SPIComponent::debug_enable(cs->get_pin()); - } - +#ifdef USE_SPI_ARDUINO_BACKEND if (this->hw_spi_ != nullptr) { uint8_t data_mode = (uint8_t(CLOCK_POLARITY) << 1) | uint8_t(CLOCK_PHASE); SPISettings settings(DATA_RATE, BIT_ORDER, data_mode); this->hw_spi_->beginTransaction(settings); } else { +#endif // USE_SPI_ARDUINO_BACKEND this->clk_->digital_write(CLOCK_POLARITY); - this->wait_cycle_ = uint32_t(F_CPU) / DATA_RATE / 2ULL; + uint32_t cpu_freq_hz = arch_get_cpu_freq_hz(); + this->wait_cycle_ = uint32_t(cpu_freq_hz) / DATA_RATE / 2ULL; +#ifdef USE_SPI_ARDUINO_BACKEND } +#endif // USE_SPI_ARDUINO_BACKEND if (cs != nullptr) { this->active_cs_ = cs; @@ -195,10 +220,6 @@ class SPIComponent : public Component { protected: inline void cycle_clock_(bool value); - static void debug_enable(uint8_t pin); - static void debug_tx(uint8_t value); - static void debug_rx(uint8_t value); - template uint8_t transfer_(uint8_t data); @@ -206,7 +227,9 @@ class SPIComponent : public Component { GPIOPin *miso_{nullptr}; GPIOPin *mosi_{nullptr}; GPIOPin *active_cs_{nullptr}; +#ifdef USE_SPI_ARDUINO_BACKEND SPIClass *hw_spi_{nullptr}; +#endif // USE_SPI_ARDUINO_BACKEND uint32_t wait_cycle_; }; diff --git a/esphome/components/sps30/sps30.cpp b/esphome/components/sps30/sps30.cpp index 3a31f4d607..472b7606ed 100644 --- a/esphome/components/sps30/sps30.cpp +++ b/esphome/components/sps30/sps30.cpp @@ -244,7 +244,7 @@ bool SPS30Component::read_data_(uint16_t *data, uint8_t len) { const uint8_t num_bytes = len * 3; std::vector buf(num_bytes); - if (!this->parent_->raw_receive(this->address_, buf.data(), num_bytes)) { + if (this->read(buf.data(), num_bytes) != i2c::ERROR_OK) { return false; } diff --git a/esphome/components/ssd1306_base/ssd1306_base.h b/esphome/components/ssd1306_base/ssd1306_base.h index 0fe09709e7..2c54af7a67 100644 --- a/esphome/components/ssd1306_base/ssd1306_base.h +++ b/esphome/components/ssd1306_base/ssd1306_base.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/display/display_buffer.h" namespace esphome { diff --git a/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp b/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp index fce9796008..45fda4870e 100644 --- a/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp +++ b/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp @@ -10,8 +10,8 @@ void I2CSSD1306::setup() { ESP_LOGCONFIG(TAG, "Setting up I2C SSD1306..."); this->init_reset_(); - this->raw_begin_transmission(); - if (!this->raw_end_transmission()) { + auto err = this->write(nullptr, 0); + if (err != i2c::ERROR_OK) { this->error_code_ = COMMUNICATION_FAILED; this->mark_failed(); return; diff --git a/esphome/components/ssd1322_base/ssd1322_base.h b/esphome/components/ssd1322_base/ssd1322_base.h index 125e374246..6a790c0199 100644 --- a/esphome/components/ssd1322_base/ssd1322_base.h +++ b/esphome/components/ssd1322_base/ssd1322_base.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/display/display_buffer.h" namespace esphome { diff --git a/esphome/components/ssd1325_base/ssd1325_base.h b/esphome/components/ssd1325_base/ssd1325_base.h index a06ba69a59..cca9412c43 100644 --- a/esphome/components/ssd1325_base/ssd1325_base.h +++ b/esphome/components/ssd1325_base/ssd1325_base.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/display/display_buffer.h" namespace esphome { diff --git a/esphome/components/ssd1327_base/ssd1327_base.h b/esphome/components/ssd1327_base/ssd1327_base.h index 03f360b258..35b021c71b 100644 --- a/esphome/components/ssd1327_base/ssd1327_base.h +++ b/esphome/components/ssd1327_base/ssd1327_base.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/display/display_buffer.h" namespace esphome { diff --git a/esphome/components/ssd1327_i2c/ssd1327_i2c.cpp b/esphome/components/ssd1327_i2c/ssd1327_i2c.cpp index 2967d2f9c4..e9e047bfb6 100644 --- a/esphome/components/ssd1327_i2c/ssd1327_i2c.cpp +++ b/esphome/components/ssd1327_i2c/ssd1327_i2c.cpp @@ -10,8 +10,8 @@ void I2CSSD1327::setup() { ESP_LOGCONFIG(TAG, "Setting up I2C SSD1327..."); this->init_reset_(); - this->raw_begin_transmission(); - if (!this->raw_end_transmission()) { + auto err = this->write(nullptr, 0); + if (err != i2c::ERROR_OK) { this->error_code_ = COMMUNICATION_FAILED; this->mark_failed(); return; diff --git a/esphome/components/ssd1331_base/ssd1331_base.h b/esphome/components/ssd1331_base/ssd1331_base.h index 8d2bca5de0..b889a47fbe 100644 --- a/esphome/components/ssd1331_base/ssd1331_base.h +++ b/esphome/components/ssd1331_base/ssd1331_base.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/display/display_buffer.h" namespace esphome { diff --git a/esphome/components/ssd1351_base/ssd1351_base.h b/esphome/components/ssd1351_base/ssd1351_base.h index 2730f798b5..422e601f8b 100644 --- a/esphome/components/ssd1351_base/ssd1351_base.h +++ b/esphome/components/ssd1351_base/ssd1351_base.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/display/display_buffer.h" namespace esphome { diff --git a/esphome/components/st7735/st7735.cpp b/esphome/components/st7735/st7735.cpp index 1f14136637..8490aa1fe4 100644 --- a/esphome/components/st7735/st7735.cpp +++ b/esphome/components/st7735/st7735.cpp @@ -1,6 +1,7 @@ #include "st7735.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/hal.h" namespace esphome { namespace st7735 { @@ -353,17 +354,17 @@ void ST7735::display_init_(const uint8_t *addr) { uint8_t num_commands, cmd, num_args; uint16_t ms; - num_commands = pgm_read_byte(addr++); // Number of commands to follow - while (num_commands--) { // For each command... - cmd = pgm_read_byte(addr++); // Read command - num_args = pgm_read_byte(addr++); // Number of args to follow - ms = num_args & ST_CMD_DELAY; // If hibit set, delay follows args - num_args &= ~ST_CMD_DELAY; // Mask out delay bit + num_commands = progmem_read_byte(addr++); // Number of commands to follow + while (num_commands--) { // For each command... + cmd = progmem_read_byte(addr++); // Read command + num_args = progmem_read_byte(addr++); // Number of args to follow + ms = num_args & ST_CMD_DELAY; // If hibit set, delay follows args + num_args &= ~ST_CMD_DELAY; // Mask out delay bit this->sendcommand_(cmd, addr, num_args); addr += num_args; if (ms) { - ms = pgm_read_byte(addr++); // Read post-command delay time (ms) + ms = progmem_read_byte(addr++); // Read post-command delay time (ms) if (ms == 255) ms = 500; // If 255, delay for 500 ms delay(ms); @@ -410,7 +411,7 @@ void HOT ST7735::senddata_(const uint8_t *data_bytes, uint8_t num_data_bytes) { this->cs_->digital_write(false); this->enable(); for (uint8_t i = 0; i < num_data_bytes; i++) { - this->write_byte(pgm_read_byte(data_bytes++)); // write byte - SPI library + this->write_byte(progmem_read_byte(data_bytes++)); // write byte - SPI library } this->cs_->digital_write(true); this->disable(); diff --git a/esphome/components/status/status_binary_sensor.cpp b/esphome/components/status/status_binary_sensor.cpp index 152e3aff9d..1795a9c41b 100644 --- a/esphome/components/status/status_binary_sensor.cpp +++ b/esphome/components/status/status_binary_sensor.cpp @@ -1,6 +1,6 @@ #include "status_binary_sensor.h" #include "esphome/core/log.h" -#include "esphome/core/util.h" +#include "esphome/components/network/util.h" #include "esphome/core/defines.h" #ifdef USE_MQTT @@ -16,7 +16,7 @@ namespace status { static const char *const TAG = "status"; void StatusBinarySensor::loop() { - bool status = network_is_connected(); + bool status = network::is_connected(); #ifdef USE_MQTT if (mqtt::global_mqtt_client != nullptr) { status = status && mqtt::global_mqtt_client->is_connected(); diff --git a/esphome/components/status_led/light/status_led_light.h b/esphome/components/status_led/light/status_led_light.h index 8a7f4b4da8..e90d381e3c 100644 --- a/esphome/components/status_led/light/status_led_light.h +++ b/esphome/components/status_led/light/status_led_light.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/light/light_output.h" namespace esphome { diff --git a/esphome/components/status_led/status_led.h b/esphome/components/status_led/status_led.h index 79f9f524a3..490557f3e7 100644 --- a/esphome/components/status_led/status_led.h +++ b/esphome/components/status_led/status_led.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" namespace esphome { namespace status_led { diff --git a/esphome/components/stepper/stepper.cpp b/esphome/components/stepper/stepper.cpp index d7f6cc6dda..7926024204 100644 --- a/esphome/components/stepper/stepper.cpp +++ b/esphome/components/stepper/stepper.cpp @@ -1,5 +1,6 @@ #include "stepper.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace stepper { diff --git a/esphome/components/sts3x/sts3x.cpp b/esphome/components/sts3x/sts3x.cpp index 77383c6daa..b1ecbc98f8 100644 --- a/esphome/components/sts3x/sts3x.cpp +++ b/esphome/components/sts3x/sts3x.cpp @@ -99,7 +99,7 @@ bool STS3XComponent::read_data_(uint16_t *data, uint8_t len) { const uint8_t num_bytes = len * 3; std::vector buf(num_bytes); - if (!this->parent_->raw_receive(this->address_, buf.data(), num_bytes)) { + if (this->read(buf.data(), num_bytes) != i2c::ERROR_OK) { return false; } diff --git a/esphome/components/sun/sun.h b/esphome/components/sun/sun.h index 6a8364a5f0..0a2e6bcf97 100644 --- a/esphome/components/sun/sun.h +++ b/esphome/components/sun/sun.h @@ -80,7 +80,7 @@ class SunTrigger : public Trigger<>, public PollingComponent, public Parentedparent_->elevation(); - if (isnan(current)) + if (std::isnan(current)) return; bool crossed; @@ -90,7 +90,7 @@ class SunTrigger : public Trigger<>, public PollingComponent, public Parentedlast_elevation_ >= this->elevation_ && this->elevation_ > current; } - if (crossed && !isnan(this->last_elevation_)) { + if (crossed && !std::isnan(this->last_elevation_)) { this->trigger(); } this->last_elevation_ = current; diff --git a/esphome/components/switch/switch.cpp b/esphome/components/switch/switch.cpp index c96f9a40d0..0e12f5af0f 100644 --- a/esphome/components/switch/switch.cpp +++ b/esphome/components/switch/switch.cpp @@ -30,7 +30,7 @@ void Switch::toggle() { this->write_state(this->inverted_ == this->state); } optional Switch::get_initial_state() { - this->rtc_ = global_preferences.make_preference(this->get_object_id_hash()); + this->rtc_ = global_preferences->make_preference(this->get_object_id_hash()); bool initial_state; if (!this->rtc_.load(&initial_state)) return {}; diff --git a/esphome/components/sx1509/__init__.py b/esphome/components/sx1509/__init__.py index 8e1239924a..f1b7d5f424 100644 --- a/esphome/components/sx1509/__init__.py +++ b/esphome/components/sx1509/__init__.py @@ -2,7 +2,15 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import i2c -from esphome.const import CONF_ID, CONF_NUMBER, CONF_MODE, CONF_INVERTED +from esphome.const import ( + CONF_ID, + CONF_INPUT, + CONF_NUMBER, + CONF_MODE, + CONF_INVERTED, + CONF_OUTPUT, + CONF_PULLUP, +) CONF_KEYPAD = "keypad" CONF_KEY_ROWS = "key_rows" @@ -10,17 +18,12 @@ CONF_KEY_COLUMNS = "key_columns" CONF_SLEEP_TIME = "sleep_time" CONF_SCAN_TIME = "scan_time" CONF_DEBOUNCE_TIME = "debounce_time" +CONF_SX1509_ID = "sx1509_id" DEPENDENCIES = ["i2c"] MULTI_CONF = True sx1509_ns = cg.esphome_ns.namespace("sx1509") -SX1509GPIOMode = sx1509_ns.enum("SX1509GPIOMode") -SX1509_GPIO_MODES = { - "INPUT": SX1509GPIOMode.SX1509_INPUT, - "INPUT_PULLUP": SX1509GPIOMode.SX1509_INPUT_PULLUP, - "OUTPUT": SX1509GPIOMode.SX1509_OUTPUT, -} SX1509Component = sx1509_ns.class_("SX1509Component", cg.Component, i2c.I2CDevice) SX1509GPIOPin = sx1509_ns.class_("SX1509GPIOPin", cg.GPIOPin) @@ -64,34 +67,43 @@ async def to_code(config): cg.add(var.set_debounce_time(keypad[CONF_DEBOUNCE_TIME])) -CONF_SX1509 = "sx1509" -CONF_SX1509_ID = "sx1509_id" +def validate_mode(value): + if not (value[CONF_INPUT] or value[CONF_OUTPUT]): + raise cv.Invalid("Mode must be either input or output") + if value[CONF_INPUT] and value[CONF_OUTPUT]: + raise cv.Invalid("Mode must be either input or output") + if value[CONF_PULLUP] and not value[CONF_INPUT]: + raise cv.Invalid("Pullup only available with input") + return value -SX1509_OUTPUT_PIN_SCHEMA = cv.Schema( + +CONF_SX1509 = "sx1509" +SX1509_PIN_SCHEMA = cv.All( { - cv.Required(CONF_SX1509): cv.use_id(SX1509Component), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum( - SX1509_GPIO_MODES, upper=True + cv.GenerateID(): cv.declare_id(SX1509Component), + cv.Required(CONF_SX1509): cv.use_id(SX1509GPIOPin), + cv.Required(CONF_NUMBER): cv.int_range(min=0, max=15), + cv.Optional(CONF_MODE, default={}): cv.All( + { + cv.Optional(CONF_INPUT, default=False): cv.boolean, + cv.Optional(CONF_PULLUP, default=False): cv.boolean, + cv.Optional(CONF_OUTPUT, default=False): cv.boolean, + }, + validate_mode, ), cv.Optional(CONF_INVERTED, default=False): cv.boolean, } ) -SX1509_INPUT_PIN_SCHEMA = cv.Schema( - { - cv.Required(CONF_SX1509): cv.use_id(SX1509Component), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="INPUT"): cv.enum(SX1509_GPIO_MODES, upper=True), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - } -) -@pins.PIN_SCHEMA_REGISTRY.register( - CONF_SX1509, (SX1509_OUTPUT_PIN_SCHEMA, SX1509_INPUT_PIN_SCHEMA) -) +@pins.PIN_SCHEMA_REGISTRY.register(CONF_SX1509, SX1509_PIN_SCHEMA) async def sx1509_pin_to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) parent = await cg.get_variable(config[CONF_SX1509]) - return SX1509GPIOPin.new( - parent, config[CONF_NUMBER], config[CONF_MODE], config[CONF_INVERTED] - ) + cg.add(var.set_parent(parent)) + + num = config[CONF_NUMBER] + cg.add(var.set_pin(num)) + cg.add(var.set_inverted(config[CONF_INVERTED])) + cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE]))) + return var diff --git a/esphome/components/sx1509/output/sx1509_float_output.cpp b/esphome/components/sx1509/output/sx1509_float_output.cpp index c68f8f9ded..e9c401eeed 100644 --- a/esphome/components/sx1509/output/sx1509_float_output.cpp +++ b/esphome/components/sx1509/output/sx1509_float_output.cpp @@ -16,7 +16,8 @@ void SX1509FloatOutputChannel::write_state(float state) { void SX1509FloatOutputChannel::setup() { ESP_LOGD(TAG, "setup pin %d", this->pin_); - this->parent_->pin_mode(this->pin_, SX1509_ANALOG_OUTPUT); + this->parent_->pin_mode(this->pin_, gpio::FLAG_OUTPUT); + this->parent_->setup_led_driver(this->pin_); this->turn_off(); } diff --git a/esphome/components/sx1509/sx1509.cpp b/esphome/components/sx1509/sx1509.cpp index 14d9ad7a61..9095dfeffa 100644 --- a/esphome/components/sx1509/sx1509.cpp +++ b/esphome/components/sx1509/sx1509.cpp @@ -18,13 +18,15 @@ void SX1509Component::setup() { this->write_byte(REG_RESET, 0x34); uint16_t data; - this->read_byte_16(REG_INTERRUPT_MASK_A, &data); - if (data == 0xFF00) { - clock_(INTERNAL_CLOCK_2MHZ); - } else { + if (!this->read_byte_16(REG_INTERRUPT_MASK_A, &data)) { this->mark_failed(); return; } + if (data != 0xFF00) { + this->mark_failed(); + return; + } + clock_(INTERNAL_CLOCK_2MHZ); delayMicroseconds(500); if (this->has_keypad_) this->setup_keypad_(); @@ -49,7 +51,8 @@ void SX1509Component::loop() { bool SX1509Component::digital_read(uint8_t pin) { if (this->ddr_mask_ & (1 << pin)) { uint16_t temp_reg_data; - this->read_byte_16(REG_DATA_B, &temp_reg_data); + if (!this->read_byte_16(REG_DATA_B, &temp_reg_data)) + return false; if (temp_reg_data & (1 << pin)) return true; } @@ -68,9 +71,9 @@ void SX1509Component::digital_write(uint8_t pin, bool bit_value) { this->write_byte_16(REG_DATA_B, temp_reg_data); } else { // Otherwise the pin is an input, pull-up/down - uint16_t temp_pullup; + uint16_t temp_pullup = 0; this->read_byte_16(REG_PULL_UP_B, &temp_pullup); - uint16_t temp_pull_down; + uint16_t temp_pull_down = 0; this->read_byte_16(REG_PULL_DOWN_B, &temp_pull_down); if (bit_value) { @@ -89,25 +92,21 @@ void SX1509Component::digital_write(uint8_t pin, bool bit_value) { } } -void SX1509Component::pin_mode(uint8_t pin, uint8_t mode) { +void SX1509Component::pin_mode(uint8_t pin, gpio::Flags flags) { this->read_byte_16(REG_DIR_B, &this->ddr_mask_); - if ((mode == SX1509_OUTPUT) || (mode == SX1509_ANALOG_OUTPUT)) + if (flags == gpio::FLAG_OUTPUT) this->ddr_mask_ &= ~(1 << pin); else this->ddr_mask_ |= (1 << pin); this->write_byte_16(REG_DIR_B, this->ddr_mask_); - if (mode == INPUT_PULLUP) - digital_write(pin, HIGH); - - if (mode == SX1509_ANALOG_OUTPUT) { - setup_led_driver_(pin); - } + if (flags & gpio::FLAG_PULLUP) + digital_write(pin, true); } -void SX1509Component::setup_led_driver_(uint8_t pin) { - uint16_t temp_word; - uint8_t temp_byte; +void SX1509Component::setup_led_driver(uint8_t pin) { + uint16_t temp_word = 0; + uint8_t temp_byte = 0; this->read_byte_16(REG_INPUT_DISABLE_B, &temp_word); temp_word |= (1 << pin); @@ -140,18 +139,18 @@ void SX1509Component::setup_led_driver_(uint8_t pin) { this->write_byte_16(REG_DATA_B, temp_word); } -void SX1509Component::clock_(byte osc_source, byte osc_pin_function, byte osc_freq_out, byte osc_divider) { +void SX1509Component::clock_(uint8_t osc_source, uint8_t osc_pin_function, uint8_t osc_freq_out, uint8_t osc_divider) { osc_source = (osc_source & 0b11) << 5; // 2-bit value, bits 6:5 osc_pin_function = (osc_pin_function & 1) << 4; // 1-bit value bit 4 osc_freq_out = (osc_freq_out & 0b1111); // 4-bit value, bits 3:0 uint8_t reg_clock = osc_source | osc_pin_function | osc_freq_out; this->write_byte(REG_CLOCK, reg_clock); - osc_divider = constrain(osc_divider, 1, 7); + osc_divider = clamp(osc_divider, 1, 7u); this->clk_x_ = 2000000; osc_divider = (osc_divider & 0b111) << 4; // 3-bit value, bits 6:4 - uint8_t reg_misc; + uint8_t reg_misc = 0; this->read_byte(REG_MISC, ®_misc); reg_misc &= ~(0b111 << 4); reg_misc |= osc_divider; @@ -159,7 +158,7 @@ void SX1509Component::clock_(byte osc_source, byte osc_pin_function, byte osc_fr } void SX1509Component::setup_keypad_() { - uint8_t temp_byte; + uint8_t temp_byte = 0; // setup row/col pins for INPUT OUTPUT this->read_byte_16(REG_DIR_B, &this->ddr_mask_); @@ -199,14 +198,14 @@ void SX1509Component::setup_keypad_() { } uint16_t SX1509Component::read_key_data() { - uint16_t key_data; + uint16_t key_data = 0; this->read_byte_16(REG_KEY_DATA_1, &key_data); return (0xFFFF ^ key_data); } void SX1509Component::set_debounce_config_(uint8_t config_value) { // First make sure clock is configured - uint8_t temp_byte; + uint8_t temp_byte = 0; this->read_byte(REG_MISC, &temp_byte); temp_byte |= (1 << 4); // Just default to no divider if not set this->write_byte(REG_MISC, temp_byte); @@ -227,13 +226,13 @@ void SX1509Component::set_debounce_time_(uint8_t time) { break; } } - config_value = constrain(config_value, 0, 7); + config_value = clamp(config_value, 0, 7); set_debounce_config_(config_value); } void SX1509Component::set_debounce_enable_(uint8_t pin) { - uint16_t debounce_enable; + uint16_t debounce_enable = 0; this->read_byte_16(REG_DEBOUNCE_ENABLE_B, &debounce_enable); debounce_enable |= (1 << pin); this->write_byte_16(REG_DEBOUNCE_ENABLE_B, debounce_enable); diff --git a/esphome/components/sx1509/sx1509.h b/esphome/components/sx1509/sx1509.h index 1390059675..5f0697b534 100644 --- a/esphome/components/sx1509/sx1509.h +++ b/esphome/components/sx1509/sx1509.h @@ -2,7 +2,7 @@ #include "esphome/components/i2c/i2c.h" #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "sx1509_gpio_pin.h" #include "sx1509_registers.h" @@ -15,16 +15,6 @@ const uint8_t EXTERNAL_CLOCK = 1; const uint8_t SOFTWARE_RESET = 0; const uint8_t HARDWARE_RESET = 1; -const uint8_t ANALOG_OUTPUT = 0x03; // To set a pin mode for PWM output - -// PinModes for SX1509 pins -enum SX1509GPIOMode : uint8_t { - SX1509_INPUT = INPUT, // 0x00 - SX1509_INPUT_PULLUP = INPUT_PULLUP, // 0x02 - SX1509_ANALOG_OUTPUT = ANALOG_OUTPUT, // 0x03 - SX1509_OUTPUT = OUTPUT, // 0x01 -}; - const uint8_t REG_I_ON[16] = {REG_I_ON_0, REG_I_ON_1, REG_I_ON_2, REG_I_ON_3, REG_I_ON_4, REG_I_ON_5, REG_I_ON_6, REG_I_ON_7, REG_I_ON_8, REG_I_ON_9, REG_I_ON_10, REG_I_ON_11, REG_I_ON_12, REG_I_ON_13, REG_I_ON_14, REG_I_ON_15}; @@ -47,9 +37,9 @@ class SX1509Component : public Component, public i2c::I2CDevice { bool digital_read(uint8_t pin); uint16_t read_key_data(); void set_pin_value(uint8_t pin, uint8_t i_on) { this->write_byte(REG_I_ON[pin], i_on); }; - void pin_mode(uint8_t pin, uint8_t mode); + void pin_mode(uint8_t pin, gpio::Flags flags); void digital_write(uint8_t pin, bool bit_value); - u_long get_clock() { return this->clk_x_; }; + uint32_t get_clock() { return this->clk_x_; }; void set_rows_cols(uint8_t rows, uint8_t cols) { this->rows_ = rows; this->cols_ = cols; @@ -60,10 +50,11 @@ class SX1509Component : public Component, public i2c::I2CDevice { void set_debounce_time(uint8_t debounce_time = 1) { this->debounce_time_ = debounce_time; }; void register_keypad_binary_sensor(SX1509Processor *binary_sensor) { this->keypad_binary_sensors_.push_back(binary_sensor); - }; + } + void setup_led_driver(uint8_t pin); protected: - u_long clk_x_ = 2000000; + uint32_t clk_x_ = 2000000; uint8_t frequency_ = 0; uint16_t ddr_mask_ = 0x00; uint16_t input_mask_ = 0x00; @@ -82,7 +73,6 @@ class SX1509Component : public Component, public i2c::I2CDevice { void set_debounce_pin_(uint8_t pin); void set_debounce_enable_(uint8_t pin); void set_debounce_keypad_(uint8_t time, uint8_t num_rows, uint8_t num_cols); - void setup_led_driver_(uint8_t pin); void clock_(uint8_t osc_source = 2, uint8_t osc_pin_function = 1, uint8_t osc_freq_out = 0, uint8_t osc_divider = 0); }; diff --git a/esphome/components/sx1509/sx1509_gpio_pin.cpp b/esphome/components/sx1509/sx1509_gpio_pin.cpp index ac55ac1ca5..2c6e0b0c32 100644 --- a/esphome/components/sx1509/sx1509_gpio_pin.cpp +++ b/esphome/components/sx1509/sx1509_gpio_pin.cpp @@ -7,14 +7,15 @@ namespace sx1509 { static const char *const TAG = "sx1509_gpio_pin"; -void SX1509GPIOPin::setup() { - ESP_LOGD(TAG, "setup pin %d", this->pin_); - this->parent_->pin_mode(this->pin_, this->mode_); -} - -void SX1509GPIOPin::pin_mode(uint8_t mode) { this->parent_->pin_mode(this->pin_, mode); } +void SX1509GPIOPin::setup() { pin_mode(flags_); } +void SX1509GPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); } bool SX1509GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; } void SX1509GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); } +std::string SX1509GPIOPin::dump_summary() const { + char buffer[32]; + snprintf(buffer, sizeof(buffer), "%u via MCP23016", pin_); + return buffer; +} } // namespace sx1509 } // namespace esphome diff --git a/esphome/components/sx1509/sx1509_gpio_pin.h b/esphome/components/sx1509/sx1509_gpio_pin.h index 39f841a2a4..4d8aa5ec83 100644 --- a/esphome/components/sx1509/sx1509_gpio_pin.h +++ b/esphome/components/sx1509/sx1509_gpio_pin.h @@ -9,15 +9,22 @@ class SX1509Component; class SX1509GPIOPin : public GPIOPin { public: - SX1509GPIOPin(SX1509Component *parent, uint8_t pin, uint8_t mode, bool inverted = false) - : GPIOPin(pin, mode, inverted), parent_(parent){}; void setup() override; - void pin_mode(uint8_t mode) override; + void pin_mode(gpio::Flags flags) override; bool digital_read() override; void digital_write(bool value) override; + std::string dump_summary() const override; + + void set_parent(SX1509Component *parent) { parent_ = parent; } + void set_pin(uint8_t pin) { pin_ = pin; } + void set_inverted(bool inverted) { inverted_ = inverted; } + void set_flags(gpio::Flags flags) { flags_ = flags; } protected: SX1509Component *parent_; + uint8_t pin_; + bool inverted_; + gpio::Flags flags_; }; } // namespace sx1509 diff --git a/esphome/components/tca9548a/__init__.py b/esphome/components/tca9548a/__init__.py index 62cbace56a..0f222b8fc7 100644 --- a/esphome/components/tca9548a/__init__.py +++ b/esphome/components/tca9548a/__init__.py @@ -1,30 +1,43 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c -from esphome.const import CONF_ID, CONF_SCAN +from esphome.const import CONF_CHANNEL, CONF_CHANNELS, CONF_ID, CONF_SCAN CODEOWNERS = ["@andreashergert1984"] DEPENDENCIES = ["i2c"] tca9548a_ns = cg.esphome_ns.namespace("tca9548a") -TCA9548AComponent = tca9548a_ns.class_( - "TCA9548AComponent", cg.PollingComponent, i2c.I2CMultiplexer -) +TCA9548AComponent = tca9548a_ns.class_("TCA9548AComponent", cg.Component, i2c.I2CDevice) +TCA9548AChannel = tca9548a_ns.class_("TCA9548AChannel", i2c.I2CBus) MULTI_CONF = True -CONFIG_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.declare_id(TCA9548AComponent), - cv.Optional(CONF_SCAN, default=True): cv.boolean, - } -).extend(i2c.i2c_device_schema(0x70)) +CONF_BUS_ID = "bus_id" +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(TCA9548AComponent), + cv.Optional(CONF_SCAN): cv.invalid("This option has been removed"), + cv.Optional(CONF_CHANNELS, default=[]): cv.ensure_list( + { + cv.Required(CONF_BUS_ID): cv.declare_id(TCA9548AChannel), + cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=7), + } + ), + } + ) + .extend(i2c.i2c_device_schema(0x70)) + .extend(cv.COMPONENT_SCHEMA) +) async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) - cg.add_define("USE_I2C_MULTIPLEXER") await cg.register_component(var, config) await i2c.register_i2c_device(var, config) - cg.add(var.set_scan(config[CONF_SCAN])) + + for conf in config[CONF_CHANNELS]: + chan = cg.new_Pvariable(conf[CONF_BUS_ID]) + cg.add(chan.set_parent(var)) + cg.add(chan.set_channel(conf[CONF_CHANNEL])) diff --git a/esphome/components/tca9548a/tca9548a.cpp b/esphome/components/tca9548a/tca9548a.cpp index 472b8b6673..5117ad8969 100644 --- a/esphome/components/tca9548a/tca9548a.cpp +++ b/esphome/components/tca9548a/tca9548a.cpp @@ -6,35 +6,46 @@ namespace tca9548a { static const char *const TAG = "tca9548a"; +i2c::ErrorCode TCA9548AChannel::readv(uint8_t address, i2c::ReadBuffer *buffers, size_t cnt) { + auto err = parent_->switch_to_channel(channel_); + if (err != i2c::ERROR_OK) + return err; + return parent_->bus_->readv(address, buffers, cnt); +} +i2c::ErrorCode TCA9548AChannel::writev(uint8_t address, i2c::WriteBuffer *buffers, size_t cnt) { + auto err = parent_->switch_to_channel(channel_); + if (err != i2c::ERROR_OK) + return err; + return parent_->bus_->writev(address, buffers, cnt); +} + void TCA9548AComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up TCA9548A..."); uint8_t status = 0; - if (!this->read_byte(0x00, &status)) { + if (!this->read_register(0x00, &status, 1)) { ESP_LOGI(TAG, "TCA9548A failed"); + this->mark_failed(); return; } - // out of range to make sure on first set_channel a new one will be set - this->current_channelno_ = 8; - ESP_LOGCONFIG(TAG, "Channels currently open: %d", status); + ESP_LOGD(TAG, "Channels currently open: %d", status); } void TCA9548AComponent::dump_config() { ESP_LOGCONFIG(TAG, "TCA9548A:"); LOG_I2C_DEVICE(this); - if (this->scan_) { - for (uint8_t i = 0; i < 8; i++) { - ESP_LOGCONFIG(TAG, "Activating channel: %d", i); - this->set_channel(i); - this->parent_->dump_config(); - } - } } -void TCA9548AComponent::set_channel(uint8_t channelno) { - if (this->current_channelno_ != channelno) { - this->current_channelno_ = channelno; - uint8_t channelbyte = 1 << channelno; - this->write_byte(0x70, channelbyte); +i2c::ErrorCode TCA9548AComponent::switch_to_channel(uint8_t channel) { + if (this->is_failed()) + return i2c::ERROR_NOT_INITIALIZED; + if (current_channel_ == channel) + return i2c::ERROR_OK; + + uint8_t channel_val = 1 << channel; + auto err = this->write_register(0x70, &channel_val, 1); + if (err == i2c::ERROR_OK) { + current_channel_ = channel; } + return err; } } // namespace tca9548a diff --git a/esphome/components/tca9548a/tca9548a.h b/esphome/components/tca9548a/tca9548a.h index 50b1eb8b56..314346d317 100644 --- a/esphome/components/tca9548a/tca9548a.h +++ b/esphome/components/tca9548a/tca9548a.h @@ -6,17 +6,31 @@ namespace esphome { namespace tca9548a { -class TCA9548AComponent : public Component, public i2c::I2CMultiplexer { +class TCA9548AComponent; +class TCA9548AChannel : public i2c::I2CBus { + public: + void set_channel(uint8_t channel) { channel_ = channel; } + void set_parent(TCA9548AComponent *parent) { parent_ = parent; } + + i2c::ErrorCode readv(uint8_t address, i2c::ReadBuffer *buffers, size_t cnt) override; + i2c::ErrorCode writev(uint8_t address, i2c::WriteBuffer *buffers, size_t cnt) override; + + protected: + uint8_t channel_; + TCA9548AComponent *parent_; +}; + +class TCA9548AComponent : public Component, public i2c::I2CDevice { public: - void set_scan(bool scan) { scan_ = scan; } void setup() override; void dump_config() override; void update(); - void set_channel(uint8_t channelno) override; + + i2c::ErrorCode switch_to_channel(uint8_t channel); protected: - bool scan_; - uint8_t current_channelno_; + friend class TCA9548AChannel; + uint8_t current_channel_ = 255; }; } // namespace tca9548a } // namespace esphome diff --git a/esphome/components/tcs34725/tcs34725.cpp b/esphome/components/tcs34725/tcs34725.cpp index 52548262c1..564d3dcda7 100644 --- a/esphome/components/tcs34725/tcs34725.cpp +++ b/esphome/components/tcs34725/tcs34725.cpp @@ -1,5 +1,6 @@ #include "tcs34725.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace tcs34725 { diff --git a/esphome/components/template/number/template_number.cpp b/esphome/components/template/number/template_number.cpp index eb9b17b976..a5b015c44d 100644 --- a/esphome/components/template/number/template_number.cpp +++ b/esphome/components/template/number/template_number.cpp @@ -14,9 +14,9 @@ void TemplateNumber::setup() { if (!this->restore_value_) { value = this->initial_value_; } else { - this->pref_ = global_preferences.make_preference(this->get_object_id_hash()); + this->pref_ = global_preferences->make_preference(this->get_object_id_hash()); if (!this->pref_.load(&value)) { - if (!isnan(this->initial_value_)) + if (!std::isnan(this->initial_value_)) value = this->initial_value_; else value = this->traits.get_min_value(); diff --git a/esphome/components/template/select/template_select.cpp b/esphome/components/template/select/template_select.cpp index 8695880856..219c341ec9 100644 --- a/esphome/components/template/select/template_select.cpp +++ b/esphome/components/template/select/template_select.cpp @@ -17,7 +17,7 @@ void TemplateSelect::setup() { ESP_LOGD(TAG, "State from initial: %s", value.c_str()); } else { size_t index; - this->pref_ = global_preferences.make_preference(this->get_object_id_hash()); + this->pref_ = global_preferences->make_preference(this->get_object_id_hash()); if (!this->pref_.load(&index)) { value = this->initial_option_; ESP_LOGD(TAG, "State from initial (could not load): %s", value.c_str()); diff --git a/esphome/components/template/sensor/template_sensor.cpp b/esphome/components/template/sensor/template_sensor.cpp index 63cbd70db0..b28eb3fed2 100644 --- a/esphome/components/template/sensor/template_sensor.cpp +++ b/esphome/components/template/sensor/template_sensor.cpp @@ -1,5 +1,6 @@ #include "template_sensor.h" #include "esphome/core/log.h" +#include namespace esphome { namespace template_ { @@ -12,7 +13,7 @@ void TemplateSensor::update() { if (val.has_value()) { this->publish_state(*val); } - } else if (!isnan(this->get_raw_state())) { + } else if (!std::isnan(this->get_raw_state())) { this->publish_state(this->get_raw_state()); } } diff --git a/esphome/components/thermostat/thermostat_climate.cpp b/esphome/components/thermostat/thermostat_climate.cpp index a75713cbb9..6193185321 100644 --- a/esphome/components/thermostat/thermostat_climate.cpp +++ b/esphome/components/thermostat/thermostat_climate.cpp @@ -88,17 +88,17 @@ climate::ClimateFanMode ThermostatClimate::locked_fan_mode() { return this->prev bool ThermostatClimate::hysteresis_valid() { if ((this->supports_cool_ || (this->supports_fan_only_ && this->supports_fan_only_cooling_)) && - (isnan(this->cooling_deadband_) || isnan(this->cooling_overrun_))) + (std::isnan(this->cooling_deadband_) || std::isnan(this->cooling_overrun_))) return false; - if (this->supports_heat_ && (isnan(this->heating_deadband_) || isnan(this->heating_overrun_))) + if (this->supports_heat_ && (std::isnan(this->heating_deadband_) || std::isnan(this->heating_overrun_))) return false; return true; } void ThermostatClimate::validate_target_temperature() { - if (isnan(this->target_temperature)) { + if (std::isnan(this->target_temperature)) { this->target_temperature = ((this->get_traits().get_visual_max_temperature() - this->get_traits().get_visual_min_temperature()) / 2) + this->get_traits().get_visual_min_temperature(); @@ -121,7 +121,7 @@ void ThermostatClimate::validate_target_temperatures() { } void ThermostatClimate::validate_target_temperature_low() { - if (isnan(this->target_temperature_low)) { + if (std::isnan(this->target_temperature_low)) { this->target_temperature_low = this->get_traits().get_visual_min_temperature(); } else { // target_temperature_low must not be lower than the visual minimum @@ -139,7 +139,7 @@ void ThermostatClimate::validate_target_temperature_low() { } void ThermostatClimate::validate_target_temperature_high() { - if (isnan(this->target_temperature_high)) { + if (std::isnan(this->target_temperature_high)) { this->target_temperature_high = this->get_traits().get_visual_max_temperature(); } else { // target_temperature_high must not be lower than the visual maximum @@ -245,7 +245,7 @@ climate::ClimateTraits ThermostatClimate::traits() { climate::ClimateAction ThermostatClimate::compute_action_(const bool ignore_timers) { auto target_action = climate::CLIMATE_ACTION_IDLE; // if any hysteresis values or current_temperature is not valid, we go to OFF; - if (isnan(this->current_temperature) || !this->hysteresis_valid()) { + if (std::isnan(this->current_temperature) || !this->hysteresis_valid()) { return climate::CLIMATE_ACTION_OFF; } // do not change the action if an "ON" timer is running @@ -307,7 +307,7 @@ climate::ClimateAction ThermostatClimate::compute_action_(const bool ignore_time climate::ClimateAction ThermostatClimate::compute_supplemental_action_() { auto target_action = climate::CLIMATE_ACTION_IDLE; // if any hysteresis values or current_temperature is not valid, we go to OFF; - if (isnan(this->current_temperature) || !this->hysteresis_valid()) { + if (std::isnan(this->current_temperature) || !this->hysteresis_valid()) { return climate::CLIMATE_ACTION_OFF; } diff --git a/esphome/components/time/automation.cpp b/esphome/components/time/automation.cpp index f133ab021c..7e16d7141f 100644 --- a/esphome/components/time/automation.cpp +++ b/esphome/components/time/automation.cpp @@ -1,5 +1,6 @@ #include "automation.h" #include "esphome/core/log.h" +#include namespace esphome { namespace time { diff --git a/esphome/components/time/real_time_clock.cpp b/esphome/components/time/real_time_clock.cpp index 27a2d84da6..064e6f899c 100644 --- a/esphome/components/time/real_time_clock.cpp +++ b/esphome/components/time/real_time_clock.cpp @@ -1,7 +1,7 @@ #include "real_time_clock.h" #include "esphome/core/log.h" #include "lwip/opt.h" -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #include "sys/time.h" #endif #include diff --git a/esphome/components/time_based/time_based_cover.cpp b/esphome/components/time_based/time_based_cover.cpp index 60a33aa82a..3fa07167ca 100644 --- a/esphome/components/time_based/time_based_cover.cpp +++ b/esphome/components/time_based/time_based_cover.cpp @@ -1,5 +1,6 @@ #include "time_based_cover.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace time_based { diff --git a/esphome/components/tlc59208f/tlc59208f_output.cpp b/esphome/components/tlc59208f/tlc59208f_output.cpp index e416a5c62b..59fb9f98ed 100644 --- a/esphome/components/tlc59208f/tlc59208f_output.cpp +++ b/esphome/components/tlc59208f/tlc59208f_output.cpp @@ -75,7 +75,7 @@ void TLC59208FOutput::setup() { ESP_LOGV(TAG, " Resetting all devices on the bus..."); // Reset all devices on the bus - if (!this->parent_->write_byte(TLC59208F_SWRST_ADDR >> 1, TLC59208F_SWRST_SEQ[0], TLC59208F_SWRST_SEQ[1])) { + if (this->bus_->write(TLC59208F_SWRST_ADDR >> 1, TLC59208F_SWRST_SEQ, 2) != i2c::ERROR_OK) { ESP_LOGE(TAG, "RESET failed"); this->mark_failed(); return; diff --git a/esphome/components/tlc59208f/tlc59208f_output.h b/esphome/components/tlc59208f/tlc59208f_output.h index 79fccc97f3..68ca8061d7 100644 --- a/esphome/components/tlc59208f/tlc59208f_output.h +++ b/esphome/components/tlc59208f/tlc59208f_output.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/helpers.h" #include "esphome/components/output/float_output.h" #include "esphome/components/i2c/i2c.h" diff --git a/esphome/components/tlc5947/tlc5947.h b/esphome/components/tlc5947/tlc5947.h index b608b861e7..0eb7f10604 100644 --- a/esphome/components/tlc5947/tlc5947.h +++ b/esphome/components/tlc5947/tlc5947.h @@ -3,8 +3,9 @@ // https://www.ti.com/lit/ds/symlink/tlc5947.pdf #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/output/float_output.h" +#include namespace esphome { namespace tlc5947 { diff --git a/esphome/components/tm1637/tm1637.cpp b/esphome/components/tm1637/tm1637.cpp index 1f1c0fd301..488f3b6727 100644 --- a/esphome/components/tm1637/tm1637.cpp +++ b/esphome/components/tm1637/tm1637.cpp @@ -1,6 +1,7 @@ #include "tm1637.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/hal.h" namespace esphome { namespace tm1637 { @@ -146,16 +147,16 @@ void TM1637Display::update() { float TM1637Display::get_setup_priority() const { return setup_priority::PROCESSOR; } void TM1637Display::bit_delay_() { delayMicroseconds(100); } void TM1637Display::start_() { - this->dio_pin_->pin_mode(OUTPUT); + this->dio_pin_->pin_mode(gpio::FLAG_OUTPUT); this->bit_delay_(); } void TM1637Display::stop_() { - this->dio_pin_->pin_mode(OUTPUT); + this->dio_pin_->pin_mode(gpio::FLAG_OUTPUT); bit_delay_(); - this->clk_pin_->pin_mode(INPUT); + this->clk_pin_->pin_mode(gpio::FLAG_INPUT); bit_delay_(); - this->dio_pin_->pin_mode(INPUT); + this->dio_pin_->pin_mode(gpio::FLAG_INPUT); bit_delay_(); } @@ -189,39 +190,39 @@ bool TM1637Display::send_byte_(uint8_t b) { // 8 Data Bits for (uint8_t i = 0; i < 8; i++) { // CLK low - this->clk_pin_->pin_mode(OUTPUT); + this->clk_pin_->pin_mode(gpio::FLAG_OUTPUT); this->bit_delay_(); // Set data bit if (data & 0x01) - this->dio_pin_->pin_mode(INPUT); + this->dio_pin_->pin_mode(gpio::FLAG_INPUT); else - this->dio_pin_->pin_mode(OUTPUT); + this->dio_pin_->pin_mode(gpio::FLAG_OUTPUT); this->bit_delay_(); // CLK high - this->clk_pin_->pin_mode(INPUT); + this->clk_pin_->pin_mode(gpio::FLAG_INPUT); this->bit_delay_(); data = data >> 1; } // Wait for acknowledge // CLK to zero - this->clk_pin_->pin_mode(OUTPUT); - this->dio_pin_->pin_mode(INPUT); + this->clk_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->dio_pin_->pin_mode(gpio::FLAG_INPUT); this->bit_delay_(); // CLK to high - this->clk_pin_->pin_mode(INPUT); + this->clk_pin_->pin_mode(gpio::FLAG_INPUT); this->bit_delay_(); uint8_t ack = this->dio_pin_->digital_read(); if (ack == 0) { - this->dio_pin_->pin_mode(OUTPUT); + this->dio_pin_->pin_mode(gpio::FLAG_OUTPUT); } this->bit_delay_(); - this->clk_pin_->pin_mode(OUTPUT); + this->clk_pin_->pin_mode(gpio::FLAG_OUTPUT); this->bit_delay_(); return ack; @@ -233,7 +234,7 @@ uint8_t TM1637Display::print(uint8_t start_pos, const char *str) { for (; *str != '\0'; str++) { uint8_t data = TM1637_UNKNOWN_CHAR; if (*str >= ' ' && *str <= '~') - data = pgm_read_byte(&TM1637_ASCII_TO_RAW[*str - ' ']); + data = progmem_read_byte(&TM1637_ASCII_TO_RAW[*str - ' ']); if (data == TM1637_UNKNOWN_CHAR) { ESP_LOGW(TAG, "Encountered character '%c' with no TM1637 representation while translating string!", *str); diff --git a/esphome/components/tm1637/tm1637.h b/esphome/components/tm1637/tm1637.h index 003344eae9..63b30ac13e 100644 --- a/esphome/components/tm1637/tm1637.h +++ b/esphome/components/tm1637/tm1637.h @@ -2,7 +2,7 @@ #include "esphome/core/component.h" #include "esphome/core/defines.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #ifdef USE_TIME #include "esphome/components/time/real_time_clock.h" diff --git a/esphome/components/tm1651/__init__.py b/esphome/components/tm1651/__init__.py index d06c1bedde..9d2b17afdc 100644 --- a/esphome/components/tm1651/__init__.py +++ b/esphome/components/tm1651/__init__.py @@ -27,12 +27,15 @@ TM1651_BRIGHTNESS_OPTIONS = { 3: TM1651Display.TM1651_BRIGHTNESS_HIGH, } -CONFIG_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.declare_id(TM1651Display), - cv.Required(CONF_CLK_PIN): pins.internal_gpio_output_pin_schema, - cv.Required(CONF_DIO_PIN): pins.internal_gpio_output_pin_schema, - } +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(TM1651Display), + cv.Required(CONF_CLK_PIN): pins.internal_gpio_output_pin_schema, + cv.Required(CONF_DIO_PIN): pins.internal_gpio_output_pin_schema, + } + ), + cv.only_with_arduino, ) validate_level_percent = cv.All(cv.int_range(min=0, max=100)) diff --git a/esphome/components/tm1651/tm1651.cpp b/esphome/components/tm1651/tm1651.cpp index 3689ef5894..c6bb1bc025 100644 --- a/esphome/components/tm1651/tm1651.cpp +++ b/esphome/components/tm1651/tm1651.cpp @@ -1,3 +1,5 @@ +#ifdef USE_ARDUINO + #include "tm1651.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" @@ -88,3 +90,5 @@ uint8_t TM1651Display::calculate_brightness_(uint8_t new_brightness) { } // namespace tm1651 } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/tm1651/tm1651.h b/esphome/components/tm1651/tm1651.h index a18519bf41..72849bc8eb 100644 --- a/esphome/components/tm1651/tm1651.h +++ b/esphome/components/tm1651/tm1651.h @@ -1,9 +1,11 @@ #pragma once +#ifdef USE_ARDUINO + #include #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/core/automation.h" #include @@ -13,8 +15,8 @@ namespace tm1651 { class TM1651Display : public Component { public: - void set_clk_pin(GPIOPin *pin) { clk_pin_ = pin; } - void set_dio_pin(GPIOPin *pin) { dio_pin_ = pin; } + void set_clk_pin(InternalGPIOPin *pin) { clk_pin_ = pin; } + void set_dio_pin(InternalGPIOPin *pin) { dio_pin_ = pin; } void setup() override; void dump_config() override; @@ -28,8 +30,8 @@ class TM1651Display : public Component { protected: std::unique_ptr battery_display_; - GPIOPin *clk_pin_; - GPIOPin *dio_pin_; + InternalGPIOPin *clk_pin_; + InternalGPIOPin *dio_pin_; bool is_on_ = true; uint8_t brightness_; @@ -83,3 +85,5 @@ template class TurnOffAction : public Action, public Pare } // namespace tm1651 } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/tmp102/tmp102.cpp b/esphome/components/tmp102/tmp102.cpp index 7b3dcad4aa..f6bb9a05c0 100644 --- a/esphome/components/tmp102/tmp102.cpp +++ b/esphome/components/tmp102/tmp102.cpp @@ -1,5 +1,6 @@ #include "tmp102.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace tmp102 { @@ -28,10 +29,16 @@ void TMP102Component::dump_config() { void TMP102Component::update() { uint16_t raw_temperature; - if (!this->read_byte_16(TMP102_REGISTER_TEMPERATURE, &raw_temperature, 50)) { + if (this->write(&TMP102_REGISTER_TEMPERATURE, 1) != i2c::ERROR_OK) { this->status_set_warning(); return; } + delay(50); // NOLINT + if (this->read(reinterpret_cast(&raw_temperature), 2) != i2c::ERROR_OK) { + this->status_set_warning(); + return; + } + raw_temperature = i2c::i2ctohs(raw_temperature); raw_temperature = raw_temperature >> 4; float temperature = raw_temperature * TMP102_CONVERSION_FACTOR; diff --git a/esphome/components/tof10120/tof10120_sensor.cpp b/esphome/components/tof10120/tof10120_sensor.cpp index cabfdad41d..4ba591f9c4 100644 --- a/esphome/components/tof10120/tof10120_sensor.cpp +++ b/esphome/components/tof10120/tof10120_sensor.cpp @@ -1,5 +1,6 @@ #include "tof10120_sensor.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" // Very basic support for TOF10120 distance sensor @@ -31,7 +32,12 @@ void TOF10120Sensor::update() { } uint8_t data[2]; - if (!this->read_bytes(TOF10120_DISTANCE_REGISTER, data, 2, TOF10120_DEFAULT_DELAY)) { + if (this->write(&TOF10120_DISTANCE_REGISTER, 1) != i2c::ERROR_OK) { + this->status_set_warning(); + return; + } + delay(TOF10120_DEFAULT_DELAY); + if (this->read(data, 2) != i2c::ERROR_OK) { ESP_LOGE(TAG, "Communication with TOF10120 failed on read"); this->status_set_warning(); return; diff --git a/esphome/components/toshiba/toshiba.cpp b/esphome/components/toshiba/toshiba.cpp index 81ed5ddce4..25528abbe1 100644 --- a/esphome/components/toshiba/toshiba.cpp +++ b/esphome/components/toshiba/toshiba.cpp @@ -127,7 +127,7 @@ void ToshibaClimate::setup() { this->fan_modes_ = this->toshiba_fan_modes_(); this->swing_modes_ = this->toshiba_swing_modes_(); // Never send nan to HA - if (isnan(this->target_temperature)) + if (std::isnan(this->target_temperature)) this->target_temperature = 24; } diff --git a/esphome/components/total_daily_energy/total_daily_energy.cpp b/esphome/components/total_daily_energy/total_daily_energy.cpp index 83333acab7..178dc7cbe0 100644 --- a/esphome/components/total_daily_energy/total_daily_energy.cpp +++ b/esphome/components/total_daily_energy/total_daily_energy.cpp @@ -7,7 +7,7 @@ namespace total_daily_energy { static const char *const TAG = "total_daily_energy"; void TotalDailyEnergy::setup() { - this->pref_ = global_preferences.make_preference(this->get_object_id_hash()); + this->pref_ = global_preferences->make_preference(this->get_object_id_hash()); float recovered; if (this->pref_.load(&recovered)) { @@ -52,7 +52,7 @@ void TotalDailyEnergy::publish_state_and_save(float state) { } void TotalDailyEnergy::process_new_state_(float state) { - if (isnan(state)) + if (std::isnan(state)) return; const uint32_t now = millis(); const float old_state = this->last_power_state_; diff --git a/esphome/components/total_daily_energy/total_daily_energy.h b/esphome/components/total_daily_energy/total_daily_energy.h index fd71b8decc..9d2396d6e3 100644 --- a/esphome/components/total_daily_energy/total_daily_energy.h +++ b/esphome/components/total_daily_energy/total_daily_energy.h @@ -2,6 +2,7 @@ #include "esphome/core/component.h" #include "esphome/core/preferences.h" +#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/time/real_time_clock.h" diff --git a/esphome/components/tsl2591/tsl2591.cpp b/esphome/components/tsl2591/tsl2591.cpp index 1785fa46b4..7755437de2 100644 --- a/esphome/components/tsl2591/tsl2591.cpp +++ b/esphome/components/tsl2591/tsl2591.cpp @@ -1,5 +1,6 @@ #include "tsl2591.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace tsl2591 { @@ -362,7 +363,7 @@ float TSL2591Component::get_calculated_lux(uint16_t full_spectrum, uint16_t infr // For the curious "cpl" is counts per lux, a term used in AMS application notes. float cpl = (atime * again) / (this->device_factor_ * this->glass_attenuation_factor_); float lux = (((float) full_spectrum - (float) infrared)) * (1.0F - ((float) infrared / (float) full_spectrum)) / cpl; - return max(lux, 0.0F); + return std::max(lux, 0.0F); } } // namespace tsl2591 diff --git a/esphome/components/ttp229_bsf/ttp229_bsf.h b/esphome/components/ttp229_bsf/ttp229_bsf.h index d73e4fb185..59749a4fa7 100644 --- a/esphome/components/ttp229_bsf/ttp229_bsf.h +++ b/esphome/components/ttp229_bsf/ttp229_bsf.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/binary_sensor/binary_sensor.h" namespace esphome { diff --git a/esphome/components/ttp229_lsf/ttp229_lsf.cpp b/esphome/components/ttp229_lsf/ttp229_lsf.cpp index 6e3e68ea7a..21c7b02740 100644 --- a/esphome/components/ttp229_lsf/ttp229_lsf.cpp +++ b/esphome/components/ttp229_lsf/ttp229_lsf.cpp @@ -8,7 +8,8 @@ static const char *const TAG = "ttp229_lsf"; void TTP229LSFComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up ttp229..."); - if (!this->parent_->raw_request_from(this->address_, 2)) { + uint8_t data[2]; + if (this->read(data, 2) != i2c::ERROR_OK) { this->error_code_ = COMMUNICATION_FAILED; this->mark_failed(); return; @@ -28,10 +29,11 @@ void TTP229LSFComponent::dump_config() { } void TTP229LSFComponent::loop() { uint16_t touched = 0; - if (!this->parent_->raw_receive_16(this->address_, &touched, 1)) { + if (this->read(reinterpret_cast(&touched), 2) != i2c::ERROR_OK) { this->status_set_warning(); return; } + touched = i2c::i2ctohs(touched); this->status_clear_warning(); touched = reverse_bits_16(touched); for (auto *channel : this->channels_) { diff --git a/esphome/components/tuya/climate/tuya_climate.cpp b/esphome/components/tuya/climate/tuya_climate.cpp index 293bfb4f88..39d4203684 100644 --- a/esphome/components/tuya/climate/tuya_climate.cpp +++ b/esphome/components/tuya/climate/tuya_climate.cpp @@ -169,7 +169,7 @@ void TuyaClimate::compute_target_temperature_() { } void TuyaClimate::compute_state_() { - if (isnan(this->current_temperature) || isnan(this->target_temperature)) { + if (std::isnan(this->current_temperature) || std::isnan(this->target_temperature)) { // if any control parameters are nan, go to OFF action (not IDLE!) this->switch_to_action_(climate::CLIMATE_ACTION_OFF); return; diff --git a/esphome/components/tuya/tuya.cpp b/esphome/components/tuya/tuya.cpp index e42c74005e..bbbc9274c3 100644 --- a/esphome/components/tuya/tuya.cpp +++ b/esphome/components/tuya/tuya.cpp @@ -1,7 +1,8 @@ #include "tuya.h" #include "esphome/core/log.h" -#include "esphome/core/util.h" +#include "esphome/components/network/util.h" #include "esphome/core/helpers.h" +#include "esphome/core/util.h" namespace esphome { namespace tuya { @@ -389,7 +390,7 @@ void Tuya::send_empty_command_(TuyaCommandType command) { void Tuya::send_wifi_status_() { uint8_t status = 0x02; - if (network_is_connected()) { + if (network::is_connected()) { status = 0x03; // Protocol version 3 also supports specifying when connected to "the cloud" diff --git a/esphome/components/tx20/sensor.py b/esphome/components/tx20/sensor.py index ceb9b88d8d..84df82b5e6 100644 --- a/esphome/components/tx20/sensor.py +++ b/esphome/components/tx20/sensor.py @@ -33,9 +33,7 @@ CONFIG_SCHEMA = cv.Schema( accuracy_decimals=1, state_class=STATE_CLASS_NONE, ), - cv.Required(CONF_PIN): cv.All( - pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt - ), + cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pin_schema), } ).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/tx20/tx20.cpp b/esphome/components/tx20/tx20.cpp index f48e29521c..6e0b6343d1 100644 --- a/esphome/components/tx20/tx20.cpp +++ b/esphome/components/tx20/tx20.cpp @@ -20,7 +20,7 @@ void Tx20Component::setup() { this->store_.pin = this->pin_->to_isr(); this->store_.reset(); - this->pin_->attach_interrupt(Tx20ComponentStore::gpio_intr, &this->store_, CHANGE); + this->pin_->attach_interrupt(Tx20ComponentStore::gpio_intr, &this->store_, gpio::INTERRUPT_ANY_EDGE); } void Tx20Component::dump_config() { ESP_LOGCONFIG(TAG, "Tx20:"); @@ -140,8 +140,8 @@ void Tx20Component::decode_and_publish_() { } } -void ICACHE_RAM_ATTR Tx20ComponentStore::gpio_intr(Tx20ComponentStore *arg) { - arg->pin_state = arg->pin->digital_read(); +void IRAM_ATTR Tx20ComponentStore::gpio_intr(Tx20ComponentStore *arg) { + arg->pin_state = arg->pin.digital_read(); const uint32_t now = micros(); if (!arg->start_time) { // only detect a start if the bit is high @@ -183,7 +183,7 @@ void ICACHE_RAM_ATTR Tx20ComponentStore::gpio_intr(Tx20ComponentStore *arg) { arg->start_time = now; arg->buffer_index++; } -void ICACHE_RAM_ATTR Tx20ComponentStore::reset() { +void IRAM_ATTR Tx20ComponentStore::reset() { tx20_available = false; buffer_index = 0; spent_time = 0; diff --git a/esphome/components/tx20/tx20.h b/esphome/components/tx20/tx20.h index 56cb723fa1..1c617d0674 100644 --- a/esphome/components/tx20/tx20.h +++ b/esphome/components/tx20/tx20.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" namespace esphome { @@ -15,7 +15,7 @@ struct Tx20ComponentStore { volatile uint32_t spent_time; volatile bool tx20_available; volatile bool pin_state; - ISRInternalGPIOPin *pin; + ISRInternalGPIOPin pin; void reset(); static void gpio_intr(Tx20ComponentStore *arg); @@ -27,7 +27,7 @@ class Tx20Component : public Component { /// Get the textual representation of the wind direction ('N', 'SSE', ..). std::string get_wind_cardinal_direction() const; - void set_pin(GPIOPin *pin) { pin_ = pin; } + void set_pin(InternalGPIOPin *pin) { pin_ = pin; } void set_wind_speed_sensor(sensor::Sensor *wind_speed_sensor) { wind_speed_sensor_ = wind_speed_sensor; } void set_wind_direction_degrees_sensor(sensor::Sensor *wind_direction_degrees_sensor) { wind_direction_degrees_sensor_ = wind_direction_degrees_sensor; @@ -42,7 +42,7 @@ class Tx20Component : public Component { void decode_and_publish_(); std::string wind_cardinal_direction_; - GPIOPin *pin_; + InternalGPIOPin *pin_; sensor::Sensor *wind_speed_sensor_; sensor::Sensor *wind_direction_degrees_sensor_; Tx20ComponentStore store_; diff --git a/esphome/components/uart/__init__.py b/esphome/components/uart/__init__.py index d2fcac2cb6..35af3eedf7 100644 --- a/esphome/components/uart/__init__.py +++ b/esphome/components/uart/__init__.py @@ -7,6 +7,7 @@ from esphome import pins, automation from esphome.const import ( CONF_BAUD_RATE, CONF_ID, + CONF_NUMBER, CONF_RX_PIN, CONF_TX_PIN, CONF_UART_ID, @@ -18,7 +19,16 @@ from esphome.core import CORE CODEOWNERS = ["@esphome/core"] uart_ns = cg.esphome_ns.namespace("uart") -UARTComponent = uart_ns.class_("UARTComponent", cg.Component) +UARTComponent = uart_ns.class_("UARTComponent") + +IDFUARTComponent = uart_ns.class_("IDFUARTComponent", UARTComponent, cg.Component) +ESP32ArduinoUARTComponent = uart_ns.class_( + "ESP32ArduinoUARTComponent", UARTComponent, cg.Component +) +ESP8266UartComponent = uart_ns.class_( + "ESP8266UartComponent", UARTComponent, cg.Component +) + UARTDevice = uart_ns.class_("UARTDevice") UARTWriteAction = uart_ns.class_("UARTWriteAction", automation.Action) MULTI_CONF = True @@ -37,12 +47,23 @@ def validate_raw_data(value): def validate_rx_pin(value): - value = pins.input_pin(value) - if CORE.is_esp8266 and value >= 16: + value = pins.internal_gpio_input_pin_schema(value) + if CORE.is_esp8266 and value[CONF_NUMBER] >= 16: raise cv.Invalid("Pins GPIO16 and GPIO17 cannot be used as RX pins on ESP8266.") return value +def _uart_declare_type(value): + if CORE.is_esp8266: + return cv.declare_id(ESP8266UartComponent)(value) + if CORE.is_esp32: + if CORE.using_arduino: + return cv.declare_id(ESP32ArduinoUARTComponent)(value) + if CORE.using_esp_idf: + return cv.declare_id(IDFUARTComponent)(value) + raise NotImplementedError + + UARTParityOptions = uart_ns.enum("UARTParityOptions") UART_PARITY_OPTIONS = { "NONE": UARTParityOptions.UART_CONFIG_PARITY_NONE, @@ -57,19 +78,19 @@ CONF_PARITY = "parity" CONFIG_SCHEMA = cv.All( cv.Schema( { - cv.GenerateID(): cv.declare_id(UARTComponent), + cv.GenerateID(): _uart_declare_type, cv.Required(CONF_BAUD_RATE): cv.int_range(min=1), - cv.Optional(CONF_TX_PIN): pins.output_pin, + cv.Optional(CONF_TX_PIN): pins.internal_gpio_output_pin_schema, cv.Optional(CONF_RX_PIN): validate_rx_pin, cv.Optional(CONF_RX_BUFFER_SIZE, default=256): cv.validate_bytes, - cv.SplitDefault(CONF_INVERT, esp32=False): cv.All( - cv.only_on_esp32, cv.boolean - ), cv.Optional(CONF_STOP_BITS, default=1): cv.one_of(1, 2, int=True), cv.Optional(CONF_DATA_BITS, default=8): cv.int_range(min=5, max=8), cv.Optional(CONF_PARITY, default="NONE"): cv.enum( UART_PARITY_OPTIONS, upper=True ), + cv.Optional(CONF_INVERT): cv.invalid( + "This option has been removed. Please instead use invert in the tx/rx pin schemas." + ), } ).extend(cv.COMPONENT_SCHEMA), cv.has_at_least_one_key(CONF_TX_PIN, CONF_RX_PIN), @@ -84,12 +105,12 @@ async def to_code(config): cg.add(var.set_baud_rate(config[CONF_BAUD_RATE])) if CONF_TX_PIN in config: - cg.add(var.set_tx_pin(config[CONF_TX_PIN])) + tx_pin = await cg.gpio_pin_expression(config[CONF_TX_PIN]) + cg.add(var.set_tx_pin(tx_pin)) if CONF_RX_PIN in config: - cg.add(var.set_rx_pin(config[CONF_RX_PIN])) + rx_pin = await cg.gpio_pin_expression(config[CONF_RX_PIN]) + cg.add(var.set_rx_pin(rx_pin)) cg.add(var.set_rx_buffer_size(config[CONF_RX_BUFFER_SIZE])) - if CONF_INVERT in config: - cg.add(var.set_invert(config[CONF_INVERT])) cg.add(var.set_stop_bits(config[CONF_STOP_BITS])) cg.add(var.set_data_bits(config[CONF_DATA_BITS])) cg.add(var.set_parity(config[CONF_PARITY])) diff --git a/esphome/components/uart/uart.cpp b/esphome/components/uart/uart.cpp index 8cc8a47b14..22a22e2772 100644 --- a/esphome/components/uart/uart.cpp +++ b/esphome/components/uart/uart.cpp @@ -4,62 +4,28 @@ #include "esphome/core/application.h" #include "esphome/core/defines.h" -#ifdef USE_LOGGER -#include "esphome/components/logger/logger.h" -#endif - namespace esphome { namespace uart { static const char *const TAG = "uart"; -size_t UARTComponent::write(uint8_t data) { - this->write_byte(data); - return 1; -} -int UARTComponent::read() { - uint8_t data; - if (!this->read_byte(&data)) - return -1; - return data; -} -int UARTComponent::peek() { - uint8_t data; - if (!this->peek_byte(&data)) - return -1; - return data; -} - -void UARTComponent::check_logger_conflict_() { -#ifdef USE_LOGGER - if (this->hw_serial_ == nullptr || logger::global_logger->get_baud_rate() == 0) { - return; - } - - if (this->hw_serial_ == logger::global_logger->get_hw_serial()) { - ESP_LOGW(TAG, " You're using the same serial port for logging and the UART component. Please " - "disable logging over the serial port by setting logger->baud_rate to 0."); - } -#endif -} - void UARTDevice::check_uart_settings(uint32_t baud_rate, uint8_t stop_bits, UARTParityOptions parity, uint8_t data_bits) { - if (this->parent_->baud_rate_ != baud_rate) { + if (this->parent_->get_baud_rate() != baud_rate) { ESP_LOGE(TAG, " Invalid baud_rate: Integration requested baud_rate %u but you have %u!", baud_rate, - this->parent_->baud_rate_); + this->parent_->get_baud_rate()); } - if (this->parent_->stop_bits_ != stop_bits) { + if (this->parent_->get_stop_bits() != stop_bits) { ESP_LOGE(TAG, " Invalid stop bits: Integration requested stop_bits %u but you have %u!", stop_bits, - this->parent_->stop_bits_); + this->parent_->get_stop_bits()); } - if (this->parent_->data_bits_ != data_bits) { + if (this->parent_->get_data_bits() != data_bits) { ESP_LOGE(TAG, " Invalid number of data bits: Integration requested %u data bits but you have %u!", data_bits, - this->parent_->data_bits_); + this->parent_->get_data_bits()); } - if (this->parent_->parity_ != parity) { + if (this->parent_->get_parity() != parity) { ESP_LOGE(TAG, " Invalid parity: Integration requested parity %s but you have %s!", - LOG_STR_ARG(parity_to_str(parity)), LOG_STR_ARG(parity_to_str(this->parent_->parity_))); + LOG_STR_ARG(parity_to_str(parity)), LOG_STR_ARG(parity_to_str(this->parent_->get_parity()))); } } diff --git a/esphome/components/uart/uart.h b/esphome/components/uart/uart.h index a79b7b841e..c368f9ed6b 100644 --- a/esphome/components/uart/uart.h +++ b/esphome/components/uart/uart.h @@ -1,132 +1,15 @@ #pragma once #include -#include -#include "esphome/core/esphal.h" #include "esphome/core/component.h" +#include "esphome/core/hal.h" #include "esphome/core/log.h" +#include "uart_component.h" namespace esphome { namespace uart { -enum UARTParityOptions { - UART_CONFIG_PARITY_NONE, - UART_CONFIG_PARITY_EVEN, - UART_CONFIG_PARITY_ODD, -}; - -const LogString *parity_to_str(UARTParityOptions parity); - -#ifdef ARDUINO_ARCH_ESP8266 -class ESP8266SoftwareSerial { - public: - void setup(int8_t tx_pin, int8_t rx_pin, uint32_t baud_rate, uint8_t stop_bits, uint32_t data_bits, - UARTParityOptions parity, size_t rx_buffer_size); - - uint8_t read_byte(); - uint8_t peek_byte(); - - void flush(); - - void write_byte(uint8_t data); - - int available(); - - GPIOPin *gpio_tx_pin_{nullptr}; - GPIOPin *gpio_rx_pin_{nullptr}; - - protected: - static void gpio_intr(ESP8266SoftwareSerial *arg); - - void wait_(uint32_t *wait, const uint32_t &start); - bool read_bit_(uint32_t *wait, const uint32_t &start); - void write_bit_(bool bit, uint32_t *wait, const uint32_t &start); - - uint32_t bit_time_{0}; - uint8_t *rx_buffer_{nullptr}; - size_t rx_buffer_size_; - volatile size_t rx_in_pos_{0}; - size_t rx_out_pos_{0}; - uint8_t stop_bits_; - uint8_t data_bits_; - UARTParityOptions parity_; - ISRInternalGPIOPin *tx_pin_{nullptr}; - ISRInternalGPIOPin *rx_pin_{nullptr}; -}; -#endif - -class UARTComponent : public Component, public Stream { - public: - void set_baud_rate(uint32_t baud_rate) { baud_rate_ = baud_rate; } - uint32_t get_baud_rate() const { return baud_rate_; } - - uint32_t get_config(); - - void setup() override; - - void dump_config() override; - - void write_byte(uint8_t data); - - void write_array(const uint8_t *data, size_t len); - void write_array(const std::vector &data) { this->write_array(&data[0], data.size()); } - - void write_str(const char *str); - - bool peek_byte(uint8_t *data); - - bool read_byte(uint8_t *data); - - bool read_array(uint8_t *data, size_t len); - - int available() override; - - /// Block until all bytes have been written to the UART bus. - void flush() override; - - float get_setup_priority() const override { return setup_priority::BUS; } - - size_t write(uint8_t data) override; - int read() override; - int peek() override; - - void set_tx_pin(uint8_t tx_pin) { this->tx_pin_ = tx_pin; } - void set_rx_pin(uint8_t rx_pin) { this->rx_pin_ = rx_pin; } - void set_rx_buffer_size(size_t rx_buffer_size) { this->rx_buffer_size_ = rx_buffer_size; } -#ifdef ARDUINO_ARCH_ESP32 - void set_invert(bool invert) { this->invert_ = invert; } -#endif - void set_stop_bits(uint8_t stop_bits) { this->stop_bits_ = stop_bits; } - void set_data_bits(uint8_t data_bits) { this->data_bits_ = data_bits; } - void set_parity(UARTParityOptions parity) { this->parity_ = parity; } - - protected: - void check_logger_conflict_(); - bool check_read_timeout_(size_t len = 1); - friend class UARTDevice; - - HardwareSerial *hw_serial_{nullptr}; -#ifdef ARDUINO_ARCH_ESP8266 - ESP8266SoftwareSerial *sw_serial_{nullptr}; -#endif - optional tx_pin_; - optional rx_pin_; - size_t rx_buffer_size_; -#ifdef ARDUINO_ARCH_ESP32 - bool invert_; -#endif - uint32_t baud_rate_; - uint8_t stop_bits_; - uint8_t data_bits_; - UARTParityOptions parity_; - - private: -#ifdef ARDUINO_ARCH_ESP8266 - static bool serial0InUse; -#endif -}; - -class UARTDevice : public Stream { +class UARTDevice { public: UARTDevice() = default; UARTDevice(UARTComponent *parent) : parent_(parent) {} @@ -155,13 +38,27 @@ class UARTDevice : public Stream { return res; } - int available() override { return this->parent_->available(); } + int available() { return this->parent_->available(); } - void flush() override { return this->parent_->flush(); } + void flush() { return this->parent_->flush(); } - size_t write(uint8_t data) override { return this->parent_->write(data); } - int read() override { return this->parent_->read(); } - int peek() override { return this->parent_->peek(); } + // Compat APIs + int read() { + uint8_t data; + if (!read_byte(&data)) + return -1; + return data; + } + size_t write(uint8_t data) { + write_byte(data); + return 1; + } + int peek() { + uint8_t data; + if (!peek_byte(&data)) + return -1; + return data; + } /// Check that the configuration of the UART bus matches the provided values and otherwise print a warning void check_uart_settings(uint32_t baud_rate, uint8_t stop_bits = 1, diff --git a/esphome/components/uart/uart_component.cpp b/esphome/components/uart/uart_component.cpp new file mode 100644 index 0000000000..09b8c975ab --- /dev/null +++ b/esphome/components/uart/uart_component.cpp @@ -0,0 +1,24 @@ +#include "uart_component.h" + +namespace esphome { +namespace uart { + +static const char *const TAG = "uart"; + +bool UARTComponent::check_read_timeout_(size_t len) { + if (this->available() >= int(len)) + return true; + + uint32_t start_time = millis(); + while (this->available() < int(len)) { + if (millis() - start_time > 100) { + ESP_LOGE(TAG, "Reading from UART timed out at byte %u!", this->available()); + return false; + } + yield(); + } + return true; +} + +} // namespace uart +} // namespace esphome diff --git a/esphome/components/uart/uart_component.h b/esphome/components/uart/uart_component.h new file mode 100644 index 0000000000..de85cd2ca3 --- /dev/null +++ b/esphome/components/uart/uart_component.h @@ -0,0 +1,67 @@ +#pragma once + +#include +#include +#include "esphome/core/component.h" +#include "esphome/core/hal.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace uart { + +enum UARTParityOptions { + UART_CONFIG_PARITY_NONE, + UART_CONFIG_PARITY_EVEN, + UART_CONFIG_PARITY_ODD, +}; + +const LogString *parity_to_str(UARTParityOptions parity); + +class UARTComponent { + public: + void write_array(const std::vector &data) { this->write_array(&data[0], data.size()); } + void write_byte(uint8_t data) { this->write_array(&data, 1); }; + void write_str(const char *str) { + const auto *data = reinterpret_cast(str); + this->write_array(data, strlen(str)); + }; + + virtual void write_array(const uint8_t *data, size_t len) = 0; + + bool read_byte(uint8_t *data) { return this->read_array(data, 1); }; + virtual bool peek_byte(uint8_t *data) = 0; + virtual bool read_array(uint8_t *data, size_t len) = 0; + + /// Return available number of bytes. + virtual int available() = 0; + /// Block until all bytes have been written to the UART bus. + virtual void flush() = 0; + + void set_tx_pin(InternalGPIOPin *tx_pin) { this->tx_pin_ = tx_pin; } + void set_rx_pin(InternalGPIOPin *rx_pin) { this->rx_pin_ = rx_pin; } + void set_rx_buffer_size(size_t rx_buffer_size) { this->rx_buffer_size_ = rx_buffer_size; } + + void set_stop_bits(uint8_t stop_bits) { this->stop_bits_ = stop_bits; } + uint8_t get_stop_bits() const { return this->stop_bits_; } + void set_data_bits(uint8_t data_bits) { this->data_bits_ = data_bits; } + uint8_t get_data_bits() const { return this->data_bits_; } + void set_parity(UARTParityOptions parity) { this->parity_ = parity; } + UARTParityOptions get_parity() const { return this->parity_; } + void set_baud_rate(uint32_t baud_rate) { baud_rate_ = baud_rate; } + uint32_t get_baud_rate() const { return baud_rate_; } + + protected: + virtual void check_logger_conflict() = 0; + bool check_read_timeout_(size_t len = 1); + + InternalGPIOPin *tx_pin_; + InternalGPIOPin *rx_pin_; + size_t rx_buffer_size_; + uint32_t baud_rate_; + uint8_t stop_bits_; + uint8_t data_bits_; + UARTParityOptions parity_; +}; + +} // namespace uart +} // namespace esphome diff --git a/esphome/components/uart/uart_esp32.cpp b/esphome/components/uart/uart_component_esp32_arduino.cpp similarity index 64% rename from esphome/components/uart/uart_esp32.cpp rename to esphome/components/uart/uart_component_esp32_arduino.cpp index db2757780e..1b1ce382f2 100644 --- a/esphome/components/uart/uart_esp32.cpp +++ b/esphome/components/uart/uart_component_esp32_arduino.cpp @@ -1,13 +1,17 @@ -#ifdef ARDUINO_ARCH_ESP32 -#include "uart.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" +#ifdef USE_ESP32_FRAMEWORK_ARDUINO #include "esphome/core/application.h" #include "esphome/core/defines.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" +#include "uart_component_esp32_arduino.h" + +#ifdef USE_LOGGER +#include "esphome/components/logger/logger.h" +#endif namespace esphome { namespace uart { -static const char *const TAG = "uart_esp32"; +static const char *const TAG = "uart.arduino_esp32"; static const uint32_t UART_PARITY_EVEN = 0 << 0; static const uint32_t UART_PARITY_ODD = 1 << 0; @@ -20,7 +24,7 @@ static const uint32_t UART_NB_STOP_BIT_1 = 1 << 4; static const uint32_t UART_NB_STOP_BIT_2 = 3 << 4; static const uint32_t UART_TICK_APB_CLOCK = 1 << 27; -uint32_t UARTComponent::get_config() { +uint32_t ESP32ArduinoUARTComponent::get_config() { uint32_t config = 0; /* @@ -67,71 +71,63 @@ uint32_t UARTComponent::get_config() { return config; } -void UARTComponent::setup() { +void ESP32ArduinoUARTComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up UART..."); // Use Arduino HardwareSerial UARTs if all used pins match the ones // preconfigured by the platform. For example if RX disabled but TX pin // is 1 we still want to use Serial. + bool is_default_tx, is_default_rx; #ifdef CONFIG_IDF_TARGET_ESP32C3 - if (this->tx_pin_.value_or(21) == 21 && this->rx_pin_.value_or(20) == 20) { + is_default_tx = tx_pin_ == nullptr || tx_pin_->get_pin() == 21; + is_default_rx = rx_pin_ == nullptr || rx_pin_->get_pin() == 20; #else - if (this->tx_pin_.value_or(1) == 1 && this->rx_pin_.value_or(3) == 3) { + is_default_tx = tx_pin_ == nullptr || tx_pin_->get_pin() == 1; + is_default_rx = rx_pin_ == nullptr || rx_pin_->get_pin() == 3; #endif + if (is_default_tx && is_default_rx) { this->hw_serial_ = &Serial; } else { static uint8_t next_uart_num = 1; this->hw_serial_ = new HardwareSerial(next_uart_num++); // NOLINT(cppcoreguidelines-owning-memory) } - int8_t tx = this->tx_pin_.has_value() ? *this->tx_pin_ : -1; - int8_t rx = this->rx_pin_.has_value() ? *this->rx_pin_ : -1; - this->hw_serial_->begin(this->baud_rate_, get_config(), rx, tx, this->invert_); + int8_t tx = this->tx_pin_ != nullptr ? this->tx_pin_->get_pin() : -1; + int8_t rx = this->rx_pin_ != nullptr ? this->rx_pin_->get_pin() : -1; + bool invert = false; + if (tx_pin_ != nullptr && tx_pin_->is_inverted()) + invert = true; + if (rx_pin_ != nullptr && rx_pin_->is_inverted()) + invert = true; + this->hw_serial_->begin(this->baud_rate_, get_config(), rx, tx, invert); this->hw_serial_->setRxBufferSize(this->rx_buffer_size_); } -void UARTComponent::dump_config() { +void ESP32ArduinoUARTComponent::dump_config() { ESP_LOGCONFIG(TAG, "UART Bus:"); - if (this->tx_pin_.has_value()) { - ESP_LOGCONFIG(TAG, " TX Pin: GPIO%d", *this->tx_pin_); - } - if (this->rx_pin_.has_value()) { - ESP_LOGCONFIG(TAG, " RX Pin: GPIO%d", *this->rx_pin_); + LOG_PIN(" TX Pin: ", tx_pin_); + LOG_PIN(" RX Pin: ", rx_pin_); + if (this->rx_pin_ != nullptr) { ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_); } ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_); ESP_LOGCONFIG(TAG, " Data Bits: %u", this->data_bits_); ESP_LOGCONFIG(TAG, " Parity: %s", LOG_STR_ARG(parity_to_str(this->parity_))); ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_); - this->check_logger_conflict_(); + this->check_logger_conflict(); } -void UARTComponent::write_byte(uint8_t data) { - this->hw_serial_->write(data); - ESP_LOGVV(TAG, " Wrote 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data), data); -} -void UARTComponent::write_array(const uint8_t *data, size_t len) { +void ESP32ArduinoUARTComponent::write_array(const uint8_t *data, size_t len) { this->hw_serial_->write(data, len); for (size_t i = 0; i < len; i++) { ESP_LOGVV(TAG, " Wrote 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]); } } -void UARTComponent::write_str(const char *str) { - this->hw_serial_->write(str); - ESP_LOGVV(TAG, " Wrote \"%s\"", str); -} -bool UARTComponent::read_byte(uint8_t *data) { - if (!this->check_read_timeout_()) - return false; - *data = this->hw_serial_->read(); - ESP_LOGVV(TAG, " Read 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(*data), *data); - return true; -} -bool UARTComponent::peek_byte(uint8_t *data) { +bool ESP32ArduinoUARTComponent::peek_byte(uint8_t *data) { if (!this->check_read_timeout_()) return false; *data = this->hw_serial_->peek(); return true; } -bool UARTComponent::read_array(uint8_t *data, size_t len) { +bool ESP32ArduinoUARTComponent::read_array(uint8_t *data, size_t len) { if (!this->check_read_timeout_(len)) return false; this->hw_serial_->readBytes(data, len); @@ -141,26 +137,25 @@ bool UARTComponent::read_array(uint8_t *data, size_t len) { return true; } -bool UARTComponent::check_read_timeout_(size_t len) { - if (this->available() >= len) - return true; - - uint32_t start_time = millis(); - while (this->available() < len) { - if (millis() - start_time > 1000) { - ESP_LOGE(TAG, "Reading from UART timed out at byte %u!", this->available()); - return false; - } - yield(); - } - return true; -} -int UARTComponent::available() { return this->hw_serial_->available(); } -void UARTComponent::flush() { +int ESP32ArduinoUARTComponent::available() { return this->hw_serial_->available(); } +void ESP32ArduinoUARTComponent::flush() { ESP_LOGVV(TAG, " Flushing..."); this->hw_serial_->flush(); } +void ESP32ArduinoUARTComponent::check_logger_conflict() { +#ifdef USE_LOGGER + if (this->hw_serial_ == nullptr || logger::global_logger->get_baud_rate() == 0) { + return; + } + + if (this->hw_serial_ == logger::global_logger->get_hw_serial()) { + ESP_LOGW(TAG, " You're using the same serial port for logging and the UART component. Please " + "disable logging over the serial port by setting logger->baud_rate to 0."); + } +#endif +} + } // namespace uart } // namespace esphome -#endif // ARDUINO_ARCH_ESP32 +#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/uart/uart_component_esp32_arduino.h b/esphome/components/uart/uart_component_esp32_arduino.h new file mode 100644 index 0000000000..c6f445ff12 --- /dev/null +++ b/esphome/components/uart/uart_component_esp32_arduino.h @@ -0,0 +1,40 @@ +#pragma once + +#ifdef USE_ESP32_FRAMEWORK_ARDUINO + +#include +#include +#include "esphome/core/component.h" +#include "esphome/core/hal.h" +#include "esphome/core/log.h" +#include "uart_component.h" + +namespace esphome { +namespace uart { + +class ESP32ArduinoUARTComponent : public UARTComponent, public Component { + public: + void setup() override; + void dump_config() override; + float get_setup_priority() const override { return setup_priority::BUS; } + + void write_array(const uint8_t *data, size_t len) override; + + bool peek_byte(uint8_t *data) override; + bool read_array(uint8_t *data, size_t len) override; + + int available() override; + void flush() override; + + uint32_t get_config(); + + protected: + void check_logger_conflict() override; + + HardwareSerial *hw_serial_{nullptr}; +}; + +} // namespace uart +} // namespace esphome + +#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/uart/uart_esp8266.cpp b/esphome/components/uart/uart_component_esp8266.cpp similarity index 60% rename from esphome/components/uart/uart_esp8266.cpp rename to esphome/components/uart/uart_component_esp8266.cpp index a74f2601e4..2188a4a4bc 100644 --- a/esphome/components/uart/uart_esp8266.cpp +++ b/esphome/components/uart/uart_component_esp8266.cpp @@ -1,9 +1,9 @@ -#ifdef ARDUINO_ARCH_ESP8266 -#include "uart.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" +#ifdef USE_ESP8266 +#include "uart_component_esp8266.h" #include "esphome/core/application.h" #include "esphome/core/defines.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" #ifdef USE_LOGGER #include "esphome/components/logger/logger.h" @@ -12,10 +12,10 @@ namespace esphome { namespace uart { -static const char *const TAG = "uart_esp8266"; -bool UARTComponent::serial0InUse = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static const char *const TAG = "uart.arduino_esp8266"; +bool ESP8266UartComponent::serial0InUse = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -uint32_t UARTComponent::get_config() { +uint32_t ESP8266UartComponent::get_config() { uint32_t config = 0; if (this->parity_ == UART_CONFIG_PARITY_NONE) @@ -48,15 +48,15 @@ uint32_t UARTComponent::get_config() { return config; } -void UARTComponent::setup() { +void ESP8266UartComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up UART bus..."); // Use Arduino HardwareSerial UARTs if all used pins match the ones // preconfigured by the platform. For example if RX disabled but TX pin // is 1 we still want to use Serial. SerialConfig config = static_cast(get_config()); - if (!UARTComponent::serial0InUse && this->tx_pin_.value_or(1) == 1 && - this->rx_pin_.value_or(3) == 3 + if (!ESP8266UartComponent::serial0InUse && (tx_pin_ == nullptr || tx_pin_->get_pin() == 1) && + (rx_pin_ == nullptr || rx_pin_->get_pin() == 3) #ifdef USE_LOGGER // we will use UART0 if logger isn't using it in swapped mode && (logger::global_logger->get_hw_serial() == nullptr || @@ -66,9 +66,9 @@ void UARTComponent::setup() { this->hw_serial_ = &Serial; this->hw_serial_->begin(this->baud_rate_, config); this->hw_serial_->setRxBufferSize(this->rx_buffer_size_); - UARTComponent::serial0InUse = true; - } else if (!UARTComponent::serial0InUse && this->tx_pin_.value_or(15) == 15 && - this->rx_pin_.value_or(13) == 13 + ESP8266UartComponent::serial0InUse = true; + } else if (!ESP8266UartComponent::serial0InUse && (tx_pin_ == nullptr || tx_pin_->get_pin() == 15) && + (rx_pin_ == nullptr || rx_pin_->get_pin() == 13) #ifdef USE_LOGGER // we will use UART0 swapped if logger isn't using it in regular mode && (logger::global_logger->get_hw_serial() == nullptr || @@ -79,27 +79,23 @@ void UARTComponent::setup() { this->hw_serial_->begin(this->baud_rate_, config); this->hw_serial_->setRxBufferSize(this->rx_buffer_size_); this->hw_serial_->swap(); - UARTComponent::serial0InUse = true; - } else if (this->tx_pin_.value_or(2) == 2 && this->rx_pin_.value_or(8) == 8) { + ESP8266UartComponent::serial0InUse = true; + } else if ((tx_pin_ == nullptr || tx_pin_->get_pin() == 2) && (rx_pin_ == nullptr || rx_pin_->get_pin() == 8)) { this->hw_serial_ = &Serial1; this->hw_serial_->begin(this->baud_rate_, config); this->hw_serial_->setRxBufferSize(this->rx_buffer_size_); } else { this->sw_serial_ = new ESP8266SoftwareSerial(); // NOLINT - int8_t tx = this->tx_pin_.has_value() ? *this->tx_pin_ : -1; - int8_t rx = this->rx_pin_.has_value() ? *this->rx_pin_ : -1; - this->sw_serial_->setup(tx, rx, this->baud_rate_, this->stop_bits_, this->data_bits_, this->parity_, + this->sw_serial_->setup(tx_pin_, rx_pin_, this->baud_rate_, this->stop_bits_, this->data_bits_, this->parity_, this->rx_buffer_size_); } } -void UARTComponent::dump_config() { +void ESP8266UartComponent::dump_config() { ESP_LOGCONFIG(TAG, "UART Bus:"); - if (this->tx_pin_.has_value()) { - ESP_LOGCONFIG(TAG, " TX Pin: GPIO%d", *this->tx_pin_); - } - if (this->rx_pin_.has_value()) { - ESP_LOGCONFIG(TAG, " RX Pin: GPIO%d", *this->rx_pin_); + LOG_PIN(" TX Pin: ", tx_pin_); + LOG_PIN(" RX Pin: ", rx_pin_); + if (this->rx_pin_ != nullptr) { ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_); // NOLINT } ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_); @@ -111,18 +107,23 @@ void UARTComponent::dump_config() { } else { ESP_LOGCONFIG(TAG, " Using software serial"); } - this->check_logger_conflict_(); + this->check_logger_conflict(); } -void UARTComponent::write_byte(uint8_t data) { - if (this->hw_serial_ != nullptr) { - this->hw_serial_->write(data); - } else { - this->sw_serial_->write_byte(data); +void ESP8266UartComponent::check_logger_conflict() { +#ifdef USE_LOGGER + if (this->hw_serial_ == nullptr || logger::global_logger->get_baud_rate() == 0) { + return; } - ESP_LOGVV(TAG, " Wrote 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data), data); + + if (this->hw_serial_ == logger::global_logger->get_hw_serial()) { + ESP_LOGW(TAG, " You're using the same serial port for logging and the UART component. Please " + "disable logging over the serial port by setting logger->baud_rate to 0."); + } +#endif } -void UARTComponent::write_array(const uint8_t *data, size_t len) { + +void ESP8266UartComponent::write_array(const uint8_t *data, size_t len) { if (this->hw_serial_ != nullptr) { this->hw_serial_->write(data, len); } else { @@ -133,28 +134,7 @@ void UARTComponent::write_array(const uint8_t *data, size_t len) { ESP_LOGVV(TAG, " Wrote 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]); } } -void UARTComponent::write_str(const char *str) { - if (this->hw_serial_ != nullptr) { - this->hw_serial_->write(str); - } else { - const auto *data = reinterpret_cast(str); - for (size_t i = 0; data[i] != 0; i++) - this->sw_serial_->write_byte(data[i]); - } - ESP_LOGVV(TAG, " Wrote \"%s\"", str); -} -bool UARTComponent::read_byte(uint8_t *data) { - if (!this->check_read_timeout_()) - return false; - if (this->hw_serial_ != nullptr) { - *data = this->hw_serial_->read(); - } else { - *data = this->sw_serial_->read_byte(); - } - ESP_LOGVV(TAG, " Read 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(*data), *data); - return true; -} -bool UARTComponent::peek_byte(uint8_t *data) { +bool ESP8266UartComponent::peek_byte(uint8_t *data) { if (!this->check_read_timeout_()) return false; if (this->hw_serial_ != nullptr) { @@ -164,7 +144,7 @@ bool UARTComponent::peek_byte(uint8_t *data) { } return true; } -bool UARTComponent::read_array(uint8_t *data, size_t len) { +bool ESP8266UartComponent::read_array(uint8_t *data, size_t len) { if (!this->check_read_timeout_(len)) return false; if (this->hw_serial_ != nullptr) { @@ -179,28 +159,14 @@ bool UARTComponent::read_array(uint8_t *data, size_t len) { return true; } -bool UARTComponent::check_read_timeout_(size_t len) { - if (this->available() >= int(len)) - return true; - - uint32_t start_time = millis(); - while (this->available() < int(len)) { - if (millis() - start_time > 100) { - ESP_LOGE(TAG, "Reading from UART timed out at byte %u!", this->available()); - return false; - } - yield(); - } - return true; -} -int UARTComponent::available() { +int ESP8266UartComponent::available() { if (this->hw_serial_ != nullptr) { return this->hw_serial_->available(); } else { return this->sw_serial_->available(); } } -void UARTComponent::flush() { +void ESP8266UartComponent::flush() { ESP_LOGVV(TAG, " Flushing..."); if (this->hw_serial_ != nullptr) { this->hw_serial_->flush(); @@ -208,32 +174,31 @@ void UARTComponent::flush() { this->sw_serial_->flush(); } } -void ESP8266SoftwareSerial::setup(int8_t tx_pin, int8_t rx_pin, uint32_t baud_rate, uint8_t stop_bits, - uint32_t data_bits, UARTParityOptions parity, size_t rx_buffer_size) { +void ESP8266SoftwareSerial::setup(InternalGPIOPin *tx_pin, InternalGPIOPin *rx_pin, uint32_t baud_rate, + uint8_t stop_bits, uint32_t data_bits, UARTParityOptions parity, + size_t rx_buffer_size) { this->bit_time_ = F_CPU / baud_rate; this->rx_buffer_size_ = rx_buffer_size; this->stop_bits_ = stop_bits; this->data_bits_ = data_bits; this->parity_ = parity; - if (tx_pin != -1) { - auto pin = GPIOPin(tx_pin, OUTPUT); - this->gpio_tx_pin_ = &pin; - pin.setup(); - this->tx_pin_ = pin.to_isr(); - this->tx_pin_->digital_write(true); + if (tx_pin != nullptr) { + gpio_tx_pin_ = tx_pin; + gpio_tx_pin_->setup(); + tx_pin_ = gpio_tx_pin_->to_isr(); + tx_pin_.digital_write(true); } - if (rx_pin != -1) { - auto pin = GPIOPin(rx_pin, INPUT); - pin.setup(); - this->gpio_rx_pin_ = &pin; - this->rx_pin_ = pin.to_isr(); - this->rx_buffer_ = new uint8_t[this->rx_buffer_size_]; // NOLINT - pin.attach_interrupt(ESP8266SoftwareSerial::gpio_intr, this, FALLING); + if (rx_pin != nullptr) { + gpio_rx_pin_ = rx_pin; + gpio_rx_pin_->setup(); + rx_pin_ = gpio_rx_pin_->to_isr(); + rx_buffer_ = new uint8_t[this->rx_buffer_size_]; // NOLINT + gpio_rx_pin_->attach_interrupt(ESP8266SoftwareSerial::gpio_intr, this, gpio::INTERRUPT_FALLING_EDGE); } } -void ICACHE_RAM_ATTR ESP8266SoftwareSerial::gpio_intr(ESP8266SoftwareSerial *arg) { +void IRAM_ATTR ESP8266SoftwareSerial::gpio_intr(ESP8266SoftwareSerial *arg) { uint32_t wait = arg->bit_time_ + arg->bit_time_ / 3 - 500; - const uint32_t start = ESP.getCycleCount(); // NOLINT(readability-static-accessed-through-instance) + const uint32_t start = arch_get_cpu_cycle_count(); uint8_t rec = 0; // Manually unroll the loop for (int i = 0; i < arg->data_bits_; i++) @@ -254,10 +219,10 @@ void ICACHE_RAM_ATTR ESP8266SoftwareSerial::gpio_intr(ESP8266SoftwareSerial *arg arg->rx_buffer_[arg->rx_in_pos_] = rec; arg->rx_in_pos_ = (arg->rx_in_pos_ + 1) % arg->rx_buffer_size_; // Clear RX pin so that the interrupt doesn't re-trigger right away again. - arg->rx_pin_->clear_interrupt(); + arg->rx_pin_.clear_interrupt(); } -void ICACHE_RAM_ATTR HOT ESP8266SoftwareSerial::write_byte(uint8_t data) { - if (this->tx_pin_ == nullptr) { +void IRAM_ATTR HOT ESP8266SoftwareSerial::write_byte(uint8_t data) { + if (this->gpio_tx_pin_ == nullptr) { ESP_LOGE(TAG, "UART doesn't have TX pins set!"); return; } @@ -273,7 +238,7 @@ void ICACHE_RAM_ATTR HOT ESP8266SoftwareSerial::write_byte(uint8_t data) { { InterruptLock lock; uint32_t wait = this->bit_time_; - const uint32_t start = ESP.getCycleCount(); // NOLINT(readability-static-accessed-through-instance) + const uint32_t start = arch_get_cpu_cycle_count(); // Start bit this->write_bit_(false, &wait, start); for (int i = 0; i < this->data_bits_; i++) { @@ -290,17 +255,17 @@ void ICACHE_RAM_ATTR HOT ESP8266SoftwareSerial::write_byte(uint8_t data) { this->wait_(&wait, start); } } -void ICACHE_RAM_ATTR ESP8266SoftwareSerial::wait_(uint32_t *wait, const uint32_t &start) { - while (ESP.getCycleCount() - start < *wait) // NOLINT(readability-static-accessed-through-instance) +void IRAM_ATTR ESP8266SoftwareSerial::wait_(uint32_t *wait, const uint32_t &start) { + while (arch_get_cpu_cycle_count() - start < *wait) ; *wait += this->bit_time_; } -bool ICACHE_RAM_ATTR ESP8266SoftwareSerial::read_bit_(uint32_t *wait, const uint32_t &start) { +bool IRAM_ATTR ESP8266SoftwareSerial::read_bit_(uint32_t *wait, const uint32_t &start) { this->wait_(wait, start); - return this->rx_pin_->digital_read(); + return this->rx_pin_.digital_read(); } -void ICACHE_RAM_ATTR ESP8266SoftwareSerial::write_bit_(bool bit, uint32_t *wait, const uint32_t &start) { - this->tx_pin_->digital_write(bit); +void IRAM_ATTR ESP8266SoftwareSerial::write_bit_(bool bit, uint32_t *wait, const uint32_t &start) { + this->tx_pin_.digital_write(bit); this->wait_(wait, start); } uint8_t ESP8266SoftwareSerial::read_byte() { @@ -327,4 +292,4 @@ int ESP8266SoftwareSerial::available() { } // namespace uart } // namespace esphome -#endif // ARDUINO_ARCH_ESP8266 +#endif // USE_ESP8266 diff --git a/esphome/components/uart/uart_component_esp8266.h b/esphome/components/uart/uart_component_esp8266.h new file mode 100644 index 0000000000..921d77e4f3 --- /dev/null +++ b/esphome/components/uart/uart_component_esp8266.h @@ -0,0 +1,79 @@ +#pragma once + +#ifdef USE_ESP8266 + +#include +#include +#include "esphome/core/component.h" +#include "esphome/core/hal.h" +#include "esphome/core/log.h" +#include "uart_component.h" + +namespace esphome { +namespace uart { + +class ESP8266SoftwareSerial { + public: + void setup(InternalGPIOPin *tx_pin, InternalGPIOPin *rx_pin, uint32_t baud_rate, uint8_t stop_bits, + uint32_t data_bits, UARTParityOptions parity, size_t rx_buffer_size); + + uint8_t read_byte(); + uint8_t peek_byte(); + + void flush(); + + void write_byte(uint8_t data); + + int available(); + + protected: + static void gpio_intr(ESP8266SoftwareSerial *arg); + + void wait_(uint32_t *wait, const uint32_t &start); + bool read_bit_(uint32_t *wait, const uint32_t &start); + void write_bit_(bool bit, uint32_t *wait, const uint32_t &start); + + uint32_t bit_time_{0}; + uint8_t *rx_buffer_{nullptr}; + size_t rx_buffer_size_; + volatile size_t rx_in_pos_{0}; + size_t rx_out_pos_{0}; + uint8_t stop_bits_; + uint8_t data_bits_; + UARTParityOptions parity_; + InternalGPIOPin *gpio_tx_pin_{nullptr}; + ISRInternalGPIOPin tx_pin_; + InternalGPIOPin *gpio_rx_pin_{nullptr}; + ISRInternalGPIOPin rx_pin_; +}; + +class ESP8266UartComponent : public UARTComponent, public Component { + public: + void setup() override; + void dump_config() override; + float get_setup_priority() const override { return setup_priority::BUS; } + + void write_array(const uint8_t *data, size_t len) override; + + bool peek_byte(uint8_t *data) override; + bool read_array(uint8_t *data, size_t len) override; + + int available() override; + void flush() override; + + uint32_t get_config(); + + protected: + void check_logger_conflict() override; + + HardwareSerial *hw_serial_{nullptr}; + ESP8266SoftwareSerial *sw_serial_{nullptr}; + + private: + static bool serial0InUse; +}; + +} // namespace uart +} // namespace esphome + +#endif // USE_ESP8266 diff --git a/esphome/components/uart/uart_component_esp_idf.cpp b/esphome/components/uart/uart_component_esp_idf.cpp new file mode 100644 index 0000000000..3d4a634a72 --- /dev/null +++ b/esphome/components/uart/uart_component_esp_idf.cpp @@ -0,0 +1,201 @@ +#ifdef USE_ESP_IDF + +#include "uart_component_esp_idf.h" +#include "esphome/core/application.h" +#include "esphome/core/defines.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +#ifdef USE_LOGGER +#include "esphome/components/logger/logger.h" +#endif + +namespace esphome { +namespace uart { +static const char *const TAG = "uart.idf"; + +uart_config_t IDFUARTComponent::get_config() { + uart_parity_t parity = UART_PARITY_DISABLE; + if (this->parity_ == UART_CONFIG_PARITY_EVEN) + parity = UART_PARITY_EVEN; + else if (this->parity_ == UART_CONFIG_PARITY_ODD) + parity = UART_PARITY_ODD; + + uart_word_length_t data_bits; + switch (this->data_bits_) { + case 5: + data_bits = UART_DATA_5_BITS; + break; + case 6: + data_bits = UART_DATA_6_BITS; + break; + case 7: + data_bits = UART_DATA_7_BITS; + break; + case 8: + data_bits = UART_DATA_8_BITS; + break; + default: + data_bits = UART_DATA_BITS_MAX; + break; + } + + uart_config_t uart_config; + uart_config.baud_rate = this->baud_rate_; + uart_config.data_bits = data_bits; + uart_config.parity = parity; + uart_config.stop_bits = this->stop_bits_ == 1 ? UART_STOP_BITS_1 : UART_STOP_BITS_2; + uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE; + uart_config.source_clk = UART_SCLK_APB; + uart_config.rx_flow_ctrl_thresh = 122; + + return uart_config; +} + +void IDFUARTComponent::setup() { + static uint8_t next_uart_num = 0; +#ifdef USE_LOGGER + if (logger::global_logger->get_uart_num() == next_uart_num) + next_uart_num++; +#endif + if (next_uart_num >= UART_NUM_MAX) { + ESP_LOGW(TAG, "Maximum number of UART components created already."); + this->mark_failed(); + return; + } + this->uart_num_ = next_uart_num++; + ESP_LOGCONFIG(TAG, "Setting up UART %u...", this->uart_num_); + + this->lock_ = xSemaphoreCreateMutex(); + + xSemaphoreTake(this->lock_, portMAX_DELAY); + + uart_config_t uart_config = this->get_config(); + esp_err_t err = uart_param_config(this->uart_num_, &uart_config); + if (err != ESP_OK) { + ESP_LOGW(TAG, "uart_param_config failed: %s", esp_err_to_name(err)); + this->mark_failed(); + return; + } + + err = uart_driver_install(this->uart_num_, this->rx_buffer_size_, 0, 0, nullptr, 0); + if (err != ESP_OK) { + ESP_LOGW(TAG, "uart_driver_install failed: %s", esp_err_to_name(err)); + this->mark_failed(); + return; + } + + int8_t tx = this->tx_pin_ != nullptr ? this->tx_pin_->get_pin() : -1; + int8_t rx = this->rx_pin_ != nullptr ? this->rx_pin_->get_pin() : -1; + + err = uart_set_pin(this->uart_num_, tx, rx, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + if (err != ESP_OK) { + ESP_LOGW(TAG, "uart_set_pin failed: %s", esp_err_to_name(err)); + this->mark_failed(); + return; + } + + uint32_t invert = 0; + if (this->tx_pin_ != nullptr && this->tx_pin_->is_inverted()) + invert |= UART_SIGNAL_TXD_INV; + if (this->rx_pin_ != nullptr && this->rx_pin_->is_inverted()) + invert |= UART_SIGNAL_RXD_INV; + + err = uart_set_line_inverse(this->uart_num_, invert); + if (err != ESP_OK) { + ESP_LOGW(TAG, "uart_set_line_inverse failed: %s", esp_err_to_name(err)); + this->mark_failed(); + return; + } + + xSemaphoreGive(this->lock_); +} + +void IDFUARTComponent::dump_config() { + ESP_LOGCONFIG(TAG, "UART Bus:"); + ESP_LOGCONFIG(TAG, " Number: %u", this->uart_num_); + LOG_PIN(" TX Pin: ", tx_pin_); + LOG_PIN(" RX Pin: ", rx_pin_); + if (this->rx_pin_ != nullptr) { + ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_); + } + ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_); + ESP_LOGCONFIG(TAG, " Data Bits: %u", this->data_bits_); + ESP_LOGCONFIG(TAG, " Parity: %s", LOG_STR_ARG(parity_to_str(this->parity_))); + ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_); + this->check_logger_conflict(); +} + +void IDFUARTComponent::write_array(const uint8_t *data, size_t len) { + xSemaphoreTake(this->lock_, portMAX_DELAY); + uart_write_bytes(this->uart_num_, data, len); + xSemaphoreGive(this->lock_); + for (size_t i = 0; i < len; i++) { + ESP_LOGVV(TAG, " Wrote 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]); + } +} +bool IDFUARTComponent::peek_byte(uint8_t *data) { + if (!this->check_read_timeout_()) + return false; + xSemaphoreTake(this->lock_, portMAX_DELAY); + if (this->has_peek_) + *data = this->peek_byte_; + else { + int len = uart_read_bytes(this->uart_num_, data, 1, 20 / portTICK_RATE_MS); + if (len == 0) { + *data = 0; + } else { + this->has_peek_ = true; + this->peek_byte_ = *data; + } + } + xSemaphoreGive(this->lock_); + return true; +} +bool IDFUARTComponent::read_array(uint8_t *data, size_t len) { + size_t length_to_read = len; + if (!this->check_read_timeout_(len)) + return false; + xSemaphoreTake(this->lock_, portMAX_DELAY); + if (this->has_peek_) { + length_to_read--; + *data = this->peek_byte_; + data++; + this->has_peek_ = false; + } + if (length_to_read > 0) + uart_read_bytes(this->uart_num_, data, length_to_read, 20 / portTICK_RATE_MS); + + xSemaphoreGive(this->lock_); + for (size_t i = 0; i < len; i++) { + ESP_LOGVV(TAG, " Read 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]); + } + + return true; +} + +int IDFUARTComponent::available() { + size_t available; + + xSemaphoreTake(this->lock_, portMAX_DELAY); + uart_get_buffered_data_len(this->uart_num_, &available); + if (this->has_peek_) + available++; + xSemaphoreGive(this->lock_); + + return available; +} + +void IDFUARTComponent::flush() { + ESP_LOGVV(TAG, " Flushing..."); + xSemaphoreTake(this->lock_, portMAX_DELAY); + uart_wait_tx_done(this->uart_num_, portMAX_DELAY); + xSemaphoreGive(this->lock_); +} + +void IDFUARTComponent::check_logger_conflict() {} + +} // namespace uart +} // namespace esphome + +#endif // USE_ESP32 diff --git a/esphome/components/uart/uart_component_esp_idf.h b/esphome/components/uart/uart_component_esp_idf.h new file mode 100644 index 0000000000..68cceafda2 --- /dev/null +++ b/esphome/components/uart/uart_component_esp_idf.h @@ -0,0 +1,39 @@ +#pragma once + +#ifdef USE_ESP_IDF + +#include +#include "esphome/core/component.h" +#include "uart_component.h" + +namespace esphome { +namespace uart { + +class IDFUARTComponent : public UARTComponent, public Component { + public: + void setup() override; + void dump_config() override; + float get_setup_priority() const override { return setup_priority::BUS; } + + void write_array(const uint8_t *data, size_t len) override; + + bool peek_byte(uint8_t *data) override; + bool read_array(uint8_t *data, size_t len) override; + + int available() override; + void flush() override; + + protected: + void check_logger_conflict() override; + uart_port_t uart_num_; + uart_config_t get_config(); + SemaphoreHandle_t lock_; + + bool has_peek_{false}; + uint8_t peek_byte_; +}; + +} // namespace uart +} // namespace esphome + +#endif // USE_ESP_IDF diff --git a/esphome/components/uln2003/uln2003.h b/esphome/components/uln2003/uln2003.h index 4bcf1e88e3..4f559ed9a0 100644 --- a/esphome/components/uln2003/uln2003.h +++ b/esphome/components/uln2003/uln2003.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/stepper/stepper.h" namespace esphome { diff --git a/esphome/components/ultrasonic/ultrasonic_sensor.cpp b/esphome/components/ultrasonic/ultrasonic_sensor.cpp index e53cd7cf7a..9f47f9f6b9 100644 --- a/esphome/components/ultrasonic/ultrasonic_sensor.cpp +++ b/esphome/components/ultrasonic/ultrasonic_sensor.cpp @@ -1,5 +1,6 @@ #include "ultrasonic_sensor.h" #include "esphome/core/log.h" +#include "esphome/core/hal.h" namespace esphome { namespace ultrasonic { @@ -11,22 +12,31 @@ void UltrasonicSensorComponent::setup() { this->trigger_pin_->setup(); this->trigger_pin_->digital_write(false); this->echo_pin_->setup(); + // isr is faster to access + echo_isr_ = echo_pin_->to_isr(); } void UltrasonicSensorComponent::update() { this->trigger_pin_->digital_write(true); delayMicroseconds(this->pulse_time_us_); this->trigger_pin_->digital_write(false); - uint32_t time = pulseIn( // NOLINT - this->echo_pin_->get_pin(), uint8_t(!this->echo_pin_->is_inverted()), this->timeout_us_); + const uint32_t start = micros(); + while (micros() - start < timeout_us_ && echo_isr_.digital_read()) + ; + while (micros() - start < timeout_us_ && !echo_isr_.digital_read()) + ; + const uint32_t pulse_start = micros(); + while (micros() - start < timeout_us_ && echo_isr_.digital_read()) + ; + const uint32_t pulse_end = micros(); - ESP_LOGV(TAG, "Echo took %uµs", time); + ESP_LOGV(TAG, "Echo took %uµs", pulse_end - pulse_start); - if (time == 0) { + if (pulse_end - start >= timeout_us_) { ESP_LOGD(TAG, "'%s' - Distance measurement timed out!", this->name_.c_str()); this->publish_state(NAN); } else { - float result = UltrasonicSensorComponent::us_to_m(time); + float result = UltrasonicSensorComponent::us_to_m(pulse_end - pulse_start); ESP_LOGD(TAG, "'%s' - Got distance: %.2f m", this->name_.c_str(), result); this->publish_state(result); } diff --git a/esphome/components/ultrasonic/ultrasonic_sensor.h b/esphome/components/ultrasonic/ultrasonic_sensor.h index 633c1b17fb..e0d71b99ef 100644 --- a/esphome/components/ultrasonic/ultrasonic_sensor.h +++ b/esphome/components/ultrasonic/ultrasonic_sensor.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/gpio.h" #include "esphome/components/sensor/sensor.h" namespace esphome { @@ -10,7 +10,7 @@ namespace ultrasonic { class UltrasonicSensorComponent : public sensor::Sensor, public PollingComponent { public: void set_trigger_pin(GPIOPin *trigger_pin) { trigger_pin_ = trigger_pin; } - void set_echo_pin(GPIOPin *echo_pin) { echo_pin_ = echo_pin; } + void set_echo_pin(InternalGPIOPin *echo_pin) { echo_pin_ = echo_pin; } /// Set the timeout for waiting for the echo in µs. void set_timeout_us(uint32_t timeout_us); @@ -34,7 +34,8 @@ class UltrasonicSensorComponent : public sensor::Sensor, public PollingComponent /// Helper function to convert the specified distance in meters to the echo duration in µs. GPIOPin *trigger_pin_; - GPIOPin *echo_pin_; + InternalGPIOPin *echo_pin_; + ISRInternalGPIOPin echo_isr_; uint32_t timeout_us_{}; /// 2 meters. uint32_t pulse_time_us_{}; }; diff --git a/esphome/components/uptime/uptime_sensor.cpp b/esphome/components/uptime/uptime_sensor.cpp index 755795ad53..40325d2a36 100644 --- a/esphome/components/uptime/uptime_sensor.cpp +++ b/esphome/components/uptime/uptime_sensor.cpp @@ -1,6 +1,7 @@ #include "uptime_sensor.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/hal.h" namespace esphome { namespace uptime { diff --git a/esphome/components/vl53l0x/vl53l0x_sensor.cpp b/esphome/components/vl53l0x/vl53l0x_sensor.cpp index 65a4ec72bb..d68d69b79c 100644 --- a/esphome/components/vl53l0x/vl53l0x_sensor.cpp +++ b/esphome/components/vl53l0x/vl53l0x_sensor.cpp @@ -36,8 +36,6 @@ void VL53L0XSensor::setup() { if (!esphome::vl53l0x::VL53L0XSensor::enable_pin_setup_complete) { for (auto &vl53_sensor : vl53_sensors) { if (vl53_sensor->enable_pin_ != nullptr) { - // Disable the enable pin to force vl53 to HW Standby mode - ESP_LOGD(TAG, "i2c vl53l0x disable enable pins: GPIO%u", (vl53_sensor->enable_pin_)->get_pin()); // Set enable pin as OUTPUT and disable the enable pin to force vl53 to HW Standby mode vl53_sensor->enable_pin_->setup(); vl53_sensor->enable_pin_->digital_write(false); @@ -111,7 +109,7 @@ void VL53L0XSensor::setup() { reg(0xFF) = 0x00; reg(0x80) = 0x00; - uint8_t ref_spad_map[6]; + uint8_t ref_spad_map[6] = {}; this->read_bytes(0xB0, ref_spad_map, 6); reg(0xFF) = 0x01; @@ -294,7 +292,7 @@ void VL53L0XSensor::loop() { } if (this->waiting_for_interrupt_) { if (reg(0x13).get() & 0x07) { - uint16_t range_mm; + uint16_t range_mm = 0; this->read_byte_16(0x14 + 10, &range_mm); reg(0x0B) = 0x01; this->waiting_for_interrupt_ = false; diff --git a/esphome/components/vl53l0x/vl53l0x_sensor.h b/esphome/components/vl53l0x/vl53l0x_sensor.h index 83a6322dcd..a2e24e7550 100644 --- a/esphome/components/vl53l0x/vl53l0x_sensor.h +++ b/esphome/components/vl53l0x/vl53l0x_sensor.h @@ -3,7 +3,7 @@ #include #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/i2c/i2c.h" diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index f82a8893eb..97777a8986 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -1,8 +1,11 @@ +#ifdef USE_ARDUINO + #include "web_server.h" #include "esphome/core/log.h" #include "esphome/core/application.h" #include "esphome/core/util.h" #include "esphome/components/json/json_util.h" +#include "esphome/components/network/util.h" #include "StreamString.h" @@ -154,7 +157,7 @@ void WebServer::setup() { } void WebServer::dump_config() { ESP_LOGCONFIG(TAG, "Web Server:"); - ESP_LOGCONFIG(TAG, " Address: %s:%u", network_get_address().c_str(), this->base_->get_port()); + ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->base_->get_port()); if (this->using_auth()) { ESP_LOGCONFIG(TAG, " Basic authentication enabled"); } @@ -853,3 +856,5 @@ bool WebServer::isRequestHandlerTrivial() { return false; } } // namespace web_server } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/web_server/web_server.h b/esphome/components/web_server/web_server.h index 54d7356ac9..0eaa2e9a75 100644 --- a/esphome/components/web_server/web_server.h +++ b/esphome/components/web_server/web_server.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include "esphome/core/component.h" #include "esphome/core/controller.h" #include "esphome/components/web_server_base/web_server_base.h" @@ -192,3 +194,5 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { } // namespace web_server } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/web_server_base/web_server_base.cpp b/esphome/components/web_server_base/web_server_base.cpp index 6351941aee..b0babcab46 100644 --- a/esphome/components/web_server_base/web_server_base.cpp +++ b/esphome/components/web_server_base/web_server_base.cpp @@ -1,12 +1,14 @@ +#ifdef USE_ARDUINO + #include "web_server_base.h" #include "esphome/core/log.h" #include "esphome/core/application.h" #include -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #include #endif @@ -27,12 +29,12 @@ void OTARequestHandler::handleUpload(AsyncWebServerRequest *request, const Strin if (index == 0) { ESP_LOGI(TAG, "OTA Update Start: %s", filename.c_str()); this->ota_read_length_ = 0; -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 Update.runAsync(true); // NOLINTNEXTLINE(readability-static-accessed-through-instance) success = Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000); #endif -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 if (Update.isRunning()) Update.abort(); success = Update.begin(UPDATE_SIZE_UNKNOWN, U_FLASH); @@ -97,3 +99,5 @@ float WebServerBase::get_setup_priority() const { } // namespace web_server_base } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/web_server_base/web_server_base.h b/esphome/components/web_server_base/web_server_base.h index 158006ae40..4ef67f959c 100644 --- a/esphome/components/web_server_base/web_server_base.h +++ b/esphome/components/web_server_base/web_server_base.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include #include "esphome/core/component.h" @@ -74,3 +76,5 @@ class OTARequestHandler : public AsyncWebHandler { } // namespace web_server_base } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index c2943d0645..7d029bbea1 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -3,7 +3,6 @@ import esphome.config_validation as cv import esphome.final_validate as fv from esphome import automation from esphome.automation import Condition -from esphome.components.network import add_mdns_library from esphome.const import ( CONF_AP, CONF_BSSID, @@ -24,7 +23,6 @@ from esphome.const import ( CONF_STATIC_IP, CONF_SUBNET, CONF_USE_ADDRESS, - CONF_ENABLE_MDNS, CONF_PRIORITY, CONF_IDENTITY, CONF_CERTIFICATE_AUTHORITY, @@ -34,6 +32,7 @@ from esphome.const import ( CONF_EAP, ) from esphome.core import CORE, HexInt, coroutine_with_priority +from esphome.components.network import IPAddress from . import wpa2_eap @@ -41,7 +40,6 @@ AUTO_LOAD = ["network"] wifi_ns = cg.esphome_ns.namespace("wifi") EAPAuth = wifi_ns.struct("EAPAuth") -IPAddress = cg.global_ns.class_("IPAddress") ManualIP = wifi_ns.struct("ManualIP") WiFiComponent = wifi_ns.class_("WiFiComponent", cg.Component) WiFiAP = wifi_ns.struct("WiFiAP") @@ -155,18 +153,17 @@ def final_validate_power_esp32_ble(value): # WiFi should be in modem sleep (!=NONE) with BLE coexistence # https://docs.espressif.com/projects/esp-idf/en/v3.3.5/api-guides/wifi.html#station-sleep return - framework_version = fv.get_arduino_framework_version() - if framework_version not in (None, "dev") and framework_version < "1.0.5": - # Only frameworks 1.0.5+ impacted - return - full = fv.full_config.get() for conflicting in [ "esp32_ble", "esp32_ble_beacon", "esp32_ble_server", "esp32_ble_tracker", ]: - if conflicting in full: + try: + cv.require_framework_version(esp32_arduino=cv.Version(1, 0, 5))(None) + except cv.Invalid: + pass + else: raise cv.Invalid( f"power_save_mode NONE is incompatible with {conflicting}. " f"Please remove the power save mode. See also " @@ -236,7 +233,6 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_MANUAL_IP): STA_MANUAL_IP_SCHEMA, cv.Optional(CONF_EAP): EAP_AUTH_SCHEMA, cv.Optional(CONF_AP): WIFI_NETWORK_AP, - cv.Optional(CONF_ENABLE_MDNS, default=True): cv.boolean, cv.Optional(CONF_DOMAIN, default=".local"): cv.domain_name, cv.Optional( CONF_REBOOT_TIMEOUT, default="15min" @@ -249,6 +245,10 @@ CONFIG_SCHEMA = cv.All( cv.SplitDefault(CONF_OUTPUT_POWER, esp8266=20.0): cv.All( cv.decibel, cv.float_range(min=10.0, max=20.5) ), + cv.Optional("enable_mdns"): cv.invalid( + "This option has been removed. Please use the [disabled] option under the " + "new mdns component instead." + ), } ), _validate, @@ -345,9 +345,6 @@ async def to_code(config): cg.add_define("USE_WIFI") - if config[CONF_ENABLE_MDNS]: - add_mdns_library() - # Register at end for OTA safe mode await cg.register_component(var, config) diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 0a1e347e8f..6e0ce8c044 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -1,9 +1,9 @@ #include "wifi_component.h" -#ifdef ARDUINO_ARCH_ESP32 +#if defined(USE_ESP32) || defined(USE_ESP_IDF) #include #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #include #endif @@ -14,7 +14,7 @@ #include "esphome/core/helpers.h" #include "esphome/core/log.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/core/util.h" #include "esphome/core/application.h" @@ -39,7 +39,7 @@ void WiFiComponent::setup() { this->wifi_pre_setup_(); uint32_t hash = fnv1_hash(App.get_compilation_time()); - this->pref_ = global_preferences.make_preference(hash, true); + this->pref_ = global_preferences->make_preference(hash, true); SavedWifiSettings save{}; if (this->pref_.load(&save)) { @@ -83,12 +83,10 @@ void WiFiComponent::setup() { esp32_improv::global_improv_component->start(); #endif this->wifi_apply_hostname_(); -#if defined(ARDUINO_ARCH_ESP32) && defined(USE_MDNS) - network_setup_mdns(); -#endif } void WiFiComponent::loop() { + this->wifi_loop_(); const uint32_t now = millis(); if (this->has_sta()) { @@ -158,8 +156,6 @@ void WiFiComponent::loop() { } } } - - network_tick_mdns(); } WiFiComponent::WiFiComponent() { global_wifi_component = this; } @@ -167,9 +163,9 @@ WiFiComponent::WiFiComponent() { global_wifi_component = this; } bool WiFiComponent::has_ap() const { return this->has_ap_; } bool WiFiComponent::has_sta() const { return !this->sta_.empty(); } void WiFiComponent::set_fast_connect(bool fast_connect) { this->fast_connect_ = fast_connect; } -IPAddress WiFiComponent::get_ip_address() { +network::IPAddress WiFiComponent::get_ip_address() { if (this->has_sta()) - return this->wifi_sta_ip_(); + return this->wifi_sta_ip(); if (this->has_ap()) return this->wifi_soft_ap_ip(); return {}; @@ -205,16 +201,13 @@ void WiFiComponent::setup_ap_config_() { ESP_LOGCONFIG(TAG, " AP Password: '%s'", this->ap_.get_password().c_str()); if (this->ap_.get_manual_ip().has_value()) { auto manual = *this->ap_.get_manual_ip(); - ESP_LOGCONFIG(TAG, " AP Static IP: '%s'", manual.static_ip.toString().c_str()); - ESP_LOGCONFIG(TAG, " AP Gateway: '%s'", manual.gateway.toString().c_str()); - ESP_LOGCONFIG(TAG, " AP Subnet: '%s'", manual.subnet.toString().c_str()); + ESP_LOGCONFIG(TAG, " AP Static IP: '%s'", manual.static_ip.str().c_str()); + ESP_LOGCONFIG(TAG, " AP Gateway: '%s'", manual.gateway.str().c_str()); + ESP_LOGCONFIG(TAG, " AP Subnet: '%s'", manual.subnet.str().c_str()); } this->ap_setup_ = this->wifi_start_ap_(this->ap_); - ESP_LOGCONFIG(TAG, " IP Address: %s", this->wifi_soft_ap_ip().toString().c_str()); -#if defined(ARDUINO_ARCH_ESP8266) && defined(USE_MDNS) - network_setup_mdns(this->wifi_soft_ap_ip(), 1); -#endif + ESP_LOGCONFIG(TAG, " IP Address: %s", this->wifi_soft_ap_ip().str().c_str()); if (!this->has_sta()) { this->state_ = WIFI_COMPONENT_STATE_AP; @@ -286,9 +279,8 @@ void WiFiComponent::start_connecting(const WiFiAP &ap, bool two) { } if (ap.get_manual_ip().has_value()) { ManualIP m = *ap.get_manual_ip(); - ESP_LOGV(TAG, " Manual IP: Static IP=%s Gateway=%s Subnet=%s DNS1=%s DNS2=%s", m.static_ip.toString().c_str(), - m.gateway.toString().c_str(), m.subnet.toString().c_str(), m.dns1.toString().c_str(), - m.dns2.toString().c_str()); + ESP_LOGV(TAG, " Manual IP: Static IP=%s Gateway=%s Subnet=%s DNS1=%s DNS2=%s", m.static_ip.str().c_str(), + m.gateway.str().c_str(), m.subnet.str().c_str(), m.dns1.str().c_str(), m.dns2.str().c_str()); } else { ESP_LOGV(TAG, " Using DHCP IP"); } @@ -353,26 +345,23 @@ const LogString *get_signal_bars(int8_t rssi) { } void WiFiComponent::print_connect_params_() { - uint8_t bssid[6] = {}; - uint8_t *raw_bssid = WiFi.BSSID(); - if (raw_bssid != nullptr) - memcpy(bssid, raw_bssid, sizeof(bssid)); + bssid_t bssid = wifi_bssid(); - ESP_LOGCONFIG(TAG, " SSID: " LOG_SECRET("'%s'"), WiFi.SSID().c_str()); - ESP_LOGCONFIG(TAG, " IP Address: %s", WiFi.localIP().toString().c_str()); + ESP_LOGCONFIG(TAG, " SSID: " LOG_SECRET("'%s'"), wifi_ssid().c_str()); + ESP_LOGCONFIG(TAG, " IP Address: %s", wifi_sta_ip().str().c_str()); ESP_LOGCONFIG(TAG, " BSSID: " LOG_SECRET("%02X:%02X:%02X:%02X:%02X:%02X"), bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]); ESP_LOGCONFIG(TAG, " Hostname: '%s'", App.get_name().c_str()); - int8_t rssi = WiFi.RSSI(); + int8_t rssi = wifi_rssi(); ESP_LOGCONFIG(TAG, " Signal strength: %d dB %s", rssi, LOG_STR_ARG(get_signal_bars(rssi))); if (this->selected_ap_.get_bssid().has_value()) { ESP_LOGV(TAG, " Priority: %.1f", this->get_sta_priority(*this->selected_ap_.get_bssid())); } - ESP_LOGCONFIG(TAG, " Channel: %d", WiFi.channel()); - ESP_LOGCONFIG(TAG, " Subnet: %s", WiFi.subnetMask().toString().c_str()); - ESP_LOGCONFIG(TAG, " Gateway: %s", WiFi.gatewayIP().toString().c_str()); - ESP_LOGCONFIG(TAG, " DNS1: %s", WiFi.dnsIP(0).toString().c_str()); - ESP_LOGCONFIG(TAG, " DNS2: %s", WiFi.dnsIP(1).toString().c_str()); + ESP_LOGCONFIG(TAG, " Channel: %d", wifi_channel_()); + ESP_LOGCONFIG(TAG, " Subnet: %s", wifi_subnet_mask_().str().c_str()); + ESP_LOGCONFIG(TAG, " Gateway: %s", wifi_gateway_ip_().str().c_str()); + ESP_LOGCONFIG(TAG, " DNS1: %s", wifi_dns_ip_(0).str().c_str()); + ESP_LOGCONFIG(TAG, " DNS2: %s", wifi_dns_ip_(1).str().c_str()); } void WiFiComponent::start_scanning() { @@ -500,10 +489,10 @@ void WiFiComponent::dump_config() { } void WiFiComponent::check_connecting_finished() { - wl_status_t status = this->wifi_sta_status_(); + auto status = this->wifi_sta_connect_status_(); - if (status == WL_CONNECTED) { - if (WiFi.SSID().equals("")) { + if (status == WiFiSTAConnectStatus::CONNECTED) { + if (wifi_ssid().empty()) { ESP_LOGW(TAG, "Incomplete connection."); this->retry_connect(); return; @@ -527,9 +516,6 @@ void WiFiComponent::check_connecting_finished() { } #endif -#if defined(ARDUINO_ARCH_ESP8266) && defined(USE_MDNS) - network_setup_mdns(this->wifi_sta_ip_(), 0); -#endif this->state_ = WIFI_COMPONENT_STATE_STA_CONNECTED; this->num_retried_ = 0; return; @@ -548,26 +534,23 @@ void WiFiComponent::check_connecting_finished() { return; } - if (status == WL_IDLE_STATUS || status == WL_DISCONNECTED || status == WL_CONNECTION_LOST) { - // WL_DISCONNECTED is set while not connected yet. - // WL_IDLE_STATUS is set while we're waiting for the IP address. - // WL_CONNECTION_LOST happens on the ESP32 + if (status == WiFiSTAConnectStatus::CONNECTING) { return; } - if (status == WL_NO_SSID_AVAIL) { + if (status == WiFiSTAConnectStatus::ERROR_NETWORK_NOT_FOUND) { ESP_LOGW(TAG, "WiFi network can not be found anymore."); this->retry_connect(); return; } - if (status == WL_CONNECT_FAILED) { + if (status == WiFiSTAConnectStatus::ERROR_CONNECT_FAILED) { ESP_LOGW(TAG, "Connecting to WiFi network failed. Are the credentials wrong?"); this->retry_connect(); return; } - ESP_LOGW(TAG, "WiFi Unknown connection status %d", status); + ESP_LOGW(TAG, "WiFi Unknown connection status %d", (int) status); } void WiFiComponent::retry_connect() { @@ -608,8 +591,8 @@ bool WiFiComponent::can_proceed() { } void WiFiComponent::set_reboot_timeout(uint32_t reboot_timeout) { this->reboot_timeout_ = reboot_timeout; } bool WiFiComponent::is_connected() { - return this->state_ == WIFI_COMPONENT_STATE_STA_CONNECTED && this->wifi_sta_status_() == WL_CONNECTED && - !this->error_from_callback_; + return this->state_ == WIFI_COMPONENT_STATE_STA_CONNECTED && + this->wifi_sta_connect_status_() == WiFiSTAConnectStatus::CONNECTED && !this->error_from_callback_; } void WiFiComponent::set_power_save_mode(WiFiPowerSaveMode power_save) { this->power_save_ = power_save; } @@ -641,7 +624,7 @@ void WiFiAP::set_password(const std::string &password) { this->password_ = passw void WiFiAP::set_eap(optional eap_auth) { this->eap_ = std::move(eap_auth); } #endif void WiFiAP::set_channel(optional channel) { this->channel_ = channel; } -void WiFiAP::set_manual_ip(optional manual_ip) { this->manual_ip_ = std::move(manual_ip); } +void WiFiAP::set_manual_ip(optional manual_ip) { this->manual_ip_ = manual_ip; } void WiFiAP::set_hidden(bool hidden) { this->hidden_ = hidden; } const std::string &WiFiAP::get_ssid() const { return this->ssid_; } const optional &WiFiAP::get_bssid() const { return this->bssid_; } diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index 3a4213c93c..d04f15695d 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -5,20 +5,20 @@ #include "esphome/core/defines.h" #include "esphome/core/automation.h" #include "esphome/core/helpers.h" +#include "esphome/components/network/ip_address.h" #include -#include -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32_FRAMEWORK_ARDUINO #include #include #include #endif -#ifdef ARDUINO_ARCH_ESP8266 -#include +#ifdef USE_ESP8266 #include +#include -#if defined(ARDUINO_ARCH_ESP8266) && ARDUINO_VERSION_CODE < VERSION_CODE(2, 4, 0) +#if defined(USE_ESP8266) && ARDUINO_VERSION_CODE < VERSION_CODE(2, 4, 0) extern "C" { #include }; @@ -54,13 +54,21 @@ enum WiFiComponentState { WIFI_COMPONENT_STATE_AP, }; +enum class WiFiSTAConnectStatus : int { + IDLE, + CONNECTING, + CONNECTED, + ERROR_NETWORK_NOT_FOUND, + ERROR_CONNECT_FAILED, +}; + /// Struct for setting static IPs in WiFiComponent. struct ManualIP { - IPAddress static_ip; - IPAddress gateway; - IPAddress subnet; - IPAddress dns1; ///< The first DNS server. 0.0.0.0 for default. - IPAddress dns2; ///< The second DNS server. 0.0.0.0 for default. + network::IPAddress static_ip; + network::IPAddress gateway; + network::IPAddress subnet; + network::IPAddress dns1; ///< The first DNS server. 0.0.0.0 for default. + network::IPAddress dns2; ///< The second DNS server. 0.0.0.0 for default. }; #ifdef USE_WIFI_WPA2_EAP @@ -153,6 +161,10 @@ enum WiFiPowerSaveMode { WIFI_POWER_SAVE_HIGH, }; +#ifdef USE_ESP_IDF +struct IDFWiFiEvent; +#endif + /// This component is responsible for managing the ESP WiFi interface. class WiFiComponent : public Component { public: @@ -207,13 +219,13 @@ class WiFiComponent : public Component { bool has_sta() const; bool has_ap() const; - IPAddress get_ip_address(); + network::IPAddress get_ip_address(); std::string get_use_address() const; void set_use_address(const std::string &use_address); const std::vector &get_scan_result() const { return scan_result_; } - IPAddress wifi_soft_ap_ip(); + network::IPAddress wifi_soft_ap_ip(); bool has_sta_priority(const bssid_t &bssid) { for (auto &it : this->sta_priorities_) @@ -239,36 +251,46 @@ class WiFiComponent : public Component { }); } + network::IPAddress wifi_sta_ip(); + std::string wifi_ssid(); + bssid_t wifi_bssid(); + + int8_t wifi_rssi(); + protected: static std::string format_mac_addr(const uint8_t mac[6]); void setup_ap_config_(); void print_connect_params_(); + void wifi_loop_(); bool wifi_mode_(optional sta, optional ap); bool wifi_sta_pre_setup_(); bool wifi_apply_output_power_(float output_power); bool wifi_apply_power_save_(); bool wifi_sta_ip_config_(optional manual_ip); - IPAddress wifi_sta_ip_(); bool wifi_apply_hostname_(); bool wifi_sta_connect_(const WiFiAP &ap); void wifi_pre_setup_(); - wl_status_t wifi_sta_status_(); + WiFiSTAConnectStatus wifi_sta_connect_status_(); bool wifi_scan_start_(); bool wifi_ap_ip_config_(optional manual_ip); bool wifi_start_ap_(const WiFiAP &ap); bool wifi_disconnect_(); + int32_t wifi_channel_(); + network::IPAddress wifi_subnet_mask_(); + network::IPAddress wifi_gateway_ip_(); + network::IPAddress wifi_dns_ip_(int num); bool is_captive_portal_active_(); bool is_esp32_improv_active_(); -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 static void wifi_event_callback(System_Event_t *event); void wifi_scan_done_callback_(void *arg, STATUS status); static void s_wifi_scan_done_callback(void *arg, STATUS status); #endif -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32_FRAMEWORK_ARDUINO #if ESP_IDF_VERSION_MAJOR >= 4 void wifi_event_callback_(arduino_event_id_t event, arduino_event_info_t info); #else @@ -276,6 +298,9 @@ class WiFiComponent : public Component { #endif void wifi_scan_done_callback_(); #endif +#ifdef USE_ESP_IDF + void wifi_process_event_(IDFWiFiEvent *); +#endif std::string use_address_; std::vector sta_; diff --git a/esphome/components/wifi/wifi_component_esp32.cpp b/esphome/components/wifi/wifi_component_esp32_arduino.cpp similarity index 89% rename from esphome/components/wifi/wifi_component_esp32.cpp rename to esphome/components/wifi/wifi_component_esp32_arduino.cpp index 38c9e12bec..df2692ea81 100644 --- a/esphome/components/wifi/wifi_component_esp32.cpp +++ b/esphome/components/wifi/wifi_component_esp32_arduino.cpp @@ -1,6 +1,6 @@ #include "wifi_component.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32_FRAMEWORK_ARDUINO #include @@ -15,7 +15,7 @@ #include "esphome/core/helpers.h" #include "esphome/core/log.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/core/application.h" #include "esphome/core/util.h" @@ -24,6 +24,12 @@ namespace wifi { static const char *const TAG = "wifi_esp32"; +static bool s_sta_connected = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_sta_got_ip = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_sta_connect_not_found = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_sta_connect_error = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_sta_connecting = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + bool WiFiComponent::wifi_mode_(optional sta, optional ap) { uint8_t current_mode = WiFiClass::getMode(); bool current_sta = current_mode & 0b01; @@ -139,12 +145,12 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { return true; } -IPAddress WiFiComponent::wifi_sta_ip_() { +network::IPAddress WiFiComponent::wifi_sta_ip() { if (!this->has_sta()) - return IPAddress(); + return {}; tcpip_adapter_ip_info_t ip; tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip); - return IPAddress(ip.ip.addr); + return {ip.ip.addr}; } bool WiFiComponent::wifi_apply_hostname_() { @@ -280,6 +286,12 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { return false; } + s_sta_connecting = true; + s_sta_connected = false; + s_sta_got_ip = false; + s_sta_connect_error = false; + s_sta_connect_not_found = false; + return true; } const char *get_auth_mode_str(uint8_t mode) { @@ -429,6 +441,7 @@ void WiFiComponent::wifi_event_callback_(system_event_id_t event, system_event_i buf[it.ssid_len] = '\0'; ESP_LOGV(TAG, "Event: Connected ssid='%s' bssid=" LOG_SECRET("%s") " channel=%u, authmode=%s", buf, format_mac_addr(it.bssid).c_str(), it.channel, get_auth_mode_str(it.authmode)); + s_sta_connected = true; break; } case SYSTEM_EVENT_STA_DISCONNECTED: { @@ -442,10 +455,14 @@ void WiFiComponent::wifi_event_callback_(system_event_id_t event, system_event_i buf[it.ssid_len] = '\0'; if (it.reason == WIFI_REASON_NO_AP_FOUND) { ESP_LOGW(TAG, "Event: Disconnected ssid='%s' reason='Probe Request Unsuccessful'", buf); + s_sta_connect_not_found = true; } else { ESP_LOGW(TAG, "Event: Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason='%s'", buf, format_mac_addr(it.bssid).c_str(), get_disconnect_reason_str(it.reason)); + s_sta_connect_error = true; } + s_sta_connected = false; + s_sta_connecting = false; break; } case SYSTEM_EVENT_STA_AUTHMODE_CHANGE: { @@ -474,10 +491,12 @@ void WiFiComponent::wifi_event_callback_(system_event_id_t event, system_event_i auto it = info.got_ip.ip_info; ESP_LOGV(TAG, "Event: Got IP static_ip=%s gateway=%s", format_ip4_addr(it.ip).c_str(), format_ip4_addr(it.gw).c_str()); + s_sta_got_ip = true; break; } case SYSTEM_EVENT_STA_LOST_IP: { ESP_LOGV(TAG, "Event: Lost IP"); + s_sta_got_ip = false; break; } case SYSTEM_EVENT_AP_START: { @@ -559,7 +578,21 @@ void WiFiComponent::wifi_pre_setup_() { // Make sure WiFi is in clean state before anything starts this->wifi_mode_(false, false); } -wl_status_t WiFiComponent::wifi_sta_status_() { return WiFiClass::status(); } +WiFiSTAConnectStatus WiFiComponent::wifi_sta_connect_status_() { + if (s_sta_connected && s_sta_got_ip) { + return WiFiSTAConnectStatus::CONNECTED; + } + if (s_sta_connect_error) { + return WiFiSTAConnectStatus::ERROR_CONNECT_FAILED; + } + if (s_sta_connect_not_found) { + return WiFiSTAConnectStatus::ERROR_NETWORK_NOT_FOUND; + } + if (s_sta_connecting) { + return WiFiSTAConnectStatus::CONNECTING; + } + return WiFiSTAConnectStatus::IDLE; +} bool WiFiComponent::wifi_scan_start_() { // enable STA if (!this->wifi_mode_(true, {})) @@ -610,9 +643,9 @@ bool WiFiComponent::wifi_ap_ip_config_(optional manual_ip) { info.gw.addr = static_cast(manual_ip->gateway); info.netmask.addr = static_cast(manual_ip->subnet); } else { - info.ip.addr = static_cast(IPAddress(192, 168, 4, 1)); - info.gw.addr = static_cast(IPAddress(192, 168, 4, 1)); - info.netmask.addr = static_cast(IPAddress(255, 255, 255, 0)); + info.ip.addr = static_cast(network::IPAddress(192, 168, 4, 1)); + info.gw.addr = static_cast(network::IPAddress(192, 168, 4, 1)); + info.netmask.addr = static_cast(network::IPAddress(255, 255, 255, 0)); } tcpip_adapter_dhcp_status_t dhcp_status; tcpip_adapter_dhcps_get_status(TCPIP_ADAPTER_IF_AP, &dhcp_status); @@ -630,13 +663,13 @@ bool WiFiComponent::wifi_ap_ip_config_(optional manual_ip) { dhcps_lease_t lease; lease.enable = true; - IPAddress start_address = info.ip.addr; + network::IPAddress start_address = info.ip.addr; start_address[3] += 99; lease.start_ip.addr = static_cast(start_address); - ESP_LOGV(TAG, "DHCP server IP lease start: %s", start_address.toString().c_str()); + ESP_LOGV(TAG, "DHCP server IP lease start: %s", start_address.str().c_str()); start_address[3] += 100; lease.end_ip.addr = static_cast(start_address); - ESP_LOGV(TAG, "DHCP server IP lease end: %s", start_address.toString().c_str()); + ESP_LOGV(TAG, "DHCP server IP lease end: %s", start_address.str().c_str()); err = tcpip_adapter_dhcps_option(TCPIP_ADAPTER_OP_SET, TCPIP_ADAPTER_REQUESTED_IP_ADDRESS, &lease, sizeof(lease)); if (err != ESP_OK) { @@ -694,14 +727,31 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { return true; } -IPAddress WiFiComponent::wifi_soft_ap_ip() { +network::IPAddress WiFiComponent::wifi_soft_ap_ip() { tcpip_adapter_ip_info_t ip; tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &ip); - return IPAddress(ip.ip.addr); + return {ip.ip.addr}; } bool WiFiComponent::wifi_disconnect_() { return esp_wifi_disconnect(); } +bssid_t WiFiComponent::wifi_bssid() { + bssid_t bssid{}; + uint8_t *raw_bssid = WiFi.BSSID(); + if (raw_bssid != nullptr) { + for (size_t i = 0; i < bssid.size(); i++) + bssid[i] = raw_bssid[i]; + } + return bssid; +} +std::string WiFiComponent::wifi_ssid() { return WiFi.SSID().c_str(); } +int8_t WiFiComponent::wifi_rssi() { return WiFi.RSSI(); } +int32_t WiFiComponent::wifi_channel_() { return WiFi.channel(); } +network::IPAddress WiFiComponent::wifi_subnet_mask_() { return {WiFi.subnetMask()}; } +network::IPAddress WiFiComponent::wifi_gateway_ip_() { return {WiFi.gatewayIP()}; } +network::IPAddress WiFiComponent::wifi_dns_ip_(int num) { return {WiFi.dnsIP(num)}; } +void WiFiComponent::wifi_loop_() {} + } // namespace wifi } // namespace esphome -#endif +#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/wifi/wifi_component_esp8266.cpp b/esphome/components/wifi/wifi_component_esp8266.cpp index 5a0ba92b09..5dcfe7a108 100644 --- a/esphome/components/wifi/wifi_component_esp8266.cpp +++ b/esphome/components/wifi/wifi_component_esp8266.cpp @@ -1,7 +1,7 @@ #include "wifi_component.h" #include "esphome/core/macros.h" -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #include @@ -30,7 +30,7 @@ extern "C" { #include "esphome/core/helpers.h" #include "esphome/core/log.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/core/util.h" #include "esphome/core/application.h" @@ -39,6 +39,12 @@ namespace wifi { static const char *const TAG = "wifi_esp8266"; +static bool s_sta_connected = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_sta_got_ip = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_sta_connect_not_found = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_sta_connect_error = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_sta_connecting = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + bool WiFiComponent::wifi_mode_(optional sta, optional ap) { uint8_t current_mode = wifi_get_opmode(); bool current_sta = current_mode & 0b01; @@ -177,7 +183,7 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { return ret; } -IPAddress WiFiComponent::wifi_sta_ip_() { +network::IPAddress WiFiComponent::wifi_sta_ip() { if (!this->has_sta()) return {}; struct ip_info ip {}; @@ -319,6 +325,12 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { } } + s_sta_connecting = true; + s_sta_connected = false; + s_sta_got_ip = false; + s_sta_connect_error = false; + s_sta_connect_not_found = false; + return true; } @@ -453,6 +465,7 @@ void WiFiComponent::wifi_event_callback(System_Event_t *event) { buf[it.ssid_len] = '\0'; ESP_LOGV(TAG, "Event: Connected ssid='%s' bssid=%s channel=%u", buf, format_mac_addr(it.bssid).c_str(), it.channel); + s_sta_connected = true; break; } case EVENT_STAMODE_DISCONNECTED: { @@ -462,10 +475,14 @@ void WiFiComponent::wifi_event_callback(System_Event_t *event) { buf[it.ssid_len] = '\0'; if (it.reason == REASON_NO_AP_FOUND) { ESP_LOGW(TAG, "Event: Disconnected ssid='%s' reason='Probe Request Unsuccessful'", buf); + s_sta_connect_not_found = true; } else { ESP_LOGW(TAG, "Event: Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason='%s'", buf, format_mac_addr(it.bssid).c_str(), LOG_STR_ARG(get_disconnect_reason_str(it.reason))); + s_sta_connect_error = true; } + s_sta_connected = false; + s_sta_connecting = false; break; } case EVENT_STAMODE_AUTHMODE_CHANGE: { @@ -487,6 +504,7 @@ void WiFiComponent::wifi_event_callback(System_Event_t *event) { auto it = event->event_info.got_ip; ESP_LOGV(TAG, "Event: Got IP static_ip=%s gateway=%s netmask=%s", format_ip_addr(it.ip).c_str(), format_ip_addr(it.gw).c_str(), format_ip_addr(it.mask).c_str()); + s_sta_got_ip = true; break; } case EVENT_STAMODE_DHCP_TIMEOUT: { @@ -563,21 +581,22 @@ void WiFiComponent::wifi_pre_setup_() { this->wifi_mode_(false, false); } -wl_status_t WiFiComponent::wifi_sta_status_() { +WiFiSTAConnectStatus WiFiComponent::wifi_sta_connect_status_() { station_status_t status = wifi_station_get_connect_status(); switch (status) { case STATION_GOT_IP: - return WL_CONNECTED; + return WiFiSTAConnectStatus::CONNECTED; case STATION_NO_AP_FOUND: - return WL_NO_SSID_AVAIL; + return WiFiSTAConnectStatus::ERROR_NETWORK_NOT_FOUND; + ; case STATION_CONNECT_FAIL: case STATION_WRONG_PASSWORD: - return WL_CONNECT_FAILED; - case STATION_IDLE: - return WL_IDLE_STATUS; + return WiFiSTAConnectStatus::ERROR_CONNECT_FAILED; case STATION_CONNECTING: + return WiFiSTAConnectStatus::CONNECTING; + case STATION_IDLE: default: - return WL_DISCONNECTED; + return WiFiSTAConnectStatus::IDLE; } } bool WiFiComponent::wifi_scan_start_() { @@ -656,9 +675,9 @@ bool WiFiComponent::wifi_ap_ip_config_(optional manual_ip) { info.gw.addr = static_cast(manual_ip->gateway); info.netmask.addr = static_cast(manual_ip->subnet); } else { - info.ip.addr = static_cast(IPAddress(192, 168, 4, 1)); - info.gw.addr = static_cast(IPAddress(192, 168, 4, 1)); - info.netmask.addr = static_cast(IPAddress(255, 255, 255, 0)); + info.ip.addr = static_cast(network::IPAddress(192, 168, 4, 1)); + info.gw.addr = static_cast(network::IPAddress(192, 168, 4, 1)); + info.netmask.addr = static_cast(network::IPAddress(255, 255, 255, 0)); } if (wifi_softap_dhcps_status() == DHCP_STARTED) { @@ -677,13 +696,13 @@ bool WiFiComponent::wifi_ap_ip_config_(optional manual_ip) { #endif struct dhcps_lease lease {}; - IPAddress start_address = info.ip.addr; + network::IPAddress start_address = info.ip.addr; start_address[3] += 99; lease.start_ip.addr = static_cast(start_address); - ESP_LOGV(TAG, "DHCP server IP lease start: %s", start_address.toString().c_str()); + ESP_LOGV(TAG, "DHCP server IP lease start: %s", start_address.str().c_str()); start_address[3] += 100; lease.end_ip.addr = static_cast(start_address); - ESP_LOGV(TAG, "DHCP server IP lease end: %s", start_address.toString().c_str()); + ESP_LOGV(TAG, "DHCP server IP lease end: %s", start_address.str().c_str()); if (!wifi_softap_set_dhcps_lease(&lease)) { ESP_LOGV(TAG, "Setting SoftAP DHCP lease failed!"); return false; @@ -746,11 +765,27 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { return true; } -IPAddress WiFiComponent::wifi_soft_ap_ip() { +network::IPAddress WiFiComponent::wifi_soft_ap_ip() { struct ip_info ip {}; wifi_get_ip_info(SOFTAP_IF, &ip); return {ip.ip.addr}; } +bssid_t WiFiComponent::wifi_bssid() { + bssid_t bssid{}; + uint8_t *raw_bssid = WiFi.BSSID(); + if (raw_bssid != nullptr) { + for (size_t i = 0; i < bssid.size(); i++) + bssid[i] = raw_bssid[i]; + } + return bssid; +} +std::string WiFiComponent::wifi_ssid() { return WiFi.SSID().c_str(); } +int8_t WiFiComponent::wifi_rssi() { return WiFi.RSSI(); } +int32_t WiFiComponent::wifi_channel_() { return WiFi.channel(); } +network::IPAddress WiFiComponent::wifi_subnet_mask_() { return {WiFi.subnetMask()}; } +network::IPAddress WiFiComponent::wifi_gateway_ip_() { return {WiFi.gatewayIP()}; } +network::IPAddress WiFiComponent::wifi_dns_ip_(int num) { return {WiFi.dnsIP(num)}; } +void WiFiComponent::wifi_loop_() {} } // namespace wifi } // namespace esphome diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp new file mode 100644 index 0000000000..676b7f8fba --- /dev/null +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -0,0 +1,901 @@ +#include "wifi_component.h" + +#ifdef USE_ESP_IDF + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#ifdef USE_WIFI_WPA2_EAP +#include +#endif +#include "lwip/err.h" +#include "lwip/dns.h" + +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" +#include "esphome/core/hal.h" +#include "esphome/core/application.h" +#include "esphome/core/util.h" + +namespace esphome { +namespace wifi { + +static const char *const TAG = "wifi_esp32"; + +static EventGroupHandle_t s_wifi_event_group; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static xQueueHandle s_event_queue; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static esp_netif_t *s_sta_netif = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static esp_netif_t *s_ap_netif = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_sta_started = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_sta_connected = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_sta_got_ip = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_ap_started = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_sta_connect_not_found = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_sta_connect_error = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_sta_connecting = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_wifi_started = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + +struct IDFWiFiEvent { + esp_event_base_t event_base; + int32_t event_id; + union { + wifi_event_sta_scan_done_t sta_scan_done; + wifi_event_sta_connected_t sta_connected; + wifi_event_sta_disconnected_t sta_disconnected; + wifi_event_sta_authmode_change_t sta_authmode_change; + wifi_event_ap_staconnected_t ap_staconnected; + wifi_event_ap_stadisconnected_t ap_stadisconnected; + wifi_event_ap_probe_req_rx_t ap_probe_req_rx; + wifi_event_bss_rssi_low_t bss_rssi_low; + ip_event_got_ip_t ip_got_ip; + ip_event_ap_staipassigned_t ip_ap_staipassigned; + } data; +}; + +// general design: event handler translates events and pushes them to a queue, +// events get processed in the main loop +void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { + IDFWiFiEvent event; + memset(&event, 0, sizeof(IDFWiFiEvent)); + event.event_base = event_base; + event.event_id = event_id; + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { + // no data + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_STOP) { + // no data + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_AUTHMODE_CHANGE) { + memcpy(&event.data.sta_authmode_change, event_data, sizeof(wifi_event_sta_authmode_change_t)); + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) { + memcpy(&event.data.sta_connected, event_data, sizeof(wifi_event_sta_connected_t)); + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { + memcpy(&event.data.sta_disconnected, event_data, sizeof(wifi_event_sta_disconnected_t)); + } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + memcpy(&event.data.ip_got_ip, event_data, sizeof(ip_event_got_ip_t)); + } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_LOST_IP) { + // no data + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_SCAN_DONE) { + memcpy(&event.data.sta_scan_done, event_data, sizeof(wifi_event_sta_scan_done_t)); + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_START) { + // no data + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STOP) { + // no data + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_PROBEREQRECVED) { + memcpy(&event.data.ap_probe_req_rx, event_data, sizeof(wifi_event_ap_probe_req_rx_t)); + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STACONNECTED) { + memcpy(&event.data.ap_staconnected, event_data, sizeof(wifi_event_ap_staconnected_t)); + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STADISCONNECTED) { + memcpy(&event.data.ap_stadisconnected, event_data, sizeof(wifi_event_ap_stadisconnected_t)); + } else if (event_base == IP_EVENT && event_id == IP_EVENT_AP_STAIPASSIGNED) { + memcpy(&event.data.ip_ap_staipassigned, event_data, sizeof(ip_event_ap_staipassigned_t)); + } else { + // did not match any event, don't send anything + return; + } + + // copy to heap to keep queue object small + auto *to_send = new IDFWiFiEvent; // NOLINT(cppcoreguidelines-owning-memory) + memcpy(to_send, &event, sizeof(IDFWiFiEvent)); + // don't block, we may miss events but the core can handle that + if (xQueueSend(s_event_queue, &to_send, 0L) != pdPASS) { + delete to_send; // NOLINT(cppcoreguidelines-owning-memory) + } +} + +void WiFiComponent::wifi_pre_setup_() { + esp_err_t err = esp_netif_init(); + if (err != ERR_OK) { + ESP_LOGE(TAG, "esp_netif_init failed: %s", esp_err_to_name(err)); + return; + } + s_wifi_event_group = xEventGroupCreate(); + if (s_wifi_event_group == nullptr) { + ESP_LOGE(TAG, "xEventGroupCreate failed"); + return; + } + // NOLINTNEXTLINE(bugprone-sizeof-expression) + s_event_queue = xQueueCreate(64, sizeof(IDFWiFiEvent *)); + if (s_event_queue == nullptr) { + ESP_LOGE(TAG, "xQueueCreate failed"); + return; + } + err = esp_event_loop_create_default(); + if (err != ERR_OK) { + ESP_LOGE(TAG, "esp_event_loop_create_default failed: %s", esp_err_to_name(err)); + return; + } + esp_event_handler_instance_t instance_wifi_id, instance_ip_id; + err = esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, nullptr, &instance_wifi_id); + if (err != ERR_OK) { + ESP_LOGE(TAG, "esp_event_handler_instance_register failed: %s", esp_err_to_name(err)); + return; + } + err = esp_event_handler_instance_register(IP_EVENT, ESP_EVENT_ANY_ID, &event_handler, nullptr, &instance_ip_id); + if (err != ERR_OK) { + ESP_LOGE(TAG, "esp_event_handler_instance_register failed: %s", esp_err_to_name(err)); + return; + } + + s_sta_netif = esp_netif_create_default_wifi_sta(); + s_ap_netif = esp_netif_create_default_wifi_ap(); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + // cfg.nvs_enable = false; + err = esp_wifi_init(&cfg); + if (err != ERR_OK) { + ESP_LOGE(TAG, "esp_wifi_init failed: %s", esp_err_to_name(err)); + return; + } + err = esp_wifi_set_storage(WIFI_STORAGE_RAM); + if (err != ERR_OK) { + ESP_LOGE(TAG, "esp_wifi_set_storage failed: %s", esp_err_to_name(err)); + return; + } +} + +bool WiFiComponent::wifi_mode_(optional sta, optional ap) { + esp_err_t err; + wifi_mode_t current_mode = WIFI_MODE_NULL; + if (s_wifi_started) { + err = esp_wifi_get_mode(¤t_mode); + if (err != ERR_OK) { + ESP_LOGW(TAG, "esp_wifi_get_mode failed: %s", esp_err_to_name(err)); + return false; + } + } + bool current_sta = current_mode == WIFI_MODE_STA || current_mode == WIFI_MODE_APSTA; + bool current_ap = current_mode == WIFI_MODE_AP || current_mode == WIFI_MODE_APSTA; + + bool set_sta = sta.has_value() ? *sta : current_sta; + bool set_ap = ap.has_value() ? *ap : current_ap; + + wifi_mode_t set_mode; + if (set_sta && set_ap) + set_mode = WIFI_MODE_APSTA; + else if (set_sta && !set_ap) + set_mode = WIFI_MODE_STA; + else if (!set_sta && set_ap) + set_mode = WIFI_MODE_AP; + else + set_mode = WIFI_MODE_NULL; + + if (current_mode == set_mode) + return true; + + if (set_sta && !current_sta) { + ESP_LOGV(TAG, "Enabling STA."); + } else if (!set_sta && current_sta) { + ESP_LOGV(TAG, "Disabling STA."); + } + if (set_ap && !current_ap) { + ESP_LOGV(TAG, "Enabling AP."); + } else if (!set_ap && current_ap) { + ESP_LOGV(TAG, "Disabling AP."); + } + + if (set_mode == WIFI_MODE_NULL && s_wifi_started) { + err = esp_wifi_stop(); + if (err != ESP_OK) { + ESP_LOGV(TAG, "esp_wifi_stop failed: %s", esp_err_to_name(err)); + return false; + } + s_wifi_started = false; + return true; + } + + err = esp_wifi_set_mode(set_mode); + if (err != ERR_OK) { + ESP_LOGW(TAG, "esp_wifi_set_mode failed: %s", esp_err_to_name(err)); + return false; + } + + if (set_mode != WIFI_MODE_NULL && !s_wifi_started) { + err = esp_wifi_start(); + if (err != ESP_OK) { + ESP_LOGV(TAG, "esp_wifi_start failed: %s", esp_err_to_name(err)); + return false; + } + s_wifi_started = true; + } + + return true; +} + +bool WiFiComponent::wifi_sta_pre_setup_() { return this->wifi_mode_(true, {}); } + +bool WiFiComponent::wifi_apply_output_power_(float output_power) { + int8_t val = static_cast(output_power * 4); + return esp_wifi_set_max_tx_power(val) == ESP_OK; +} + +bool WiFiComponent::wifi_apply_power_save_() { + wifi_ps_type_t power_save; + switch (this->power_save_) { + case WIFI_POWER_SAVE_LIGHT: + power_save = WIFI_PS_MIN_MODEM; + break; + case WIFI_POWER_SAVE_HIGH: + power_save = WIFI_PS_MAX_MODEM; + break; + case WIFI_POWER_SAVE_NONE: + default: + power_save = WIFI_PS_NONE; + break; + } + return esp_wifi_set_ps(power_save) == ESP_OK; +} + +bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { + // enable STA + if (!this->wifi_mode_(true, {})) + return false; + + // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv417wifi_sta_config_t + wifi_config_t conf; + memset(&conf, 0, sizeof(conf)); + strncpy(reinterpret_cast(conf.sta.ssid), ap.get_ssid().c_str(), sizeof(conf.sta.ssid)); + strncpy(reinterpret_cast(conf.sta.password), ap.get_password().c_str(), sizeof(conf.sta.password)); + + // The weakest authmode to accept in the fast scan mode + if (ap.get_password().empty()) { + conf.sta.threshold.authmode = WIFI_AUTH_OPEN; + } else { + conf.sta.threshold.authmode = WIFI_AUTH_WPA_WPA2_PSK; + } + +#ifdef USE_WIFI_WPA2_EAP + if (ap.get_eap().has_value()) { + conf.sta.threshold.authmode = WIFI_AUTH_WPA2_ENTERPRISE; + } +#endif + + if (ap.get_bssid().has_value()) { + conf.sta.bssid_set = true; + memcpy(conf.sta.bssid, ap.get_bssid()->data(), 6); + } else { + conf.sta.bssid_set = false; + } + if (ap.get_channel().has_value()) { + conf.sta.channel = *ap.get_channel(); + conf.sta.scan_method = WIFI_FAST_SCAN; + } else { + conf.sta.scan_method = WIFI_ALL_CHANNEL_SCAN; + } + // Listen interval for ESP32 station to receive beacon when WIFI_PS_MAX_MODEM is set. + // Units: AP beacon intervals. Defaults to 3 if set to 0. + conf.sta.listen_interval = 0; + +#if ESP_IDF_VERSION_MAJOR >= 4 + // Protected Management Frame + // Device will prefer to connect in PMF mode if other device also advertizes PMF capability. + conf.sta.pmf_cfg.capable = true; + conf.sta.pmf_cfg.required = false; +#endif + + // note, we do our own filtering + // The minimum rssi to accept in the fast scan mode + conf.sta.threshold.rssi = -127; + + conf.sta.threshold.authmode = WIFI_AUTH_OPEN; + + wifi_config_t current_conf; + esp_err_t err; + err = esp_wifi_get_config(WIFI_IF_STA, ¤t_conf); + if (err != ERR_OK) { + ESP_LOGW(TAG, "esp_wifi_get_config failed: %s", esp_err_to_name(err)); + // can continue + } + + if (memcmp(¤t_conf, &conf, sizeof(wifi_config_t)) != 0) { + err = esp_wifi_disconnect(); + if (err != ESP_OK) { + ESP_LOGV(TAG, "esp_wifi_disconnect failed: %s", esp_err_to_name(err)); + return false; + } + } + + err = esp_wifi_set_config(WIFI_IF_STA, &conf); + if (err != ESP_OK) { + ESP_LOGV(TAG, "esp_wifi_set_config failed: %s", esp_err_to_name(err)); + return false; + } + + if (!this->wifi_sta_ip_config_(ap.get_manual_ip())) { + return false; + } + + // setup enterprise authentication if required +#ifdef USE_WIFI_WPA2_EAP + if (ap.get_eap().has_value()) { + // note: all certificates and keys have to be null terminated. Lengths are appended by +1 to include \0. + EAPAuth eap = ap.get_eap().value(); + err = esp_wifi_sta_wpa2_ent_set_identity((uint8_t *) eap.identity.c_str(), eap.identity.length()); + if (err != ESP_OK) { + ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_identity failed! %d", err); + } + int ca_cert_len = strlen(eap.ca_cert); + int client_cert_len = strlen(eap.client_cert); + int client_key_len = strlen(eap.client_key); + if (ca_cert_len) { + err = esp_wifi_sta_wpa2_ent_set_ca_cert((uint8_t *) eap.ca_cert, ca_cert_len + 1); + if (err != ESP_OK) { + ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_ca_cert failed! %d", err); + } + } + // workout what type of EAP this is + // validation is not required as the config tool has already validated it + if (client_cert_len && client_key_len) { + // if we have certs, this must be EAP-TLS + err = esp_wifi_sta_wpa2_ent_set_cert_key((uint8_t *) eap.client_cert, client_cert_len + 1, + (uint8_t *) eap.client_key, client_key_len + 1, + (uint8_t *) eap.password.c_str(), strlen(eap.password.c_str())); + if (err != ESP_OK) { + ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_cert_key failed! %d", err); + } + } else { + // in the absence of certs, assume this is username/password based + err = esp_wifi_sta_wpa2_ent_set_username((uint8_t *) eap.username.c_str(), eap.username.length()); + if (err != ESP_OK) { + ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_username failed! %d", err); + } + err = esp_wifi_sta_wpa2_ent_set_password((uint8_t *) eap.password.c_str(), eap.password.length()); + if (err != ESP_OK) { + ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_password failed! %d", err); + } + } + esp_wpa2_config_t wpa2_config = WPA2_CONFIG_INIT_DEFAULT(); + err = esp_wifi_sta_wpa2_ent_enable(&wpa2_config); + if (err != ESP_OK) { + ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_enable failed! %d", err); + } + } +#endif // USE_WIFI_WPA2_EAP + + err = esp_wifi_connect(); + if (err != ESP_OK) { + ESP_LOGW(TAG, "esp_wifi_connect failed: %s", esp_err_to_name(err)); + return false; + } + + s_sta_connecting = true; + s_sta_connected = false; + s_sta_got_ip = false; + s_sta_connect_error = false; + s_sta_connect_not_found = false; + return true; +} + +bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { + // enable STA + if (!this->wifi_mode_(true, {})) + return false; + + tcpip_adapter_dhcp_status_t dhcp_status; + esp_err_t err = tcpip_adapter_dhcpc_get_status(TCPIP_ADAPTER_IF_STA, &dhcp_status); + if (err != ESP_OK) { + ESP_LOGV(TAG, "tcpip_adapter_dhcpc_get_status failed: %s", esp_err_to_name(err)); + return false; + } + + if (!manual_ip.has_value()) { + // Use DHCP client + if (dhcp_status != TCPIP_ADAPTER_DHCP_STARTED) { + err = tcpip_adapter_dhcpc_start(TCPIP_ADAPTER_IF_STA); + if (err != ESP_OK) { + ESP_LOGV(TAG, "Starting DHCP client failed! %d", err); + } + return err == ESP_OK; + } + return true; + } + + tcpip_adapter_ip_info_t info; + memset(&info, 0, sizeof(info)); + info.ip.addr = static_cast(manual_ip->static_ip); + info.gw.addr = static_cast(manual_ip->gateway); + info.netmask.addr = static_cast(manual_ip->subnet); + + err = tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA); + if (err != ESP_OK) { + ESP_LOGV(TAG, "tcpip_adapter_dhcpc_stop failed: %s", esp_err_to_name(err)); + return false; + } + + err = tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &info); + if (err != ESP_OK) { + ESP_LOGV(TAG, "tcpip_adapter_set_ip_info failed: %s", esp_err_to_name(err)); + return false; + } + + ip_addr_t dns; + dns.type = IPADDR_TYPE_V4; + if (uint32_t(manual_ip->dns1) != 0) { + dns.u_addr.ip4.addr = static_cast(manual_ip->dns1); + dns_setserver(0, &dns); + } + if (uint32_t(manual_ip->dns2) != 0) { + dns.u_addr.ip4.addr = static_cast(manual_ip->dns2); + dns_setserver(1, &dns); + } + + return true; +} + +network::IPAddress WiFiComponent::wifi_sta_ip() { + if (!this->has_sta()) + return {}; + tcpip_adapter_ip_info_t ip; + esp_err_t err = tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip); + if (err != ESP_OK) { + ESP_LOGV(TAG, "tcpip_adapter_get_ip_info failed: %s", esp_err_to_name(err)); + return false; + } + return {ip.ip.addr}; +} + +bool WiFiComponent::wifi_apply_hostname_() { + // setting is done in SYSTEM_EVENT_STA_START callback + return true; +} +const char *get_auth_mode_str(uint8_t mode) { + switch (mode) { + case WIFI_AUTH_OPEN: + return "OPEN"; + case WIFI_AUTH_WEP: + return "WEP"; + case WIFI_AUTH_WPA_PSK: + return "WPA PSK"; + case WIFI_AUTH_WPA2_PSK: + return "WPA2 PSK"; + case WIFI_AUTH_WPA_WPA2_PSK: + return "WPA/WPA2 PSK"; + case WIFI_AUTH_WPA2_ENTERPRISE: + return "WPA2 Enterprise"; + case WIFI_AUTH_WPA3_PSK: + return "WPA3 PSK"; + case WIFI_AUTH_WPA2_WPA3_PSK: + return "WPA2/WPA3 PSK"; + case WIFI_AUTH_WAPI_PSK: + return "WAPI PSK"; + default: + return "UNKNOWN"; + } +} + +std::string format_ip4_addr(const esp_ip4_addr_t &ip) { + char buf[20]; + snprintf(buf, sizeof(buf), "%u.%u.%u.%u", uint8_t(ip.addr >> 0), uint8_t(ip.addr >> 8), uint8_t(ip.addr >> 16), + uint8_t(ip.addr >> 24)); + return buf; +} +const char *get_disconnect_reason_str(uint8_t reason) { + switch (reason) { + case WIFI_REASON_AUTH_EXPIRE: + return "Auth Expired"; + case WIFI_REASON_AUTH_LEAVE: + return "Auth Leave"; + case WIFI_REASON_ASSOC_EXPIRE: + return "Association Expired"; + case WIFI_REASON_ASSOC_TOOMANY: + return "Too Many Associations"; + case WIFI_REASON_NOT_AUTHED: + return "Not Authenticated"; + case WIFI_REASON_NOT_ASSOCED: + return "Not Associated"; + case WIFI_REASON_ASSOC_LEAVE: + return "Association Leave"; + case WIFI_REASON_ASSOC_NOT_AUTHED: + return "Association not Authenticated"; + case WIFI_REASON_DISASSOC_PWRCAP_BAD: + return "Disassociate Power Cap Bad"; + case WIFI_REASON_DISASSOC_SUPCHAN_BAD: + return "Disassociate Supported Channel Bad"; + case WIFI_REASON_IE_INVALID: + return "IE Invalid"; + case WIFI_REASON_MIC_FAILURE: + return "Mic Failure"; + case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT: + return "4-Way Handshake Timeout"; + case WIFI_REASON_GROUP_KEY_UPDATE_TIMEOUT: + return "Group Key Update Timeout"; + case WIFI_REASON_IE_IN_4WAY_DIFFERS: + return "IE In 4-Way Handshake Differs"; + case WIFI_REASON_GROUP_CIPHER_INVALID: + return "Group Cipher Invalid"; + case WIFI_REASON_PAIRWISE_CIPHER_INVALID: + return "Pairwise Cipher Invalid"; + case WIFI_REASON_AKMP_INVALID: + return "AKMP Invalid"; + case WIFI_REASON_UNSUPP_RSN_IE_VERSION: + return "Unsupported RSN IE version"; + case WIFI_REASON_INVALID_RSN_IE_CAP: + return "Invalid RSN IE Cap"; + case WIFI_REASON_802_1X_AUTH_FAILED: + return "802.1x Authentication Failed"; + case WIFI_REASON_CIPHER_SUITE_REJECTED: + return "Cipher Suite Rejected"; + case WIFI_REASON_BEACON_TIMEOUT: + return "Beacon Timeout"; + case WIFI_REASON_NO_AP_FOUND: + return "AP Not Found"; + case WIFI_REASON_AUTH_FAIL: + return "Authentication Failed"; + case WIFI_REASON_ASSOC_FAIL: + return "Association Failed"; + case WIFI_REASON_HANDSHAKE_TIMEOUT: + return "Handshake Failed"; + case WIFI_REASON_CONNECTION_FAIL: + return "Connection Failed"; + case WIFI_REASON_UNSPECIFIED: + default: + return "Unspecified"; + } +} + +void WiFiComponent::wifi_loop_() { + while (true) { + IDFWiFiEvent *data; + if (xQueueReceive(s_event_queue, &data, 0L) != pdTRUE) { + // no event ready + break; + } + + // process event + wifi_process_event_(data); + + delete data; // NOLINT(cppcoreguidelines-owning-memory) + } +} +void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) { + esp_err_t err; + if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_STA_START) { + ESP_LOGV(TAG, "Event: WiFi STA start"); + // apply hostname + err = tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_STA, App.get_name().c_str()); + if (err != ERR_OK) { + ESP_LOGW(TAG, "tcpip_adapter_set_hostname failed: %s", esp_err_to_name(err)); + } + + s_sta_started = true; + // re-apply power save mode + wifi_apply_power_save_(); + + } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_STA_STOP) { + ESP_LOGV(TAG, "Event: WiFi STA stop"); + s_sta_started = false; + + } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_STA_AUTHMODE_CHANGE) { + const auto &it = data->data.sta_authmode_change; + ESP_LOGV(TAG, "Event: Authmode Change old=%s new=%s", get_auth_mode_str(it.old_mode), + get_auth_mode_str(it.new_mode)); + + } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_STA_CONNECTED) { + const auto &it = data->data.sta_connected; + char buf[33]; + assert(it.ssid_len <= 32); + memcpy(buf, it.ssid, it.ssid_len); + buf[it.ssid_len] = '\0'; + ESP_LOGV(TAG, "Event: Connected ssid='%s' bssid=" LOG_SECRET("%s") " channel=%u, authmode=%s", buf, + format_mac_addr(it.bssid).c_str(), it.channel, get_auth_mode_str(it.authmode)); + s_sta_connected = true; + + } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_STA_DISCONNECTED) { + const auto &it = data->data.sta_disconnected; + char buf[33]; + assert(it.ssid_len <= 32); + memcpy(buf, it.ssid, it.ssid_len); + buf[it.ssid_len] = '\0'; + if (it.reason == WIFI_REASON_NO_AP_FOUND) { + ESP_LOGW(TAG, "Event: Disconnected ssid='%s' reason='Probe Request Unsuccessful'", buf); + s_sta_connect_not_found = true; + + } else { + ESP_LOGW(TAG, "Event: Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason='%s'", buf, + format_mac_addr(it.bssid).c_str(), get_disconnect_reason_str(it.reason)); + s_sta_connect_error = true; + } + s_sta_connected = false; + s_sta_connecting = false; + error_from_callback_ = true; + + } else if (data->event_base == IP_EVENT && data->event_id == IP_EVENT_STA_GOT_IP) { + const auto &it = data->data.ip_got_ip; + ESP_LOGV(TAG, "Event: Got IP static_ip=%s gateway=%s", format_ip4_addr(it.ip_info.ip).c_str(), + format_ip4_addr(it.ip_info.gw).c_str()); + s_sta_got_ip = true; + + } else if (data->event_base == IP_EVENT && data->event_id == IP_EVENT_STA_LOST_IP) { + ESP_LOGV(TAG, "Event: Lost IP"); + s_sta_got_ip = false; + + } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_SCAN_DONE) { + const auto &it = data->data.sta_scan_done; + ESP_LOGV(TAG, "Event: WiFi Scan Done status=%u number=%u scan_id=%u", it.status, it.number, it.scan_id); + + scan_result_.clear(); + this->scan_done_ = true; + if (it.status != 0) { + // scan error + return; + } + + uint16_t number = it.number; + std::vector records(number); + err = esp_wifi_scan_get_ap_records(&number, records.data()); + if (err != ESP_OK) { + ESP_LOGW(TAG, "esp_wifi_scan_get_ap_records failed: %s", esp_err_to_name(err)); + return; + } + records.resize(number); + + scan_result_.reserve(number); + for (int i = 0; i < number; i++) { + auto &record = records[i]; + bssid_t bssid; + std::copy(record.bssid, record.bssid + 6, bssid.begin()); + std::string ssid(reinterpret_cast(record.ssid)); + WiFiScanResult result(bssid, ssid, record.primary, record.rssi, record.authmode != WIFI_AUTH_OPEN, ssid.empty()); + scan_result_.push_back(result); + } + + } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_START) { + ESP_LOGV(TAG, "Event: WiFi AP start"); + s_ap_started = true; + + } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_STOP) { + ESP_LOGV(TAG, "Event: WiFi AP stop"); + s_ap_started = false; + + } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_PROBEREQRECVED) { + const auto &it = data->data.ap_probe_req_rx; + ESP_LOGVV(TAG, "Event: AP receive Probe Request MAC=%s RSSI=%d", format_mac_addr(it.mac).c_str(), it.rssi); + + } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_STACONNECTED) { + const auto &it = data->data.ap_staconnected; + ESP_LOGV(TAG, "Event: AP client connected MAC=%s", format_mac_addr(it.mac).c_str()); + + } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_STADISCONNECTED) { + const auto &it = data->data.ap_stadisconnected; + ESP_LOGV(TAG, "Event: AP client disconnected MAC=%s", format_mac_addr(it.mac).c_str()); + + } else if (data->event_base == IP_EVENT && data->event_id == IP_EVENT_AP_STAIPASSIGNED) { + const auto &it = data->data.ip_ap_staipassigned; + ESP_LOGV(TAG, "Event: AP client assigned IP %s", format_ip4_addr(it.ip).c_str()); + } +} + +WiFiSTAConnectStatus WiFiComponent::wifi_sta_connect_status_() { + if (s_sta_connected && s_sta_got_ip) { + return WiFiSTAConnectStatus::CONNECTED; + } + if (s_sta_connect_error) { + return WiFiSTAConnectStatus::ERROR_CONNECT_FAILED; + } + if (s_sta_connect_not_found) { + return WiFiSTAConnectStatus::ERROR_NETWORK_NOT_FOUND; + } + if (s_sta_connecting) { + return WiFiSTAConnectStatus::CONNECTING; + } + return WiFiSTAConnectStatus::IDLE; +} +bool WiFiComponent::wifi_scan_start_() { + // enable STA + if (!this->wifi_mode_(true, {})) + return false; + + wifi_scan_config_t config{}; + config.ssid = nullptr; + config.bssid = nullptr; + config.channel = 0; + config.show_hidden = true; + config.scan_type = WIFI_SCAN_TYPE_ACTIVE; + config.scan_time.active.min = 100; + config.scan_time.active.max = 300; + + esp_err_t err = esp_wifi_scan_start(&config, false); + if (err != ESP_OK) { + ESP_LOGV(TAG, "esp_wifi_scan_start failed: %s", esp_err_to_name(err)); + return false; + } + + scan_done_ = false; + return true; +} +bool WiFiComponent::wifi_ap_ip_config_(optional manual_ip) { + esp_err_t err; + + // enable AP + if (!this->wifi_mode_({}, true)) + return false; + + tcpip_adapter_ip_info_t info; + memset(&info, 0, sizeof(info)); + if (manual_ip.has_value()) { + info.ip.addr = static_cast(manual_ip->static_ip); + info.gw.addr = static_cast(manual_ip->gateway); + info.netmask.addr = static_cast(manual_ip->subnet); + } else { + info.ip.addr = static_cast(network::IPAddress(192, 168, 4, 1)); + info.gw.addr = static_cast(network::IPAddress(192, 168, 4, 1)); + info.netmask.addr = static_cast(network::IPAddress(255, 255, 255, 0)); + } + tcpip_adapter_dhcp_status_t dhcp_status; + tcpip_adapter_dhcps_get_status(TCPIP_ADAPTER_IF_AP, &dhcp_status); + err = tcpip_adapter_dhcps_stop(TCPIP_ADAPTER_IF_AP); + if (err != ESP_OK) { + ESP_LOGV(TAG, "tcpip_adapter_dhcps_stop failed! %d", err); + return false; + } + + err = tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_AP, &info); + if (err != ESP_OK) { + ESP_LOGV(TAG, "tcpip_adapter_set_ip_info failed! %d", err); + return false; + } + + dhcps_lease_t lease; + lease.enable = true; + network::IPAddress start_address = info.ip.addr; + start_address[3] += 99; + lease.start_ip.addr = static_cast(start_address); + ESP_LOGV(TAG, "DHCP server IP lease start: %s", start_address.str().c_str()); + start_address[3] += 100; + lease.end_ip.addr = static_cast(start_address); + ESP_LOGV(TAG, "DHCP server IP lease end: %s", start_address.str().c_str()); + err = tcpip_adapter_dhcps_option(TCPIP_ADAPTER_OP_SET, TCPIP_ADAPTER_REQUESTED_IP_ADDRESS, &lease, sizeof(lease)); + + if (err != ESP_OK) { + ESP_LOGV(TAG, "tcpip_adapter_dhcps_option failed! %d", err); + return false; + } + + err = tcpip_adapter_dhcps_start(TCPIP_ADAPTER_IF_AP); + + if (err != ESP_OK) { + ESP_LOGV(TAG, "tcpip_adapter_dhcps_start failed! %d", err); + return false; + } + + return true; +} +bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { + // enable AP + if (!this->wifi_mode_({}, true)) + return false; + + wifi_config_t conf; + memset(&conf, 0, sizeof(conf)); + strncpy(reinterpret_cast(conf.ap.ssid), ap.get_ssid().c_str(), sizeof(conf.ap.ssid)); + conf.ap.channel = ap.get_channel().value_or(1); + conf.ap.ssid_hidden = ap.get_ssid().size(); + conf.ap.max_connection = 5; + conf.ap.beacon_interval = 100; + + if (ap.get_password().empty()) { + conf.ap.authmode = WIFI_AUTH_OPEN; + *conf.ap.password = 0; + } else { + conf.ap.authmode = WIFI_AUTH_WPA2_PSK; + strncpy(reinterpret_cast(conf.ap.password), ap.get_password().c_str(), sizeof(conf.ap.password)); + } + +#if ESP_IDF_VERSION_MAJOR >= 4 + // pairwise cipher of SoftAP, group cipher will be derived using this. + conf.ap.pairwise_cipher = WIFI_CIPHER_TYPE_CCMP; +#endif + + esp_err_t err = esp_wifi_set_config(WIFI_IF_AP, &conf); + if (err != ESP_OK) { + ESP_LOGV(TAG, "esp_wifi_set_config failed! %d", err); + return false; + } + + if (!this->wifi_ap_ip_config_(ap.get_manual_ip())) { + ESP_LOGV(TAG, "wifi_ap_ip_config_ failed!"); + return false; + } + + return true; +} +network::IPAddress WiFiComponent::wifi_soft_ap_ip() { + tcpip_adapter_ip_info_t ip; + tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &ip); + return {ip.ip.addr}; +} +bool WiFiComponent::wifi_disconnect_() { return esp_wifi_disconnect(); } + +bssid_t WiFiComponent::wifi_bssid() { + wifi_ap_record_t info; + esp_err_t err = esp_wifi_sta_get_ap_info(&info); + bssid_t res{}; + if (err != ESP_OK) { + ESP_LOGW(TAG, "esp_wifi_sta_get_ap_info failed: %s", esp_err_to_name(err)); + return res; + } + std::copy(info.bssid, info.bssid + 6, res.begin()); + return res; +} +std::string WiFiComponent::wifi_ssid() { + wifi_ap_record_t info{}; + esp_err_t err = esp_wifi_sta_get_ap_info(&info); + if (err != ESP_OK) { + ESP_LOGW(TAG, "esp_wifi_sta_get_ap_info failed: %s", esp_err_to_name(err)); + return ""; + } + auto *ssid_s = reinterpret_cast(info.ssid); + size_t len = strnlen(ssid_s, sizeof(info.ssid)); + return {ssid_s, len}; +} +int8_t WiFiComponent::wifi_rssi() { + wifi_ap_record_t info; + esp_err_t err = esp_wifi_sta_get_ap_info(&info); + if (err != ESP_OK) { + ESP_LOGW(TAG, "esp_wifi_sta_get_ap_info failed: %s", esp_err_to_name(err)); + return 0; + } + return info.rssi; +} +int32_t WiFiComponent::wifi_channel_() { + uint8_t primary; + wifi_second_chan_t second; + esp_err_t err = esp_wifi_get_channel(&primary, &second); + if (err != ESP_OK) { + ESP_LOGW(TAG, "esp_wifi_get_channel failed: %s", esp_err_to_name(err)); + return 0; + } + return primary; +} +network::IPAddress WiFiComponent::wifi_subnet_mask_() { + esp_netif_ip_info_t ip; + esp_err_t err = esp_netif_get_ip_info(s_sta_netif, &ip); + if (err != ESP_OK) { + ESP_LOGW(TAG, "esp_netif_get_ip_info failed: %s", esp_err_to_name(err)); + return {}; + } + return {ip.netmask.addr}; +} +network::IPAddress WiFiComponent::wifi_gateway_ip_() { + esp_netif_ip_info_t ip; + esp_err_t err = esp_netif_get_ip_info(s_sta_netif, &ip); + if (err != ESP_OK) { + ESP_LOGW(TAG, "esp_netif_get_ip_info failed: %s", esp_err_to_name(err)); + return {}; + } + return {ip.gw.addr}; +} +network::IPAddress WiFiComponent::wifi_dns_ip_(int num) { + const ip_addr_t *dns_ip = dns_getserver(num); + return {dns_ip->u_addr.ip4.addr}; +} + +} // namespace wifi +} // namespace esphome + +#endif diff --git a/esphome/components/wifi_info/wifi_info_text_sensor.h b/esphome/components/wifi_info/wifi_info_text_sensor.h index 6d2be08fa0..de1c7f71fc 100644 --- a/esphome/components/wifi_info/wifi_info_text_sensor.h +++ b/esphome/components/wifi_info/wifi_info_text_sensor.h @@ -10,10 +10,10 @@ namespace wifi_info { class IPAddressWiFiInfo : public Component, public text_sensor::TextSensor { public: void loop() override { - IPAddress ip = WiFi.localIP(); + auto ip = wifi::global_wifi_component->wifi_sta_ip(); if (ip != this->last_ip_) { this->last_ip_ = ip; - this->publish_state(ip.toString().c_str()); + this->publish_state(ip.str().c_str()); } } float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } @@ -21,15 +21,15 @@ class IPAddressWiFiInfo : public Component, public text_sensor::TextSensor { void dump_config() override; protected: - IPAddress last_ip_; + network::IPAddress last_ip_; }; class SSIDWiFiInfo : public Component, public text_sensor::TextSensor { public: void loop() override { - String ssid = WiFi.SSID(); - if (this->last_ssid_ != ssid.c_str()) { - this->last_ssid_ = std::string(ssid.c_str()); + std::string ssid = wifi::global_wifi_component->wifi_ssid(); + if (this->last_ssid_ != ssid) { + this->last_ssid_ = ssid; this->publish_state(this->last_ssid_); } } @@ -44,9 +44,9 @@ class SSIDWiFiInfo : public Component, public text_sensor::TextSensor { class BSSIDWiFiInfo : public Component, public text_sensor::TextSensor { public: void loop() override { - uint8_t *bssid = WiFi.BSSID(); - if (memcmp(bssid, this->last_bssid_.data(), 6) != 0) { - std::copy(bssid, bssid + 6, this->last_bssid_.data()); + wifi::bssid_t bssid = wifi::global_wifi_component->wifi_bssid(); + if (memcmp(bssid.data(), last_bssid_.data(), 6) != 0) { + std::copy(bssid.begin(), bssid.end(), last_bssid_.begin()); char buf[30]; sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]); this->publish_state(buf); diff --git a/esphome/components/wifi_signal/wifi_signal_sensor.h b/esphome/components/wifi_signal/wifi_signal_sensor.h index 8fe108a530..f797aaa590 100644 --- a/esphome/components/wifi_signal/wifi_signal_sensor.h +++ b/esphome/components/wifi_signal/wifi_signal_sensor.h @@ -10,7 +10,7 @@ namespace wifi_signal { class WiFiSignalSensor : public sensor::Sensor, public PollingComponent { public: - void update() override { this->publish_state(WiFi.RSSI()); } + void update() override { this->publish_state(wifi::global_wifi_component->wifi_rssi()); } void dump_config() override; std::string unique_id() override { return get_mac_address() + "-wifisignal"; } diff --git a/esphome/components/wled/__init__.py b/esphome/components/wled/__init__.py index c9e23bb7eb..2795529203 100644 --- a/esphome/components/wled/__init__.py +++ b/esphome/components/wled/__init__.py @@ -7,7 +7,7 @@ from esphome.const import CONF_NAME, CONF_PORT wled_ns = cg.esphome_ns.namespace("wled") WLEDLightEffect = wled_ns.class_("WLEDLightEffect", AddressableLightEffect) -CONFIG_SCHEMA = cv.Schema({}) +CONFIG_SCHEMA = cv.All(cv.Schema({}), cv.only_with_arduino) @register_addressable_effect( diff --git a/esphome/components/wled/wled_light_effect.cpp b/esphome/components/wled/wled_light_effect.cpp index 500b9bbad2..8c68bca6e3 100644 --- a/esphome/components/wled/wled_light_effect.cpp +++ b/esphome/components/wled/wled_light_effect.cpp @@ -1,12 +1,14 @@ +#ifdef USE_ARDUINO + #include "wled_light_effect.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 #include #include #endif @@ -246,3 +248,5 @@ bool WLEDLightEffect::parse_dnrgb_frame_(light::AddressableLight &it, const uint } // namespace wled } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/wled/wled_light_effect.h b/esphome/components/wled/wled_light_effect.h index 2a7654ec27..f0021ca978 100644 --- a/esphome/components/wled/wled_light_effect.h +++ b/esphome/components/wled/wled_light_effect.h @@ -1,5 +1,7 @@ #pragma once +#ifdef USE_ARDUINO + #include "esphome/core/component.h" #include "esphome/components/light/addressable_light_effect.h" @@ -39,3 +41,5 @@ class WLEDLightEffect : public light::AddressableLightEffect { } // namespace wled } // namespace esphome + +#endif // USE_ARDUINO diff --git a/esphome/components/xiaomi_ble/xiaomi_ble.cpp b/esphome/components/xiaomi_ble/xiaomi_ble.cpp index 3e7e591f9a..884969f793 100644 --- a/esphome/components/xiaomi_ble/xiaomi_ble.cpp +++ b/esphome/components/xiaomi_ble/xiaomi_ble.cpp @@ -2,7 +2,7 @@ #include "esphome/core/log.h" #include "esphome/core/helpers.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 #include #include "mbedtls/ccm.h" diff --git a/esphome/components/xiaomi_ble/xiaomi_ble.h b/esphome/components/xiaomi_ble/xiaomi_ble.h index 7681cbd89c..54ab9a144f 100644 --- a/esphome/components/xiaomi_ble/xiaomi_ble.h +++ b/esphome/components/xiaomi_ble/xiaomi_ble.h @@ -3,7 +3,7 @@ #include "esphome/core/component.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_ble { diff --git a/esphome/components/xiaomi_cgd1/xiaomi_cgd1.cpp b/esphome/components/xiaomi_cgd1/xiaomi_cgd1.cpp index cfbe986fff..97bbd6e6d6 100644 --- a/esphome/components/xiaomi_cgd1/xiaomi_cgd1.cpp +++ b/esphome/components/xiaomi_cgd1/xiaomi_cgd1.cpp @@ -1,7 +1,7 @@ #include "xiaomi_cgd1.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_cgd1 { diff --git a/esphome/components/xiaomi_cgd1/xiaomi_cgd1.h b/esphome/components/xiaomi_cgd1/xiaomi_cgd1.h index b9e05f857c..d05cffc4d1 100644 --- a/esphome/components/xiaomi_cgd1/xiaomi_cgd1.h +++ b/esphome/components/xiaomi_cgd1/xiaomi_cgd1.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/xiaomi_ble/xiaomi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_cgd1 { diff --git a/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.cpp b/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.cpp index 8f42eaecaf..a97ca93206 100644 --- a/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.cpp +++ b/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.cpp @@ -1,7 +1,7 @@ #include "xiaomi_cgdk2.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_cgdk2 { diff --git a/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.h b/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.h index 70f2ae9e2e..8fd9946537 100644 --- a/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.h +++ b/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/xiaomi_ble/xiaomi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_cgdk2 { diff --git a/esphome/components/xiaomi_cgg1/xiaomi_cgg1.cpp b/esphome/components/xiaomi_cgg1/xiaomi_cgg1.cpp index d0a91d23f0..e1f83e4ddd 100644 --- a/esphome/components/xiaomi_cgg1/xiaomi_cgg1.cpp +++ b/esphome/components/xiaomi_cgg1/xiaomi_cgg1.cpp @@ -1,7 +1,7 @@ #include "xiaomi_cgg1.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_cgg1 { diff --git a/esphome/components/xiaomi_cgg1/xiaomi_cgg1.h b/esphome/components/xiaomi_cgg1/xiaomi_cgg1.h index e1d812e929..966c05ac79 100644 --- a/esphome/components/xiaomi_cgg1/xiaomi_cgg1.h +++ b/esphome/components/xiaomi_cgg1/xiaomi_cgg1.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/xiaomi_ble/xiaomi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_cgg1 { diff --git a/esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.cpp b/esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.cpp index 75ed947c25..db63beea89 100644 --- a/esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.cpp +++ b/esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.cpp @@ -1,7 +1,7 @@ #include "xiaomi_cgpr1.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_cgpr1 { diff --git a/esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.h b/esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.h index 0b7369c798..eff4b1c6fb 100644 --- a/esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.h +++ b/esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.h @@ -6,7 +6,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/xiaomi_ble/xiaomi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_cgpr1 { diff --git a/esphome/components/xiaomi_gcls002/xiaomi_gcls002.cpp b/esphome/components/xiaomi_gcls002/xiaomi_gcls002.cpp index f626daad88..990346e01e 100644 --- a/esphome/components/xiaomi_gcls002/xiaomi_gcls002.cpp +++ b/esphome/components/xiaomi_gcls002/xiaomi_gcls002.cpp @@ -1,7 +1,7 @@ #include "xiaomi_gcls002.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_gcls002 { diff --git a/esphome/components/xiaomi_gcls002/xiaomi_gcls002.h b/esphome/components/xiaomi_gcls002/xiaomi_gcls002.h index d800e2837d..08e1bd7e54 100644 --- a/esphome/components/xiaomi_gcls002/xiaomi_gcls002.h +++ b/esphome/components/xiaomi_gcls002/xiaomi_gcls002.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/xiaomi_ble/xiaomi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_gcls002 { diff --git a/esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.cpp b/esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.cpp index 3f2cbf963a..30990b121d 100644 --- a/esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.cpp +++ b/esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.cpp @@ -1,7 +1,7 @@ #include "xiaomi_hhccjcy01.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_hhccjcy01 { diff --git a/esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.h b/esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.h index bd9d742b2d..aa99cc004a 100644 --- a/esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.h +++ b/esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/xiaomi_ble/xiaomi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_hhccjcy01 { diff --git a/esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.cpp b/esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.cpp index 92b7171004..3ae29088bb 100644 --- a/esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.cpp +++ b/esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.cpp @@ -1,7 +1,7 @@ #include "xiaomi_hhccpot002.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_hhccpot002 { diff --git a/esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.h b/esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.h index 1add8e27b1..ce746b9ee0 100644 --- a/esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.h +++ b/esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/xiaomi_ble/xiaomi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_hhccpot002 { diff --git a/esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.cpp b/esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.cpp index 646796525e..1efebc2849 100644 --- a/esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.cpp +++ b/esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.cpp @@ -1,7 +1,7 @@ #include "xiaomi_jqjcy01ym.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_jqjcy01ym { diff --git a/esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.h b/esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.h index d750e1e97f..ca1ad0f27e 100644 --- a/esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.h +++ b/esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/xiaomi_ble/xiaomi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_jqjcy01ym { diff --git a/esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.cpp b/esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.cpp index 1ecf0606a2..a6f27c58b9 100644 --- a/esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.cpp +++ b/esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.cpp @@ -1,7 +1,7 @@ #include "xiaomi_lywsd02.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_lywsd02 { diff --git a/esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.h b/esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.h index ec00464cb5..641a02bd5a 100644 --- a/esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.h +++ b/esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/xiaomi_ble/xiaomi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_lywsd02 { diff --git a/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp b/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp index 3eb0b68318..547cc7c114 100644 --- a/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp +++ b/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp @@ -1,7 +1,7 @@ #include "xiaomi_lywsd03mmc.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_lywsd03mmc { diff --git a/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.h b/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.h index c2828e3cd1..95710a1508 100644 --- a/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.h +++ b/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/xiaomi_ble/xiaomi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_lywsd03mmc { diff --git a/esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.cpp b/esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.cpp index f16fce296b..749ca83afb 100644 --- a/esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.cpp +++ b/esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.cpp @@ -1,7 +1,7 @@ #include "xiaomi_lywsdcgq.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_lywsdcgq { diff --git a/esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.h b/esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.h index 553b5965fd..cbc76f9dd3 100644 --- a/esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.h +++ b/esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/xiaomi_ble/xiaomi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_lywsdcgq { diff --git a/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp b/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp index fdbb799ea6..0cad5c67b2 100644 --- a/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp +++ b/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp @@ -1,7 +1,7 @@ #include "xiaomi_mhoc401.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_mhoc401 { diff --git a/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.h b/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.h index e80916f855..4ab882b2af 100644 --- a/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.h +++ b/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/xiaomi_ble/xiaomi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_mhoc401 { diff --git a/esphome/components/xiaomi_miscale/xiaomi_miscale.cpp b/esphome/components/xiaomi_miscale/xiaomi_miscale.cpp index 36b4c8cc00..62378de72c 100644 --- a/esphome/components/xiaomi_miscale/xiaomi_miscale.cpp +++ b/esphome/components/xiaomi_miscale/xiaomi_miscale.cpp @@ -1,7 +1,7 @@ #include "xiaomi_miscale.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_miscale { diff --git a/esphome/components/xiaomi_miscale/xiaomi_miscale.h b/esphome/components/xiaomi_miscale/xiaomi_miscale.h index d9da4f9421..409fdeaf93 100644 --- a/esphome/components/xiaomi_miscale/xiaomi_miscale.h +++ b/esphome/components/xiaomi_miscale/xiaomi_miscale.h @@ -4,7 +4,7 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_miscale { diff --git a/esphome/components/xiaomi_miscale2/xiaomi_miscale2.cpp b/esphome/components/xiaomi_miscale2/xiaomi_miscale2.cpp index 34c5a975c7..9ae95c5f97 100644 --- a/esphome/components/xiaomi_miscale2/xiaomi_miscale2.cpp +++ b/esphome/components/xiaomi_miscale2/xiaomi_miscale2.cpp @@ -1,7 +1,7 @@ #include "xiaomi_miscale2.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_miscale2 { diff --git a/esphome/components/xiaomi_miscale2/xiaomi_miscale2.h b/esphome/components/xiaomi_miscale2/xiaomi_miscale2.h index ead522e1f2..9f7ebf6a1f 100644 --- a/esphome/components/xiaomi_miscale2/xiaomi_miscale2.h +++ b/esphome/components/xiaomi_miscale2/xiaomi_miscale2.h @@ -4,7 +4,7 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_miscale2 { diff --git a/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.cpp b/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.cpp index df1cede068..16c0b42279 100644 --- a/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.cpp +++ b/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.cpp @@ -1,7 +1,7 @@ #include "xiaomi_mjyd02yla.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_mjyd02yla { diff --git a/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.h b/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.h index 973b19a372..34b1fe4af0 100644 --- a/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.h +++ b/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.h @@ -6,7 +6,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/xiaomi_ble/xiaomi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_mjyd02yla { diff --git a/esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.cpp b/esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.cpp index 53429c1806..1a8e72bd2c 100644 --- a/esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.cpp +++ b/esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.cpp @@ -1,7 +1,7 @@ #include "xiaomi_mue4094rt.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_mue4094rt { diff --git a/esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.h b/esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.h index 31f913ec94..904c575ae6 100644 --- a/esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.h +++ b/esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.h @@ -5,7 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/xiaomi_ble/xiaomi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_mue4094rt { diff --git a/esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.cpp b/esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.cpp index 7529bb7431..b57bf5cd05 100644 --- a/esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.cpp +++ b/esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.cpp @@ -1,7 +1,7 @@ #include "xiaomi_wx08zm.h" #include "esphome/core/log.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_wx08zm { diff --git a/esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.h b/esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.h index f3eba0e159..297c7ab47d 100644 --- a/esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.h +++ b/esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.h @@ -6,7 +6,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/xiaomi_ble/xiaomi_ble.h" -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 namespace esphome { namespace xiaomi_wx08zm { diff --git a/esphome/components/zyaura/sensor.py b/esphome/components/zyaura/sensor.py index 74f1f9ec61..28a708b866 100644 --- a/esphome/components/zyaura/sensor.py +++ b/esphome/components/zyaura/sensor.py @@ -26,12 +26,8 @@ ZyAuraSensor = zyaura_ns.class_("ZyAuraSensor", cg.PollingComponent) CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(ZyAuraSensor), - cv.Required(CONF_CLOCK_PIN): cv.All( - pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt - ), - cv.Required(CONF_DATA_PIN): cv.All( - pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt - ), + cv.Required(CONF_CLOCK_PIN): cv.All(pins.internal_gpio_input_pin_schema), + cv.Required(CONF_DATA_PIN): cv.All(pins.internal_gpio_input_pin_schema), cv.Optional(CONF_CO2): sensor.sensor_schema( unit_of_measurement=UNIT_PARTS_PER_MILLION, icon=ICON_MOLECULE_CO2, diff --git a/esphome/components/zyaura/zyaura.cpp b/esphome/components/zyaura/zyaura.cpp index 989791f098..11643a5c23 100644 --- a/esphome/components/zyaura/zyaura.cpp +++ b/esphome/components/zyaura/zyaura.cpp @@ -6,7 +6,7 @@ namespace zyaura { static const char *const TAG = "zyaura"; -bool ICACHE_RAM_ATTR ZaDataProcessor::decode(uint32_t ms, bool data) { +bool IRAM_ATTR ZaDataProcessor::decode(uint32_t ms, bool data) { // check if a new message has started, based on time since previous bit if ((ms - this->prev_ms_) > ZA_MAX_MS) { this->num_bits_ = 0; @@ -37,24 +37,24 @@ bool ICACHE_RAM_ATTR ZaDataProcessor::decode(uint32_t ms, bool data) { return false; } -void ZaSensorStore::setup(GPIOPin *pin_clock, GPIOPin *pin_data) { +void ZaSensorStore::setup(InternalGPIOPin *pin_clock, InternalGPIOPin *pin_data) { pin_clock->setup(); pin_data->setup(); this->pin_clock_ = pin_clock->to_isr(); this->pin_data_ = pin_data->to_isr(); - pin_clock->attach_interrupt(ZaSensorStore::interrupt, this, FALLING); + pin_clock->attach_interrupt(ZaSensorStore::interrupt, this, gpio::INTERRUPT_FALLING_EDGE); } -void ICACHE_RAM_ATTR ZaSensorStore::interrupt(ZaSensorStore *arg) { +void IRAM_ATTR ZaSensorStore::interrupt(ZaSensorStore *arg) { uint32_t now = millis(); - bool data_bit = arg->pin_data_->digital_read(); + bool data_bit = arg->pin_data_.digital_read(); if (arg->processor_.decode(now, data_bit)) { arg->set_data_(arg->processor_.message); } } -void ICACHE_RAM_ATTR ZaSensorStore::set_data_(ZaMessage *message) { +void IRAM_ATTR ZaSensorStore::set_data_(ZaMessage *message) { switch (message->type) { case HUMIDITY: this->humidity = (message->value > 10000) ? NAN : (message->value / 100.0f); @@ -82,7 +82,7 @@ bool ZyAuraSensor::publish_state_(sensor::Sensor *sensor, float *value) { sensor->publish_state(*value); // Sensor reported wrong value - if (isnan(*value)) { + if (std::isnan(*value)) { ESP_LOGW(TAG, "Sensor reported invalid data. Is the update interval too small?"); this->status_set_warning(); return false; diff --git a/esphome/components/zyaura/zyaura.h b/esphome/components/zyaura/zyaura.h index eed0c55c35..2b9e3fbb35 100644 --- a/esphome/components/zyaura/zyaura.h +++ b/esphome/components/zyaura/zyaura.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" namespace esphome { @@ -46,12 +46,12 @@ class ZaSensorStore { float temperature = NAN; float humidity = NAN; - void setup(GPIOPin *pin_clock, GPIOPin *pin_data); + void setup(InternalGPIOPin *pin_clock, InternalGPIOPin *pin_data); static void interrupt(ZaSensorStore *arg); protected: - ISRInternalGPIOPin *pin_clock_; - ISRInternalGPIOPin *pin_data_; + ISRInternalGPIOPin pin_clock_; + ISRInternalGPIOPin pin_data_; ZaDataProcessor processor_; void set_data_(ZaMessage *message); @@ -60,8 +60,8 @@ class ZaSensorStore { /// Component for reading temperature/co2/humidity measurements from ZyAura sensors. class ZyAuraSensor : public PollingComponent { public: - void set_pin_clock(GPIOPin *pin) { pin_clock_ = pin; } - void set_pin_data(GPIOPin *pin) { pin_data_ = pin; } + void set_pin_clock(InternalGPIOPin *pin) { pin_clock_ = pin; } + void set_pin_data(InternalGPIOPin *pin) { pin_data_ = pin; } void set_co2_sensor(sensor::Sensor *co2_sensor) { co2_sensor_ = co2_sensor; } void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; } void set_humidity_sensor(sensor::Sensor *humidity_sensor) { humidity_sensor_ = humidity_sensor; } @@ -73,8 +73,8 @@ class ZyAuraSensor : public PollingComponent { protected: ZaSensorStore store_; - GPIOPin *pin_clock_; - GPIOPin *pin_data_; + InternalGPIOPin *pin_clock_; + InternalGPIOPin *pin_data_; sensor::Sensor *co2_sensor_{nullptr}; sensor::Sensor *temperature_sensor_{nullptr}; sensor::Sensor *humidity_sensor_{nullptr}; diff --git a/esphome/config.py b/esphome/config.py index e49293e6b2..af6c5b0b64 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -1,4 +1,6 @@ -import collections +import abc +import functools +import heapq import logging import re @@ -15,6 +17,7 @@ from esphome.const import ( CONF_PACKAGES, CONF_SUBSTITUTIONS, CONF_EXTERNAL_COMPONENTS, + TARGET_PLATFORMS, ) from esphome.core import CORE, EsphomeError from esphome.helpers import indent @@ -56,6 +59,27 @@ def _path_begins_with(path, other): # type: (ConfigPath, ConfigPath) -> bool return path[: len(other)] == other +@functools.total_ordering +class _ValidationStepTask: + def __init__(self, priority: float, id_number: int, step: "ConfigValidationStep"): + self.priority = priority + self.id_number = id_number + self.step = step + + @property + def _cmp_tuple(self) -> Tuple[float, int]: + return (-self.priority, self.id_number) + + def __eq__(self, other): + return self._cmp_tuple == other._cmp_tuple + + def __ne__(self, other): + return not (self == other) + + def __lt__(self, other): + return self._cmp_tuple < other._cmp_tuple + + class Config(OrderedDict, fv.FinalValidateConfig): def __init__(self): super().__init__() @@ -68,6 +92,10 @@ class Config(OrderedDict, fv.FinalValidateConfig): # A list of components ids with the config path self.declare_ids = [] # type: List[Tuple[core.ID, ConfigPath]] self._data = {} + # Store pending validation tasks (in heap order) + self._validation_tasks: List[_ValidationStepTask] = [] + # ID to ensure stable order for keys with equal priority + self._validation_tasks_id = 0 def add_error(self, error): # type: (vol.Invalid) -> None @@ -83,6 +111,18 @@ class Config(OrderedDict, fv.FinalValidateConfig): error.path = error.path[last_root + 1 :] self.errors.append(error) + def add_validation_step(self, step: "ConfigValidationStep"): + id_num = self._validation_tasks_id + self._validation_tasks_id += 1 + heapq.heappush( + self._validation_tasks, _ValidationStepTask(step.priority, id_num, step) + ) + + def run_validation_steps(self): + while self._validation_tasks: + task = heapq.heappop(self._validation_tasks) + task.step.run(self) + @contextmanager def catch_error(self, path=None): path = path or [] @@ -206,92 +246,6 @@ def iter_ids(config, path=None): yield from iter_ids(value, path + [key]) -def do_id_pass(result): # type: (Config) -> None - from esphome.cpp_generator import MockObjClass - from esphome.cpp_types import Component - - searching_ids = [] # type: List[Tuple[core.ID, ConfigPath]] - for id, path in iter_ids(result): - if id.is_declaration: - if id.id is not None: - # Look for duplicate definitions - match = next((v for v in result.declare_ids if v[0].id == id.id), None) - if match is not None: - opath = "->".join(str(v) for v in match[1]) - result.add_str_error(f"ID {id.id} redefined! Check {opath}", path) - continue - result.declare_ids.append((id, path)) - else: - searching_ids.append((id, path)) - # Resolve default ids after manual IDs - for id, _ in result.declare_ids: - id.resolve([v[0].id for v in result.declare_ids]) - if isinstance(id.type, MockObjClass) and id.type.inherits_from(Component): - CORE.component_ids.add(id.id) - - # Check searched IDs - for id, path in searching_ids: - if id.id is not None: - # manually declared - match = next((v[0] for v in result.declare_ids if v[0].id == id.id), None) - if match is None or not match.is_manual: - # No declared ID with this name - import difflib - - error = f"Couldn't find ID '{id.id}'. Please check you have defined an ID with that name in your configuration." - # Find candidates - matches = difflib.get_close_matches( - id.id, [v[0].id for v in result.declare_ids if v[0].is_manual] - ) - if matches: - matches_s = ", ".join(f'"{x}"' for x in matches) - error += f" These IDs look similar: {matches_s}." - result.add_str_error(error, path) - continue - if not isinstance(match.type, MockObjClass) or not isinstance( - id.type, MockObjClass - ): - continue - if not match.type.inherits_from(id.type): - result.add_str_error( - f"ID '{id.id}' of type {match.type} doesn't inherit from {id.type}. Please double check your ID is pointing to the correct value", - path, - ) - - if id.id is None and id.type is not None: - matches = [] - for v in result.declare_ids: - if v[0] is None or not isinstance(v[0].type, MockObjClass): - continue - inherits = v[0].type.inherits_from(id.type) - if inherits: - matches.append(v[0]) - - if len(matches) == 0: - result.add_str_error( - f"Couldn't find any component that can be used for '{id.type}'. Are you missing a hub declaration?", - path, - ) - elif len(matches) == 1: - id.id = matches[0].id - elif len(matches) > 1: - if str(id.type) == "time::RealTimeClock": - id.id = matches[0].id - else: - manual_declared_count = sum(1 for m in matches if m.is_manual) - if manual_declared_count > 0: - ids = ", ".join([f"'{m.id}'" for m in matches if m.is_manual]) - result.add_str_error( - f"Too many candidates found for '{path[-1]}' type '{id.type}' {'Some are' if manual_declared_count > 1 else 'One is'} {ids}", - path, - ) - else: - result.add_str_error( - f"Too many candidates found for '{path[-1]}' type '{id.type}' You must assign an explicit ID to the parent component you want to use.", - path, - ) - - def recursive_check_replaceme(value): if isinstance(value, list): return cv.Schema([recursive_check_replaceme])(value) @@ -310,6 +264,389 @@ def recursive_check_replaceme(value): return value +class ConfigValidationStep(abc.ABC): + """A step to for the validation phase.""" + + # Priority of this step, higher means run earlier + priority: float = 0.0 + + @abc.abstractmethod + def run(self, result: Config) -> None: + ... + + +class LoadValidationStep(ConfigValidationStep): + """Load step, this step is called once for each domain config fragment. + + Responsibilties: + - Load component code + - Ensure all AUTO_LOADs are added + - Set output paths of result + """ + + def __init__(self, domain: str, conf: ConfigType): + self.domain = domain + self.conf = conf + + def run(self, result: Config) -> None: + if self.domain.startswith("."): + # Ignore top-level keys starting with a dot + return + result.add_output_path([self.domain], self.domain) + result[self.domain] = self.conf + component = get_component(self.domain) + path = [self.domain] + if component is None: + result.add_str_error(f"Component not found: {self.domain}", path) + return + CORE.loaded_integrations.add(self.domain) + + # Process AUTO_LOAD + for load in component.auto_load: + if load not in result: + result.add_validation_step(AutoLoadValidationStep(load)) + + if not component.is_platform_component: + result.add_validation_step( + MetadataValidationStep([self.domain], self.domain, self.conf, component) + ) + return + + # This is a platform component, proceed to reading platform entries + # Remove this is as an output path + result.remove_output_path([self.domain], self.domain) + + # Ensure conf is a list + if not self.conf: + result[self.domain] = self.conf = [] + elif not isinstance(self.conf, list): + result[self.domain] = self.conf = [self.conf] + + for i, p_config in enumerate(self.conf): + path = [self.domain, i] + # Construct temporary unknown output path + p_domain = f"{self.domain}.unknown" + result.add_output_path(path, p_domain) + result[self.domain][i] = p_config + if not isinstance(p_config, dict): + result.add_str_error("Platform schemas must be key-value pairs.", path) + continue + p_name = p_config.get("platform") + if p_name is None: + result.add_str_error("No platform specified! See 'platform' key.", path) + continue + # Remove temp output path and construct new one + result.remove_output_path(path, p_domain) + p_domain = f"{self.domain}.{p_name}" + result.add_output_path(path, p_domain) + # Try Load platform + platform = get_platform(self.domain, p_name) + if platform is None: + result.add_str_error(f"Platform not found: '{p_domain}'", path) + continue + CORE.loaded_integrations.add(p_name) + + # Process AUTO_LOAD + for load in platform.auto_load: + if load not in result: + result.add_validation_step(AutoLoadValidationStep(load)) + + result.add_validation_step( + MetadataValidationStep(path, p_domain, p_config, platform) + ) + + +class AutoLoadValidationStep(ConfigValidationStep): + """Auto load step. This step is used to automatically load components if + a component requested that with AUTO_LOAD. + """ + + # Only load after all regular loads have taken place + priority = -1.0 + + def __init__(self, domain: str): + self.domain = domain + + def run(self, result: Config) -> None: + if self.domain in result: + # already loaded + return + result.add_validation_step(LoadValidationStep(self.domain, core.AutoLoad())) + + +class MetadataValidationStep(ConfigValidationStep): + """Validate component metadata + + Responsibilties: + - Config transformation (nullable, multi conf) + - Check dependencies + - Check conflicts + - Check supported target platforms + """ + + # All components need to be loaded first to ensure dependency check works + priority = -2.0 + + def __init__( + self, + path: ConfigPath, + domain: str, + conf: ConfigType, + component: ComponentManifest, + ) -> None: + self.path = path + self.domain = domain + self.conf = conf + self.comp = component + + def run(self, result: Config) -> None: + if self.conf is None: + result[self.domain] = self.conf = {} + + success = True + for dependency in self.comp.dependencies: + if dependency not in result: + result.add_str_error( + f"Component {self.domain} requires component {dependency}", + self.path, + ) + success = False + if not success: + return + + success = True + for conflict in self.comp.conflicts_with: + if conflict in result: + result.add_str_error( + f"Component {self.domain} cannot be used together with component {conflict}", + self.path, + ) + success = False + if not success: + return + + if ( + not self.comp.is_platform_component + and self.comp.config_schema is None + and not isinstance(self.conf, core.AutoLoad) + ): + result.add_str_error( + f"Component {self.domain} cannot be loaded via YAML " + "(no CONFIG_SCHEMA).", + self.path, + ) + return + + if self.comp.multi_conf: + if not isinstance(self.conf, list): + result[self.domain] = self.conf = [self.conf] + if ( + not isinstance(self.comp.multi_conf, bool) + and len(self.conf) > self.comp.multi_conf + ): + result.add_str_error( + f"Component {self.domain} supports a maximum of {self.comp.multi_conf} " + f"entries ({len(self.conf)} found).", + self.path, + ) + return + for i, part_conf in enumerate(self.conf): + result.add_validation_step( + SchemaValidationStep( + self.domain, self.path + [i], part_conf, self.comp + ) + ) + return + + result.add_validation_step( + SchemaValidationStep(self.domain, self.path, self.conf, self.comp) + ) + + +class SchemaValidationStep(ConfigValidationStep): + """Schema validation step. + + During this step all CONFIG_SCHEMAs are checked against the configs. + """ + + def __init__( + self, domain: str, path: ConfigPath, conf: ConfigType, comp: ComponentManifest + ): + self.path = path + self.conf = conf + self.comp = comp + + def run(self, result: Config) -> None: + if self.comp.config_schema is None: + return + with result.catch_error(self.path): + if self.comp.is_platform: + # Remove 'platform' key for validation + input_conf = OrderedDict(self.conf) + platform_val = input_conf.pop("platform") + schema = cv.Schema(self.comp.config_schema) + validated = schema(input_conf) + # Ensure result is OrderedDict so we can call move_to_end + if not isinstance(validated, OrderedDict): + validated = OrderedDict(validated) + validated["platform"] = platform_val + validated.move_to_end("platform", last=False) + result.set_by_path(self.path, validated) + else: + schema = cv.Schema(self.comp.config_schema) + validated = schema(self.conf) + result.set_by_path(self.path, validated) + + result.add_validation_step(FinalValidateValidationStep(self.path, self.comp)) + + +class IDPassValidationStep(ConfigValidationStep): + """ID Pass step. + + During this step all ID references are checked. + + If an automatic ID reference is used, a fitting declared ID is automatically searched. + Also checks duplicate ID names, and that referenced IDs are declared. + """ + + # Has to happen after all schemas validated + priority = -10.0 + + def __init__(self) -> None: + pass + + def run(self, result: Config) -> None: + from esphome.cpp_generator import MockObjClass + from esphome.cpp_types import Component + + if result.errors: + # If result already has errors, skip this step + # Otherwise the user will get a bunch of missing ID warnings + # because the component that did not validate doesn't have any IDs set + return + + searching_ids = [] # type: List[Tuple[core.ID, ConfigPath]] + for id, path in iter_ids(result): + if id.is_declaration: + if id.id is not None: + # Look for duplicate definitions + match = next( + (v for v in result.declare_ids if v[0].id == id.id), None + ) + if match is not None: + opath = "->".join(str(v) for v in match[1]) + result.add_str_error( + f"ID {id.id} redefined! Check {opath}", path + ) + continue + result.declare_ids.append((id, path)) + else: + searching_ids.append((id, path)) + + # Resolve default ids after manual IDs + for id, _ in result.declare_ids: + id.resolve([v[0].id for v in result.declare_ids]) + if isinstance(id.type, MockObjClass) and id.type.inherits_from(Component): + CORE.component_ids.add(id.id) + + # Check searched IDs + for id, path in searching_ids: + if id.id is not None: + # manually declared + match = next( + (v[0] for v in result.declare_ids if v[0].id == id.id), None + ) + if match is None or not match.is_manual: + # No declared ID with this name + import difflib + + error = ( + f"Couldn't find ID '{id.id}'. Please check you have defined " + "an ID with that name in your configuration." + ) + # Find candidates + matches = difflib.get_close_matches( + id.id, [v[0].id for v in result.declare_ids if v[0].is_manual] + ) + if matches: + matches_s = ", ".join(f'"{x}"' for x in matches) + error += f" These IDs look similar: {matches_s}." + result.add_str_error(error, path) + continue + if not isinstance(match.type, MockObjClass) or not isinstance( + id.type, MockObjClass + ): + continue + if not match.type.inherits_from(id.type): + result.add_str_error( + f"ID '{id.id}' of type {match.type} doesn't inherit from {id.type}. " + "Please double check your ID is pointing to the correct value", + path, + ) + + if id.id is None and id.type is not None: + matches = [] + for v in result.declare_ids: + if v[0] is None or not isinstance(v[0].type, MockObjClass): + continue + inherits = v[0].type.inherits_from(id.type) + if inherits: + matches.append(v[0]) + + if len(matches) == 0: + result.add_str_error( + f"Couldn't find any component that can be used for '{id.type}'. Are you missing a hub declaration?", + path, + ) + elif len(matches) == 1: + id.id = matches[0].id + elif len(matches) > 1: + if str(id.type) == "time::RealTimeClock": + id.id = matches[0].id + else: + manual_declared_count = sum(1 for m in matches if m.is_manual) + if manual_declared_count > 0: + ids = ", ".join( + [f"'{m.id}'" for m in matches if m.is_manual] + ) + result.add_str_error( + f"Too many candidates found for '{path[-1]}' type '{id.type}' {'Some are' if manual_declared_count > 1 else 'One is'} {ids}", + path, + ) + else: + result.add_str_error( + f"Too many candidates found for '{path[-1]}' type '{id.type}' You must assign an explicit ID to the parent component you want to use.", + path, + ) + + +class FinalValidateValidationStep(ConfigValidationStep): + """Run final_validate_schema for all components.""" + + # Has to happen after ID pass validated + priority = -20.0 + + def __init__(self, path: ConfigPath, comp: ComponentManifest) -> None: + self.path = path + self.comp = comp + + def run(self, result: Config) -> None: + if result.errors: + # If result already has errors, skip this step + return + + if self.comp.final_validate_schema is None: + return + + token = fv.full_config.set(result) + + conf = result.get_nested_item(self.path) + with result.catch_error(self.path): + self.comp.final_validate_schema(conf) + + fv.full_config.reset(token) + + def validate_config(config, command_line_substitutions): result = Config() @@ -384,217 +721,31 @@ def validate_config(config, command_line_substitutions): result[CONF_ESPHOME] = config[CONF_ESPHOME] result.add_output_path([CONF_ESPHOME], CONF_ESPHOME) try: - core_config.preload_core_config(config) + core_config.preload_core_config(config, result) except vol.Invalid as err: result.add_error(err) return result # Remove temporary esphome config path again, it will be reloaded later result.remove_output_path([CONF_ESPHOME], CONF_ESPHOME) - # 3. Load components. - # Load components (also AUTO_LOAD) and set output paths of result - # Queue of items to load, FIFO - load_queue = collections.deque() + # First run platform validation steps + for key in TARGET_PLATFORMS: + if key in config: + result.add_validation_step(LoadValidationStep(key, config[key])) + result.run_validation_steps() + + if result.errors: + return result + for domain, conf in config.items(): - load_queue.append((domain, conf)) + result.add_validation_step(LoadValidationStep(domain, conf)) + result.add_validation_step(IDPassValidationStep()) - # List of items to enter next stage - check_queue = ( - [] - ) # type: List[Tuple[ConfigPath, str, ConfigType, ComponentManifest]] - - # This step handles: - # - Adding output path - # - Auto Load - # - Loading configs into result - - while load_queue: - domain, conf = load_queue.popleft() - if domain.startswith("."): - # Ignore top-level keys starting with a dot - continue - result.add_output_path([domain], domain) - result[domain] = conf - component = get_component(domain) - path = [domain] - if component is None: - result.add_str_error(f"Component not found: {domain}", path) - continue - CORE.loaded_integrations.add(domain) - - # Process AUTO_LOAD - for load in component.auto_load: - if load not in config: - load_conf = core.AutoLoad() - config[load] = load_conf - load_queue.append((load, load_conf)) - - if not component.is_platform_component: - check_queue.append(([domain], domain, conf, component)) - continue - - # This is a platform component, proceed to reading platform entries - # Remove this is as an output path - result.remove_output_path([domain], domain) - - # Ensure conf is a list - if not conf: - result[domain] = conf = [] - elif not isinstance(conf, list): - result[domain] = conf = [conf] - - for i, p_config in enumerate(conf): - path = [domain, i] - # Construct temporary unknown output path - p_domain = f"{domain}.unknown" - result.add_output_path(path, p_domain) - result[domain][i] = p_config - if not isinstance(p_config, dict): - result.add_str_error("Platform schemas must be key-value pairs.", path) - continue - p_name = p_config.get("platform") - if p_name is None: - result.add_str_error("No platform specified! See 'platform' key.", path) - continue - # Remove temp output path and construct new one - result.remove_output_path(path, p_domain) - p_domain = f"{domain}.{p_name}" - result.add_output_path(path, p_domain) - # Try Load platform - platform = get_platform(domain, p_name) - if platform is None: - result.add_str_error(f"Platform not found: '{p_domain}'", path) - continue - CORE.loaded_integrations.add(p_name) - - # Process AUTO_LOAD - for load in platform.auto_load: - if load not in config: - load_conf = core.AutoLoad() - config[load] = load_conf - load_queue.append((load, load_conf)) - - check_queue.append((path, p_domain, p_config, platform)) - - # 4. Validate component metadata, including - # - Transformation (nullable, multi conf) - # - Dependencies - # - Conflicts - # - Supported ESP Platform - - # List of items to proceed to next stage - validate_queue = [] # type: List[Tuple[ConfigPath, ConfigType, ComponentManifest]] - for path, domain, conf, comp in check_queue: - if conf is None: - result[domain] = conf = {} - - success = True - for dependency in comp.dependencies: - if dependency not in config: - result.add_str_error( - f"Component {domain} requires component {dependency}", - path, - ) - success = False - if not success: - continue - - success = True - for conflict in comp.conflicts_with: - if conflict in config: - result.add_str_error( - f"Component {domain} cannot be used together with component {conflict}", - path, - ) - success = False - if not success: - continue - - if CORE.esp_platform not in comp.esp_platforms: - result.add_str_error( - f"Component {domain} doesn't support {CORE.esp_platform}.", - path, - ) - continue - - if ( - not comp.is_platform_component - and comp.config_schema is None - and not isinstance(conf, core.AutoLoad) - ): - result.add_str_error( - f"Component {domain} cannot be loaded via YAML (no CONFIG_SCHEMA).", - path, - ) - continue - - if comp.multi_conf: - if not isinstance(conf, list): - result[domain] = conf = [conf] - if not isinstance(comp.multi_conf, bool) and len(conf) > comp.multi_conf: - result.add_str_error( - f"Component {domain} supports a maximum of {comp.multi_conf} entries ({len(conf)} found).", - path, - ) - continue - for i, part_conf in enumerate(conf): - validate_queue.append((path + [i], part_conf, comp)) - continue - - validate_queue.append((path, conf, comp)) - - # 5. Validate configuration schema - for path, conf, comp in validate_queue: - if comp.config_schema is None: - continue - with result.catch_error(path): - if comp.is_platform: - # Remove 'platform' key for validation - input_conf = OrderedDict(conf) - platform_val = input_conf.pop("platform") - validated = comp.config_schema(input_conf) - # Ensure result is OrderedDict so we can call move_to_end - if not isinstance(validated, OrderedDict): - validated = OrderedDict(validated) - validated["platform"] = platform_val - validated.move_to_end("platform", last=False) - result.set_by_path(path, validated) - else: - validated = comp.config_schema(conf) - result.set_by_path(path, validated) - - # 6. If no validation errors, check IDs - if not result.errors: - # Only parse IDs if no validation error. Otherwise - # user gets confusing messages - do_id_pass(result) - - # 7. Final validation - if not result.errors: - # Inter - components validation - token = fv.full_config.set(result) - - for path, _, comp in validate_queue: - if comp.final_validate_schema is None: - continue - conf = result.get_nested_item(path) - with result.catch_error(path): - comp.final_validate_schema(conf) - - fv.full_config.reset(token) + result.run_validation_steps() return result -def _nested_getitem(data, path): - for item_index in path: - try: - data = data[item_index] - except (KeyError, IndexError, TypeError): - return None - return data - - def humanize_error(config, validation_error): validation_error = str(validation_error) m = re.match( @@ -661,7 +812,6 @@ def _load_config(command_line_substitutions): config = yaml_util.load_yaml(CORE.config_path) except EsphomeError as e: raise InvalidYAMLError(e) from e - CORE.raw_config = config try: result = validate_config(config, command_line_substitutions) diff --git a/esphome/config_validation.py b/esphome/config_validation.py index a864c65dc7..0d8f4f3b64 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -1,5 +1,6 @@ """Helpers for config validation using voluptuous.""" +from dataclasses import dataclass import logging import os import re @@ -33,6 +34,9 @@ from esphome.const import ( CONF_UPDATE_INTERVAL, CONF_TYPE_ID, CONF_TYPE, + KEY_CORE, + KEY_FRAMEWORK_VERSION, + KEY_TARGET_FRAMEWORK, ) from esphome.core import ( CORE, @@ -492,20 +496,37 @@ def templatable(other_validators): def only_on(platforms): - """Validate that this option can only be specified on the given ESP platforms.""" + """Validate that this option can only be specified on the given target platforms.""" if not isinstance(platforms, list): platforms = [platforms] def validator_(obj): - if CORE.esp_platform not in platforms: + if CORE.target_platform not in platforms: raise Invalid(f"This feature is only available on {platforms}") return obj return validator_ -only_on_esp32 = only_on("ESP32") -only_on_esp8266 = only_on("ESP8266") +def only_with_framework(frameworks): + """Validate that this option can only be specified on the given frameworks.""" + if not isinstance(frameworks, list): + frameworks = [frameworks] + + def validator_(obj): + if CORE.target_framework not in frameworks: + raise Invalid( + f"This feature is only available with frameworks {frameworks}" + ) + return obj + + return validator_ + + +only_on_esp32 = only_on("esp32") +only_on_esp8266 = only_on("esp8266") +only_with_arduino = only_with_framework("arduino") +only_with_esp_idf = only_with_framework("esp-idf") # Adapted from: @@ -1025,7 +1046,7 @@ def requires_component(comp): # pylint: disable=unsupported-membership-test def validator(value): # pylint: disable=unsupported-membership-test - if comp not in CORE.raw_config: + if comp not in CORE.loaded_integrations: raise Invalid(f"This option requires component {comp}") return value @@ -1401,18 +1422,32 @@ class GenerateID(Optional): class SplitDefault(Optional): """Mark this key to have a split default for ESP8266/ESP32.""" - def __init__(self, key, esp8266=vol.UNDEFINED, esp32=vol.UNDEFINED): + def __init__( + self, + key, + esp8266=vol.UNDEFINED, + esp32=vol.UNDEFINED, + esp32_arduino=vol.UNDEFINED, + esp32_idf=vol.UNDEFINED, + ): super().__init__(key) self._esp8266_default = vol.default_factory(esp8266) - self._esp32_default = vol.default_factory(esp32) + self._esp32_arduino_default = vol.default_factory( + esp32_arduino if esp32 is vol.UNDEFINED else esp32 + ) + self._esp32_idf_default = vol.default_factory( + esp32_idf if esp32 is vol.UNDEFINED else esp32 + ) @property def default(self): if CORE.is_esp8266: return self._esp8266_default - if CORE.is_esp32: - return self._esp32_default - raise ValueError + if CORE.is_esp32 and CORE.using_arduino: + return self._esp32_arduino_default + if CORE.is_esp32 and CORE.using_esp_idf: + return self._esp32_idf_default + raise NotImplementedError @default.setter def default(self, value): @@ -1431,7 +1466,7 @@ class OnlyWith(Optional): @property def default(self): # pylint: disable=unsupported-membership-test - if self._component in CORE.raw_config: + if self._component in CORE.loaded_integrations: return self._default return vol.UNDEFINED @@ -1613,3 +1648,73 @@ def source_refresh(value: str): if value.lower() == "never": return source_refresh("1000y") return positive_time_period_seconds(value) + + +@dataclass(frozen=True, order=True) +class Version: + major: int + minor: int + patch: int + + def __str__(self): + return f"{self.major}.{self.minor}.{self.patch}" + + @classmethod + def parse(cls, value: str) -> "Version": + match = re.match(r"(\d+).(\d+).(\d+)", value) + if match is None: + raise ValueError(f"Not a valid version number {value}") + major = int(match[1]) + minor = int(match[2]) + patch = int(match[3]) + return Version(major=major, minor=minor, patch=patch) + + +def version_number(value): + value = string_strict(value) + try: + return str(Version.parse(value)) + except ValueError as e: + raise Invalid("Not a version number") from e + + +def require_framework_version( + *, + esp_idf=None, + esp32_arduino=None, + esp8266_arduino=None, +): + def validator(value): + core_data = CORE.data[KEY_CORE] + framework = core_data[KEY_TARGET_FRAMEWORK] + if framework == "esp-idf": + if esp_idf is None: + raise Invalid("This feature is incompatible with esp-idf") + required = esp_idf + elif CORE.is_esp32 and framework == "arduino": + if esp32_arduino is None: + raise Invalid( + "This feature is incompatible with ESP32 using arduino framework" + ) + required = esp32_arduino + elif CORE.is_esp8266 and framework == "arduino": + if esp8266_arduino is None: + raise Invalid("This feature is incompatible with ESP8266") + required = esp8266_arduino + else: + raise NotImplementedError + if core_data[KEY_FRAMEWORK_VERSION] < required: + raise Invalid( + f"This feature requires at least framework version {required}" + ) + return value + + return validator + + +@contextmanager +def suppress_invalid(): + try: + yield + except vol.Invalid: + pass diff --git a/esphome/const.py b/esphome/const.py index 598b6351b4..6a3296bcea 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -2,25 +2,11 @@ __version__ = "2021.10.0-dev" -ESP_PLATFORM_ESP32 = "ESP32" -ESP_PLATFORM_ESP8266 = "ESP8266" -ESP_PLATFORMS = [ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266] - ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" -# Lookup table from ESP32 arduino framework version to latest platformio -# package with that version -# See also https://github.com/platformio/platform-espressif32/releases -ARDUINO_VERSION_ESP32 = { - "dev": "https://github.com/platformio/platform-espressif32.git", - "1.0.6": "platformio/espressif32@3.2.0", - "1.0.5": "platformio/espressif32@3.1.1", - "1.0.4": "platformio/espressif32@3.0.0", - "1.0.3": "platformio/espressif32@1.10.0", - "1.0.2": "platformio/espressif32@1.9.0", - "1.0.1": "platformio/espressif32@1.7.0", - "1.0.0": "platformio/espressif32@1.5.0", -} +TARGET_PLATFORMS = ["esp32", "esp8266"] +TARGET_FRAMEWORKS = ["arduino", "esp-idf"] + # See also https://github.com/platformio/platform-espressif8266/releases ARDUINO_VERSION_ESP8266 = { "dev": "https://github.com/platformio/platform-espressif8266.git", @@ -204,13 +190,11 @@ CONF_ECO2 = "eco2" CONF_EFFECT = "effect" CONF_EFFECTS = "effects" CONF_ELSE = "else" -CONF_ENABLE_MDNS = "enable_mdns" CONF_ENABLE_PIN = "enable_pin" CONF_ENABLE_TIME = "enable_time" CONF_ENERGY = "energy" CONF_ENTITY_ID = "entity_id" CONF_ESP8266_DISABLE_SSL_SUPPORT = "esp8266_disable_ssl_support" -CONF_ESP8266_RESTORE_FROM_FLASH = "esp8266_restore_from_flash" CONF_ESPHOME = "esphome" CONF_ETHERNET = "ethernet" CONF_EVENT = "event" @@ -253,6 +237,7 @@ CONF_FORCE_UPDATE = "force_update" CONF_FORMALDEHYDE = "formaldehyde" CONF_FORMAT = "format" CONF_FORWARD_ACTIVE_ENERGY = "forward_active_energy" +CONF_FRAMEWORK = "framework" CONF_FREQUENCY = "frequency" CONF_FROM = "from" CONF_FULL_SPECTRUM = "full_spectrum" @@ -307,6 +292,7 @@ CONF_INFRARED = "infrared" CONF_INITIAL_MODE = "initial_mode" CONF_INITIAL_OPTION = "initial_option" CONF_INITIAL_VALUE = "initial_value" +CONF_INPUT = "input" CONF_INTEGRATION_TIME = "integration_time" CONF_INTENSITY = "intensity" CONF_INTERLOCK = "interlock" @@ -447,6 +433,7 @@ CONF_ON_VALUE = "on_value" CONF_ON_VALUE_RANGE = "on_value_range" CONF_ONE = "one" CONF_OPEN_ACTION = "open_action" +CONF_OPEN_DRAIN = "open_drain" CONF_OPEN_DRAIN_INTERRUPT = "open_drain_interrupt" CONF_OPEN_DURATION = "open_duration" CONF_OPEN_ENDSTOP = "open_endstop" @@ -520,6 +507,8 @@ CONF_PRIORITY = "priority" CONF_PROJECT = "project" CONF_PROTOCOL = "protocol" CONF_PULL_MODE = "pull_mode" +CONF_PULLDOWN = "pulldown" +CONF_PULLUP = "pullup" CONF_PULSE_LENGTH = "pulse_length" CONF_QOS = "qos" CONF_RADON = "radon" @@ -890,3 +879,8 @@ STATE_CLASS_MEASUREMENT = "measurement" # The state represents a total that only increases, a decrease is considered a reset. STATE_CLASS_TOTAL_INCREASING = "total_increasing" + +KEY_CORE = "core" +KEY_TARGET_PLATFORM = "target_platform" +KEY_TARGET_FRAMEWORK = "target_framework" +KEY_FRAMEWORK_VERSION = "framework_version" diff --git a/esphome/core/__init__.py b/esphome/core/__init__.py index e89f64d14c..9ec7fe358d 100644 --- a/esphome/core/__init__.py +++ b/esphome/core/__init__.py @@ -2,16 +2,17 @@ import logging import math import os import re -from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple +from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, Union from esphome.const import ( - CONF_ARDUINO_VERSION, CONF_COMMENT, CONF_ESPHOME, CONF_USE_ADDRESS, CONF_ETHERNET, CONF_WIFI, - SOURCE_FILE_EXTENSIONS, + KEY_CORE, + KEY_TARGET_FRAMEWORK, + KEY_TARGET_PLATFORM, ) from esphome.coroutine import FakeAwaitable as _FakeAwaitable from esphome.coroutine import FakeEventLoop as _FakeEventLoop @@ -20,7 +21,6 @@ from esphome.coroutine import FakeEventLoop as _FakeEventLoop from esphome.coroutine import coroutine, coroutine_with_priority # noqa from esphome.helpers import ensure_unique_string, is_hassio from esphome.util import OrderedDict -from esphome import boards if TYPE_CHECKING: from ..cpp_generator import MockObj, MockObjClass, Statement @@ -441,19 +441,6 @@ class Library: return NotImplemented -def find_source_files(file): - files = set() - directory = os.path.abspath(os.path.dirname(file)) - for f in os.listdir(directory): - if not os.path.isfile(os.path.join(directory, f)): - continue - _, ext = os.path.splitext(f) - if ext.lower() not in SOURCE_FILE_EXTENSIONS: - continue - files.add(f) - return files - - # pylint: disable=too-many-instance-attributes,too-many-public-methods class EsphomeCore: def __init__(self): @@ -464,16 +451,13 @@ class EsphomeCore: self.ace = False # The name of the node self.name: Optional[str] = None + # Additional data components can store temporary data in + # The first key to this dict should always be the integration name + self.data = {} # The relative path to the configuration YAML self.config_path: Optional[str] = None # The relative path to where all build files are stored self.build_path: Optional[str] = None - # The platform (ESP8266, ESP32) of this device - self.esp_platform: Optional[str] = None - # The board that's used (for example nodemcuv2) - self.board: Optional[str] = None - # The full raw configuration - self.raw_config: Optional["ConfigType"] = None # The validated configuration, this is None until the config has been validated self.config: Optional["ConfigType"] = None # The pending tasks in the task queue (mostly for C++ generation) @@ -494,6 +478,8 @@ class EsphomeCore: self.build_flags: Set[str] = set() # A set of defines to set for the compile process in esphome/core/defines.h self.defines: Set["Define"] = set() + # A map of all platformio options to apply + self.platformio_options: Dict[str, Union[str, List[str]]] = {} # A set of strings of names of loaded integrations, used to find namespace ID conflicts self.loaded_integrations = set() # A set of component IDs to track what Component subclasses are declared @@ -504,11 +490,9 @@ class EsphomeCore: def reset(self): self.dashboard = False self.name = None + self.data = {} self.config_path = None self.build_path = None - self.esp_platform = None - self.board = None - self.raw_config = None self.config = None self.event_loop = _FakeEventLoop() self.task_counter = 0 @@ -518,6 +502,7 @@ class EsphomeCore: self.libraries = [] self.build_flags = set() self.defines = set() + self.platformio_options = {} self.loaded_integrations = set() self.component_ids = set() @@ -544,13 +529,6 @@ class EsphomeCore: return None - @property - def arduino_version(self) -> str: - if self.config is None: - raise ValueError("Config has not been loaded yet") - - return self.config[CONF_ESPHOME][CONF_ARDUINO_VERSION] - @property def config_dir(self): return os.path.dirname(self.config_path) @@ -586,27 +564,29 @@ class EsphomeCore: def firmware_bin(self): return self.relative_pioenvs_path(self.name, "firmware.bin") + @property + def target_platform(self): + return self.data[KEY_CORE][KEY_TARGET_PLATFORM] + @property def is_esp8266(self): - if self.esp_platform is None: - raise ValueError("No platform specified") - return self.esp_platform == "ESP8266" + return self.target_platform == "esp8266" @property def is_esp32(self): - """Check if the ESP32 platform is used. - - This checks if the ESP32 platform is in use, which - support ESP32 as well as other chips such as ESP32-C3 - """ - if self.esp_platform is None: - raise ValueError("No platform specified") - return self.esp_platform == "ESP32" + return self.target_platform == "esp32" @property - def is_esp32_c3(self): - """Check if the ESP32-C3 SoC is being used.""" - return self.is_esp32 and self.board in boards.ESP32_C3_BOARD_PINS + def target_framework(self): + return self.data[KEY_CORE][KEY_TARGET_FRAMEWORK] + + @property + def using_arduino(self): + return self.target_framework == "arduino" + + @property + def using_esp_idf(self): + return self.target_framework == "esp-idf" def add_job(self, func, *args, **kwargs): self.event_loop.add_job(func, *args, **kwargs) @@ -703,6 +683,14 @@ class EsphomeCore: _LOGGER.debug("Adding define: %s", define) return define + def add_platformio_option(self, key: str, value: Union[str, List[str]]) -> None: + new_val = value + old_val = self.platformio_options.get(key) + if isinstance(old_val, list): + assert isinstance(value, list) + new_val = old_val + value + self.platformio_options[key] = new_val + def _get_variable_generator(self, id): while True: try: diff --git a/esphome/core/application.cpp b/esphome/core/application.cpp index d59ad23a5e..a4d61f819c 100644 --- a/esphome/core/application.cpp +++ b/esphome/core/application.cpp @@ -1,7 +1,7 @@ #include "esphome/core/application.h" #include "esphome/core/log.h" #include "esphome/core/version.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #ifdef USE_STATUS_LED #include "esphome/components/status_led/status_led.h" @@ -60,9 +60,6 @@ void Application::setup() { ESP_LOGI(TAG, "setup() finished successfully!"); this->schedule_dump_config(); this->calculate_looping_components_(); - - // Dummy function to link some symbols into the binary. - force_link_symbols(); } void Application::loop() { uint32_t new_app_state = 0; @@ -110,11 +107,11 @@ void Application::loop() { } } -void ICACHE_RAM_ATTR HOT Application::feed_wdt() { +void IRAM_ATTR HOT Application::feed_wdt() { static uint32_t last_feed = 0; uint32_t now = millis(); if (now - last_feed > 3) { - this->feed_wdt_arch_(); + arch_feed_wdt(); last_feed = now; #ifdef USE_STATUS_LED if (status_led::global_status_led != nullptr) { @@ -127,11 +124,7 @@ void Application::reboot() { ESP_LOGI(TAG, "Forcing a reboot..."); for (auto *comp : this->components_) comp->on_shutdown(); - ESP.restart(); // NOLINT(readability-static-accessed-through-instance) - // restart() doesn't always end execution - while (true) { - yield(); - } + arch_restart(); } void Application::safe_reboot() { ESP_LOGI(TAG, "Rebooting safely..."); @@ -139,11 +132,7 @@ void Application::safe_reboot() { comp->on_safe_shutdown(); for (auto *comp : this->components_) comp->on_shutdown(); - ESP.restart(); // NOLINT(readability-static-accessed-through-instance) - // restart() doesn't always end execution - while (true) { - yield(); - } + arch_restart(); } void Application::calculate_looping_components_() { diff --git a/esphome/core/application.h b/esphome/core/application.h index e5f686a320..5c1483d301 100644 --- a/esphome/core/application.h +++ b/esphome/core/application.h @@ -51,7 +51,6 @@ class Application { this->name_ = name; } this->compilation_time_ = compilation_time; - global_preferences.begin(); } #ifdef USE_BINARY_SENSOR diff --git a/esphome/core/application_esp32.cpp b/esphome/core/application_esp32.cpp deleted file mode 100644 index 9f084428bb..0000000000 --- a/esphome/core/application_esp32.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "esphome/core/application.h" - -#ifdef ARDUINO_ARCH_ESP32 - -namespace esphome { - -static const char *const TAG = "app_esp32"; - -void ICACHE_RAM_ATTR HOT Application::feed_wdt_arch_() { -#if CONFIG_ARDUINO_RUNNING_CORE == 0 -#ifdef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0 - // ESP32 uses "Task Watchdog" which is hooked to the FreeRTOS idle task. - // To cause the Watchdog to be triggered we need to put the current task - // to sleep to get the idle task scheduled. - delay(1); -#endif -#endif -} - -} // namespace esphome -#endif diff --git a/esphome/core/application_esp8266.cpp b/esphome/core/application_esp8266.cpp deleted file mode 100644 index ee32417634..0000000000 --- a/esphome/core/application_esp8266.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "esphome/core/application.h" - -#ifdef ARDUINO_ARCH_ESP8266 - -namespace esphome { - -static const char *const TAG = "app_esp8266"; - -void ICACHE_RAM_ATTR HOT Application::feed_wdt_arch_() { - ESP.wdtFeed(); // NOLINT(readability-static-accessed-through-instance) -} - -} // namespace esphome -#endif diff --git a/esphome/core/component.cpp b/esphome/core/component.cpp index e3b32978cd..c85b445b08 100644 --- a/esphome/core/component.cpp +++ b/esphome/core/component.cpp @@ -1,7 +1,7 @@ #include "esphome/core/component.h" #include "esphome/core/application.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" #include @@ -144,7 +144,7 @@ void Component::status_momentary_error(const std::string &name, uint32_t length) } void Component::dump_config() {} float Component::get_actual_setup_priority() const { - if (isnan(this->setup_priority_override_)) + if (std::isnan(this->setup_priority_override_)) return this->get_setup_priority(); return this->setup_priority_override_; } diff --git a/esphome/core/component.h b/esphome/core/component.h index 2654504fe8..85256c0f0f 100644 --- a/esphome/core/component.h +++ b/esphome/core/component.h @@ -2,7 +2,7 @@ #include #include -#include "Arduino.h" +#include #include "esphome/core/optional.h" diff --git a/esphome/core/config.py b/esphome/core/config.py index 222a775ee6..71add56b13 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -4,7 +4,7 @@ import re import esphome.codegen as cg import esphome.config_validation as cv -from esphome import automation, boards +from esphome import automation from esphome.const import ( CONF_ARDUINO_VERSION, CONF_BOARD, @@ -12,6 +12,7 @@ from esphome.const import ( CONF_BUILD_PATH, CONF_COMMENT, CONF_ESPHOME, + CONF_FRAMEWORK, CONF_INCLUDES, CONF_LIBRARIES, CONF_NAME, @@ -23,11 +24,10 @@ from esphome.const import ( CONF_PRIORITY, CONF_PROJECT, CONF_TRIGGER_ID, - CONF_ESP8266_RESTORE_FROM_FLASH, - ARDUINO_VERSION_ESP8266, - ARDUINO_VERSION_ESP32, + CONF_TYPE, CONF_VERSION, - ESP_PLATFORMS, + KEY_CORE, + TARGET_PLATFORMS, ) from esphome.core import CORE, coroutine_with_priority from esphome.helpers import copy_file_if_changed, walk_files @@ -50,79 +50,6 @@ VERSION_REGEX = re.compile(r"^[0-9]+\.[0-9]+\.[0-9]+(?:[ab]\d+)?$") CONF_NAME_ADD_MAC_SUFFIX = "name_add_mac_suffix" -def validate_board(value: str): - if CORE.is_esp8266: - boardlist = boards.ESP8266_BOARD_PINS.keys() - elif CORE.is_esp32: - boardlist = list(boards.ESP32_BOARD_PINS.keys()) - boardlist += list(boards.ESP32_C3_BOARD_PINS.keys()) - else: - raise NotImplementedError - - if value not in boardlist: - raise cv.Invalid( - f"Could not find board '{value}'. Valid boards are {', '.join(sorted(boardlist))}" - ) - return value - - -validate_platform = cv.one_of(*ESP_PLATFORMS, upper=True) - -PLATFORMIO_ESP8266_LUT = { - **ARDUINO_VERSION_ESP8266, - # Keep this in mind when updating the recommended version: - # * New framework historically have had some regressions, especially for WiFi, BLE and the - # bootloader system. The new version needs to be thoroughly validated before changing the - # recommended version as otherwise a bunch of devices could be bricked - # * The docker images need to be updated to ship the new recommended version, in order not - # to DDoS platformio servers. - # Update this file: https://github.com/esphome/esphome-docker-base/blob/main/platformio.ini - "RECOMMENDED": ARDUINO_VERSION_ESP8266["2.7.4"], - "LATEST": "espressif8266", - "DEV": ARDUINO_VERSION_ESP8266["dev"], -} - -PLATFORMIO_ESP32_LUT = { - **ARDUINO_VERSION_ESP32, - # See PLATFORMIO_ESP8266_LUT for considerations when changing the recommended version - "RECOMMENDED": ARDUINO_VERSION_ESP32["1.0.6"], - "LATEST": "espressif32", - "DEV": ARDUINO_VERSION_ESP32["dev"], -} - - -def validate_arduino_version(value): - value = cv.string_strict(value) - value_ = value.upper() - if CORE.is_esp8266: - if ( - VERSION_REGEX.match(value) is not None - and value_ not in PLATFORMIO_ESP8266_LUT - ): - raise cv.Invalid( - f"Unfortunately the arduino framework version '{value}' is unsupported at this time. You can override this by manually using espressif8266@" - ) - if value_ in PLATFORMIO_ESP8266_LUT: - return PLATFORMIO_ESP8266_LUT[value_] - return value - if CORE.is_esp32: - if ( - VERSION_REGEX.match(value) is not None - and value_ not in PLATFORMIO_ESP32_LUT - ): - raise cv.Invalid( - f"Unfortunately the arduino framework version '{value}' is unsupported at this time. You can override this by manually using espressif32@" - ) - if value_ in PLATFORMIO_ESP32_LUT: - return PLATFORMIO_ESP32_LUT[value_] - return value - raise NotImplementedError - - -def default_build_path(): - return CORE.name - - VALID_INCLUDE_EXTS = {".h", ".hpp", ".tcc", ".ino", ".cpp", ".c"} @@ -149,27 +76,17 @@ def valid_project_name(value: str): return value +CONF_ESP8266_RESTORE_FROM_FLASH = "esp8266_restore_from_flash" CONFIG_SCHEMA = cv.Schema( { cv.Required(CONF_NAME): cv.hostname, - cv.Required(CONF_PLATFORM): cv.one_of("ESP8266", "ESP32", upper=True), - cv.Required(CONF_BOARD): validate_board, cv.Optional(CONF_COMMENT): cv.string, - cv.Optional( - CONF_ARDUINO_VERSION, default="recommended" - ): validate_arduino_version, - cv.Optional(CONF_BUILD_PATH, default=default_build_path): cv.string, + cv.Required(CONF_BUILD_PATH): cv.string, cv.Optional(CONF_PLATFORMIO_OPTIONS, default={}): cv.Schema( { cv.string_strict: cv.Any([cv.string], cv.string), } ), - cv.SplitDefault(CONF_ESP8266_RESTORE_FROM_FLASH, esp8266=False): cv.All( - cv.only_on_esp8266, cv.boolean - ), - cv.SplitDefault(CONF_BOARD_FLASH_MODE, esp8266="dout"): cv.one_of( - *BUILD_FLASH_MODES, lower=True - ), cv.Optional(CONF_ON_BOOT): automation.validate_automation( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StartupTrigger), @@ -201,38 +118,78 @@ CONFIG_SCHEMA = cv.Schema( PRELOAD_CONFIG_SCHEMA = cv.Schema( { cv.Required(CONF_NAME): cv.valid_name, - cv.Required(CONF_PLATFORM): validate_platform, + cv.Optional(CONF_BUILD_PATH): cv.string, + # Compat options, these were moved to target-platform specific sections + # but we'll keep these around for a long time because every config would + # be impacted + cv.Optional(CONF_PLATFORM): cv.one_of(*TARGET_PLATFORMS, lower=True), + cv.Optional(CONF_BOARD): cv.string_strict, + cv.Optional(CONF_ESP8266_RESTORE_FROM_FLASH): cv.valid, + cv.Optional(CONF_BOARD_FLASH_MODE): cv.valid, + cv.Optional(CONF_ARDUINO_VERSION): cv.valid, }, extra=cv.ALLOW_EXTRA, ) -PRELOAD_CONFIG_SCHEMA2 = PRELOAD_CONFIG_SCHEMA.extend( - { - cv.Required(CONF_BOARD): validate_board, - cv.Optional(CONF_BUILD_PATH, default=default_build_path): cv.string, - } -) +def preload_core_config(config, result): + with cv.prepend_path(CONF_ESPHOME): + conf = PRELOAD_CONFIG_SCHEMA(config[CONF_ESPHOME]) -def preload_core_config(config): - core_key = "esphome" - if "esphomeyaml" in config: - _LOGGER.warning( - "The esphomeyaml section has been renamed to esphome in 1.11.0. " - "Please replace 'esphomeyaml:' in your configuration with 'esphome:'." + CORE.name = conf[CONF_NAME] + CORE.data[KEY_CORE] = {} + + if CONF_BUILD_PATH not in conf: + conf[CONF_BUILD_PATH] = CORE.name + CORE.build_path = CORE.relative_config_path(conf[CONF_BUILD_PATH]) + + has_oldstyle = CONF_PLATFORM in conf + newstyle_found = [key for key in TARGET_PLATFORMS if key in config] + oldstyle_opts = [ + CONF_ESP8266_RESTORE_FROM_FLASH, + CONF_BOARD_FLASH_MODE, + CONF_ARDUINO_VERSION, + CONF_BOARD, + ] + + if not has_oldstyle and not newstyle_found: + raise cv.Invalid("Platform missing for core options!", [CONF_ESPHOME]) + if has_oldstyle and newstyle_found: + raise cv.Invalid( + f"Please remove the `platform` key from the [esphome] block. You're already using the new style with the [{conf[CONF_PLATFORM]}] block", + [CONF_ESPHOME, CONF_PLATFORM], ) - config[CONF_ESPHOME] = config.pop("esphomeyaml") - core_key = "esphomeyaml" - if CONF_ESPHOME not in config: - raise cv.RequiredFieldInvalid("required key not provided", CONF_ESPHOME) - with cv.prepend_path(core_key): - out = PRELOAD_CONFIG_SCHEMA(config[CONF_ESPHOME]) - CORE.name = out[CONF_NAME] - CORE.esp_platform = out[CONF_PLATFORM] - with cv.prepend_path(core_key): - out2 = PRELOAD_CONFIG_SCHEMA2(config[CONF_ESPHOME]) - CORE.board = out2[CONF_BOARD] - CORE.build_path = CORE.relative_config_path(out2[CONF_BUILD_PATH]) + if len(newstyle_found) > 1: + raise cv.Invalid( + f"Found multiple target platform blocks: {', '.join(newstyle_found)}. Only one is allowed.", + [newstyle_found[0]], + ) + if newstyle_found: + # Convert to newstyle + for key in oldstyle_opts: + if key in conf: + raise cv.Invalid( + f"Please move {key} to the [{newstyle_found[0]}] block.", + [CONF_ESPHOME, key], + ) + + if has_oldstyle: + plat = conf.pop(CONF_PLATFORM) + plat_conf = {} + if CONF_ESP8266_RESTORE_FROM_FLASH in conf: + plat_conf["restore_from_flash"] = conf.pop(CONF_ESP8266_RESTORE_FROM_FLASH) + if CONF_BOARD_FLASH_MODE in conf: + plat_conf[CONF_BOARD_FLASH_MODE] = conf.pop(CONF_BOARD_FLASH_MODE) + if CONF_ARDUINO_VERSION in conf: + plat_conf[CONF_FRAMEWORK] = { + CONF_TYPE: "arduino", + CONF_VERSION: conf.pop(CONF_ARDUINO_VERSION), + } + if CONF_BOARD in conf: + plat_conf[CONF_BOARD] = conf.pop(CONF_BOARD) + # Insert generated target platform config to main config + config[plat] = plat_conf + config[CONF_ESPHOME] = conf def include_file(path, basename): @@ -263,25 +220,10 @@ async def add_includes(includes): @coroutine_with_priority(-1000.0) -async def _esp8266_add_lwip_type(): - # If any component has already set this, do not change it - if any( - flag.startswith("-DPIO_FRAMEWORK_ARDUINO_LWIP2_") for flag in CORE.build_flags - ): - return - - # Default for platformio is LWIP2_LOW_MEMORY with: - # - MSS=536 - # - LWIP_FEATURES enabled - # - this only adds some optional features like IP incoming packet reassembly and NAPT - # see also: - # https://github.com/esp8266/Arduino/blob/master/tools/sdk/lwip2/include/lwipopts.h - - # Instead we use LWIP2_HIGHER_BANDWIDTH_LOW_FLASH with: - # - MSS=1460 - # - LWIP_FEATURES disabled (because we don't need them) - # Other projects like Tasmota & ESPEasy also use this - cg.add_build_flag("-DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH") +async def _add_platformio_options(pio_options): + # Add includes at the very end, so that they override everything + for key, val in pio_options.items(): + cg.add_platformio_option(key, val) @coroutine_with_priority(30.0) @@ -315,10 +257,6 @@ async def to_code(config): CORE.add_job(_add_automations, config) - # Set LWIP build constants for ESP8266 - if CORE.is_esp8266: - CORE.add_job(_esp8266_add_lwip_type) - cg.add_build_flag("-fno-exceptions") # Libraries @@ -337,25 +275,16 @@ async def to_code(config): else: cg.add_library(lib, None) - if CORE.is_esp8266: - # Arduino 2 has a non-standards conformant new that returns a nullptr instead of failing when - # out of memory and exceptions are disabled. Since Arduino 2.6.0, this flag can be used to make - # new abort instead. Use it so that OOM fails early (on allocation) instead of on dereference of - # a NULL pointer (so the stacktrace makes more sense), and for consistency with Arduino 3, - # which always aborts if exceptions are disabled. - # For cases where nullptrs can be handled, use nothrow: `new (std::nothrow) T;` - cg.add_build_flag("-DNEW_OOM_ABORT") - cg.add_build_flag("-Wno-unused-variable") cg.add_build_flag("-Wno-unused-but-set-variable") cg.add_build_flag("-Wno-sign-compare") - if config.get(CONF_ESP8266_RESTORE_FROM_FLASH, False): - cg.add_define("USE_ESP8266_PREFERENCES_FLASH") if config[CONF_INCLUDES]: CORE.add_job(add_includes, config[CONF_INCLUDES]) - cg.add_define("ESPHOME_BOARD", CORE.board) if CONF_PROJECT in config: cg.add_define("ESPHOME_PROJECT_NAME", config[CONF_PROJECT][CONF_NAME]) cg.add_define("ESPHOME_PROJECT_VERSION", config[CONF_PROJECT][CONF_VERSION]) + + if config[CONF_PLATFORMIO_OPTIONS]: + CORE.add_job(_add_platformio_options, config[CONF_PLATFORMIO_OPTIONS]) diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 89010ce246..ac40921a4a 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -13,7 +13,9 @@ // Feature flags #define USE_API #define USE_BINARY_SENSOR +#ifdef USE_ARDUINO #define USE_CAPTIVE_PORTAL +#endif #define USE_CLIMATE #define USE_COVER #define USE_DEEP_SLEEP @@ -21,13 +23,17 @@ #define USE_FAN #define USE_GRAPH #define USE_HOMEASSISTANT_TIME -#define USE_HTTP_REQUEST_ESP8266_HTTPS -#define USE_I2C_MULTIPLEXER + +#ifdef USE_ARDUINO #define USE_JSON +#define USE_NEXTION_TFT_UPLOAD +#define USE_MQTT +#define USE_CAPTIVE_PORTAL +#endif // USE_ARDUINO + #define USE_LIGHT #define USE_LOGGER #define USE_MDNS -#define USE_MQTT #define USE_NUMBER #define USE_OTA_STATE_CALLBACK #define USE_POWER_SUPPLY @@ -37,22 +43,29 @@ #define USE_STATUS_LED #define USE_SWITCH #define USE_TEXT_SENSOR -#define USE_TFT_UPLOAD + #define USE_TIME #define USE_WIFI +#ifdef USE_ARDUINO #define USE_WIFI_WPA2_EAP - -#ifdef ARDUINO_ARCH_ESP32 -#define USE_ESP32_BLE_SERVER -#define USE_ESP32_CAMERA -#define USE_ETHERNET -#define USE_IMPROV -#define USE_SOCKET_IMPL_BSD_SOCKETS #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP32 +#define USE_ESP32_BLE_SERVER +#define USE_ESP32_CAMERA + +#ifdef USE_ARDUINO +#define USE_ETHERNET +#endif // USE_ARDUINO + +#define USE_IMPROV +#define USE_SOCKET_IMPL_BSD_SOCKETS +#endif // USE_ESP32 + +#ifdef USE_ESP8266 #define USE_ADC_SENSOR_VCC #define USE_SOCKET_IMPL_LWIP_TCP +#define USE_HTTP_REQUEST_ESP8266_HTTPS #endif #define USE_API_PLAINTEXT @@ -60,3 +73,9 @@ // Disabled feature flags //#define USE_BSEC // Requires a library with proprietary license. +#define USE_TIME +#define USE_DEEP_SLEEP +#define ESPHOME_BOARD "dummy_board" +#define USE_MDNS +#define USE_API_NOISE +#define USE_API_PLAINTEXT diff --git a/esphome/core/esphal.cpp b/esphome/core/esphal.cpp deleted file mode 100644 index 849fdf95ad..0000000000 --- a/esphome/core/esphal.cpp +++ /dev/null @@ -1,303 +0,0 @@ -#include "esphome/core/esphal.h" -#include "esphome/core/macros.h" -#include "esphome/core/helpers.h" -#include "esphome/core/defines.h" -#include "esphome/core/log.h" - -#ifdef ARDUINO_ARCH_ESP8266 -extern "C" { -typedef struct { // NOLINT - void *interruptInfo; // NOLINT - void *functionInfo; // NOLINT -} ArgStructure; - -void ICACHE_RAM_ATTR __attachInterruptArg(uint8_t pin, void (*)(void *), void *fp, // NOLINT - int mode); -void ICACHE_RAM_ATTR __detachInterrupt(uint8_t pin); // NOLINT -}; -#endif - -namespace esphome { - -static const char *const TAG = "esphal"; - -GPIOPin::GPIOPin(uint8_t pin, uint8_t mode, bool inverted) - : pin_(pin), - mode_(mode), - inverted_(inverted), -#ifdef ARDUINO_ARCH_ESP8266 - gpio_read_(pin < 16 ? &GPI : &GP16I), - gpio_mask_(pin < 16 ? (1UL << pin) : 1) -#elif ARDUINO_ARCH_ESP32 -#ifdef CONFIG_IDF_TARGET_ESP32C3 - gpio_set_(&GPIO.out_w1ts.val), - gpio_clear_(&GPIO.out_w1tc.val), - gpio_read_(&GPIO.in.val), -#else - gpio_set_(pin < 32 ? &GPIO.out_w1ts : &GPIO.out1_w1ts.val), - gpio_clear_(pin < 32 ? &GPIO.out_w1tc : &GPIO.out1_w1tc.val), - gpio_read_(pin < 32 ? &GPIO.in : &GPIO.in1.val), -#endif - gpio_mask_(pin < 32 ? (1UL << pin) : (1UL << (pin - 32))) -#endif -{ -} - -const LogString *GPIOPin::get_pin_mode_name() const { - const char *mode_s; - switch (this->mode_) { - case INPUT: - return LOG_STR("INPUT"); - case OUTPUT: - return LOG_STR("OUTPUT"); - case INPUT_PULLUP: - return LOG_STR("INPUT_PULLUP"); - case OUTPUT_OPEN_DRAIN: - return LOG_STR("OUTPUT_OPEN_DRAIN"); - case SPECIAL: - return LOG_STR("SPECIAL"); - case FUNCTION_1: - return LOG_STR("FUNCTION_1"); - case FUNCTION_2: - return LOG_STR("FUNCTION_2"); - case FUNCTION_3: - return LOG_STR("FUNCTION_3"); - case FUNCTION_4: - return LOG_STR("FUNCTION_4"); -#ifdef ARDUINO_ARCH_ESP32 - case PULLUP: - return LOG_STR("PULLUP"); - case PULLDOWN: - return LOG_STR("PULLDOWN"); - case INPUT_PULLDOWN: - return LOG_STR("INPUT_PULLDOWN"); - case OPEN_DRAIN: - return LOG_STR("OPEN_DRAIN"); - case FUNCTION_5: - return LOG_STR("FUNCTION_5"); - case FUNCTION_6: - return LOG_STR("FUNCTION_6"); - case ANALOG: - return LOG_STR("ANALOG"); -#endif -#ifdef ARDUINO_ARCH_ESP8266 - case FUNCTION_0: - return LOG_STR("FUNCTION_0"); - case WAKEUP_PULLUP: - return LOG_STR("WAKEUP_PULLUP"); - case WAKEUP_PULLDOWN: - return LOG_STR("WAKEUP_PULLDOWN"); - case INPUT_PULLDOWN_16: - return LOG_STR("INPUT_PULLDOWN_16"); -#endif - default: - return LOG_STR("UNKNOWN"); - } -} - -unsigned char GPIOPin::get_pin() const { return this->pin_; } -unsigned char GPIOPin::get_mode() const { return this->mode_; } - -bool GPIOPin::is_inverted() const { return this->inverted_; } -void GPIOPin::setup() { this->pin_mode(this->mode_); } -bool ICACHE_RAM_ATTR HOT GPIOPin::digital_read() { - return bool((*this->gpio_read_) & this->gpio_mask_) != this->inverted_; -} -bool ICACHE_RAM_ATTR HOT ISRInternalGPIOPin::digital_read() { - return bool((*this->gpio_read_) & this->gpio_mask_) != this->inverted_; -} -void ICACHE_RAM_ATTR HOT GPIOPin::digital_write(bool value) { -#ifdef ARDUINO_ARCH_ESP8266 - if (this->pin_ != 16) { - if (value != this->inverted_) { - GPOS = this->gpio_mask_; - } else { - GPOC = this->gpio_mask_; - } - } else { - if (value != this->inverted_) { - GP16O |= 1; - } else { - GP16O &= ~1; - } - } -#endif -#ifdef ARDUINO_ARCH_ESP32 - if (value != this->inverted_) { - (*this->gpio_set_) = this->gpio_mask_; - } else { - (*this->gpio_clear_) = this->gpio_mask_; - } -#endif -} -void ICACHE_RAM_ATTR HOT ISRInternalGPIOPin::digital_write(bool value) { -#ifdef ARDUINO_ARCH_ESP8266 - if (this->pin_ != 16) { - if (value != this->inverted_) { - GPOS = this->gpio_mask_; - } else { - GPOC = this->gpio_mask_; - } - } else { - if (value != this->inverted_) { - GP16O |= 1; - } else { - GP16O &= ~1; - } - } -#endif -#ifdef ARDUINO_ARCH_ESP32 - if (value != this->inverted_) { - (*this->gpio_set_) = this->gpio_mask_; - } else { - (*this->gpio_clear_) = this->gpio_mask_; - } -#endif -} -ISRInternalGPIOPin::ISRInternalGPIOPin(uint8_t pin, -#ifdef ARDUINO_ARCH_ESP32 - volatile uint32_t *gpio_clear, volatile uint32_t *gpio_set, -#endif - volatile uint32_t *gpio_read, uint32_t gpio_mask, bool inverted) - : pin_(pin), - inverted_(inverted), - gpio_read_(gpio_read), - gpio_mask_(gpio_mask) -#ifdef ARDUINO_ARCH_ESP32 - , - gpio_clear_(gpio_clear), - gpio_set_(gpio_set) -#endif -{ -} -void ICACHE_RAM_ATTR ISRInternalGPIOPin::clear_interrupt() { -#ifdef ARDUINO_ARCH_ESP8266 - GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, this->gpio_mask_); -#endif -#ifdef ARDUINO_ARCH_ESP32 -#ifdef CONFIG_IDF_TARGET_ESP32C3 - GPIO.status_w1tc.val = this->gpio_mask_; -#else - if (this->pin_ < 32) { - GPIO.status_w1tc = this->gpio_mask_; - } else { - GPIO.status1_w1tc.intr_st = this->gpio_mask_; - } -#endif -#endif -} - -void ICACHE_RAM_ATTR HOT GPIOPin::pin_mode(uint8_t mode) { -#ifdef ARDUINO_ARCH_ESP8266 - if (this->pin_ == 16 && mode == INPUT_PULLUP) { - // pullups are not available on GPIO16, manually override with - // input mode. - pinMode(16, INPUT); - return; - } -#endif - pinMode(this->pin_, mode); -} - -#ifdef ARDUINO_ARCH_ESP8266 -struct ESPHomeInterruptFuncInfo { - void (*func)(void *); - void *arg; -}; - -void ICACHE_RAM_ATTR interrupt_handler(void *arg) { - ArgStructure *as = static_cast(arg); - auto *info = static_cast(as->functionInfo); - info->func(info->arg); -} -#endif - -void GPIOPin::detach_interrupt() const { this->detach_interrupt_(); } -void GPIOPin::detach_interrupt_() const { -#ifdef ARDUINO_ARCH_ESP8266 - __detachInterrupt(get_pin()); -#endif -#ifdef ARDUINO_ARCH_ESP32 - detachInterrupt(get_pin()); -#endif -} -void GPIOPin::attach_interrupt_(void (*func)(void *), void *arg, int mode) const { - if (this->inverted_) { - if (mode == RISING) { - mode = FALLING; - } else if (mode == FALLING) { - mode = RISING; - } - } -#ifdef ARDUINO_ARCH_ESP8266 - ArgStructure *as = new ArgStructure; // NOLINT - as->interruptInfo = nullptr; - - as->functionInfo = new ESPHomeInterruptFuncInfo{ - // NOLINT - .func = func, - .arg = arg, - }; - - __attachInterruptArg(this->pin_, interrupt_handler, as, mode); -#endif -#ifdef ARDUINO_ARCH_ESP32 - // work around issue https://github.com/espressif/arduino-esp32/pull/1776 in arduino core - // yet again proves how horrible code is there :( - how could that have been accepted... - auto *attach = reinterpret_cast(attachInterruptArg); - attach(this->pin_, func, arg, mode); -#endif -} - -ISRInternalGPIOPin *GPIOPin::to_isr() const { - return new ISRInternalGPIOPin(this->pin_, // NOLINT -#ifdef ARDUINO_ARCH_ESP32 - this->gpio_clear_, this->gpio_set_, -#endif - this->gpio_read_, this->gpio_mask_, this->inverted_); -} - -void force_link_symbols() { -#ifdef ARDUINO_ARCH_ESP8266 - // Tasmota uses magic bytes in the binary to check if an OTA firmware is compatible - // with their settings - ESPHome uses a different settings system (that can also survive - // erases). So set magic bytes indicating all tasmota versions are supported. - // This only adds 12 bytes of binary size, which is an acceptable price to pay for easier support - // for Tasmota. - // https://github.com/arendst/Tasmota/blob/b05301b1497942167a015a6113b7f424e42942cd/tasmota/settings.ino#L346-L380 - // https://github.com/arendst/Tasmota/blob/b05301b1497942167a015a6113b7f424e42942cd/tasmota/i18n.h#L652-L654 - const static uint32_t TASMOTA_MAGIC_BYTES[] PROGMEM = {0x5AA55AA5, 0xFFFFFFFF, 0xA55AA55A}; - // Force link symbol by using a volatile integer (GCC attribute used does not work because of LTO) - volatile int x = 0; - x = TASMOTA_MAGIC_BYTES[x]; -#endif -} - -} // namespace esphome - -#if defined(ARDUINO_ARCH_ESP8266) && ARDUINO_VERSION_CODE < VERSION_CODE(2, 4, 0) -// Fix 2.3.0 std missing memchr -extern "C" { -void *memchr(const void *s, int c, size_t n) { - if (n == 0) - return nullptr; - const uint8_t *p = reinterpret_cast(s); - do { - if (*p++ == c) - return const_cast(reinterpret_cast(p - 1)); - } while (--n != 0); - return nullptr; -} -}; -#endif - -#ifdef ARDUINO_ARCH_ESP8266 -extern "C" { -extern void resetPins() { // NOLINT - // Added in framework 2.7.0 - // usually this sets up all pins to be in INPUT mode - // however, not strictly needed as we set up the pins properly - // ourselves and this causes pins to toggle during reboot. -} -} -#endif diff --git a/esphome/core/esphal.h b/esphome/core/esphal.h deleted file mode 100644 index 1b92f816b1..0000000000 --- a/esphome/core/esphal.h +++ /dev/null @@ -1,128 +0,0 @@ -#pragma once - -#include "Arduino.h" - -// Fix some arduino defs -#ifdef round -#undef round -#endif -#ifdef bool -#undef bool -#endif -#ifdef true -#undef true -#endif -#ifdef false -#undef false -#endif -#ifdef min -#undef min -#endif -#ifdef max -#undef max -#endif -#ifdef abs -#undef abs -#endif - -#include "esphome/core/log.h" - -namespace esphome { - -#define LOG_PIN(prefix, pin) \ - if ((pin) != nullptr) { \ - ESP_LOGCONFIG(TAG, prefix LOG_PIN_PATTERN, LOG_PIN_ARGS(pin)); \ - } -#define LOG_PIN_PATTERN "GPIO%u (Mode: %s%s)" -#define LOG_PIN_ARGS(pin) \ - (pin)->get_pin(), LOG_STR_ARG((pin)->get_pin_mode_name()), ((pin)->is_inverted() ? ", INVERTED" : "") - -/// Copy of GPIOPin that is safe to use from ISRs (with no virtual functions) -class ISRInternalGPIOPin { - public: - ISRInternalGPIOPin(uint8_t pin, -#ifdef ARDUINO_ARCH_ESP32 - volatile uint32_t *gpio_clear, volatile uint32_t *gpio_set, -#endif - volatile uint32_t *gpio_read, uint32_t gpio_mask, bool inverted); - bool digital_read(); - void digital_write(bool value); - void clear_interrupt(); - - protected: - const uint8_t pin_; - const bool inverted_; - volatile uint32_t *const gpio_read_; - const uint32_t gpio_mask_; -#ifdef ARDUINO_ARCH_ESP32 - volatile uint32_t *const gpio_clear_; - volatile uint32_t *const gpio_set_; -#endif -}; - -/** A high-level abstraction class that can expose a pin together with useful options like pinMode. - * - * Set the parameters for this at construction time and use setup() to apply them. The inverted parameter will - * automatically invert the input/output for you. - * - * Use read_value() and write_value() to use digitalRead() and digitalWrite(), respectively. - */ -class GPIOPin { - public: - /** Construct the GPIOPin instance. - * - * @param pin The GPIO pin number of this instance. - * @param mode The Arduino pinMode that this pin should be put into at setup(). - * @param inverted Whether all digitalRead/digitalWrite calls should be inverted. - */ - GPIOPin(uint8_t pin, uint8_t mode, bool inverted = false); - - /// Setup the pin mode. - virtual void setup(); - /// Read the binary value from this pin using digitalRead (and inverts automatically). - virtual bool digital_read(); - /// Write the binary value to this pin using digitalWrite (and inverts automatically). - virtual void digital_write(bool value); - /// Set the pin mode - virtual void pin_mode(uint8_t mode); - - /// Get the GPIO pin number. - uint8_t get_pin() const; - const LogString *get_pin_mode_name() const; - /// Get the pinMode of this pin. - uint8_t get_mode() const; - /// Return whether this pin shall be treated as inverted. (for example active-low) - bool is_inverted() const; - - template void attach_interrupt(void (*func)(T *), T *arg, int mode) const; - void detach_interrupt() const; - - ISRInternalGPIOPin *to_isr() const; - - protected: - void attach_interrupt_(void (*func)(void *), void *arg, int mode) const; - void detach_interrupt_() const; - - const uint8_t pin_; - const uint8_t mode_; - const bool inverted_; -#ifdef ARDUINO_ARCH_ESP32 - volatile uint32_t *const gpio_set_; - volatile uint32_t *const gpio_clear_; -#endif - volatile uint32_t *const gpio_read_; - const uint32_t gpio_mask_; -}; - -template void GPIOPin::attach_interrupt(void (*func)(T *), T *arg, int mode) const { - this->attach_interrupt_(reinterpret_cast(func), arg, mode); -} -/** This function can be used by the HAL to force-link specific symbols - * into the generated binary without modifying the linker script. - * - * It is called by the application very early on startup and should not be used for anything - * other than forcing symbols to be linked. - */ -void force_link_symbols(); - -} // namespace esphome diff --git a/esphome/core/gpio.h b/esphome/core/gpio.h new file mode 100644 index 0000000000..25d56b9020 --- /dev/null +++ b/esphome/core/gpio.h @@ -0,0 +1,98 @@ +#pragma once +#include +#include + +namespace esphome { + +#define LOG_PIN(prefix, pin) \ + if ((pin) != nullptr) { \ + ESP_LOGCONFIG(TAG, prefix "%s", pin->dump_summary().c_str()); \ + } + +// put GPIO flags in a namepsace to not pollute esphome namespace +namespace gpio { + +enum Flags : uint8_t { + // Can't name these just INPUT because of Arduino defines :( + FLAG_NONE = 0x00, + FLAG_INPUT = 0x01, + FLAG_OUTPUT = 0x02, + FLAG_OPEN_DRAIN = 0x04, + FLAG_PULLUP = 0x08, + FLAG_PULLDOWN = 0x10, +}; + +class FlagsHelper { + public: + constexpr FlagsHelper(Flags val) : val_(val) {} + constexpr operator Flags() const { return val_; } + + protected: + Flags val_; +}; +constexpr FlagsHelper operator&(Flags lhs, Flags rhs) { + return static_cast(static_cast(lhs) & static_cast(rhs)); +} +constexpr FlagsHelper operator|(Flags lhs, Flags rhs) { + return static_cast(static_cast(lhs) | static_cast(rhs)); +} + +enum InterruptType : uint8_t { + INTERRUPT_RISING_EDGE = 1, + INTERRUPT_FALLING_EDGE = 2, + INTERRUPT_ANY_EDGE = 3, + INTERRUPT_LOW_LEVEL = 4, + INTERRUPT_HIGH_LEVEL = 5, +}; + +} // namespace gpio + +class GPIOPin { + public: + virtual void setup() = 0; + + virtual void pin_mode(gpio::Flags flags) = 0; + + virtual bool digital_read() = 0; + + virtual void digital_write(bool value) = 0; + + virtual std::string dump_summary() const = 0; + + virtual bool is_internal() { return false; } +}; + +/// Copy of GPIOPin that is safe to use from ISRs (with no virtual functions) +class ISRInternalGPIOPin { + public: + ISRInternalGPIOPin() = default; + ISRInternalGPIOPin(void *arg) : arg_(arg) {} + bool digital_read(); + void digital_write(bool value); + void clear_interrupt(); + + protected: + void *arg_ = nullptr; +}; + +class InternalGPIOPin : public GPIOPin { + public: + template void attach_interrupt(void (*func)(T *), T *arg, gpio::InterruptType type) const { + this->attach_interrupt_(reinterpret_cast(func), arg, type); + } + + virtual void detach_interrupt() const = 0; + + virtual ISRInternalGPIOPin to_isr() const = 0; + + virtual uint8_t get_pin() const = 0; + + bool is_internal() override { return true; } + + virtual bool is_inverted() const = 0; + + protected: + virtual void attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const = 0; +}; + +} // namespace esphome diff --git a/esphome/core/hal.h b/esphome/core/hal.h new file mode 100644 index 0000000000..2843bb9b15 --- /dev/null +++ b/esphome/core/hal.h @@ -0,0 +1,47 @@ +#pragma once +#include +#include +#include "gpio.h" + +#if defined(USE_ESP32_FRAMEWORK_ESP_IDF) +#include +#ifndef PROGMEM +#define PROGMEM +#endif + +#elif defined(USE_ESP32_FRAMEWORK_ARDUINO) + +#include + +#ifndef PROGMEM +#define PROGMEM +#endif + +#elif defined(USE_ESP8266) + +#include +#ifndef PROGMEM +#define PROGMEM ICACHE_RODATA_ATTR +#endif + +#else + +#define IRAM_ATTR +#define PROGMEM + +#endif + +namespace esphome { + +void yield(); +uint32_t millis(); +uint32_t micros(); +void delay(uint32_t ms); +void delayMicroseconds(uint32_t us); +void __attribute__((noreturn)) arch_restart(); +void arch_feed_wdt(); +uint32_t arch_get_cpu_cycle_count(); +uint32_t arch_get_cpu_freq_hz(); +uint8_t progmem_read_byte(const uint8_t *addr); + +} // namespace esphome diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 53c7d1de43..a90eb74be2 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -1,15 +1,22 @@ #include "esphome/core/helpers.h" #include #include +#include +#include -#ifdef ARDUINO_ARCH_ESP8266 +#if defined(USE_ESP8266) #include -#else +#include +#elif defined(USE_ESP32_FRAMEWORK_ARDUINO) #include +#elif defined(USE_ESP_IDF) +#include "esp_system.h" +#include +#include #endif #include "esphome/core/log.h" -#include "esphome/core/esphal.h" +#include "esphome/core/hal.h" namespace esphome { @@ -18,10 +25,10 @@ static const char *const TAG = "helpers"; std::string get_mac_address() { char tmp[20]; uint8_t mac[6]; -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 esp_efuse_mac_get_default(mac); #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 WiFi.macAddress(mac); #endif sprintf(tmp, "%02x%02x%02x%02x%02x%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); @@ -31,10 +38,10 @@ std::string get_mac_address() { std::string get_mac_address_pretty() { char tmp[20]; uint8_t mac[6]; -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 esp_efuse_mac_get_default(mac); #endif -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 WiFi.macAddress(mac); #endif sprintf(tmp, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); @@ -44,9 +51,9 @@ std::string get_mac_address_pretty() { std::string generate_hostname(const std::string &base) { return base + std::string("-") + get_mac_address(); } uint32_t random_uint32() { -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32 return esp_random(); -#else +#elif defined(USE_ESP8266) return os_random(); #endif } @@ -56,11 +63,13 @@ double random_double() { return random_uint32() / double(UINT32_MAX); } float random_float() { return float(random_double()); } void fill_random(uint8_t *data, size_t len) { -#ifdef ARDUINO_ARCH_ESP32 +#if defined(USE_ESP_IDF) || defined(USE_ESP32_FRAMEWORK_ARDUINO) esp_fill_random(data, len); -#else +#elif defined(USE_ESP8266) int err = os_get_random(data, len); assert(err == 0); +#else +#error "No random source for this system config" #endif } @@ -123,10 +132,13 @@ std::string truncate_string(const std::string &s, size_t length) { } std::string value_accuracy_to_string(float value, int8_t accuracy_decimals) { - auto multiplier = float(powf(10.0f, accuracy_decimals)); - float value_rounded = roundf(value * multiplier) / multiplier; + if (accuracy_decimals < 0) { + auto multiplier = powf(10.0f, accuracy_decimals); + value = roundf(value * multiplier) / multiplier; + accuracy_decimals = 0; + } char tmp[32]; // should be enough, but we should maybe improve this at some point. - dtostrf(value_rounded, 0, uint8_t(std::max(0, int(accuracy_decimals))), tmp); + snprintf(tmp, sizeof(tmp), "%.*f", accuracy_decimals, value); return std::string(tmp); } std::string uint64_to_string(uint64_t num) { @@ -334,13 +346,13 @@ std::string hexencode(const uint8_t *data, uint32_t len) { return res; } -#ifdef ARDUINO_ARCH_ESP8266 -ICACHE_RAM_ATTR InterruptLock::InterruptLock() { xt_state_ = xt_rsil(15); } -ICACHE_RAM_ATTR InterruptLock::~InterruptLock() { xt_wsr_ps(xt_state_); } +#ifdef USE_ESP8266 +IRAM_ATTR InterruptLock::InterruptLock() { xt_state_ = xt_rsil(15); } +IRAM_ATTR InterruptLock::~InterruptLock() { xt_wsr_ps(xt_state_); } #endif -#ifdef ARDUINO_ARCH_ESP32 -ICACHE_RAM_ATTR InterruptLock::InterruptLock() { portDISABLE_INTERRUPTS(); } -ICACHE_RAM_ATTR InterruptLock::~InterruptLock() { portENABLE_INTERRUPTS(); } +#ifdef USE_ESP32_FRAMEWORK_ARDUINO +IRAM_ATTR InterruptLock::InterruptLock() { portDISABLE_INTERRUPTS(); } +IRAM_ATTR InterruptLock::~InterruptLock() { portENABLE_INTERRUPTS(); } #endif } // namespace esphome diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 6c0ee8399e..40d94005e7 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -6,7 +6,7 @@ #include #include -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32_FRAMEWORK_ARDUINO #include "esp32-hal-psram.h" #endif @@ -151,7 +151,7 @@ uint32_t encode_uint32(uint8_t msb, uint8_t byte2, uint8_t byte3, uint8_t lsb); * This behaves like std::lock_guard. As long as the value is visible in the current stack, all interrupts * (including flash reads) will be disabled. * - * Please note all functions called when the interrupt lock must be marked ICACHE_RAM_ATTR (loading code into + * Please note all functions called when the interrupt lock must be marked IRAM_ATTR (loading code into * instruction cache is done via interrupts; disabling interrupts prevents data not already in cache from being * pulled from flash). * @@ -173,7 +173,7 @@ class InterruptLock { ~InterruptLock(); protected: -#ifdef ARDUINO_ARCH_ESP8266 +#ifdef USE_ESP8266 uint32_t xt_state_; #endif }; @@ -277,8 +277,8 @@ template class TemplatableValue { LAMBDA, } type_; - T value_; - std::function f_; + T value_{}; + std::function f_{}; }; template class TemplatableStringValue : public TemplatableValue { @@ -329,7 +329,7 @@ uint32_t fnv1_hash(const std::string &str); template T *new_buffer(size_t length) { T *buffer; -#ifdef ARDUINO_ARCH_ESP32 +#ifdef USE_ESP32_FRAMEWORK_ARDUINO if (psramFound()) { buffer = (T *) ps_malloc(length); } else { diff --git a/esphome/core/log.cpp b/esphome/core/log.cpp index 9b49a4c6ba..424154d253 100644 --- a/esphome/core/log.cpp +++ b/esphome/core/log.cpp @@ -46,7 +46,7 @@ void HOT esp_log_vprintf_(int level, const char *tag, int line, const __FlashStr } #endif -#ifdef ARDUINO_ARCH_ESP32 +#if defined(USE_ESP32_FRAMEWORK_ARDUINO) || defined(USE_ESP_IDF) int HOT esp_idf_log_vprintf_(const char *format, va_list args) { // NOLINT #ifdef USE_LOGGER auto *log = logger::global_logger; diff --git a/esphome/core/log.h b/esphome/core/log.h index 3c2f1e6999..590ad26032 100644 --- a/esphome/core/log.h +++ b/esphome/core/log.h @@ -8,10 +8,14 @@ #include "WString.h" #endif -// Both the ESP-IDF and Arduino also define ESP_LOG* macros. Include them here, so that they won't -// be reincluded later on and redefine our macros. -#ifdef ARDUINO_ARCH_ESP32 +#include "esphome/core/macros.h" + +// Include ESP-IDF/Arduino based logging methods here so they don't undefine ours later +#if defined(USE_ESP32_FRAMEWORK_ARDUINO) || defined(USE_ESP_IDF) +#include #include +#endif +#ifdef USE_ESP32_FRAMEWORK_ARDUINO #include #endif @@ -58,7 +62,7 @@ void esp_log_vprintf_(int level, const char *tag, int line, const char *format, #ifdef USE_STORE_LOG_STR_IN_FLASH void esp_log_vprintf_(int level, const char *tag, int line, const __FlashStringHelper *format, va_list args); #endif -#ifdef ARDUINO_ARCH_ESP32 +#if defined(USE_ESP32_FRAMEWORK_ARDUINO) || defined(USE_ESP_IDF) int esp_idf_log_vprintf_(const char *format, va_list args); // NOLINT #endif diff --git a/esphome/core/macros.h b/esphome/core/macros.h index 59b52bf7a1..b0027a276c 100644 --- a/esphome/core/macros.h +++ b/esphome/core/macros.h @@ -2,7 +2,7 @@ #define VERSION_CODE(major, minor, patch) ((major) << 16 | (minor) << 8 | (patch)) -#if defined(ARDUINO_ARCH_ESP8266) +#if defined(USE_ESP8266) #include #if defined(ARDUINO_ESP8266_MAJOR) && defined(ARDUINO_ESP8266_MINOR) && defined(ARDUINO_ESP8266_REVISION) // v3.0.1+ @@ -43,7 +43,7 @@ #warning "Could not determine Arduino framework version, update esphome/core/macros.h!" #endif -#elif defined(ARDUINO_ARCH_ESP32) +#elif defined(USE_ESP32_FRAMEWORK_ARDUINO) #if defined(IDF_VER) // identifies v2, needed since v1 doesn't have the esp_arduino_version.h header #include diff --git a/esphome/core/preferences.cpp b/esphome/core/preferences.cpp deleted file mode 100644 index f051ce0e8a..0000000000 --- a/esphome/core/preferences.cpp +++ /dev/null @@ -1,303 +0,0 @@ -#include "esphome/core/preferences.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" -#include "esphome/core/application.h" - -#ifdef ARDUINO_ARCH_ESP8266 -extern "C" { -#include "spi_flash.h" -} -#endif -#ifdef ARDUINO_ARCH_ESP32 -#include "nvs.h" -#include "nvs_flash.h" -#endif - -namespace esphome { - -static const char *const TAG = "preferences"; - -ESPPreferenceObject::ESPPreferenceObject(size_t offset, size_t length, uint32_t type) - : offset_(offset), length_words_(length), type_(type), data_(length + 1) {} -bool ESPPreferenceObject::load_() { - if (!this->is_initialized()) { - ESP_LOGV(TAG, "Load Pref Not initialized!"); - return false; - } - if (!this->load_internal_()) - return false; - - bool valid = this->data_[this->length_words_] == this->calculate_crc_(); - - ESP_LOGVV(TAG, "LOAD %u: valid=%s, 0=0x%08X 1=0x%08X (Type=%u, CRC=0x%08X)", this->offset_, // NOLINT - YESNO(valid), this->data_[0], this->data_[1], this->type_, this->calculate_crc_()); - return valid; -} -bool ESPPreferenceObject::save_() { - if (!this->is_initialized()) { - ESP_LOGV(TAG, "Save Pref Not initialized!"); - return false; - } - - this->data_[this->length_words_] = this->calculate_crc_(); - if (!this->save_internal_()) - return false; - ESP_LOGVV(TAG, "SAVE %u: 0=0x%08X 1=0x%08X (Type=%u, CRC=0x%08X)", this->offset_, // NOLINT - this->data_[0], this->data_[1], this->type_, this->calculate_crc_()); - return true; -} - -#ifdef ARDUINO_ARCH_ESP8266 - -static const uint32_t ESP_RTC_USER_MEM_START = 0x60001200; -#define ESP_RTC_USER_MEM ((uint32_t *) ESP_RTC_USER_MEM_START) -static const uint32_t ESP_RTC_USER_MEM_SIZE_WORDS = 128; -static const uint32_t ESP_RTC_USER_MEM_SIZE_BYTES = ESP_RTC_USER_MEM_SIZE_WORDS * 4; - -#ifdef USE_ESP8266_PREFERENCES_FLASH -static const uint32_t ESP8266_FLASH_STORAGE_SIZE = 128; -#else -static const uint32_t ESP8266_FLASH_STORAGE_SIZE = 64; -#endif - -static inline bool esp_rtc_user_mem_read(uint32_t index, uint32_t *dest) { - if (index >= ESP_RTC_USER_MEM_SIZE_WORDS) { - return false; - } - *dest = ESP_RTC_USER_MEM[index]; // NOLINT(performance-no-int-to-ptr) - return true; -} - -static bool esp8266_flash_dirty = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) - -static inline bool esp_rtc_user_mem_write(uint32_t index, uint32_t value) { - if (index >= ESP_RTC_USER_MEM_SIZE_WORDS) { - return false; - } - if (index < 32 && global_preferences.is_prevent_write()) { - return false; - } - - auto *ptr = &ESP_RTC_USER_MEM[index]; // NOLINT(performance-no-int-to-ptr) - *ptr = value; - return true; -} - -extern "C" uint32_t _SPIFFS_end; // NOLINT - -static const uint32_t get_esp8266_flash_sector() { - union { - uint32_t *ptr; - uint32_t uint; - } data{}; - data.ptr = &_SPIFFS_end; - return (data.uint - 0x40200000) / SPI_FLASH_SEC_SIZE; -} -static const uint32_t get_esp8266_flash_address() { return get_esp8266_flash_sector() * SPI_FLASH_SEC_SIZE; } - -void ESPPreferences::save_esp8266_flash_() { - if (!esp8266_flash_dirty) - return; - - ESP_LOGVV(TAG, "Saving preferences to flash..."); - SpiFlashOpResult erase_res, write_res = SPI_FLASH_RESULT_OK; - { - InterruptLock lock; - erase_res = spi_flash_erase_sector(get_esp8266_flash_sector()); - if (erase_res == SPI_FLASH_RESULT_OK) { - write_res = spi_flash_write(get_esp8266_flash_address(), this->flash_storage_, ESP8266_FLASH_STORAGE_SIZE * 4); - } - } - if (erase_res != SPI_FLASH_RESULT_OK) { - ESP_LOGV(TAG, "Erase ESP8266 flash failed!"); - return; - } - if (write_res != SPI_FLASH_RESULT_OK) { - ESP_LOGV(TAG, "Write ESP8266 flash failed!"); - return; - } - - esp8266_flash_dirty = false; -} - -bool ESPPreferenceObject::save_internal_() { - if (this->in_flash_) { - for (uint32_t i = 0; i <= this->length_words_; i++) { - uint32_t j = this->offset_ + i; - if (j >= ESP8266_FLASH_STORAGE_SIZE) - return false; - uint32_t v = this->data_[i]; - uint32_t *ptr = &global_preferences.flash_storage_[j]; - if (*ptr != v) - esp8266_flash_dirty = true; - *ptr = v; - } - global_preferences.save_esp8266_flash_(); - return true; - } - - for (uint32_t i = 0; i <= this->length_words_; i++) { - if (!esp_rtc_user_mem_write(this->offset_ + i, this->data_[i])) - return false; - } - - return true; -} -bool ESPPreferenceObject::load_internal_() { - if (this->in_flash_) { - for (uint32_t i = 0; i <= this->length_words_; i++) { - uint32_t j = this->offset_ + i; - if (j >= ESP8266_FLASH_STORAGE_SIZE) - return false; - this->data_[i] = global_preferences.flash_storage_[j]; - } - - return true; - } - - for (uint32_t i = 0; i <= this->length_words_; i++) { - if (!esp_rtc_user_mem_read(this->offset_ + i, &this->data_[i])) - return false; - } - return true; -} -ESPPreferences::ESPPreferences() - // offset starts from start of user RTC mem (64 words before that are reserved for system), - // an additional 32 words at the start of user RTC are for eboot (OTA, see eboot_command.h), - // which will be reset each time OTA occurs - : current_offset_(0) {} - -void ESPPreferences::begin() { - this->flash_storage_ = new uint32_t[ESP8266_FLASH_STORAGE_SIZE]; // NOLINT - ESP_LOGVV(TAG, "Loading preferences from flash..."); - - { - InterruptLock lock; - spi_flash_read(get_esp8266_flash_address(), this->flash_storage_, ESP8266_FLASH_STORAGE_SIZE * 4); - } -} - -ESPPreferenceObject ESPPreferences::make_preference(size_t length, uint32_t type, bool in_flash) { - if (in_flash) { - uint32_t start = this->current_flash_offset_; - uint32_t end = start + length + 1; - if (end > ESP8266_FLASH_STORAGE_SIZE) - return {}; - auto pref = ESPPreferenceObject(start, length, type); - pref.in_flash_ = true; - this->current_flash_offset_ = end; - return pref; - } - - uint32_t start = this->current_offset_; - uint32_t end = start + length + 1; - bool in_normal = start < 96; - // Normal: offset 0-95 maps to RTC offset 32 - 127, - // Eboot: offset 96-127 maps to RTC offset 0 - 31 words - if (in_normal && end > 96) { - // start is in normal but end is not -> switch to Eboot - this->current_offset_ = start = 96; - end = start + length + 1; - in_normal = false; - } - - if (end > 128) { - // Doesn't fit in data, return uninitialized preference obj. - return {}; - } - - uint32_t rtc_offset; - if (in_normal) { - rtc_offset = start + 32; - } else { - rtc_offset = start - 96; - } - - auto pref = ESPPreferenceObject(rtc_offset, length, type); - this->current_offset_ += length + 1; - return pref; -} -void ESPPreferences::prevent_write(bool prevent) { this->prevent_write_ = prevent; } -bool ESPPreferences::is_prevent_write() { return this->prevent_write_; } -#endif - -#ifdef ARDUINO_ARCH_ESP32 -bool ESPPreferenceObject::save_internal_() { - if (global_preferences.nvs_handle_ == 0) - return false; - - char key[32]; - sprintf(key, "%u", this->offset_); - uint32_t len = (this->length_words_ + 1) * 4; - esp_err_t err = nvs_set_blob(global_preferences.nvs_handle_, key, this->data_.data(), len); - if (err) { - ESP_LOGV(TAG, "nvs_set_blob('%s', len=%u) failed: %s", key, len, esp_err_to_name(err)); - return false; - } - err = nvs_commit(global_preferences.nvs_handle_); - if (err) { - ESP_LOGV(TAG, "nvs_commit('%s', len=%u) failed: %s", key, len, esp_err_to_name(err)); - return false; - } - return true; -} -bool ESPPreferenceObject::load_internal_() { - if (global_preferences.nvs_handle_ == 0) - return false; - - char key[32]; - sprintf(key, "%u", this->offset_); - size_t len = (this->length_words_ + 1) * 4; - - size_t actual_len; - esp_err_t err = nvs_get_blob(global_preferences.nvs_handle_, key, nullptr, &actual_len); - if (err) { - ESP_LOGV(TAG, "nvs_get_blob('%s'): %s - the key might not be set yet", key, esp_err_to_name(err)); - return false; - } - if (actual_len != len) { - ESP_LOGVV(TAG, "NVS length does not match. Assuming key changed (%u!=%u)", actual_len, len); - return false; - } - err = nvs_get_blob(global_preferences.nvs_handle_, key, this->data_.data(), &len); - if (err) { - ESP_LOGV(TAG, "nvs_get_blob('%s') failed: %s", key, esp_err_to_name(err)); - return false; - } - return true; -} -ESPPreferences::ESPPreferences() : current_offset_(0) {} -void ESPPreferences::begin() { - auto ns = truncate_string(App.get_name(), 15); - esp_err_t err = nvs_open(ns.c_str(), NVS_READWRITE, &this->nvs_handle_); - if (err) { - ESP_LOGW(TAG, "nvs_open failed: %s - erasing NVS...", esp_err_to_name(err)); - nvs_flash_deinit(); - nvs_flash_erase(); - nvs_flash_init(); - - err = nvs_open(ns.c_str(), NVS_READWRITE, &this->nvs_handle_); - if (err) { - this->nvs_handle_ = 0; - } - } -} - -ESPPreferenceObject ESPPreferences::make_preference(size_t length, uint32_t type, bool in_flash) { - auto pref = ESPPreferenceObject(this->current_offset_, length, type); - this->current_offset_++; - return pref; -} -#endif -uint32_t ESPPreferenceObject::calculate_crc_() const { - uint32_t crc = this->type_; - for (size_t i = 0; i < this->length_words_; i++) { - crc ^= (this->data_[i] * 2654435769UL) >> 1; - } - return crc; -} -bool ESPPreferenceObject::is_initialized() const { return !this->data_.empty(); } - -ESPPreferences global_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) - -} // namespace esphome diff --git a/esphome/core/preferences.h b/esphome/core/preferences.h index 7ed9a2c8c5..ff7911ed3a 100644 --- a/esphome/core/preferences.h +++ b/esphome/core/preferences.h @@ -1,112 +1,61 @@ #pragma once -#include -#include -#include #include - -#include "esphome/core/defines.h" +#include +#include namespace esphome { +class ESPPreferenceBackend { + public: + virtual bool save(const uint8_t *data, size_t len) = 0; + virtual bool load(uint8_t *data, size_t len) = 0; +}; + class ESPPreferenceObject { public: ESPPreferenceObject() = default; - ESPPreferenceObject(size_t offset, size_t length, uint32_t type); + ESPPreferenceObject(ESPPreferenceBackend *backend) : backend_(backend) {} - template bool save(T *src); + template bool save(const T *src) { + if (backend_ == nullptr) + return false; + return backend_->save(reinterpret_cast(src), sizeof(T)); + } - template bool load(T *dest); - - bool is_initialized() const; + template bool load(T *dest) { + if (backend_ == nullptr) + return false; + return backend_->load(reinterpret_cast(dest), sizeof(T)); + } protected: - friend class ESPPreferences; - - bool save_(); - bool load_(); - bool save_internal_(); - bool load_internal_(); - - uint32_t calculate_crc_() const; - - size_t offset_ = 0; - size_t length_words_ = 0; - uint32_t type_ = 0; - std::vector data_; -#ifdef ARDUINO_ARCH_ESP8266 - bool in_flash_ = false; -#endif + ESPPreferenceBackend *backend_; }; -#ifdef ARDUINO_ARCH_ESP8266 -#ifdef USE_ESP8266_PREFERENCES_FLASH -static const bool DEFAULT_IN_FLASH = true; -#else -static const bool DEFAULT_IN_FLASH = false; -#endif -#endif - -#ifdef ARDUINO_ARCH_ESP32 -static const bool DEFAULT_IN_FLASH = true; -#endif - class ESPPreferences { public: - ESPPreferences(); - void begin(); - ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash = DEFAULT_IN_FLASH); - template ESPPreferenceObject make_preference(uint32_t type, bool in_flash = DEFAULT_IN_FLASH); - -#ifdef ARDUINO_ARCH_ESP8266 - /** On the ESP8266, we can't override the first 128 bytes during OTA uploads - * as the eboot parameters are stored there. Writing there during an OTA upload - * would invalidate applying the new firmware. During normal operation, we use - * this part of the RTC user memory, but stop writing to it during OTA uploads. - * - * @param prevent Whether to prevent writing to the first 32 words of RTC user memory. - */ - void prevent_write(bool prevent); - bool is_prevent_write(); + virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash) = 0; + virtual ESPPreferenceObject make_preference(size_t length, uint32_t type) = 0; +#ifndef USE_ESP8266 + template::value, bool>::type = true> +#else + // esp8266 toolchain doesn't have is_trivially_copyable + template #endif - - protected: - friend ESPPreferenceObject; - - uint32_t current_offset_; -#ifdef ARDUINO_ARCH_ESP32 - uint32_t nvs_handle_; -#endif -#ifdef ARDUINO_ARCH_ESP8266 - void save_esp8266_flash_(); - bool prevent_write_{false}; - uint32_t *flash_storage_; - uint32_t current_flash_offset_; + ESPPreferenceObject make_preference(uint32_t type, bool in_flash) { + return this->make_preference(sizeof(T), type, in_flash); + } +#ifndef USE_ESP8266 + template::value, bool>::type = true> +#else + template #endif + ESPPreferenceObject make_preference(uint32_t type) { + return this->make_preference(sizeof(T), type); + } }; -extern ESPPreferences global_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) - -template ESPPreferenceObject ESPPreferences::make_preference(uint32_t type, bool in_flash) { - return this->make_preference((sizeof(T) + 3) / 4, type, in_flash); -} - -template bool ESPPreferenceObject::save(T *src) { - if (!this->is_initialized()) - return false; - // ensure all bytes are 0 (in case sizeof(T) is not multiple of 4) - std::fill_n(data_.begin(), length_words_, 0); - memcpy(data_.data(), src, sizeof(T)); - return this->save_(); -} - -template bool ESPPreferenceObject::load(T *dest) { - std::fill_n(data_.begin(), length_words_, 0); - if (!this->load_()) - return false; - - memcpy(dest, data_.data(), sizeof(T)); - return true; -} +extern ESPPreferences *global_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) } // namespace esphome diff --git a/esphome/core/scheduler.cpp b/esphome/core/scheduler.cpp index 5718e3b396..a6d3e0307e 100644 --- a/esphome/core/scheduler.cpp +++ b/esphome/core/scheduler.cpp @@ -1,6 +1,7 @@ #include "scheduler.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" +#include "esphome/core/hal.h" #include namespace esphome { @@ -81,7 +82,7 @@ optional HOT Scheduler::next_schedule_in() { return 0; return next_time - now; } -void ICACHE_RAM_ATTR HOT Scheduler::call() { +void IRAM_ATTR HOT Scheduler::call() { const uint32_t now = this->millis_(); this->process_to_add(); diff --git a/esphome/core/util.cpp b/esphome/core/util.cpp index e0132e2e4a..996cf8e310 100644 --- a/esphome/core/util.cpp +++ b/esphome/core/util.cpp @@ -4,47 +4,16 @@ #include "esphome/core/version.h" #include "esphome/core/log.h" -#ifdef USE_WIFI -#include "esphome/components/wifi/wifi_component.h" -#endif - #ifdef USE_API #include "esphome/components/api/api_server.h" #endif -#ifdef USE_ETHERNET -#include "esphome/components/ethernet/ethernet_component.h" -#endif - #ifdef USE_MQTT #include "esphome/components/mqtt/mqtt_client.h" #endif -#ifdef USE_MDNS -#ifdef ARDUINO_ARCH_ESP32 -#include -#endif -#ifdef ARDUINO_ARCH_ESP8266 -#include -#endif -#endif - namespace esphome { -bool network_is_connected() { -#ifdef USE_ETHERNET - if (ethernet::global_eth_component != nullptr && ethernet::global_eth_component->is_connected()) - return true; -#endif - -#ifdef USE_WIFI - if (wifi::global_wifi_component != nullptr) - return wifi::global_wifi_component->is_connected(); -#endif - - return false; -} - bool api_is_connected() { #ifdef USE_API if (api::global_api_server != nullptr) { @@ -65,78 +34,4 @@ bool mqtt_is_connected() { bool remote_is_connected() { return api_is_connected() || mqtt_is_connected(); } -#if defined(ARDUINO_ARCH_ESP8266) && defined(USE_MDNS) -static bool mdns_setup; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -#endif - -#ifndef WEBSERVER_PORT -static const uint8_t WEBSERVER_PORT = 80; -#endif - -#ifdef USE_MDNS -#ifdef ARDUINO_ARCH_ESP8266 -void network_setup_mdns(const IPAddress &address, int interface) { - // Latest arduino framework breaks mDNS for AP interface - // see https://github.com/esp8266/Arduino/issues/6114 - if (interface == 1) - return; - MDNS.begin(App.get_name().c_str(), address); - mdns_setup = true; -#endif -#ifdef ARDUINO_ARCH_ESP32 - void network_setup_mdns() { - MDNS.begin(App.get_name().c_str()); -#endif -#ifdef USE_API - if (api::global_api_server != nullptr) { - MDNS.addService("esphomelib", "tcp", api::global_api_server->get_port()); - // DNS-SD (!=mDNS !) requires at least one TXT record for service discovery - let's add version - MDNS.addServiceTxt("esphomelib", "tcp", "version", ESPHOME_VERSION); - MDNS.addServiceTxt("esphomelib", "tcp", "address", network_get_address().c_str()); - MDNS.addServiceTxt("esphomelib", "tcp", "mac", get_mac_address().c_str()); -#ifdef ARDUINO_ARCH_ESP8266 - MDNS.addServiceTxt("esphomelib", "tcp", "platform", "ESP8266"); -#endif -#ifdef ARDUINO_ARCH_ESP32 - MDNS.addServiceTxt("esphomelib", "tcp", "platform", "ESP32"); -#endif - MDNS.addServiceTxt("esphomelib", "tcp", "board", ESPHOME_BOARD); -#ifdef ESPHOME_PROJECT_NAME - MDNS.addServiceTxt("esphomelib", "tcp", "project_name", ESPHOME_PROJECT_NAME); - MDNS.addServiceTxt("esphomelib", "tcp", "project_version", ESPHOME_PROJECT_VERSION); -#endif - } else { -#endif - // Publish "http" service if not using native API nor the webserver component - // This is just to have *some* mDNS service so that .local resolution works - MDNS.addService("http", "tcp", WEBSERVER_PORT); - MDNS.addServiceTxt("http", "tcp", "version", ESPHOME_VERSION); -#ifdef USE_API - } -#endif -#ifdef USE_PROMETHEUS - MDNS.addService("prometheus-http", "tcp", WEBSERVER_PORT); -#endif - } -#endif - - void network_tick_mdns() { -#if defined(ARDUINO_ARCH_ESP8266) && defined(USE_MDNS) - if (mdns_setup) - MDNS.update(); -#endif - } - - std::string network_get_address() { -#ifdef USE_ETHERNET - if (ethernet::global_eth_component != nullptr) - return ethernet::global_eth_component->get_use_address(); -#endif -#ifdef USE_WIFI - if (wifi::global_wifi_component != nullptr) - return wifi::global_wifi_component->get_use_address(); -#endif - return ""; - } - } // namespace esphome diff --git a/esphome/core/util.h b/esphome/core/util.h index 764c6aaf03..1ca0173eab 100644 --- a/esphome/core/util.h +++ b/esphome/core/util.h @@ -1,15 +1,8 @@ #pragma once #include -#include "IPAddress.h" - namespace esphome { -/// Return whether the node is connected to the network (through wifi, eth, ...) -bool network_is_connected(); -/// Get the active network hostname -std::string network_get_address(); - /// Return whether the node has at least one client connected to the native API bool api_is_connected(); @@ -19,14 +12,4 @@ bool mqtt_is_connected(); /// Return whether the node has any form of "remote" connection via the API or to an MQTT broker bool remote_is_connected(); -/// Manually set up the network stack (outside of the App.setup() loop, for example in OTA safe mode) -#ifdef ARDUINO_ARCH_ESP8266 -void network_setup_mdns(const IPAddress &address, int interface); -#endif -#ifdef ARDUINO_ARCH_ESP32 -void network_setup_mdns(); -#endif - -void network_tick_mdns(); - } // namespace esphome diff --git a/esphome/cpp_generator.py b/esphome/cpp_generator.py index 0442e2633b..691f45f91f 100644 --- a/esphome/cpp_generator.py +++ b/esphome/cpp_generator.py @@ -566,6 +566,10 @@ def add_define(name: str, value: SafeExpType = None): CORE.add_define(Define(name, safe_exp(value))) +def add_platformio_option(key: str, value: Union[str, List[str]]): + CORE.add_platformio_option(key, value) + + async def get_variable(id_: ID) -> "MockObj": """ Wait for the given ID to be defined in the code generation and diff --git a/esphome/cpp_helpers.py b/esphome/cpp_helpers.py index 33fb485c2b..a2eafaa0e8 100644 --- a/esphome/cpp_helpers.py +++ b/esphome/cpp_helpers.py @@ -1,9 +1,6 @@ import logging from esphome.const import ( - CONF_INVERTED, - CONF_MODE, - CONF_NUMBER, CONF_SETUP_PRIORITY, CONF_UPDATE_INTERVAL, CONF_TYPE_ID, @@ -12,8 +9,8 @@ from esphome.const import ( # pylint: disable=unused-import from esphome.core import coroutine, ID, CORE from esphome.types import ConfigType -from esphome.cpp_generator import RawExpression, add, get_variable -from esphome.cpp_types import App, GPIOPin +from esphome.cpp_generator import add, get_variable +from esphome.cpp_types import App from esphome.util import Registry, RegistryEntry @@ -26,17 +23,13 @@ async def gpio_pin_expression(conf): This is a coroutine, you must await it with a 'await' expression! """ if conf is None: - return + return None from esphome import pins for key, (func, _) in pins.PIN_SCHEMA_REGISTRY.items(): if key in conf: return await coroutine(func)(conf) - - number = conf[CONF_NUMBER] - mode = conf[CONF_MODE] - inverted = conf.get(CONF_INVERTED) - return GPIOPin.new(number, RawExpression(mode), inverted) + return await coroutine(pins.PIN_SCHEMA_REGISTRY[CORE.target_platform][0])(conf) async def register_component(var, config): diff --git a/esphome/cpp_types.py b/esphome/cpp_types.py index ee606ec7b3..7a8eb8e04c 100644 --- a/esphome/cpp_types.py +++ b/esphome/cpp_types.py @@ -30,5 +30,7 @@ JsonObject = arduino_json_ns.class_("JsonObject") JsonObjectRef = JsonObject.operator("ref") JsonObjectConstRef = JsonObjectRef.operator("const") Controller = esphome_ns.class_("Controller") - GPIOPin = esphome_ns.class_("GPIOPin") +InternalGPIOPin = esphome_ns.class_("InternalGPIOPin", GPIOPin) +gpio_ns = esphome_ns.namespace("gpio") +gpio_Flags = gpio_ns.enum("Flags", is_class=True) diff --git a/esphome/dashboard/dashboard.py b/esphome/dashboard/dashboard.py index 38689675af..bfe6808729 100644 --- a/esphome/dashboard/dashboard.py +++ b/esphome/dashboard/dashboard.py @@ -441,16 +441,10 @@ class DashboardEntry: return self.storage.comment @property - def esp_platform(self): + def target_platform(self): if self.storage is None: return None - return self.storage.esp_platform - - @property - def board(self): - if self.storage is None: - return None - return self.storage.board + return self.storage.target_platform @property def update_available(self): diff --git a/esphome/final_validate.py b/esphome/final_validate.py index 199c68210e..96dd2fd651 100644 --- a/esphome/final_validate.py +++ b/esphome/final_validate.py @@ -4,13 +4,6 @@ import contextvars from esphome.types import ConfigFragmentType, ID, ConfigPathType import esphome.config_validation as cv -from esphome.const import ( - ARDUINO_VERSION_ESP32, - ARDUINO_VERSION_ESP8266, - CONF_ESPHOME, - CONF_ARDUINO_VERSION, -) -from esphome.core import CORE class FinalValidateConfig(ABC): @@ -63,20 +56,3 @@ def id_declaration_match_schema(schema): return schema(declaration_config) return validator - - -def get_arduino_framework_version(): - path = [CONF_ESPHOME, CONF_ARDUINO_VERSION] - # This is run after core validation, so the property is set even if user didn't - version: str = full_config.get().get_config_for_path(path) - - if CORE.is_esp32: - version_map = ARDUINO_VERSION_ESP32 - elif CORE.is_esp8266: - version_map = ARDUINO_VERSION_ESP8266 - else: - raise ValueError("Platform not supported yet for this validator") - - reverse_map = {v: k for k, v in version_map.items()} - framework_version = reverse_map.get(version) - return framework_version diff --git a/esphome/helpers.py b/esphome/helpers.py index 3420b2cc12..1193d61eaa 100644 --- a/esphome/helpers.py +++ b/esphome/helpers.py @@ -209,15 +209,21 @@ def write_file(path: Union[Path, str], text: str): raise EsphomeError(f"Could not write file at {path}") from err -def write_file_if_changed(path: Union[Path, str], text: str): +def write_file_if_changed(path: Union[Path, str], text: str) -> bool: + """Write text to the given path, but not if the contents match already. + + Returns true if the file was changed. + """ if not isinstance(path, Path): path = Path(path) src_content = None if path.is_file(): src_content = read_file(path) - if src_content != text: - write_file(path, text) + if src_content == text: + return False + write_file(path, text) + return True def copy_file_if_changed(src: os.PathLike, dst: os.PathLike) -> None: diff --git a/esphome/loader.py b/esphome/loader.py index f74fc6367d..05d2e5a213 100644 --- a/esphome/loader.py +++ b/esphome/loader.py @@ -1,6 +1,5 @@ import logging -import typing -from typing import Callable, List, Optional, Dict, Any, ContextManager +from typing import Callable, List, Optional, Any, ContextManager from types import ModuleType import importlib import importlib.util @@ -8,8 +7,9 @@ import importlib.resources import importlib.abc import sys from pathlib import Path +from dataclasses import dataclass -from esphome.const import ESP_PLATFORMS, SOURCE_FILE_EXTENSIONS +from esphome.const import SOURCE_FILE_EXTENSIONS import esphome.core.config from esphome.core import CORE from esphome.types import ConfigType @@ -17,20 +17,13 @@ from esphome.types import ConfigType _LOGGER = logging.getLogger(__name__) -class SourceFile: - def __init__( - self, - package: importlib.resources.Package, - resource: importlib.resources.Resource, - ) -> None: - self._package = package - self._resource = resource - - def open_binary(self) -> typing.BinaryIO: - return importlib.resources.open_binary(self._package, self._resource) +@dataclass(frozen=True, order=True) +class FileResource: + package: str + resource: str def path(self) -> ContextManager[Path]: - return importlib.resources.path(self._package, self._resource) + return importlib.resources.path(self.package, self.resource) class ComponentManifest: @@ -39,6 +32,13 @@ class ComponentManifest: @property def package(self) -> str: + """Return the package name the module is contained in. + + Examples: + - esphome/components/gpio/__init__.py -> esphome.components.gpio + - esphome/components/gpio/switch/__init__.py -> esphome.components.gpio.switch + - esphome/components/a4988/stepper.py -> esphome.components.a4988 + """ return self.module.__package__ @property @@ -61,10 +61,6 @@ class ComponentManifest: def to_code(self) -> Optional[Callable[[Any], None]]: return getattr(self.module, "to_code", None) - @property - def esp_platforms(self) -> List[str]: - return getattr(self.module, "ESP_PLATFORMS", ESP_PLATFORMS) - @property def dependencies(self) -> List[str]: return getattr(self.module, "DEPENDENCIES", []) @@ -91,17 +87,20 @@ class ComponentManifest: return getattr(self.module, "FINAL_VALIDATE_SCHEMA", None) @property - def source_files(self) -> Dict[Path, SourceFile]: - ret = {} + def resources(self) -> List[FileResource]: + """Return a list of all file resources defined in the package of this component. + + This will return all cpp source files that are located in the same folder as the + loaded .py file (does not look through subdirectories) + """ + ret = [] for resource in importlib.resources.contents(self.package): if Path(resource).suffix not in SOURCE_FILE_EXTENSIONS: continue if not importlib.resources.is_resource(self.package, resource): # Not a resource = this is a directory (yeah this is confusing) continue - # Always use / for C++ include names - target_path = Path(*self.package.split(".")) / resource - ret[target_path] = SourceFile(self.package, resource) + ret.append(FileResource(self.package, resource)) return ret diff --git a/esphome/pins.py b/esphome/pins.py index c717424ff3..ae762c1a1a 100644 --- a/esphome/pins.py +++ b/esphome/pins.py @@ -1,330 +1,143 @@ -import logging +import operator +from functools import reduce -import esphome.config_validation as cv -from esphome.const import CONF_INVERTED, CONF_MODE, CONF_NUMBER -from esphome.core import CORE +from esphome.const import ( + CONF_INPUT, + CONF_MODE, + CONF_NUMBER, + CONF_OPEN_DRAIN, + CONF_OUTPUT, + CONF_PULLDOWN, + CONF_PULLUP, +) from esphome.util import SimpleRegistry -from esphome import boards - -_LOGGER = logging.getLogger(__name__) - - -def _lookup_pin(value): - if CORE.is_esp8266: - board_pins_dict = boards.ESP8266_BOARD_PINS - base_pins = boards.ESP8266_BASE_PINS - elif CORE.is_esp32: - if CORE.board in boards.ESP32_C3_BOARD_PINS: - board_pins_dict = boards.ESP32_C3_BOARD_PINS - base_pins = boards.ESP32_C3_BASE_PINS - else: - board_pins_dict = boards.ESP32_BOARD_PINS - base_pins = boards.ESP32_BASE_PINS - else: - raise NotImplementedError - - board_pins = board_pins_dict.get(CORE.board, {}) - - # Resolved aliased board pins (shorthand when two boards have the same pin configuration) - while isinstance(board_pins, str): - board_pins = board_pins_dict[board_pins] - - if value in board_pins: - return board_pins[value] - if value in base_pins: - return base_pins[value] - raise cv.Invalid(f"Cannot resolve pin name '{value}' for board {CORE.board}.") - - -def _translate_pin(value): - if isinstance(value, dict) or value is None: - raise cv.Invalid( - "This variable only supports pin numbers, not full pin schemas " - "(with inverted and mode)." - ) - if isinstance(value, int): - return value - try: - return int(value) - except ValueError: - pass - if value.startswith("GPIO"): - return cv.Coerce(int)(value[len("GPIO") :].strip()) - return _lookup_pin(value) - - -_ESP_SDIO_PINS = { - 6: "Flash Clock", - 7: "Flash Data 0", - 8: "Flash Data 1", - 11: "Flash Command", -} - -_ESP32C3_SDIO_PINS = { - 12: "Flash IO3/HOLD#", - 13: "Flash IO2/WP#", - 14: "Flash CS#", - 15: "Flash CLK", - 16: "Flash IO0/DI", - 17: "Flash IO1/DO", -} - - -def validate_gpio_pin(value): - value = _translate_pin(value) - if CORE.is_esp32_c3: - if value < 0 or value > 22: - raise cv.Invalid(f"ESP32-C3: Invalid pin number: {value}") - if value in _ESP32C3_SDIO_PINS: - raise cv.Invalid( - f"This pin cannot be used on ESP32-C3s and is already used by the flash interface (function: {_ESP_SDIO_PINS[value]})" - ) - return value - if CORE.is_esp32: - if value < 0 or value > 39: - raise cv.Invalid(f"ESP32: Invalid pin number: {value}") - if value in _ESP_SDIO_PINS: - raise cv.Invalid( - f"This pin cannot be used on ESP32s and is already used by the flash interface (function: {_ESP_SDIO_PINS[value]})" - ) - if 9 <= value <= 10: - _LOGGER.warning( - "ESP32: Pin %s (9-10) might already be used by the " - "flash interface in QUAD IO flash mode.", - value, - ) - if value in (20, 24, 28, 29, 30, 31): - # These pins are not exposed in GPIO mux (reason unknown) - # but they're missing from IO_MUX list in datasheet - raise cv.Invalid(f"The pin GPIO{value} is not usable on ESP32s.") - return value - if CORE.is_esp8266: - if value < 0 or value > 17: - raise cv.Invalid(f"ESP8266: Invalid pin number: {value}") - if value in _ESP_SDIO_PINS: - raise cv.Invalid( - f"This pin cannot be used on ESP8266s and is already used by the flash interface (function: {_ESP_SDIO_PINS[value]})" - ) - if 9 <= value <= 10: - _LOGGER.warning( - "ESP8266: Pin %s (9-10) might already be used by the " - "flash interface in QUAD IO flash mode.", - value, - ) - return value - raise NotImplementedError - - -def input_pin(value): - value = validate_gpio_pin(value) - if CORE.is_esp8266 and value == 17: - raise cv.Invalid("GPIO17 (TOUT) is an analog-only pin on the ESP8266.") - return value - - -def input_pullup_pin(value): - value = input_pin(value) - if CORE.is_esp32: - return output_pin(value) - if CORE.is_esp8266: - if value == 0: - raise cv.Invalid( - "GPIO Pin 0 does not support pullup pin mode. " - "Please choose another pin." - ) - return value - raise NotImplementedError - - -def output_pin(value): - value = validate_gpio_pin(value) - if CORE.is_esp32: - if 34 <= value <= 39: - raise cv.Invalid( - f"ESP32: GPIO{value} (34-39) can only be used as an input pin." - ) - return value - if CORE.is_esp8266: - if value == 17: - raise cv.Invalid("GPIO17 (TOUT) is an analog-only pin on the ESP8266.") - return value - raise NotImplementedError - - -def analog_pin(value): - value = validate_gpio_pin(value) - if CORE.is_esp32: - if CORE.is_esp32_c3: - if 0 <= value <= 4: # ADC1 - return value - raise cv.Invalid("ESP32-C3: Only pins 0 though 4 support ADC.") - if 32 <= value <= 39: # ADC1 - return value - raise cv.Invalid("ESP32: Only pins 32 though 39 support ADC.") - if CORE.is_esp8266: - if value == 17: # A0 - return value - raise cv.Invalid("ESP8266: Only pin A0 (GPIO17) supports ADC.") - raise NotImplementedError - - -input_output_pin = cv.All(input_pin, output_pin) - -PIN_MODES_ESP8266 = [ - "INPUT", - "OUTPUT", - "INPUT_PULLUP", - "OUTPUT_OPEN_DRAIN", - "SPECIAL", - "FUNCTION_1", - "FUNCTION_2", - "FUNCTION_3", - "FUNCTION_4", - "FUNCTION_0", - "WAKEUP_PULLUP", - "WAKEUP_PULLDOWN", - "INPUT_PULLDOWN_16", -] -PIN_MODES_ESP32 = [ - "INPUT", - "OUTPUT", - "INPUT_PULLUP", - "OUTPUT_OPEN_DRAIN", - "SPECIAL", - "FUNCTION_1", - "FUNCTION_2", - "FUNCTION_3", - "FUNCTION_4", - "PULLUP", - "PULLDOWN", - "INPUT_PULLDOWN", - "OPEN_DRAIN", - "FUNCTION_5", - "FUNCTION_6", - "ANALOG", -] - - -def pin_mode(value): - if CORE.is_esp32: - return cv.one_of(*PIN_MODES_ESP32, upper=True)(value) - if CORE.is_esp8266: - return cv.one_of(*PIN_MODES_ESP8266, upper=True)(value) - raise NotImplementedError - - -GPIO_FULL_OUTPUT_PIN_SCHEMA = cv.Schema( - { - cv.Required(CONF_NUMBER): output_pin, - cv.Optional(CONF_MODE, default="OUTPUT"): pin_mode, - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - } -) - -GPIO_FULL_INPUT_PIN_SCHEMA = cv.Schema( - { - cv.Required(CONF_NUMBER): input_pin, - cv.Optional(CONF_MODE, default="INPUT"): pin_mode, - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - } -) - -GPIO_FULL_INPUT_PULLUP_PIN_SCHEMA = cv.Schema( - { - cv.Required(CONF_NUMBER): input_pin, - cv.Optional(CONF_MODE, default="INPUT_PULLUP"): pin_mode, - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - } -) - -GPIO_FULL_ANALOG_PIN_SCHEMA = cv.Schema( - { - cv.Required(CONF_NUMBER): analog_pin, - cv.Optional(CONF_MODE, default="INPUT"): pin_mode, - } -) - - -def shorthand_output_pin(value): - value = output_pin(value) - return GPIO_FULL_OUTPUT_PIN_SCHEMA({CONF_NUMBER: value}) - - -def shorthand_input_pin(value): - value = input_pin(value) - return GPIO_FULL_INPUT_PIN_SCHEMA({CONF_NUMBER: value}) - - -def shorthand_input_pullup_pin(value): - value = input_pullup_pin(value) - return GPIO_FULL_INPUT_PIN_SCHEMA( - { - CONF_NUMBER: value, - CONF_MODE: "INPUT_PULLUP", - } - ) - - -def shorthand_analog_pin(value): - value = analog_pin(value) - return GPIO_FULL_ANALOG_PIN_SCHEMA({CONF_NUMBER: value}) - - -def validate_has_interrupt(value): - if CORE.is_esp8266: - if value[CONF_NUMBER] >= 16: - raise cv.Invalid( - f"Pins GPIO16 and GPIO17 do not support interrupts and cannot be used here, got {value[CONF_NUMBER]}" - ) - return value +from esphome.core import CORE PIN_SCHEMA_REGISTRY = SimpleRegistry() -def internal_gpio_output_pin_schema(value): - if isinstance(value, dict): - return GPIO_FULL_OUTPUT_PIN_SCHEMA(value) - return shorthand_output_pin(value) +def _set_mode(value, default_mode): + import esphome.config_validation as cv + + if CONF_MODE not in value: + return {**value, CONF_MODE: default_mode} + mode = value[CONF_MODE] + if not isinstance(mode, str): + return value + # mode is a string, try parsing it like arduino pin modes + PIN_MODES = { + "INPUT": { + CONF_INPUT: True, + }, + "OUTPUT": { + CONF_OUTPUT: True, + }, + "INPUT_PULLUP": { + CONF_INPUT: True, + CONF_PULLUP: True, + }, + "OUTPUT_OPEN_DRAIN": { + CONF_OUTPUT: True, + CONF_OPEN_DRAIN: True, + }, + "INPUT_PULLDOWN_16": { + CONF_INPUT: True, + CONF_PULLDOWN: True, + }, + "INPUT_PULLDOWN": { + CONF_INPUT: True, + CONF_PULLDOWN: True, + }, + } + if mode.upper() not in PIN_MODES: + raise cv.Invalid(f"Unknown pin mode {mode}", [CONF_MODE]) + return {**value, CONF_MODE: PIN_MODES[mode.upper()]} -def gpio_output_pin_schema(value): - if isinstance(value, dict): - for key, entry in PIN_SCHEMA_REGISTRY.items(): - if key in value: - return entry[1][0](value) - return internal_gpio_output_pin_schema(value) +def _schema_creator(default_mode, internal: bool = False): + def validator(value): + if not isinstance(value, dict): + return validator({CONF_NUMBER: value}) + value = _set_mode(value, default_mode) + if not internal: + for key, entry in PIN_SCHEMA_REGISTRY.items(): + if key != CORE.target_platform and key in value: + return entry[1](value) + return PIN_SCHEMA_REGISTRY[CORE.target_platform][1](value) + + return validator -def internal_gpio_input_pin_schema(value): - if isinstance(value, dict): - return GPIO_FULL_INPUT_PIN_SCHEMA(value) - return shorthand_input_pin(value) +def _internal_number_creator(mode): + def validator(value): + value_d = {CONF_NUMBER: value} + value_d = _set_mode(value_d, mode) + return PIN_SCHEMA_REGISTRY[CORE.target_platform][1](value_d)[CONF_NUMBER] + + return validator -def internal_gpio_analog_pin_schema(value): - if isinstance(value, dict): - return GPIO_FULL_ANALOG_PIN_SCHEMA(value) - return shorthand_analog_pin(value) +def gpio_flags_expr(mode): + """Convert the given mode dict to a gpio Flags expression""" + import esphome.codegen as cg + + FLAGS_MAPPING = { + CONF_INPUT: cg.gpio_Flags.FLAG_INPUT, + CONF_OUTPUT: cg.gpio_Flags.FLAG_OUTPUT, + CONF_OPEN_DRAIN: cg.gpio_Flags.FLAG_OPEN_DRAIN, + CONF_PULLUP: cg.gpio_Flags.FLAG_PULLUP, + CONF_PULLDOWN: cg.gpio_Flags.FLAG_PULLDOWN, + } + active_flags = [v for k, v in FLAGS_MAPPING.items() if mode.get(k)] + if active_flags: + return cg.gpio_Flags.FLAG_NONE + + return reduce(operator.or_, active_flags) -def gpio_input_pin_schema(value): - if isinstance(value, dict): - for key, entry in PIN_SCHEMA_REGISTRY.items(): - if key in value: - return entry[1][1](value) - return internal_gpio_input_pin_schema(value) - - -def internal_gpio_input_pullup_pin_schema(value): - if isinstance(value, dict): - return GPIO_FULL_INPUT_PULLUP_PIN_SCHEMA(value) - return shorthand_input_pullup_pin(value) - - -def gpio_input_pullup_pin_schema(value): - if isinstance(value, dict): - for key, entry in PIN_SCHEMA_REGISTRY.items(): - if key in value: - return entry[1][1](value) - return internal_gpio_input_pullup_pin_schema(value) +gpio_pin_schema = _schema_creator +internal_gpio_pin_number = _internal_number_creator +gpio_output_pin_schema = _schema_creator( + { + CONF_OUTPUT: True, + } +) +gpio_input_pin_schema = _schema_creator( + { + CONF_INPUT: True, + } +) +gpio_input_pullup_pin_schema = _schema_creator( + { + CONF_INPUT: True, + CONF_PULLUP: True, + } +) +internal_gpio_output_pin_schema = _schema_creator( + { + CONF_OUTPUT: True, + }, + internal=True, +) +internal_gpio_output_pin_number = _internal_number_creator({CONF_OUTPUT: True}) +internal_gpio_input_pin_schema = _schema_creator( + { + CONF_INPUT: True, + }, + internal=True, +) +internal_gpio_input_pin_number = _internal_number_creator({CONF_INPUT: True}) +internal_gpio_input_pullup_pin_schema = _schema_creator( + { + CONF_INPUT: True, + CONF_PULLUP: True, + }, + internal=True, +) +internal_gpio_input_pullup_pin_number = _internal_number_creator( + { + CONF_INPUT: True, + CONF_PULLUP: True, + } +) diff --git a/esphome/platformio_api.py b/esphome/platformio_api.py index a99081a650..100def3310 100644 --- a/esphome/platformio_api.py +++ b/esphome/platformio_api.py @@ -196,7 +196,7 @@ def _parse_register(config, regex, line): STACKTRACE_ESP8266_EXCEPTION_TYPE_RE = re.compile(r"[eE]xception \((\d+)\):") STACKTRACE_ESP8266_PC_RE = re.compile(r"epc1=0x(4[0-9a-fA-F]{7})") STACKTRACE_ESP8266_EXCVADDR_RE = re.compile(r"excvaddr=0x(4[0-9a-fA-F]{7})") -STACKTRACE_ESP32_PC_RE = re.compile(r"PC\s*:\s*(?:0x)?(4[0-9a-fA-F]{7})") +STACKTRACE_ESP32_PC_RE = re.compile(r".*PC\s*:\s*(?:0x)?(4[0-9a-fA-F]{7}).*") STACKTRACE_ESP32_EXCVADDR_RE = re.compile(r"EXCVADDR\s*:\s*(?:0x)?(4[0-9a-fA-F]{7})") STACKTRACE_ESP32_C3_PC_RE = re.compile(r"MEPC\s*:\s*(?:0x)?(4[0-9a-fA-F]{7})") STACKTRACE_ESP32_C3_RA_RE = re.compile(r"RA\s*:\s*(?:0x)?(4[0-9a-fA-F]{7})") @@ -204,7 +204,7 @@ STACKTRACE_BAD_ALLOC_RE = re.compile( r"^last failed alloc call: (4[0-9a-fA-F]{7})\((\d+)\)$" ) STACKTRACE_ESP32_BACKTRACE_RE = re.compile( - r"Backtrace:(?:\s+0x[0-9a-fA-F]{8}:0x[0-9a-fA-F]{8})+" + r"Backtrace:(?:\s*0x[0-9a-fA-F]{8}:0x[0-9a-fA-F]{8})+" ) STACKTRACE_ESP32_BACKTRACE_PC_RE = re.compile(r"4[0-9a-f]{7}") STACKTRACE_ESP8266_BACKTRACE_PC_RE = re.compile(r"4[0-9a-f]{7}") diff --git a/esphome/storage_json.py b/esphome/storage_json.py index 517bb508ba..e70d3d6c93 100644 --- a/esphome/storage_json.py +++ b/esphome/storage_json.py @@ -40,10 +40,8 @@ class StorageJSON: comment, esphome_version, src_version, - arduino_version, address, - esp_platform, - board, + target_platform, build_path, firmware_bin_path, loaded_integrations, @@ -60,15 +58,10 @@ class StorageJSON: # The version of the file in src/main.cpp - Used to migrate the file assert src_version is None or isinstance(src_version, int) self.src_version = src_version # type: int - # The version of the Arduino framework, the build files need to be cleared each time - # this changes - self.arduino_version = arduino_version # type: str # Address of the ESP, for example livingroom.local or a static IP self.address = address # type: str # The type of ESP in use, either ESP32 or ESP8266 - self.esp_platform = esp_platform # type: str - # The ESP board used, for example nodemcuv2 - self.board = board # type: str + self.target_platform = target_platform # type: str # The absolute path to the platformio project self.build_path = build_path # type: str # The absolute path to the firmware binary @@ -84,10 +77,8 @@ class StorageJSON: "comment": self.comment, "esphome_version": self.esphome_version, "src_version": self.src_version, - "arduino_version": self.arduino_version, "address": self.address, - "esp_platform": self.esp_platform, - "board": self.board, + "esp_platform": self.target_platform, "build_path": self.build_path, "firmware_bin_path": self.firmware_bin_path, "loaded_integrations": self.loaded_integrations, @@ -109,28 +100,24 @@ class StorageJSON: comment=esph.comment, esphome_version=const.__version__, src_version=1, - arduino_version=esph.arduino_version, address=esph.address, - esp_platform=esph.esp_platform, - board=esph.board, + target_platform=esph.target_platform, build_path=esph.build_path, firmware_bin_path=esph.firmware_bin, loaded_integrations=list(esph.loaded_integrations), ) @staticmethod - def from_wizard(name, address, esp_platform, board): - # type: (str, str, str, str) -> StorageJSON + def from_wizard(name, address, esp_platform): + # type: (str, str, str) -> StorageJSON return StorageJSON( storage_version=1, name=name, comment=None, esphome_version=const.__version__, src_version=1, - arduino_version=None, address=address, - esp_platform=esp_platform, - board=board, + target_platform=esp_platform, build_path=None, firmware_bin_path=None, loaded_integrations=[], @@ -147,10 +134,8 @@ class StorageJSON: "esphome_version", storage.get("esphomeyaml_version") ) src_version = storage.get("src_version") - arduino_version = storage.get("arduino_version") address = storage.get("address") esp_platform = storage.get("esp_platform") - board = storage.get("board") build_path = storage.get("build_path") firmware_bin_path = storage.get("firmware_bin_path") loaded_integrations = storage.get("loaded_integrations", []) @@ -160,10 +145,8 @@ class StorageJSON: comment, esphome_version, src_version, - arduino_version, address, esp_platform, - board, build_path, firmware_bin_path, loaded_integrations, diff --git a/esphome/wizard.py b/esphome/wizard.py index abb17e3119..5c35fac73a 100644 --- a/esphome/wizard.py +++ b/esphome/wizard.py @@ -10,7 +10,6 @@ from esphome.helpers import get_bool_env, write_file from esphome.log import color, Fore # pylint: disable=anomalous-backslash-in-string -from esphome.boards import ESP32_BOARD_PINS, ESP8266_BOARD_PINS from esphome.storage_json import StorageJSON, ext_storage_path from esphome.util import safe_print from esphome.const import ALLOWED_NAME_CHARS, ENV_QUICKWIZARD @@ -116,6 +115,8 @@ captive_portal: def wizard_write(path, **kwargs): + from esphome.components.esp8266 import boards as esp8266_boards + name = kwargs["name"] board = kwargs["board"] @@ -124,11 +125,13 @@ def wizard_write(path, **kwargs): kwargs[key] = sanitize_double_quotes(kwargs[key]) if "platform" not in kwargs: - kwargs["platform"] = "ESP8266" if board in ESP8266_BOARD_PINS else "ESP32" + kwargs["platform"] = ( + "ESP8266" if board in esp8266_boards.ESP8266_BOARD_PINS else "ESP32" + ) platform = kwargs["platform"] write_file(path, wizard_file(**kwargs)) - storage = StorageJSON.from_wizard(name, f"{name}.local", platform, board) + storage = StorageJSON.from_wizard(name, f"{name}.local", platform) storage_path = ext_storage_path(os.path.dirname(path), os.path.basename(path)) storage.save(storage_path) @@ -168,6 +171,9 @@ def strip_accents(value): def wizard(path): + from esphome.components.esp32 import boards as esp32_boards + from esphome.components.esp8266 import boards as esp8266_boards + if not path.endswith(".yaml") and not path.endswith(".yml"): safe_print( f"Please make your configuration file {color(Fore.CYAN, path)} have the extension .yaml or .yml" @@ -266,10 +272,10 @@ def wizard(path): # Don't sleep because user needs to copy link if platform == "ESP32": safe_print(f"For example \"{color(Fore.BOLD_WHITE, 'nodemcu-32s')}\".") - boards = list(ESP32_BOARD_PINS.keys()) + boards = list(esp32_boards.ESP32_BOARD_PINS.keys()) else: safe_print(f"For example \"{color(Fore.BOLD_WHITE, 'nodemcuv2')}\".") - boards = list(ESP8266_BOARD_PINS.keys()) + boards = list(esp8266_boards.ESP8266_BOARD_PINS.keys()) safe_print(f"Options: {', '.join(sorted(boards))}") while True: diff --git a/esphome/writer.py b/esphome/writer.py index 2e1e19de54..29532d4f64 100644 --- a/esphome/writer.py +++ b/esphome/writer.py @@ -2,17 +2,13 @@ import logging import os import re from pathlib import Path -from typing import Dict +from typing import Dict, List, Union from esphome.config import iter_components from esphome.const import ( - CONF_BOARD_FLASH_MODE, - CONF_ESPHOME, - CONF_PLATFORMIO_OPTIONS, HEADER_FILE_EXTENSIONS, SOURCE_FILE_EXTENSIONS, __version__, - ARDUINO_VERSION_ESP8266, ENV_NOGITIGNORE, ) from esphome.core import CORE, EsphomeError @@ -25,7 +21,6 @@ from esphome.helpers import ( get_bool_env, ) from esphome.storage_json import StorageJSON, storage_path -from esphome.boards import ESP8266_FLASH_SIZES, ESP8266_LD_SCRIPTS from esphome import loader _LOGGER = logging.getLogger(__name__) @@ -72,7 +67,9 @@ upload_flags = """, ) -UPLOAD_SPEED_OVERRIDE = {"esp210": 57600} +UPLOAD_SPEED_OVERRIDE = { + "esp210": 57600, +} def get_flags(key): @@ -166,10 +163,6 @@ def storage_should_clean(old, new): # type: (StorageJSON, StorageJSON) -> bool if old.src_version != new.src_version: return True - if old.arduino_version != new.arduino_version: - return True - if old.board != new.board: - return True if old.build_path != new.build_path: return True return False @@ -192,10 +185,10 @@ def update_storage_json(): new.save(path) -def format_ini(data): +def format_ini(data: Dict[str, Union[str, List[str]]]) -> str: content = "" for key, value in sorted(data.items()): - if isinstance(value, (list, set, tuple)): + if isinstance(value, list): content += f"{key} =\n" for x in value: content += f" {x}\n" @@ -204,82 +197,15 @@ def format_ini(data): return content -def gather_lib_deps(): - return [x.as_lib_dep for x in CORE.libraries] - - -def gather_build_flags(overrides): - build_flags = list(CORE.build_flags) - build_flags += [overrides] if isinstance(overrides, str) else overrides - - # avoid changing build flags order - return list(sorted(build_flags)) - - -ESP32_LARGE_PARTITIONS_CSV = """\ -nvs, data, nvs, 0x009000, 0x005000, -otadata, data, ota, 0x00e000, 0x002000, -app0, app, ota_0, 0x010000, 0x1C0000, -app1, app, ota_1, 0x1D0000, 0x1C0000, -eeprom, data, 0x99, 0x390000, 0x001000, -spiffs, data, spiffs, 0x391000, 0x00F000 -""" - - def get_ini_content(): - overrides = CORE.config[CONF_ESPHOME].get(CONF_PLATFORMIO_OPTIONS, {}) - - lib_deps = gather_lib_deps() - build_flags = gather_build_flags(overrides.pop("build_flags", [])) - - data = { - "platform": CORE.arduino_version, - "board": CORE.board, - "framework": "arduino", - "lib_deps": lib_deps + ["${common.lib_deps}"], - "build_flags": build_flags + ["${common.build_flags}"], - "upload_speed": UPLOAD_SPEED_OVERRIDE.get(CORE.board, 115200), - } - - if CORE.is_esp32: - data["board_build.partitions"] = "partitions.csv" - partitions_csv = CORE.relative_build_path("partitions.csv") - write_file_if_changed(partitions_csv, ESP32_LARGE_PARTITIONS_CSV) - - # pylint: disable=unsubscriptable-object - if CONF_BOARD_FLASH_MODE in CORE.config[CONF_ESPHOME]: - flash_mode = CORE.config[CONF_ESPHOME][CONF_BOARD_FLASH_MODE] - data["board_build.flash_mode"] = flash_mode - - # Build flags - if CORE.is_esp8266 and CORE.board in ESP8266_FLASH_SIZES: - flash_size = ESP8266_FLASH_SIZES[CORE.board] - ld_scripts = ESP8266_LD_SCRIPTS[flash_size] - - versions_with_old_ldscripts = [ - ARDUINO_VERSION_ESP8266["2.4.0"], - ARDUINO_VERSION_ESP8266["2.4.1"], - ARDUINO_VERSION_ESP8266["2.4.2"], - ] - if CORE.arduino_version == ARDUINO_VERSION_ESP8266["2.3.0"]: - # No ld script support - ld_script = None - if CORE.arduino_version in versions_with_old_ldscripts: - # Old ld script path - ld_script = ld_scripts[0] - else: - ld_script = ld_scripts[1] - - if ld_script is not None: - data["board_build.ldscript"] = ld_script - - # Ignore libraries that are not explicitly used, but may - # be added by LDF - # data['lib_ldf_mode'] = 'chain' - data.update(overrides) + CORE.add_platformio_option( + "lib_deps", [x.as_lib_dep for x in CORE.libraries] + ["${common.lib_deps}"] + ) + # Sort to avoid changing build flags order + CORE.add_platformio_option("build_flags", sorted(CORE.build_flags)) content = f"[env:{CORE.name}]\n" - content += format_ini(data) + content += format_ini(CORE.platformio_options) return content @@ -361,12 +287,15 @@ or use the custom_components folder. def copy_src_tree(): - source_files: Dict[Path, loader.SourceFile] = {} + source_files: List[loader.FileResource] = [] for _, component, _ in iter_components(CORE.config): - source_files.update(component.source_files) + source_files += component.resources + source_files_map = { + Path(x.package.replace(".", "/") + "/" + x.resource): x for x in source_files + } # Convert to list and sort - source_files_l = list(source_files.items()) + source_files_l = list(source_files_map.items()) source_files_l.sort() # Build #include list for esphome.h @@ -377,7 +306,7 @@ def copy_src_tree(): include_l.append("") include_s = "\n".join(include_l) - source_files_copy = source_files.copy() + source_files_copy = source_files_map.copy() ignore_targets = [Path(x) for x in (DEFINES_H_TARGET, VERSION_H_TARGET)] for t in ignore_targets: source_files_copy.pop(t) @@ -420,6 +349,11 @@ def copy_src_tree(): CORE.relative_src_path("esphome", "core", "version.h"), generate_version_h() ) + if CORE.is_esp32: + from esphome.components.esp32 import copy_files + + copy_files() + def generate_defines_h(): define_content_l = [x.as_macro for x in CORE.defines] diff --git a/platformio.ini b/platformio.ini index 89e9ce9374..0e2649e63a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -5,7 +5,7 @@ [platformio] default_envs = esp8266, esp32 -src_dir = . +src_dir = esphome include_dir = [runtime] @@ -47,7 +47,11 @@ src_filter = [common:esp8266] extends = common -platform = platformio/espressif8266@3.1.0 +; when changing this also copy it to esphome-docker-base images +platform = platformio/espressif8266 @ 3.2.0 +platform_packages = + platformio/framework-arduinoespressif8266 @ ~3.30002.0 + framework = arduino board = nodemcuv2 lib_deps = @@ -55,29 +59,88 @@ lib_deps = ESP8266WiFi ; wifi (Arduino built-in) Update ; ota (Arduino built-in) ottowinter/ESPAsyncTCP-esphome@1.2.3 ; async_tcp +build_flags = + ${common.build_flags} + -DUSE_ESP8266 [common:esp32] extends = common -platform = platformio/espressif32@3.2.0 +; when changing this also copy it to esphome-docker-base images +platform = platformio/espressif32 @ 3.3.2 +platform_packages = + platformio/framework-arduinoespressif32 @ ~3.10006.0 + framework = arduino board = nodemcu-32s lib_deps = ${common.lib_deps} Hash ; ota (Arduino built-in) esphome/AsyncTCP-esphome@1.2.2 ; async_tcp +build_flags = + ${common.build_flags} + -DUSE_ESP32 +src_filter = ${common.src_filter} + +[common:espidf] +; when changing this also copy it to esphome-docker-base images +platform = platformio/espressif32 @ 3.3.2 +platform_packages = + platformio/framework-espidf @ ~3.40300.0 + +framework = espidf +board = nodemcu-32s +lib_deps = + esphome/noise-c@0.1.1 ; used by api + espressif/esp32-camera@1.0.0 ; used by esp32_camera + NeoPixelBus@2.6.7 +build_flags = + ${common.build_flags} + ${common:esp32.build_flags} + -DUSE_ESP_IDF + -DUSE_ESP32_FRAMEWORK_ESP_IDF +src_filter = ${common:esp32.src_filter} [env:esp8266] extends = common:esp8266 -build_flags = ${common:esp8266.build_flags} ${runtime.build_flags} +build_flags = + ${common:esp8266.build_flags} + ${runtime.build_flags} + -DUSE_ARDUINO + -DUSE_ESP8266_FRAMEWORK_ARDUINO [env:esp8266-tidy] extends = common:esp8266 -build_flags = ${common:esp8266.build_flags} ${clangtidy.build_flags} +build_flags = + ${common:esp8266.build_flags} + ${clangtidy.build_flags} + -DUSE_ARDUINO + -DUSE_ESP8266_FRAMEWORK_ARDUINO [env:esp32] extends = common:esp32 -build_flags = ${common:esp32.build_flags} ${runtime.build_flags} +build_flags = + ${common:esp32.build_flags} + ${runtime.build_flags} + -DUSE_ARDUINO + -DUSE_ESP32_FRAMEWORK_ARDUINO [env:esp32-tidy] extends = common:esp32 -build_flags = ${common:esp32.build_flags} ${clangtidy.build_flags} +build_flags = + ${common:esp32.build_flags} + ${clangtidy.build_flags} + -DUSE_ARDUINO + -DUSE_ESP32_FRAMEWORK_ARDUINO + +[env:esp32-idf] +extends = common:espidf +build_flags = + ${common:espidf.build_flags} + ${runtime.build_flags} + + +[env:esp32-idf-tidy] +extends = common:espidf +build_flags = + ${common:espidf.build_flags} + ${clangtidy.build_flags} diff --git a/requirements.txt b/requirements.txt index 64ffd366e1..c6d967fc33 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,3 +11,7 @@ esptool==3.1 click==8.0.1 esphome-dashboard==20210908.0 aioesphomeapi==9.1.0 + +# esp-idf requires this, but doesn't bundle it by default +# https://github.com/espressif/esp-idf/blob/220590d599e134d7a5e7f1e683cc4550349ffbf8/requirements.txt#L24 +kconfiglib==13.7.1 diff --git a/script/ci-custom.py b/script/ci-custom.py index 3191842d8c..8e9ca487a6 100755 --- a/script/ci-custom.py +++ b/script/ci-custom.py @@ -408,7 +408,6 @@ ARDUINO_FORBIDDEN_RE = r"[^\w\d](" + r"|".join(ARDUINO_FORBIDDEN) + r")\(.*" exclude=[ "esphome/components/mqtt/custom_mqtt_device.h", "esphome/components/sun/sun.cpp", - "esphome/core/esphal.*", ], ) def lint_no_arduino_framework_functions(fname, match): @@ -422,6 +421,28 @@ def lint_no_arduino_framework_functions(fname, match): ) +IDF_CONVERSION_FORBIDDEN = { + "ARDUINO_ARCH_ESP32": "USE_ESP32", + "ARDUINO_ARCH_ESP8266": "USE_ESP8266", + "pgm_read_byte": "progmem_read_byte", + "ICACHE_RAM_ATTR": "IRAM_ATTR", + "esphome/core/esphal.h": "esphome/core/hal.h", +} +IDF_CONVERSION_FORBIDDEN_RE = r"(" + r"|".join(IDF_CONVERSION_FORBIDDEN) + r").*" + + +@lint_re_check( + IDF_CONVERSION_FORBIDDEN_RE, + include=cpp_include, +) +def lint_no_removed_in_idf_conversions(fname, match): + replacement = IDF_CONVERSION_FORBIDDEN[match.group(1)] + return ( + f"The macro {highlight(match.group(1))} can no longer be used in ESPHome directly. " + f"Plese use {highlight(replacement)} instead." + ) + + @lint_re_check( r"[^\w\d]byte\s+[\w\d]+\s*=", include=cpp_include, @@ -498,6 +519,8 @@ def lint_relative_py_import(fname): ], exclude=[ "esphome/components/socket/headers.h", + "esphome/components/esp32/core.cpp", + "esphome/components/esp8266/core.cpp", ], ) def lint_namespace(fname, content): @@ -575,7 +598,7 @@ def lint_inclusive_language(fname, match): "esphome/components/text_sensor/text_sensor.h", "esphome/components/climate/climate.h", "esphome/core/component.h", - "esphome/core/esphal.h", + "esphome/core/gpio.h", "esphome/core/log.h", "tests/custom.h", ], diff --git a/script/clang-tidy b/script/clang-tidy index 463959fa5d..8a1baf8a22 100755 --- a/script/clang-tidy +++ b/script/clang-tidy @@ -45,7 +45,11 @@ def clang_options(idedata): # pretend we're an Xtensa compiler, which gates some features in the headers '-D__XTENSA__', # allow to condition code on the presence of clang-tidy - '-DCLANG_TIDY' + '-DCLANG_TIDY', + # (esp-idf) Disable this header because they use asm with registers clang-tidy doesn't know + '-D__XTENSA_API_H__', + # (esp-idf) Fix __once_callable in some libstdc++ headers + '-D_GLIBCXX_HAVE_TLS', ] # copy compiler flags, except those clang doesn't understand. @@ -57,7 +61,8 @@ def clang_options(idedata): # add include directories, using -isystem for dependencies to suppress their errors for directory in idedata['includes']['toolchain']: - cmd.extend(['-isystem', directory]) + if 'xtensa-esp32s2-elf' not in directory: + cmd.extend(['-isystem', directory]) for directory in sorted(set(idedata['includes']['build'])): dependency = "framework-arduino" in directory or "/libdeps/" in directory cmd.extend(['-isystem' if dependency else '-I', directory]) diff --git a/script/helpers.py b/script/helpers.py index d32e0eafbe..2c40d47e04 100644 --- a/script/helpers.py +++ b/script/helpers.py @@ -112,15 +112,36 @@ def git_ls_files(patterns=None): return {s[3].strip(): int(s[0]) for s in lines} +IDF_TIDY_SDKCONFIG = """\ +CONFIG_BT_ENABLED=y +""" + + def load_idedata(environment): platformio_ini = Path(root_path) / "platformio.ini" temp_idedata = Path(temp_folder) / f"idedata-{environment}.json" + changed = False if not platformio_ini.is_file() or not temp_idedata.is_file(): changed = True elif platformio_ini.stat().st_mtime >= temp_idedata.stat().st_mtime: changed = True - else: - changed = False + + if environment == "esp32-idf-tidy": + # sdkconfig needs to be written before idedata is run + # but the file is also modified by the build process, so + # store a temp file to keep track of the + + sdk_internal = Path(temp_folder) / f"{environment}-internal-sdkconfig" + sdkconfig = Path(root_path) / f"sdkconfig.{environment}" + if ( + changed + or not sdk_internal.is_file() + or sdk_internal.read_text() != IDF_TIDY_SDKCONFIG + ): + changed = True + sdkconfig.write_text(IDF_TIDY_SDKCONFIG) + sdk_internal.parent.mkdir(exist_ok=True) + sdk_internal.write_text(IDF_TIDY_SDKCONFIG) if not changed: return json.loads(temp_idedata.read_text()) diff --git a/tests/component_tests/binary_sensor/test_binary_sensor.py b/tests/component_tests/binary_sensor/test_binary_sensor.py index 8da93a476e..514bc6ee5f 100644 --- a/tests/component_tests/binary_sensor/test_binary_sensor.py +++ b/tests/component_tests/binary_sensor/test_binary_sensor.py @@ -30,7 +30,7 @@ def test_binary_sensor_sets_mandatory_fields(generate_main): # Then assert 'bs_1->set_name("test bs1");' in main_cpp - assert "bs_1->set_pin(new GPIOPin" in main_cpp + assert "bs_1->set_pin(" in main_cpp def test_binary_sensor_config_value_internal_set(generate_main): diff --git a/tests/dummy_main.cpp b/tests/dummy_main.cpp index f9ba868096..d956387665 100644 --- a/tests/dummy_main.cpp +++ b/tests/dummy_main.cpp @@ -27,12 +27,6 @@ void setup() { auto *ota = new ota::OTAComponent(); // NOLINT ota->set_port(8266); - auto *gpio = new gpio::GPIOSwitch(); // NOLINT - gpio->set_name("GPIO Switch"); - gpio->set_pin(new GPIOPin(8, OUTPUT, false)); // NOLINT - App.register_component(gpio); - App.register_switch(gpio); - App.setup(); } diff --git a/tests/test1.yaml b/tests/test1.yaml index cd4179f394..ab089dbc15 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -71,7 +71,6 @@ wifi: password: '' channel: 14 bssid: 'A1:63:95:47:D3:1D' - enable_mdns: true manual_ip: static_ip: 192.168.178.230 gateway: 192.168.178.1 @@ -82,6 +81,9 @@ wifi: reboot_timeout: 120s power_save_mode: light +mdns: + disabled: false + http_request: useragent: esphome/device timeout: 10s @@ -170,6 +172,7 @@ i2c: scan: True frequency: 100kHz setup_priority: -100 + id: i2c_bus spi: clk_pin: GPIO21 @@ -177,15 +180,18 @@ spi: miso_pin: GPIO23 uart: - - tx_pin: GPIO22 - rx_pin: GPIO23 + - tx_pin: + number: GPIO22 + inverted: yes + rx_pin: + number: GPIO23 + inverted: yes baud_rate: 115200 id: uart0 parity: NONE data_bits: 8 stop_bits: 1 rx_buffer_size: 512 - invert: false - id: adalight_uart tx_pin: GPIO25 @@ -246,6 +252,7 @@ deep_sleep: ads1115: address: 0x48 + i2c_id: i2c_bus dallas: pin: GPIO23 @@ -436,6 +443,7 @@ sensor: availability: state_topic: livingroom/custom_state_topic measurement_duration: 31 + i2c_id: i2c_bus - platform: bme280 temperature: name: 'Outside Temperature' @@ -449,6 +457,7 @@ sensor: address: 0x77 iir_filter: 16x update_interval: 15s + i2c_id: i2c_bus - platform: bme680 temperature: name: 'Outside Temperature' @@ -464,6 +473,7 @@ sensor: temperature: 320 duration: 150ms update_interval: 15s + i2c_id: i2c_bus - platform: bmp085 temperature: name: 'Outside Temperature' @@ -473,6 +483,7 @@ sensor: - lambda: >- return x / powf(1.0 - (x / 44330.0), 5.255); update_interval: 15s + i2c_id: i2c_bus - platform: bmp280 temperature: name: 'Outside Temperature' @@ -482,6 +493,7 @@ sensor: address: 0x77 update_interval: 15s iir_filter: 16x + i2c_id: i2c_bus - platform: dallas address: 0x1C0000031EDD2A28 name: 'Living Room Temperature' @@ -503,6 +515,7 @@ sensor: humidity: name: 'Living Room Humidity 4' update_interval: 15s + i2c_id: i2c_bus - platform: duty_cycle pin: GPIO25 name: Duty Cycle Sensor @@ -515,6 +528,7 @@ sensor: humidity: name: 'Living Room Pressure 5' update_interval: 15s + i2c_id: i2c_bus - platform: hlw8012 sel_pin: 5 cf_pin: 14 @@ -560,6 +574,7 @@ sensor: range: 130uT oversampling: 8x update_interval: 15s + i2c_id: i2c_bus - platform: qmc5883l address: 0x0D field_strength_x: @@ -573,6 +588,7 @@ sensor: range: 800uT oversampling: 256x update_interval: 15s + i2c_id: i2c_bus - platform: hx711 name: 'HX711 Value' dout_pin: GPIO23 @@ -593,6 +609,7 @@ sensor: max_voltage: 32.0V max_current: 3.2A update_interval: 15s + i2c_id: i2c_bus - platform: ina226 address: 0x40 shunt_resistance: 0.1 ohm @@ -606,6 +623,7 @@ sensor: name: 'INA226 Shunt Voltage' max_current: 3.2A update_interval: 15s + i2c_id: i2c_bus - platform: ina3221 address: 0x40 channel_1: @@ -619,12 +637,14 @@ sensor: shunt_voltage: name: 'INA3221 Channel 1 Shunt Voltage' update_interval: 15s + i2c_id: i2c_bus - platform: htu21d temperature: name: 'Living Room Temperature 6' humidity: name: 'Living Room Humidity 6' update_interval: 15s + i2c_id: i2c_bus - platform: max6675 name: 'Living Room Temperature' cs_pin: GPIO23 @@ -670,6 +690,7 @@ sensor: name: 'MPU6050 Gyro z' temperature: name: 'MPU6050 Temperature' + i2c_id: i2c_bus - platform: ms5611 temperature: name: 'Outside Temperature' @@ -677,6 +698,7 @@ sensor: name: 'Outside Pressure' address: 0x77 update_interval: 15s + i2c_id: i2c_bus - platform: pmsa003i pm_1_0: name: "PMSA003i PM1.0" @@ -698,6 +720,7 @@ sensor: name: "PMSA003i PMC <10µm" address: 0x12 standard_units: True + i2c_id: i2c_bus - platform: pulse_counter name: 'Pulse Counter' pin: GPIO12 @@ -768,10 +791,12 @@ sensor: humidity: name: 'Living Room Humidity 8' address: 0x44 + i2c_id: i2c_bus update_interval: 15s - platform: sts3x name: 'Living Room Temperature 9' address: 0x4A + i2c_id: i2c_bus - platform: scd30 co2: name: 'Living Room CO2 9' @@ -785,6 +810,7 @@ sensor: altitude_compensation: 10m ambient_pressure_compensation: 961mBar temperature_offset: 4.2C + i2c_id: i2c_bus - platform: sgp30 eco2: name: 'Workshop eCO2' @@ -794,6 +820,7 @@ sensor: accuracy_decimals: 1 address: 0x58 update_interval: 5s + i2c_id: i2c_bus - platform: sps30 pm_1_0: name: 'Workshop PM <1µm Weight concentration' @@ -824,6 +851,7 @@ sensor: id: 'workshop_PMC_10_0' address: 0x69 update_interval: 10s + i2c_id: i2c_bus - platform: sht4x temperature: name: 'SHT4X Temperature' @@ -831,6 +859,7 @@ sensor: name: 'SHT4X Humidity' address: 0x44 update_interval: 15s + i2c_id: i2c_bus - platform: shtcx temperature: name: 'Living Room Temperature 10' @@ -838,6 +867,7 @@ sensor: name: 'Living Room Humidity 10' address: 0x70 update_interval: 15s + i2c_id: i2c_bus - platform: template name: 'Template Sensor' state_class: measurement @@ -863,6 +893,7 @@ sensor: is_cs_package: true integration_time: 402ms gain: 16x + i2c_id: i2c_bus - platform: tsl2591 id: this_little_light_of_mine address: 0x29 @@ -882,6 +913,7 @@ sensor: calculated_lux: name: "tsl2591 calculated_lux" id: tsl2591_cl + i2c_id: i2c_bus - platform: ultrasonic trigger_pin: GPIO25 echo_pin: @@ -921,6 +953,7 @@ sensor: name: CCS811 TVOC update_interval: 30s baseline: 0x4242 + i2c_id: i2c_bus - platform: tx20 wind_speed: name: 'Windspeed' @@ -946,6 +979,7 @@ sensor: - platform: tmp117 name: 'TMP117 Temperature' update_interval: 5s + i2c_id: i2c_bus - platform: hm3301 pm_1_0: name: 'PM1.0' @@ -956,6 +990,7 @@ sensor: aqi: name: 'AQI' calculation_type: 'CAQI' + i2c_id: i2c_bus - platform: teleinfo tag_name: "HCHC" name: "hchc" @@ -965,15 +1000,18 @@ sensor: - platform: mcp9808 name: 'MCP9808 Temperature' update_interval: 15s + i2c_id: i2c_bus - platform: ezo id: ph_ezo address: 99 unit_of_measurement: 'pH' + i2c_id: i2c_bus - platform: sdp3x name: "HVAC Filter Pressure drop" id: filter_pressure update_interval: 5s accuracy_decimals: 3 + i2c_id: i2c_bus - platform: cs5460a id: cs5460a1 current: @@ -1216,14 +1254,18 @@ binary_sensor: pca9685: frequency: 500 address: 0x0 + i2c_id: i2c_bus tlc59208f: - address: 0x20 id: tlc59208f_1 + i2c_id: i2c_bus - address: 0x22 id: tlc59208f_2 + i2c_id: i2c_bus - address: 0x24 id: tlc59208f_3 + i2c_id: i2c_bus my9231: data_pin: GPIO12 @@ -1363,6 +1405,7 @@ output: id: dac_output - platform: mcp4725 id: mcp4725_dac_output + i2c_id: i2c_bus e131: @@ -1992,6 +2035,7 @@ display: address: 0x3F lambda: |- it.print("Hello World!"); + i2c_id: i2c_bus - platform: max7219 cs_pin: GPIO23 num_chips: 1 @@ -2039,6 +2083,7 @@ display: then: lambda: |- ESP_LOGD("display", "1 -> 2"); + i2c_id: i2c_bus - platform: ssd1306_spi model: 'SSD1306 128x64' cs_pin: GPIO23 @@ -2073,6 +2118,7 @@ display: - id: page13272 lambda: |- // Nothing + i2c_id: i2c_bus - platform: ssd1327_spi model: 'SSD1327 128x128' cs_pin: GPIO23 @@ -2151,6 +2197,7 @@ pn532_spi: payload: !lambda 'return x;' pn532_i2c: + i2c_id: i2c_bus rdm6300: uart_id: uart0 @@ -2167,11 +2214,13 @@ rc522_i2c: on_tag: - lambda: |- ESP_LOGD("main", "Found tag %s", x.c_str()); + i2c_id: i2c_bus - update_interval: 1s on_tag: - lambda: |- ESP_LOGD("main", "Found tag %s", x.c_str()); + i2c_id: i2c_bus gps: uart_id: uart0 @@ -2198,6 +2247,7 @@ time: on_time: seconds: 0 then: ds1307.read_time + i2c_id: i2c_bus cover: - platform: template @@ -2225,31 +2275,35 @@ debug: tca9548a: - address: 0x70 id: multiplex0 - scan: True + channels: + - bus_id: multiplex0_chan0 + channel: 0 + i2c_id: i2c_bus - address: 0x71 id: multiplex1 - scan: True - multiplexer: - id: multiplex0 - channel: 0 + i2c_id: multiplex0_chan0 pcf8574: - id: 'pcf8574_hub' address: 0x21 pcf8575: False + i2c_id: i2c_bus mcp23017: - id: 'mcp23017_hub' open_drain_interrupt: 'true' + i2c_id: i2c_bus mcp23008: - id: 'mcp23008_hub' address: 0x22 open_drain_interrupt: 'true' + i2c_id: i2c_bus mcp23016: - id: 'mcp23016_hub' address: 0x23 + i2c_id: i2c_bus stepper: - platform: a4988 diff --git a/tests/test2.yaml b/tests/test2.yaml index 54932953d5..e6df4d513e 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -14,13 +14,15 @@ ethernet: clk_mode: GPIO0_IN phy_addr: 0 power_pin: GPIO25 - enable_mdns: false manual_ip: static_ip: 192.168.178.56 gateway: 192.168.178.1 subnet: 255.255.255.0 domain: .local +mdns: + disabled: true + api: i2c: @@ -360,7 +362,7 @@ esp32_ble_tracker: ble_client: - mac_address: 01:02:03:04:05:06 id: airthings01 - + airthings_ble: #esp32_ble_beacon: diff --git a/tests/test3.yaml b/tests/test3.yaml index 5602481c36..de46cec99c 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -259,7 +259,7 @@ ota: logger: hardware_uart: UART1 level: DEBUG - esp8266_store_log_strings_in_flash: false + esp8266_store_log_strings_in_flash: true web_server: diff --git a/tests/test4.yaml b/tests/test4.yaml index e52e8cc33c..a205f0802e 100644 --- a/tests/test4.yaml +++ b/tests/test4.yaml @@ -379,7 +379,7 @@ display: it.rectangle(3, 3, it.get_width()-6, it.get_height()-6, red); rotation: 0° update_interval: 16ms - + - platform: waveshare_epaper cs_pin: GPIO23 dc_pin: GPIO23 @@ -407,6 +407,23 @@ display: full_update_every: 30 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: inkplate6 + id: inkplate_display + greyscale: false + partial_updating: false + update_interval: 60s + + ckv_pin: GPIO1 + sph_pin: GPIO1 + gmod_pin: GPIO1 + gpio0_enable_pin: GPIO1 + oe_pin: GPIO1 + spv_pin: GPIO1 + powerup_pin: GPIO1 + wakeup_pin: GPIO1 + vcom_pin: GPIO1 + + text_sensor: - platform: pipsolar diff --git a/tests/test5.yaml b/tests/test5.yaml index 84d4a5a44e..f165617551 100644 --- a/tests/test5.yaml +++ b/tests/test5.yaml @@ -1,12 +1,15 @@ esphome: name: test5 - platform: ESP32 - board: nodemcu-32s build_path: build/test5 project: name: esphome.test5_project version: "1.0.0" +esp32: + board: nodemcu-32s + framework: + type: esp-idf + wifi: networks: - ssid: 'MySSID' @@ -151,20 +154,3 @@ sensor: uart_id: uart2 co2: name: CO2 Sensor - -display: - - platform: inkplate6 - id: inkplate_display - greyscale: false - partial_updating: false - update_interval: 60s - - ckv_pin: GPIO1 - sph_pin: GPIO1 - gmod_pin: GPIO1 - gpio0_enable_pin: GPIO1 - oe_pin: GPIO1 - spv_pin: GPIO1 - powerup_pin: GPIO1 - wakeup_pin: GPIO1 - vcom_pin: GPIO1 diff --git a/tests/unit_tests/test_core.py b/tests/unit_tests/test_core.py index 37b4d6db57..9a15bf0b9c 100644 --- a/tests/unit_tests/test_core.py +++ b/tests/unit_tests/test_core.py @@ -531,13 +531,13 @@ class TestEsphomeCore: assert target.address == "4.3.2.1" def test_is_esp32(self, target): - target.esp_platform = "ESP32" + target.data[const.KEY_CORE] = {const.KEY_TARGET_PLATFORM: "esp32"} assert target.is_esp32 is True assert target.is_esp8266 is False def test_is_esp8266(self, target): - target.esp_platform = "ESP8266" + target.data[const.KEY_CORE] = {const.KEY_TARGET_PLATFORM: "esp8266"} assert target.is_esp32 is False assert target.is_esp8266 is True diff --git a/tests/unit_tests/test_cpp_helpers.py b/tests/unit_tests/test_cpp_helpers.py index ae7a61e01f..ad234250ce 100644 --- a/tests/unit_tests/test_cpp_helpers.py +++ b/tests/unit_tests/test_cpp_helpers.py @@ -3,7 +3,6 @@ from mock import Mock from esphome import cpp_helpers as ch from esphome import const -from esphome.cpp_generator import MockObj @pytest.mark.asyncio @@ -13,15 +12,6 @@ async def test_gpio_pin_expression__conf_is_none(monkeypatch): assert actual is None -@pytest.mark.asyncio -async def test_gpio_pin_expression__new_pin(monkeypatch): - actual = await ch.gpio_pin_expression( - {const.CONF_NUMBER: 42, const.CONF_MODE: "input", const.CONF_INVERTED: False} - ) - - assert isinstance(actual, MockObj) - - @pytest.mark.asyncio async def test_register_component(monkeypatch): var = Mock(base="foo.bar") diff --git a/tests/unit_tests/test_pins.py b/tests/unit_tests/test_pins.py deleted file mode 100644 index d2ffd5f7cd..0000000000 --- a/tests/unit_tests/test_pins.py +++ /dev/null @@ -1,346 +0,0 @@ -""" -Please Note: - -These tests cover the process of identifying information about pins, they do not -check if the definition of MCUs and pins is correct. - -""" -import logging - -import pytest - -from esphome.config_validation import Invalid -from esphome.core import EsphomeCore -from esphome import boards, pins - - -MOCK_ESP8266_BOARD_ID = "_mock_esp8266" -MOCK_ESP8266_PINS = {"X0": 16, "X1": 5, "X2": 4, "LED": 2} -MOCK_ESP8266_BOARD_ALIAS_ID = "_mock_esp8266_alias" -MOCK_ESP8266_FLASH_SIZE = boards.FLASH_SIZE_2_MB - -MOCK_ESP32_BOARD_ID = "_mock_esp32" -MOCK_ESP32_PINS = {"Y0": 12, "Y1": 8, "Y2": 3, "LED": 9, "A0": 8} -MOCK_ESP32_BOARD_ALIAS_ID = "_mock_esp32_alias" - -UNKNOWN_PLATFORM = "STM32" - - -@pytest.fixture -def mock_mcu(monkeypatch): - """ - Add a mock MCU into the lists as a stable fixture - """ - boards.ESP8266_BOARD_PINS[MOCK_ESP8266_BOARD_ID] = MOCK_ESP8266_PINS - boards.ESP8266_FLASH_SIZES[MOCK_ESP8266_BOARD_ID] = MOCK_ESP8266_FLASH_SIZE - boards.ESP8266_BOARD_PINS[MOCK_ESP8266_BOARD_ALIAS_ID] = MOCK_ESP8266_BOARD_ID - boards.ESP8266_FLASH_SIZES[MOCK_ESP8266_BOARD_ALIAS_ID] = MOCK_ESP8266_FLASH_SIZE - boards.ESP32_BOARD_PINS[MOCK_ESP32_BOARD_ID] = MOCK_ESP32_PINS - boards.ESP32_BOARD_PINS[MOCK_ESP32_BOARD_ALIAS_ID] = MOCK_ESP32_BOARD_ID - yield - del boards.ESP8266_BOARD_PINS[MOCK_ESP8266_BOARD_ID] - del boards.ESP8266_FLASH_SIZES[MOCK_ESP8266_BOARD_ID] - del boards.ESP8266_BOARD_PINS[MOCK_ESP8266_BOARD_ALIAS_ID] - del boards.ESP8266_FLASH_SIZES[MOCK_ESP8266_BOARD_ALIAS_ID] - del boards.ESP32_BOARD_PINS[MOCK_ESP32_BOARD_ID] - del boards.ESP32_BOARD_PINS[MOCK_ESP32_BOARD_ALIAS_ID] - - -@pytest.fixture -def core(monkeypatch, mock_mcu): - core = EsphomeCore() - monkeypatch.setattr(pins, "CORE", core) - return core - - -@pytest.fixture -def core_esp8266(core): - core.esp_platform = "ESP8266" - core.board = MOCK_ESP8266_BOARD_ID - return core - - -@pytest.fixture -def core_esp32(core): - core.esp_platform = "ESP32" - core.board = MOCK_ESP32_BOARD_ID - return core - - -class Test_lookup_pin: - @pytest.mark.parametrize( - "value, expected", - ( - ("X1", 5), - ("MOSI", 13), - ), - ) - def test_valid_esp8266_pin(self, core_esp8266, value, expected): - actual = pins._lookup_pin(value) - - assert actual == expected - - def test_valid_esp8266_pin_alias(self, core_esp8266): - core_esp8266.board = MOCK_ESP8266_BOARD_ALIAS_ID - - actual = pins._lookup_pin("X2") - - assert actual == 4 - - @pytest.mark.parametrize( - "value, expected", - ( - ("Y1", 8), - ("A0", 8), - ("MOSI", 23), - ), - ) - def test_valid_esp32_pin(self, core_esp32, value, expected): - actual = pins._lookup_pin(value) - - assert actual == expected - - def test_valid_32_pin_alias(self, core_esp32): - core_esp32.board = MOCK_ESP32_BOARD_ALIAS_ID - - actual = pins._lookup_pin("Y2") - - assert actual == 3 - - def test_invalid_pin(self, core_esp8266): - with pytest.raises( - Invalid, match="Cannot resolve pin name 'X42' for board _mock_esp8266." - ): - pins._lookup_pin("X42") - - def test_unsupported_platform(self, core): - core.esp_platform = UNKNOWN_PLATFORM - - with pytest.raises(NotImplementedError): - pins._lookup_pin("TX") - - -class Test_translate_pin: - @pytest.mark.parametrize( - "value, expected", - ( - (2, 2), - ("3", 3), - ("GPIO4", 4), - ("TX", 1), - ("Y0", 12), - ), - ) - def test_valid_values(self, core_esp32, value, expected): - actual = pins._translate_pin(value) - - assert actual == expected - - @pytest.mark.parametrize("value", ({}, None)) - def test_invalid_values(self, core_esp32, value): - with pytest.raises(Invalid, match="This variable only supports"): - pins._translate_pin(value) - - -class Test_validate_gpio_pin: - def test_esp32_valid(self, core_esp32): - actual = pins.validate_gpio_pin("GPIO22") - - assert actual == 22 - - @pytest.mark.parametrize( - "value, match", - ( - (-1, "ESP32: Invalid pin number: -1"), - (40, "ESP32: Invalid pin number: 40"), - (6, "This pin cannot be used on ESP32s and"), - (7, "This pin cannot be used on ESP32s and"), - (8, "This pin cannot be used on ESP32s and"), - (11, "This pin cannot be used on ESP32s and"), - (20, "The pin GPIO20 is not usable on ESP32s"), - (24, "The pin GPIO24 is not usable on ESP32s"), - (28, "The pin GPIO28 is not usable on ESP32s"), - (29, "The pin GPIO29 is not usable on ESP32s"), - (30, "The pin GPIO30 is not usable on ESP32s"), - (31, "The pin GPIO31 is not usable on ESP32s"), - ), - ) - def test_esp32_invalid_pin(self, core_esp32, value, match): - with pytest.raises(Invalid, match=match): - pins.validate_gpio_pin(value) - - @pytest.mark.parametrize("value", (9, 10)) - def test_esp32_warning(self, core_esp32, caplog, value): - caplog.at_level(logging.WARNING) - pins.validate_gpio_pin(value) - - assert len(caplog.messages) == 1 - assert caplog.messages[0].endswith("flash interface in QUAD IO flash mode.") - - def test_esp8266_valid(self, core_esp8266): - actual = pins.validate_gpio_pin("GPIO12") - - assert actual == 12 - - @pytest.mark.parametrize( - "value, match", - ( - (-1, "ESP8266: Invalid pin number: -1"), - (18, "ESP8266: Invalid pin number: 18"), - (6, "This pin cannot be used on ESP8266s and"), - (7, "This pin cannot be used on ESP8266s and"), - (8, "This pin cannot be used on ESP8266s and"), - (11, "This pin cannot be used on ESP8266s and"), - ), - ) - def test_esp8266_invalid_pin(self, core_esp8266, value, match): - with pytest.raises(Invalid, match=match): - pins.validate_gpio_pin(value) - - @pytest.mark.parametrize("value", (9, 10)) - def test_esp8266_warning(self, core_esp8266, caplog, value): - caplog.at_level(logging.WARNING) - pins.validate_gpio_pin(value) - - assert len(caplog.messages) == 1 - assert caplog.messages[0].endswith("flash interface in QUAD IO flash mode.") - - def test_unknown_device(self, core): - core.esp_platform = UNKNOWN_PLATFORM - - with pytest.raises(NotImplementedError): - pins.validate_gpio_pin("0") - - -class Test_input_pin: - @pytest.mark.parametrize("value, expected", (("X0", 16),)) - def test_valid_esp8266_values(self, core_esp8266, value, expected): - actual = pins.input_pin(value) - - assert actual == expected - - @pytest.mark.parametrize( - "value, expected", - ( - ("Y0", 12), - (17, 17), - ), - ) - def test_valid_esp32_values(self, core_esp32, value, expected): - actual = pins.input_pin(value) - - assert actual == expected - - @pytest.mark.parametrize("value", (17,)) - def test_invalid_esp8266_values(self, core_esp8266, value): - with pytest.raises(Invalid): - pins.input_pin(value) - - def test_unknown_platform(self, core): - core.esp_platform = UNKNOWN_PLATFORM - - with pytest.raises(NotImplementedError): - pins.input_pin(2) - - -class Test_input_pullup_pin: - @pytest.mark.parametrize("value, expected", (("X0", 16),)) - def test_valid_esp8266_values(self, core_esp8266, value, expected): - actual = pins.input_pullup_pin(value) - - assert actual == expected - - @pytest.mark.parametrize( - "value, expected", - ( - ("Y0", 12), - (17, 17), - ), - ) - def test_valid_esp32_values(self, core_esp32, value, expected): - actual = pins.input_pullup_pin(value) - - assert actual == expected - - @pytest.mark.parametrize("value", (0,)) - def test_invalid_esp8266_values(self, core_esp8266, value): - with pytest.raises(Invalid): - pins.input_pullup_pin(value) - - def test_unknown_platform(self, core): - core.esp_platform = UNKNOWN_PLATFORM - - with pytest.raises(NotImplementedError): - pins.input_pullup_pin(2) - - -class Test_output_pin: - @pytest.mark.parametrize("value, expected", (("X0", 16),)) - def test_valid_esp8266_values(self, core_esp8266, value, expected): - actual = pins.output_pin(value) - - assert actual == expected - - @pytest.mark.parametrize( - "value, expected", - ( - ("Y0", 12), - (17, 17), - ), - ) - def test_valid_esp32_values(self, core_esp32, value, expected): - actual = pins.output_pin(value) - - assert actual == expected - - @pytest.mark.parametrize("value", (17,)) - def test_invalid_esp8266_values(self, core_esp8266, value): - with pytest.raises(Invalid): - pins.output_pin(value) - - @pytest.mark.parametrize("value", range(34, 40)) - def test_invalid_esp32_values(self, core_esp32, value): - with pytest.raises(Invalid): - pins.output_pin(value) - - def test_unknown_platform(self, core): - core.esp_platform = UNKNOWN_PLATFORM - - with pytest.raises(NotImplementedError): - pins.output_pin(2) - - -class Test_analog_pin: - @pytest.mark.parametrize("value, expected", ((17, 17),)) - def test_valid_esp8266_values(self, core_esp8266, value, expected): - actual = pins.analog_pin(value) - - assert actual == expected - - @pytest.mark.parametrize( - "value, expected", - ( - (32, 32), - (39, 39), - ), - ) - def test_valid_esp32_values(self, core_esp32, value, expected): - actual = pins.analog_pin(value) - - assert actual == expected - - @pytest.mark.parametrize("value", ("X0",)) - def test_invalid_esp8266_values(self, core_esp8266, value): - with pytest.raises(Invalid): - pins.analog_pin(value) - - @pytest.mark.parametrize("value", ("Y0",)) - def test_invalid_esp32_values(self, core_esp32, value): - with pytest.raises(Invalid): - pins.analog_pin(value) - - def test_unknown_platform(self, core): - core.esp_platform = UNKNOWN_PLATFORM - - with pytest.raises(NotImplementedError): - pins.analog_pin(2) diff --git a/tests/unit_tests/test_wizard.py b/tests/unit_tests/test_wizard.py index 56bd5119b5..18e040b0a6 100644 --- a/tests/unit_tests/test_wizard.py +++ b/tests/unit_tests/test_wizard.py @@ -2,7 +2,7 @@ import esphome.wizard as wz import pytest -from esphome.boards import ESP8266_BOARD_PINS +from esphome.components.esp8266.boards import ESP8266_BOARD_PINS from mock import MagicMock From e65a7d887fa4b7e0188c677ea14d26a39019ff64 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 20 Sep 2021 12:02:37 +0200 Subject: [PATCH 071/207] Bump aioesphomeapi to 9.1.1 (#2350) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c6d967fc33..e97c79985f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ platformio==5.2.0 esptool==3.1 click==8.0.1 esphome-dashboard==20210908.0 -aioesphomeapi==9.1.0 +aioesphomeapi==9.1.1 # esp-idf requires this, but doesn't bundle it by default # https://github.com/espressif/esp-idf/blob/220590d599e134d7a5e7f1e683cc4550349ffbf8/requirements.txt#L24 From 250bf3f0541f9f70f11634ed6a70ef2d0d25ded8 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 20 Sep 2021 13:14:05 +0200 Subject: [PATCH 072/207] CI cache only restore from direct matches (#2351) --- .github/workflows/ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c2c434a9b8..45e2f2735c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -96,8 +96,6 @@ jobs: with: path: ~/.platformio key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }} - restore-keys: | - platformio-${{ matrix.pio_cache_key }}- if: matrix.id == 'test' || matrix.id == 'clang-tidy' - name: Install clang tools From bac58bba4d7edddd8a75bfd7caf054d64499cbd3 Mon Sep 17 00:00:00 2001 From: Martin <25747549+martgras@users.noreply.github.com> Date: Mon, 20 Sep 2021 22:13:46 +0200 Subject: [PATCH 073/207] fixes compilation error in rtttl (#2357) Compilation error for millis() and delay() after #2303 --- esphome/components/rtttl/rtttl.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/rtttl/rtttl.cpp b/esphome/components/rtttl/rtttl.cpp index 84cc2ce48c..d571c2f287 100644 --- a/esphome/components/rtttl/rtttl.cpp +++ b/esphome/components/rtttl/rtttl.cpp @@ -1,4 +1,5 @@ #include "rtttl.h" +#include "esphome/core/hal.h" #include "esphome/core/log.h" namespace esphome { From 7c884329eb570214aff952a597e8922312b7df4b Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Tue, 21 Sep 2021 06:34:56 +0200 Subject: [PATCH 074/207] Fix MDNS not registered (#2359) --- esphome/components/mdns/__init__.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/esphome/components/mdns/__init__.py b/esphome/components/mdns/__init__.py index ec568ae2d5..c0c0865643 100644 --- a/esphome/components/mdns/__init__.py +++ b/esphome/components/mdns/__init__.py @@ -1,3 +1,4 @@ +from esphome.const import CONF_ID import esphome.codegen as cg import esphome.config_validation as cv from esphome.core import CORE @@ -5,11 +6,26 @@ from esphome.core import CORE CODEOWNERS = ["@esphome/core"] DEPENDENCIES = ["network"] +mdns_ns = cg.esphome_ns.namespace("mdns") +MDNSComponent = mdns_ns.class_("MDNSComponent", cg.Component) + + +def _remove_id_if_disabled(value): + value = value.copy() + if value[CONF_DISABLED]: + value.pop(CONF_ID) + return value + + CONF_DISABLED = "disabled" -CONFIG_SCHEMA = cv.Schema( - { - cv.Optional(CONF_DISABLED, default=False): cv.boolean, - } +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(MDNSComponent), + cv.Optional(CONF_DISABLED, default=False): cv.boolean, + } + ), + _remove_id_if_disabled, ) @@ -23,3 +39,6 @@ async def to_code(config): cg.add_library("ESPmDNS", None) elif CORE.is_esp8266: cg.add_library("ESP8266mDNS", None) + + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) From 24f445dade6279bbdcb0fb8123e0285d7494a17e Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Tue, 21 Sep 2021 06:37:13 +0200 Subject: [PATCH 075/207] Fix src_filter in platformio.ini after src_dir change (#2353) --- platformio.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platformio.ini b/platformio.ini index 0e2649e63a..412c18085b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -41,9 +41,9 @@ lib_deps = build_flags = -DESPHOME_LOG_LEVEL=ESPHOME_LOG_LEVEL_VERY_VERBOSE src_filter = - + - + - +<.temp/all-include.cpp> + +<./> + +<../tests/dummy_main.cpp> + +<../.temp/all-include.cpp> [common:esp8266] extends = common From 71fc61117b8f90ab15418447068ce3302d4e67d2 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Tue, 21 Sep 2021 06:52:01 +0200 Subject: [PATCH 076/207] Fix duplicate defines and restore alphabetical order (#2352) --- esphome/core/defines.h | 48 ++++++++++++++++-------------------------- 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/esphome/core/defines.h b/esphome/core/defines.h index ac40921a4a..62468dfbfe 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -12,10 +12,9 @@ // Feature flags #define USE_API +#define USE_API_NOISE +#define USE_API_PLAINTEXT #define USE_BINARY_SENSOR -#ifdef USE_ARDUINO -#define USE_CAPTIVE_PORTAL -#endif #define USE_CLIMATE #define USE_COVER #define USE_DEEP_SLEEP @@ -23,14 +22,6 @@ #define USE_FAN #define USE_GRAPH #define USE_HOMEASSISTANT_TIME - -#ifdef USE_ARDUINO -#define USE_JSON -#define USE_NEXTION_TFT_UPLOAD -#define USE_MQTT -#define USE_CAPTIVE_PORTAL -#endif // USE_ARDUINO - #define USE_LIGHT #define USE_LOGGER #define USE_MDNS @@ -43,39 +34,36 @@ #define USE_STATUS_LED #define USE_SWITCH #define USE_TEXT_SENSOR - #define USE_TIME #define USE_WIFI + +// Arduino-specific feature flags #ifdef USE_ARDUINO +#define USE_CAPTIVE_PORTAL +#define USE_JSON +#define USE_NEXTION_TFT_UPLOAD +#define USE_MQTT #define USE_WIFI_WPA2_EAP #endif +// ESP32-specific feature flags #ifdef USE_ESP32 #define USE_ESP32_BLE_SERVER #define USE_ESP32_CAMERA +#define USE_IMPROV +#define USE_SOCKET_IMPL_BSD_SOCKETS #ifdef USE_ARDUINO #define USE_ETHERNET -#endif // USE_ARDUINO - -#define USE_IMPROV -#define USE_SOCKET_IMPL_BSD_SOCKETS -#endif // USE_ESP32 - -#ifdef USE_ESP8266 -#define USE_ADC_SENSOR_VCC -#define USE_SOCKET_IMPL_LWIP_TCP -#define USE_HTTP_REQUEST_ESP8266_HTTPS +#endif #endif -#define USE_API_PLAINTEXT -#define USE_API_NOISE +// ESP8266-specific feature flags +#ifdef USE_ESP8266 +#define USE_ADC_SENSOR_VCC +#define USE_HTTP_REQUEST_ESP8266_HTTPS +#define USE_SOCKET_IMPL_LWIP_TCP +#endif // Disabled feature flags //#define USE_BSEC // Requires a library with proprietary license. -#define USE_TIME -#define USE_DEEP_SLEEP -#define ESPHOME_BOARD "dummy_board" -#define USE_MDNS -#define USE_API_NOISE -#define USE_API_PLAINTEXT From 491f8cc61193ae7900f13c9d4ea497f870d62c98 Mon Sep 17 00:00:00 2001 From: Alex <33379584+alexyao2015@users.noreply.github.com> Date: Tue, 21 Sep 2021 06:47:51 -0500 Subject: [PATCH 077/207] Configurable Flash Write Interval (#2119) Co-authored-by: Alex <33379584+alexyao2015@users.noreply.github.com> Co-authored-by: Otto winter --- CODEOWNERS | 1 + esphome/components/esp32/__init__.py | 1 + esphome/components/esp32/preferences.cpp | 72 +++++++++++++++++++--- esphome/components/esp8266/__init__.py | 1 + esphome/components/esp8266/preferences.cpp | 64 +++++++++---------- esphome/components/ota/ota_component.cpp | 5 +- esphome/components/preferences/__init__.py | 24 ++++++++ esphome/components/preferences/syncer.h | 23 +++++++ esphome/components/wifi/wifi_component.cpp | 2 + esphome/core/preferences.h | 8 +++ 10 files changed, 160 insertions(+), 41 deletions(-) create mode 100644 esphome/components/preferences/__init__.py create mode 100644 esphome/components/preferences/syncer.h diff --git a/CODEOWNERS b/CODEOWNERS index 385ec4d892..adf96b7382 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -104,6 +104,7 @@ esphome/components/pn532/* @OttoWinter @jesserockz esphome/components/pn532_i2c/* @OttoWinter @jesserockz esphome/components/pn532_spi/* @OttoWinter @jesserockz esphome/components/power_supply/* @esphome/core +esphome/components/preferences/* @esphome/core esphome/components/pulse_meter/* @stevebaxter esphome/components/pvvx_mithermometer/* @pasiz esphome/components/rc522/* @glmnet diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index c86087cc25..719b7b5f31 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -34,6 +34,7 @@ from .gpio import esp32_pin_to_code # noqa _LOGGER = logging.getLogger(__name__) CODEOWNERS = ["@esphome/core"] +AUTO_LOAD = ["preferences"] def set_core_data(config): diff --git a/esphome/components/esp32/preferences.cpp b/esphome/components/esp32/preferences.cpp index 639e6434d6..96b7e7809e 100644 --- a/esphome/components/esp32/preferences.cpp +++ b/esphome/components/esp32/preferences.cpp @@ -4,30 +4,53 @@ #include "esphome/core/helpers.h" #include "esphome/core/log.h" #include +#include +#include +#include namespace esphome { namespace esp32 { static const char *const TAG = "esp32.preferences"; +struct NVSData { + std::string key; + std::vector data; +}; + +static std::vector s_pending_save; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + class ESP32PreferenceBackend : public ESPPreferenceBackend { public: std::string key; uint32_t nvs_handle; bool save(const uint8_t *data, size_t len) override { - esp_err_t err = nvs_set_blob(nvs_handle, key.c_str(), data, len); - if (err != 0) { - ESP_LOGV(TAG, "nvs_set_blob('%s', len=%u) failed: %s", key.c_str(), len, esp_err_to_name(err)); - return false; - } - err = nvs_commit(nvs_handle); - if (err != 0) { - ESP_LOGV(TAG, "nvs_commit('%s', len=%u) failed: %s", key.c_str(), len, esp_err_to_name(err)); - return false; + // try find in pending saves and update that + for (auto &obj : s_pending_save) { + if (obj.key == key) { + obj.data.assign(data, data + len); + return true; + } } + NVSData save{}; + save.key = key; + save.data.assign(data, data + len); + s_pending_save.emplace_back(save); return true; } bool load(uint8_t *data, size_t len) override { + // try find in pending saves and load from that + for (auto &obj : s_pending_save) { + if (obj.key == key) { + if (obj.data.size() != len) { + // size mismatch + return false; + } + memcpy(data, obj.data.data(), len); + return true; + } + } + size_t actual_len; esp_err_t err = nvs_get_blob(nvs_handle, key.c_str(), nullptr, &actual_len); if (err != 0) { @@ -82,6 +105,37 @@ class ESP32Preferences : public ESPPreferences { return ESPPreferenceObject(pref); } + + bool sync() override { + if (s_pending_save.empty()) + return true; + + ESP_LOGD(TAG, "Saving preferences to flash..."); + // goal try write all pending saves even if one fails + bool any_failed = false; + + // go through vector from back to front (makes erase easier/more efficient) + for (ssize_t i = s_pending_save.size() - 1; i >= 0; i--) { + const auto &save = s_pending_save[i]; + esp_err_t err = nvs_set_blob(nvs_handle, save.key.c_str(), save.data.data(), save.data.size()); + if (err != 0) { + ESP_LOGV(TAG, "nvs_set_blob('%s', len=%u) failed: %s", save.key.c_str(), save.data.size(), + esp_err_to_name(err)); + any_failed = true; + continue; + } + s_pending_save.erase(s_pending_save.begin() + i); + } + + // note: commit on esp-idf currently is a no-op, nvs_set_blob always writes + esp_err_t err = nvs_commit(nvs_handle); + if (err != 0) { + ESP_LOGV(TAG, "nvs_commit() failed: %s", esp_err_to_name(err)); + return false; + } + + return !any_failed; + } }; void setup_preferences() { diff --git a/esphome/components/esp8266/__init__.py b/esphome/components/esp8266/__init__.py index 592de06440..6eb4f67115 100644 --- a/esphome/components/esp8266/__init__.py +++ b/esphome/components/esp8266/__init__.py @@ -23,6 +23,7 @@ from .gpio import esp8266_pin_to_code # noqa CODEOWNERS = ["@esphome/core"] _LOGGER = logging.getLogger(__name__) +AUTO_LOAD = ["preferences"] def set_core_data(config): diff --git a/esphome/components/esp8266/preferences.cpp b/esphome/components/esp8266/preferences.cpp index 7a8fdb8289..7c0c264054 100644 --- a/esphome/components/esp8266/preferences.cpp +++ b/esphome/components/esp8266/preferences.cpp @@ -73,33 +73,7 @@ template uint32_t calculate_crc(It first, It last, uint32_t type) { return crc; } -static bool safe_flash() { - if (!s_flash_dirty) - return true; - - ESP_LOGVV(TAG, "Saving preferences to flash..."); - SpiFlashOpResult erase_res, write_res = SPI_FLASH_RESULT_OK; - { - InterruptLock lock; - erase_res = spi_flash_erase_sector(get_esp8266_flash_sector()); - if (erase_res == SPI_FLASH_RESULT_OK) { - write_res = spi_flash_write(get_esp8266_flash_address(), s_flash_storage, ESP8266_FLASH_STORAGE_SIZE * 4); - } - } - if (erase_res != SPI_FLASH_RESULT_OK) { - ESP_LOGV(TAG, "Erase ESP8266 flash failed!"); - return false; - } - if (write_res != SPI_FLASH_RESULT_OK) { - ESP_LOGV(TAG, "Write ESP8266 flash failed!"); - return false; - } - - s_flash_dirty = false; - return true; -} - -static bool safe_to_flash(size_t offset, const uint32_t *data, size_t len) { +static bool save_to_flash(size_t offset, const uint32_t *data, size_t len) { for (uint32_t i = 0; i < len; i++) { uint32_t j = offset + i; if (j >= ESP8266_FLASH_STORAGE_SIZE) @@ -110,7 +84,7 @@ static bool safe_to_flash(size_t offset, const uint32_t *data, size_t len) { s_flash_dirty = true; *ptr = v; } - return safe_flash(); + return true; } static bool load_from_flash(size_t offset, uint32_t *data, size_t len) { @@ -123,7 +97,7 @@ static bool load_from_flash(size_t offset, uint32_t *data, size_t len) { return true; } -static bool safe_to_rtc(size_t offset, const uint32_t *data, size_t len) { +static bool save_to_rtc(size_t offset, const uint32_t *data, size_t len) { for (uint32_t i = 0; i < len; i++) if (!esp_rtc_user_mem_write(offset + i, data[i])) return false; @@ -154,9 +128,9 @@ class ESP8266PreferenceBackend : public ESPPreferenceBackend { buffer[buffer.size() - 1] = calculate_crc(buffer.begin(), buffer.end() - 1, type); if (in_flash) { - return safe_to_flash(offset, buffer.data(), buffer.size()); + return save_to_flash(offset, buffer.data(), buffer.size()); } else { - return safe_to_rtc(offset, buffer.data(), buffer.size()); + return save_to_rtc(offset, buffer.data(), buffer.size()); } } bool load(uint8_t *data, size_t len) override { @@ -245,6 +219,34 @@ class ESP8266Preferences : public ESPPreferences { return make_preference(length, type, false); #endif } + + bool sync() override { + if (!s_flash_dirty) + return true; + if (s_prevent_write) + return false; + + ESP_LOGD(TAG, "Saving preferences to flash..."); + SpiFlashOpResult erase_res, write_res = SPI_FLASH_RESULT_OK; + { + InterruptLock lock; + erase_res = spi_flash_erase_sector(get_esp8266_flash_sector()); + if (erase_res == SPI_FLASH_RESULT_OK) { + write_res = spi_flash_write(get_esp8266_flash_address(), s_flash_storage, ESP8266_FLASH_STORAGE_SIZE * 4); + } + } + if (erase_res != SPI_FLASH_RESULT_OK) { + ESP_LOGV(TAG, "Erase ESP8266 flash failed!"); + return false; + } + if (write_res != SPI_FLASH_RESULT_OK) { + ESP_LOGV(TAG, "Write ESP8266 flash failed!"); + return false; + } + + s_flash_dirty = false; + return true; + } }; void setup_preferences() { diff --git a/esphome/components/ota/ota_component.cpp b/esphome/components/ota/ota_component.cpp index 7ee3ed2866..f217bd32d1 100644 --- a/esphome/components/ota/ota_component.cpp +++ b/esphome/components/ota/ota_component.cpp @@ -548,7 +548,10 @@ bool OTAComponent::should_enter_safe_mode(uint8_t num_attempts, uint32_t enable_ return false; } } -void OTAComponent::write_rtc_(uint32_t val) { this->rtc_.save(&val); } +void OTAComponent::write_rtc_(uint32_t val) { + this->rtc_.save(&val); + global_preferences->sync(); +} uint32_t OTAComponent::read_rtc_() { uint32_t val; if (!this->rtc_.load(&val)) diff --git a/esphome/components/preferences/__init__.py b/esphome/components/preferences/__init__.py new file mode 100644 index 0000000000..4844ad6c02 --- /dev/null +++ b/esphome/components/preferences/__init__.py @@ -0,0 +1,24 @@ +from esphome.const import CONF_ID +import esphome.codegen as cg +import esphome.config_validation as cv + +CODEOWNERS = ["@esphome/core"] + +preferences_ns = cg.esphome_ns.namespace("preferences") +IntervalSyncer = preferences_ns.class_("IntervalSyncer", cg.Component) + +CONF_FLASH_WRITE_INTERVAL = "flash_write_interval" +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(IntervalSyncer), + cv.Optional( + CONF_FLASH_WRITE_INTERVAL, default="60s" + ): cv.positive_time_period_milliseconds, + } +).extend(cv.COMPONENT_SCHEMA) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + cg.add(var.set_write_interval(config[CONF_FLASH_WRITE_INTERVAL])) + await cg.register_component(var, config) diff --git a/esphome/components/preferences/syncer.h b/esphome/components/preferences/syncer.h new file mode 100644 index 0000000000..af1fe9ba4a --- /dev/null +++ b/esphome/components/preferences/syncer.h @@ -0,0 +1,23 @@ +#pragma once + +#include "esphome/core/preferences.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace preferences { + +class IntervalSyncer : public Component { + public: + void set_write_interval(uint32_t write_interval) { write_interval_ = write_interval; } + void setup() override { + set_interval(write_interval_, []() { global_preferences->sync(); }); + } + void on_shutdown() override { global_preferences->sync(); } + float get_setup_priority() const override { return setup_priority::BUS; } + + protected: + uint32_t write_interval_; +}; + +} // namespace preferences +} // namespace esphome diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 6e0ce8c044..47cd0ef9ad 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -232,6 +232,8 @@ void WiFiComponent::save_wifi_sta(const std::string &ssid, const std::string &pa strncpy(save.ssid, ssid.c_str(), sizeof(save.ssid)); strncpy(save.password, password.c_str(), sizeof(save.password)); this->pref_.save(&save); + // ensure it's written immediately + global_preferences->sync(); WiFiAP sta{}; sta.set_ssid(ssid); diff --git a/esphome/core/preferences.h b/esphome/core/preferences.h index ff7911ed3a..b3f4b77f78 100644 --- a/esphome/core/preferences.h +++ b/esphome/core/preferences.h @@ -37,6 +37,14 @@ class ESPPreferences { public: virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash) = 0; virtual ESPPreferenceObject make_preference(size_t length, uint32_t type) = 0; + + /** + * Commit pending writes to flash. + * + * @return true if write is successful. + */ + virtual bool sync() = 0; + #ifndef USE_ESP8266 template::value, bool>::type = true> #else From 92a24d52be5ee60772c10554967a0d011e7ca723 Mon Sep 17 00:00:00 2001 From: Maurice Makaay Date: Tue, 21 Sep 2021 17:11:58 +0200 Subject: [PATCH 078/207] Fix OTA password mismatch error. (#2363) Co-authored-by: Maurice Makaay --- esphome/components/ota/ota_component.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/ota/ota_component.cpp b/esphome/components/ota/ota_component.cpp index f217bd32d1..5e07bb64e7 100644 --- a/esphome/components/ota/ota_component.cpp +++ b/esphome/components/ota/ota_component.cpp @@ -325,7 +325,7 @@ void OTAComponent::handle_() { ESP_LOGV(TAG, "Auth: Result is %s", sbuf); // Receive result, 32 bytes hex MD5 - if (!this->writeall_(buf + 64, 32)) { + if (!this->readall_(buf + 64, 32)) { ESP_LOGW(TAG, "Auth: Reading response failed!"); goto error; } From 637b55bfbfe8d55b372271507f11040189d140a0 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Tue, 21 Sep 2021 17:12:17 +0200 Subject: [PATCH 079/207] Allow compilation against IDF from repository (#2355) * Fix src_filter in platformio.ini after src_dir change * Add -Wno-nonnull-compare to platformio.ini as well * Create default sdkconfig for static analysis * Add more compiler flags to clang ignore list * Clean-up platformio.ini * Remove unnecessary blank line * Fix accidentally dropped library * Don't gitignore sdkconfig.defaults Co-authored-by: Otto winter Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- .gitignore | 1 + platformio.ini | 99 +++++++++++++++++++++++++--------------------- script/clang-tidy | 4 +- script/helpers.py | 31 ++++++--------- sdkconfig.defaults | 17 ++++++++ 5 files changed, 85 insertions(+), 67 deletions(-) create mode 100644 sdkconfig.defaults diff --git a/.gitignore b/.gitignore index c52909f7c9..57b8478bd7 100644 --- a/.gitignore +++ b/.gitignore @@ -126,3 +126,4 @@ tests/.esphome/ .pio/ sdkconfig.* +!sdkconfig.defaults diff --git a/platformio.ini b/platformio.ini index 412c18085b..1901f175af 100644 --- a/platformio.ini +++ b/platformio.ini @@ -4,7 +4,7 @@ ; It's *not* used during runtime. [platformio] -default_envs = esp8266, esp32 +default_envs = esp8266, esp32, esp32-idf src_dir = esphome include_dir = @@ -26,18 +26,8 @@ build_flags = [common] lib_deps = - ottowinter/AsyncMqttClient-esphome@0.8.4 ; mqtt - ottowinter/ArduinoJson-esphomelib@5.13.3 ; json - esphome/ESPAsyncWebServer-esphome@1.3.0 ; web_server_base - fastled/FastLED@3.3.2 ; fastled_base - makuna/NeoPixelBus@2.6.7 ; neopixelbus - mikalhart/TinyGPSPlus@1.0.2 ; gps - freekode/TM1651@1.0.1 ; tm1651 - seeed-studio/Grove - Laser PM2.5 Sensor HM3301@1.0.3 ; hm3301 - glmnet/Dsmr@0.5 ; dsmr - rweather/Crypto@0.2.0 ; dsmr - esphome/noise-c@0.1.1 ; api - dudanov/MideaUART@1.1.0 ; midea + esphome/noise-c@0.1.1 ; api + makuna/NeoPixelBus@2.6.7 ; neopixelbus build_flags = -DESPHOME_LOG_LEVEL=ESPHOME_LOG_LEVEL_VERY_VERBOSE src_filter = @@ -45,8 +35,32 @@ src_filter = +<../tests/dummy_main.cpp> +<../.temp/all-include.cpp> -[common:esp8266] +[common:arduino] extends = common +lib_deps = + ${common.lib_deps} + ottowinter/AsyncMqttClient-esphome@0.8.4 ; mqtt + ottowinter/ArduinoJson-esphomelib@5.13.3 ; json + esphome/ESPAsyncWebServer-esphome@1.3.0 ; web_server_base + fastled/FastLED@3.3.2 ; fastled_base + mikalhart/TinyGPSPlus@1.0.2 ; gps + freekode/TM1651@1.0.1 ; tm1651 + seeed-studio/Grove - Laser PM2.5 Sensor HM3301@1.0.3 ; hm3301 + glmnet/Dsmr@0.5 ; dsmr + rweather/Crypto@0.2.0 ; dsmr + dudanov/MideaUART@1.1.0 ; midea +build_flags = + ${common.build_flags} + -DUSE_ARDUINO + +[common:idf] +extends = common +build_flags = + ${common.build_flags} + -DUSE_ESP_IDF + +[common:esp8266] +extends = common:arduino ; when changing this also copy it to esphome-docker-base images platform = platformio/espressif8266 @ 3.2.0 platform_packages = @@ -55,16 +69,17 @@ platform_packages = framework = arduino board = nodemcuv2 lib_deps = - ${common.lib_deps} + ${common:arduino.lib_deps} ESP8266WiFi ; wifi (Arduino built-in) Update ; ota (Arduino built-in) ottowinter/ESPAsyncTCP-esphome@1.2.3 ; async_tcp build_flags = - ${common.build_flags} + ${common:arduino.build_flags} -DUSE_ESP8266 + -DUSE_ESP8266_FRAMEWORK_ARDUINO -[common:esp32] -extends = common +[common:esp32-arduino] +extends = common:arduino ; when changing this also copy it to esphome-docker-base images platform = platformio/espressif32 @ 3.3.2 platform_packages = @@ -73,15 +88,16 @@ platform_packages = framework = arduino board = nodemcu-32s lib_deps = - ${common.lib_deps} + ${common:arduino.lib_deps} Hash ; ota (Arduino built-in) esphome/AsyncTCP-esphome@1.2.2 ; async_tcp build_flags = - ${common.build_flags} + ${common:arduino.build_flags} -DUSE_ESP32 -src_filter = ${common.src_filter} + -DUSE_ESP32_FRAMEWORK_ARDUINO -[common:espidf] +[common:esp32-idf] +extends = common:idf ; when changing this also copy it to esphome-docker-base images platform = platformio/espressif32 @ 3.3.2 platform_packages = @@ -90,57 +106,48 @@ platform_packages = framework = espidf board = nodemcu-32s lib_deps = - esphome/noise-c@0.1.1 ; used by api - espressif/esp32-camera@1.0.0 ; used by esp32_camera - NeoPixelBus@2.6.7 + ${common:idf.lib_deps} + espressif/esp32-camera@1.0.0 ; esp32_camera build_flags = - ${common.build_flags} - ${common:esp32.build_flags} - -DUSE_ESP_IDF + ${common:idf.build_flags} + -Wno-nonnull-compare + -DUSE_ESP32 -DUSE_ESP32_FRAMEWORK_ESP_IDF -src_filter = ${common:esp32.src_filter} [env:esp8266] extends = common:esp8266 build_flags = ${common:esp8266.build_flags} ${runtime.build_flags} - -DUSE_ARDUINO - -DUSE_ESP8266_FRAMEWORK_ARDUINO [env:esp8266-tidy] extends = common:esp8266 build_flags = ${common:esp8266.build_flags} ${clangtidy.build_flags} - -DUSE_ARDUINO - -DUSE_ESP8266_FRAMEWORK_ARDUINO [env:esp32] -extends = common:esp32 +extends = common:esp32-arduino build_flags = - ${common:esp32.build_flags} + ${common:esp32-arduino.build_flags} ${runtime.build_flags} - -DUSE_ARDUINO - -DUSE_ESP32_FRAMEWORK_ARDUINO [env:esp32-tidy] -extends = common:esp32 +extends = common:esp32-arduino build_flags = - ${common:esp32.build_flags} + ${common:esp32-arduino.build_flags} ${clangtidy.build_flags} - -DUSE_ARDUINO - -DUSE_ESP32_FRAMEWORK_ARDUINO [env:esp32-idf] -extends = common:espidf +extends = common:esp32-idf +board_build.esp-idf.sdkconfig_path = .temp/sdkconfig-esp32-idf build_flags = - ${common:espidf.build_flags} + ${common:esp32-idf.build_flags} ${runtime.build_flags} - [env:esp32-idf-tidy] -extends = common:espidf +extends = common:esp32-idf +board_build.esp-idf.sdkconfig_path = .temp/sdkconfig-esp32-idf-tidy build_flags = - ${common:espidf.build_flags} + ${common:esp32-idf.build_flags} ${clangtidy.build_flags} diff --git a/script/clang-tidy b/script/clang-tidy index 8a1baf8a22..2612a18c1c 100755 --- a/script/clang-tidy +++ b/script/clang-tidy @@ -54,7 +54,9 @@ def clang_options(idedata): # copy compiler flags, except those clang doesn't understand. cmd.extend(flag for flag in idedata['cxx_flags'].split(' ') - if flag not in ('-free', '-fipa-pta', '-fstrict-volatile-bitfields', '-mlongcalls', '-mtext-section-literals')) + if flag not in ('-free', '-fipa-pta', '-fstrict-volatile-bitfields', + '-mlongcalls', '-mtext-section-literals', + '-mfix-esp32-psram-cache-issue', '-mfix-esp32-psram-cache-strategy=memw')) # defines cmd.extend(f'-D{define}' for define in idedata['defines']) diff --git a/script/helpers.py b/script/helpers.py index 2c40d47e04..430d8a8e7f 100644 --- a/script/helpers.py +++ b/script/helpers.py @@ -112,11 +112,6 @@ def git_ls_files(patterns=None): return {s[3].strip(): int(s[0]) for s in lines} -IDF_TIDY_SDKCONFIG = """\ -CONFIG_BT_ENABLED=y -""" - - def load_idedata(environment): platformio_ini = Path(root_path) / "platformio.ini" temp_idedata = Path(temp_folder) / f"idedata-{environment}.json" @@ -126,30 +121,26 @@ def load_idedata(environment): elif platformio_ini.stat().st_mtime >= temp_idedata.stat().st_mtime: changed = True - if environment == "esp32-idf-tidy": - # sdkconfig needs to be written before idedata is run - # but the file is also modified by the build process, so - # store a temp file to keep track of the + if "idf" in environment: + # remove full sdkconfig when the defaults have changed so that it is regenerated + default_sdkconfig = Path(root_path) / "sdkconfig.defaults" + temp_sdkconfig = Path(temp_folder) / f"sdkconfig-{environment}" - sdk_internal = Path(temp_folder) / f"{environment}-internal-sdkconfig" - sdkconfig = Path(root_path) / f"sdkconfig.{environment}" - if ( - changed - or not sdk_internal.is_file() - or sdk_internal.read_text() != IDF_TIDY_SDKCONFIG - ): + if not temp_sdkconfig.is_file(): + changed = True + elif default_sdkconfig.stat().st_mtime >= temp_sdkconfig.stat().st_mtime: + temp_sdkconfig.unlink() changed = True - sdkconfig.write_text(IDF_TIDY_SDKCONFIG) - sdk_internal.parent.mkdir(exist_ok=True) - sdk_internal.write_text(IDF_TIDY_SDKCONFIG) if not changed: return json.loads(temp_idedata.read_text()) + # ensure temp directory exists before running pio, as it writes sdkconfig to it + Path(temp_folder).mkdir(exist_ok=True) + stdout = subprocess.check_output(["pio", "run", "-t", "idedata", "-e", environment]) match = re.search(r'{\s*".*}', stdout.decode("utf-8")) data = json.loads(match.group()) - temp_idedata.parent.mkdir(exist_ok=True) temp_idedata.write_text(json.dumps(data, indent=2) + "\n") return data diff --git a/sdkconfig.defaults b/sdkconfig.defaults new file mode 100644 index 0000000000..6b2d6f8f2e --- /dev/null +++ b/sdkconfig.defaults @@ -0,0 +1,17 @@ +# ESP-IDF sdkconfig defaults used for development purposes only, not used during runtime. Used when PlatformIO is ran +# directly from the source directory, e.g. by IDEs or for static analysis (clang-tidy). This should enable all flags +# that are set by any component. + +# esp32 +CONFIG_COMPILER_OPTIMIZATION_DEFAULT=n +CONFIG_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_PARTITION_TABLE_CUSTOM=y +#CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_SINGLE_APP=n + +# esp32_ble +CONFIG_BT_ENABLED=y + +# esp32_camera +CONFIG_RTCIO_SUPPORT_RTC_GPIO_DESC=y +CONFIG_ESP32_SPIRAM_SUPPORT=y From bbac1534a3b2b1c961c13e8da23dd4ffdbe2ed22 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Tue, 21 Sep 2021 21:59:11 +0200 Subject: [PATCH 080/207] Fix ESP8266 preferences not set up (#2362) --- esphome/components/esp8266/__init__.py | 7 +++++-- esphome/components/esp8266/core.cpp | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/esphome/components/esp8266/__init__.py b/esphome/components/esp8266/__init__.py index 6eb4f67115..93a461ba1f 100644 --- a/esphome/components/esp8266/__init__.py +++ b/esphome/components/esp8266/__init__.py @@ -10,11 +10,11 @@ from esphome.const import ( KEY_TARGET_FRAMEWORK, KEY_TARGET_PLATFORM, ) -from esphome.core import CORE +from esphome.core import CORE, coroutine_with_priority import esphome.config_validation as cv import esphome.codegen as cg -from .const import CONF_RESTORE_FROM_FLASH, KEY_BOARD, KEY_ESP8266 +from .const import CONF_RESTORE_FROM_FLASH, KEY_BOARD, KEY_ESP8266, esp8266_ns from .boards import ESP8266_FLASH_SIZES, ESP8266_LD_SCRIPTS # force import gpio to register pin schema @@ -153,7 +153,10 @@ CONFIG_SCHEMA = cv.All( ) +@coroutine_with_priority(1000) async def to_code(config): + cg.add(esp8266_ns.setup_preferences()) + cg.add_platformio_option("board", config[CONF_BOARD]) cg.add_build_flag("-DUSE_ESP8266") cg.add_define("ESPHOME_BOARD", config[CONF_BOARD]) diff --git a/esphome/components/esp8266/core.cpp b/esphome/components/esp8266/core.cpp index 453f772e09..b78600e7a3 100644 --- a/esphome/components/esp8266/core.cpp +++ b/esphome/components/esp8266/core.cpp @@ -32,6 +32,28 @@ uint32_t arch_get_cpu_cycle_count() { } uint32_t arch_get_cpu_freq_hz() { return F_CPU; } +void force_link_symbols() { + // Tasmota uses magic bytes in the binary to check if an OTA firmware is compatible + // with their settings - ESPHome uses a different settings system (that can also survive + // erases). So set magic bytes indicating all tasmota versions are supported. + // This only adds 12 bytes of binary size, which is an acceptable price to pay for easier support + // for Tasmota. + // https://github.com/arendst/Tasmota/blob/b05301b1497942167a015a6113b7f424e42942cd/tasmota/settings.ino#L346-L380 + // https://github.com/arendst/Tasmota/blob/b05301b1497942167a015a6113b7f424e42942cd/tasmota/i18n.h#L652-L654 + const static uint32_t TASMOTA_MAGIC_BYTES[] PROGMEM = {0x5AA55AA5, 0xFFFFFFFF, 0xA55AA55A}; + // Force link symbol by using a volatile integer (GCC attribute used does not work because of LTO) + volatile int x = 0; + x = TASMOTA_MAGIC_BYTES[x]; +} + +extern "C" void resetPins() { // NOLINT + // Added in framework 2.7.0 + // usually this sets up all pins to be in INPUT mode + // however, not strictly needed as we set up the pins properly + // ourselves and this causes pins to toggle during reboot. + force_link_symbols(); +} + } // namespace esphome #endif // USE_ESP8266 From c8a8acd46ef0d04cee2af02dde0e87f263860189 Mon Sep 17 00:00:00 2001 From: Paul Monigatti Date: Wed, 22 Sep 2021 13:55:49 +1200 Subject: [PATCH 081/207] Fix ESP8266 preference loading (#2367) --- esphome/components/esp8266/preferences.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/esphome/components/esp8266/preferences.cpp b/esphome/components/esp8266/preferences.cpp index 7c0c264054..041736943b 100644 --- a/esphome/components/esp8266/preferences.cpp +++ b/esphome/components/esp8266/preferences.cpp @@ -149,7 +149,12 @@ class ESP8266PreferenceBackend : public ESPPreferenceBackend { return false; uint32_t crc = calculate_crc(buffer.begin(), buffer.end() - 1, type); - return buffer[buffer.size() - 1] == crc; + if (buffer[buffer.size() - 1] != crc) { + return false; + } + + memcpy(data, buffer.data(), len); + return true; } }; From c51352d04d5440f507cc3b79193bbb98cbfa17c1 Mon Sep 17 00:00:00 2001 From: Paul Monigatti Date: Wed, 22 Sep 2021 13:59:21 +1200 Subject: [PATCH 082/207] Allow non-addressable lights in light partitions (#2256) --- .../light/addressable_light_wrapper.h | 56 +++++++++++++++++ esphome/components/partition/light.py | 60 ++++++++++++++----- esphome/const.py | 2 + tests/test1.yaml | 1 + 4 files changed, 104 insertions(+), 15 deletions(-) create mode 100644 esphome/components/light/addressable_light_wrapper.h diff --git a/esphome/components/light/addressable_light_wrapper.h b/esphome/components/light/addressable_light_wrapper.h new file mode 100644 index 0000000000..813dd43313 --- /dev/null +++ b/esphome/components/light/addressable_light_wrapper.h @@ -0,0 +1,56 @@ +#pragma once + +#include "esphome/core/component.h" +#include "addressable_light.h" + +namespace esphome { +namespace light { + +class AddressableLightWrapper : public light::AddressableLight { + public: + explicit AddressableLightWrapper(light::LightState *light_state) : light_state_(light_state) { + this->wrapper_state_ = new uint8_t[5]; + } + + int32_t size() const override { return 1; } + + void clear_effect_data() override { this->wrapper_state_[4] = 0; } + + light::LightTraits get_traits() override { return this->light_state_->get_traits(); } + + void write_state(light::LightState *state) override { + float gamma = this->light_state_->get_gamma_correct(); + float r = gamma_uncorrect(this->wrapper_state_[0] / 255.0f, gamma); + float g = gamma_uncorrect(this->wrapper_state_[1] / 255.0f, gamma); + float b = gamma_uncorrect(this->wrapper_state_[2] / 255.0f, gamma); + float w = gamma_uncorrect(this->wrapper_state_[3] / 255.0f, gamma); + float brightness = fmaxf(r, fmaxf(g, b)); + + auto call = this->light_state_->make_call(); + call.set_state(true); + call.set_brightness_if_supported(1.0f); + call.set_color_brightness_if_supported(brightness); + call.set_red_if_supported(r); + call.set_green_if_supported(g); + call.set_blue_if_supported(b); + call.set_white_if_supported(w); + call.set_transition_length_if_supported(0); + call.set_publish(false); + call.set_save(false); + call.perform(); + + this->mark_shown_(); + } + + protected: + light::ESPColorView get_view_internal(int32_t index) const override { + return {&this->wrapper_state_[0], &this->wrapper_state_[1], &this->wrapper_state_[2], + &this->wrapper_state_[3], &this->wrapper_state_[4], &this->correction_}; + } + + light::LightState *light_state_; + uint8_t *wrapper_state_; +}; + +} // namespace light +} // namespace esphome diff --git a/esphome/components/partition/light.py b/esphome/components/partition/light.py index 06bee2143e..ada83a123e 100644 --- a/esphome/components/partition/light.py +++ b/esphome/components/partition/light.py @@ -2,9 +2,12 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import light from esphome.const import ( + CONF_ADDRESSABLE_LIGHT_ID, CONF_FROM, CONF_ID, + CONF_LIGHT_ID, CONF_SEGMENTS, + CONF_SINGLE_LIGHT_ID, CONF_TO, CONF_OUTPUT_ID, CONF_REVERSED, @@ -12,30 +15,47 @@ from esphome.const import ( partitions_ns = cg.esphome_ns.namespace("partition") AddressableSegment = partitions_ns.class_("AddressableSegment") +AddressableLightWrapper = cg.esphome_ns.namespace("light").class_( + "AddressableLightWrapper" +) PartitionLightOutput = partitions_ns.class_( "PartitionLightOutput", light.AddressableLight ) def validate_from_to(value): - if value[CONF_FROM] > value[CONF_TO]: + if CONF_ID in value and value[CONF_FROM] > value[CONF_TO]: raise cv.Invalid( f"From ({value[CONF_FROM]}) must not be larger than to ({value[CONF_TO]})" ) return value +ADDRESSABLE_SEGMENT_SCHEMA = cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(light.AddressableLightState), + cv.Required(CONF_FROM): cv.positive_int, + cv.Required(CONF_TO): cv.positive_int, + cv.Optional(CONF_REVERSED, default=False): cv.boolean, + } +) + +NONADDRESSABLE_SEGMENT_SCHEMA = cv.COMPONENT_SCHEMA.extend( + { + cv.Required(CONF_SINGLE_LIGHT_ID): cv.use_id(light.LightState), + cv.GenerateID(CONF_ADDRESSABLE_LIGHT_ID): cv.declare_id( + AddressableLightWrapper + ), + cv.GenerateID(CONF_LIGHT_ID): cv.declare_id(light.types.LightState), + } +) + CONFIG_SCHEMA = light.ADDRESSABLE_LIGHT_SCHEMA.extend( { cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(PartitionLightOutput), cv.Required(CONF_SEGMENTS): cv.All( cv.ensure_list( - { - cv.Required(CONF_ID): cv.use_id(light.AddressableLightState), - cv.Required(CONF_FROM): cv.positive_int, - cv.Required(CONF_TO): cv.positive_int, - cv.Optional(CONF_REVERSED, default=False): cv.boolean, - }, + cv.Any(ADDRESSABLE_SEGMENT_SCHEMA, NONADDRESSABLE_SEGMENT_SCHEMA), validate_from_to, ), cv.Length(min=1), @@ -47,15 +67,25 @@ CONFIG_SCHEMA = light.ADDRESSABLE_LIGHT_SCHEMA.extend( async def to_code(config): segments = [] for conf in config[CONF_SEGMENTS]: - var = await cg.get_variable(conf[CONF_ID]) - segments.append( - AddressableSegment( - var, - conf[CONF_FROM], - conf[CONF_TO] - conf[CONF_FROM] + 1, - conf[CONF_REVERSED], + if CONF_SINGLE_LIGHT_ID in conf: + wrapper = cg.new_Pvariable( + conf[CONF_ADDRESSABLE_LIGHT_ID], + await cg.get_variable(conf[CONF_SINGLE_LIGHT_ID]), + ) + light_state = cg.new_Pvariable(conf[CONF_LIGHT_ID], "", wrapper) + await cg.register_component(light_state, conf) + cg.add(cg.App.register_light(light_state)) + segments.append(AddressableSegment(light_state, 0, 1, False)) + + else: + segments.append( + AddressableSegment( + await cg.get_variable(conf[CONF_ID]), + conf[CONF_FROM], + conf[CONF_TO] - conf[CONF_FROM] + 1, + conf[CONF_REVERSED], + ) ) - ) var = cg.new_Pvariable(config[CONF_OUTPUT_ID], segments) await cg.register_component(var, config) diff --git a/esphome/const.py b/esphome/const.py index 6a3296bcea..f032cf0fc3 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -320,6 +320,7 @@ CONF_LEVEL = "level" CONF_LG = "lg" CONF_LIBRARIES = "libraries" CONF_LIGHT = "light" +CONF_LIGHT_ID = "light_id" CONF_LIGHTNING_ENERGY = "lightning_energy" CONF_LIGHTNING_THRESHOLD = "lightning_threshold" CONF_LINE_THICKNESS = "line_thickness" @@ -586,6 +587,7 @@ CONF_SHOW_VALUES = "show_values" CONF_SHUNT_RESISTANCE = "shunt_resistance" CONF_SHUNT_VOLTAGE = "shunt_voltage" CONF_SHUTDOWN_MESSAGE = "shutdown_message" +CONF_SINGLE_LIGHT_ID = "single_light_id" CONF_SIZE = "size" CONF_SLEEP_DURATION = "sleep_duration" CONF_SLEEP_PIN = "sleep_pin" diff --git a/tests/test1.yaml b/tests/test1.yaml index ab089dbc15..c1ddf26488 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1615,6 +1615,7 @@ light: - id: addr2 from: 20 to: 25 + - single_light_id: ${roomname}_lights remote_transmitter: - pin: 32 From 40e0100c1efb10be745523c13b66a17f3f02e18f Mon Sep 17 00:00:00 2001 From: WeekendWarrior1 Date: Wed, 22 Sep 2021 14:57:16 +1000 Subject: [PATCH 083/207] add = to default font glpyh list (#2361) --- esphome/components/font/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/font/__init__.py b/esphome/components/font/__init__.py index e47a6f38af..7225bf5bb9 100644 --- a/esphome/components/font/__init__.py +++ b/esphome/components/font/__init__.py @@ -72,7 +72,7 @@ def validate_truetype_file(value): DEFAULT_GLYPHS = ( - ' !"%()+,-.:/0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz°' + ' !"%()+=,-.:/0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz°' ) CONF_RAW_DATA_ID = "raw_data_id" CONF_RAW_GLYPH_ID = "raw_glyph_id" From 11daabc9c25a7f4dda4bf3c5a111ab31e15774cf Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Wed, 22 Sep 2021 10:32:39 +0200 Subject: [PATCH 084/207] Fix docker pio settings not applied (#2370) --- docker/docker_entrypoint.sh | 16 +++++++++++----- docker/hassio-rootfs/etc/cont-init.d/30-dirs.sh | 4 ++-- docker/hassio-rootfs/etc/services.d/esphome/run | 8 +++++++- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/docker/docker_entrypoint.sh b/docker/docker_entrypoint.sh index b3905d1fed..75d5e0b7b5 100755 --- a/docker/docker_entrypoint.sh +++ b/docker/docker_entrypoint.sh @@ -4,15 +4,21 @@ # otherwise use path in /config (so that PIO packages aren't downloaded on each compile) if [[ -d /cache ]]; then - export PLATFORMIO_CORE_DIR=/cache/platformio + pio_cache_base=/cache/platformio else - export PLATFORMIO_CORE_DIR=/config/.esphome/platformio + pio_cache_base=/config/.esphome/platformio fi -if [[ ! -d "${PLATFORMIO_CORE_DIR}" ]]; then - echo "Creating cache directory ${PLATFORMIO_CORE_DIR}" +if [[ ! -d "${pio_cache_base}" ]]; then + echo "Creating cache directory ${pio_cache_base}" echo "You can change this behavior by mounting a directory to the container's /cache directory." - mkdir -p "${PLATFORMIO_CORE_DIR}" + mkdir -p "${pio_cache_base}" fi +# we can't set core_dir, because the settings file is stored in `core_dir/appstate.json` +# setting `core_dir` would therefore prevent pio from accessing +export PLATFORMIO_PLATFORMS_DIR="${pio_cache_base}/platforms" +export PLATFORMIO_PACKAGES_DIR="${pio_cache_base}/packages" +export PLATFORMIO_CACHE_DIR="${pio_cache_base}/cache" + exec esphome "$@" diff --git a/docker/hassio-rootfs/etc/cont-init.d/30-dirs.sh b/docker/hassio-rootfs/etc/cont-init.d/30-dirs.sh index 301fe4db63..1073a2fa45 100644 --- a/docker/hassio-rootfs/etc/cont-init.d/30-dirs.sh +++ b/docker/hassio-rootfs/etc/cont-init.d/30-dirs.sh @@ -4,6 +4,6 @@ # This files creates all directories used by esphome # ============================================================================== -PLATFORMIO_CORE_DIR=/data/cache/platformio +pio_cache_base=/data/cache/platformio -mkdir -p "${PLATFORMIO_CORE_DIR}" +mkdir -p "${pio_cache_base}" diff --git a/docker/hassio-rootfs/etc/services.d/esphome/run b/docker/hassio-rootfs/etc/services.d/esphome/run index 6218b200bd..a0f20d63d6 100755 --- a/docker/hassio-rootfs/etc/services.d/esphome/run +++ b/docker/hassio-rootfs/etc/services.d/esphome/run @@ -22,7 +22,13 @@ if bashio::config.has_value 'relative_url'; then export ESPHOME_DASHBOARD_RELATIVE_URL=$(bashio::config 'relative_url') fi -export PLATFORMIO_CORE_DIR=/data/cache/platformio +pio_cache_base=/data/cache/platformio +# we can't set core_dir, because the settings file is stored in `core_dir/appstate.json` +# setting `core_dir` would therefore prevent pio from accessing +export PLATFORMIO_PLATFORMS_DIR="${pio_cache_base}/platforms" +export PLATFORMIO_PACKAGES_DIR="${pio_cache_base}/packages" +export PLATFORMIO_CACHE_DIR="${pio_cache_base}/cache" + export PLATFORMIO_GLOBALLIB_DIR=/piolibs bashio::log.info "Starting ESPHome dashboard..." From 888e315553dafef858996fa937db51a932fe7e84 Mon Sep 17 00:00:00 2001 From: Maurice Makaay Date: Wed, 22 Sep 2021 10:37:46 +0200 Subject: [PATCH 085/207] Fix OTA crash during reading of new bin file. (#2366) Co-authored-by: Maurice Makaay --- esphome/components/ota/ota_component.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/esphome/components/ota/ota_component.cpp b/esphome/components/ota/ota_component.cpp index 5e07bb64e7..0c13efa135 100644 --- a/esphome/components/ota/ota_component.cpp +++ b/esphome/components/ota/ota_component.cpp @@ -388,8 +388,10 @@ void OTAComponent::handle_() { size_t requested = std::min(sizeof(buf), ota_size - total); ssize_t read = this->client_->read(buf, requested); if (read == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) + if (errno == EAGAIN || errno == EWOULDBLOCK) { + delay(1); continue; + } ESP_LOGW(TAG, "Error receiving data for update, errno: %d", errno); goto error; } From 13b3412b459f6e2e124e22d1806386db0c063a9a Mon Sep 17 00:00:00 2001 From: Paul Monigatti Date: Wed, 22 Sep 2021 21:12:42 +1200 Subject: [PATCH 086/207] Fix Dallas parent not being set (#2369) --- esphome/components/dallas/sensor.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/dallas/sensor.py b/esphome/components/dallas/sensor.py index 05c2c8b90c..14ad0efa7b 100644 --- a/esphome/components/dallas/sensor.py +++ b/esphome/components/dallas/sensor.py @@ -46,5 +46,7 @@ async def to_code(config): if CONF_RESOLUTION in config: cg.add(var.set_resolution(config[CONF_RESOLUTION])) + cg.add(var.set_parent(hub)) + cg.add(hub.register_sensor(var)) await sensor.register_sensor(var, config) From 0929a0f8aa55954bf9269ee2f2dc591605a7da96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Maggioni?= Date: Wed, 22 Sep 2021 11:15:51 +0200 Subject: [PATCH 087/207] Discard senseair commands echoes & fix calibration result check (#2358) --- esphome/components/senseair/senseair.cpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/esphome/components/senseair/senseair.cpp b/esphome/components/senseair/senseair.cpp index 8fbb6f69db..610892dd9e 100644 --- a/esphome/components/senseair/senseair.cpp +++ b/esphome/components/senseair/senseair.cpp @@ -78,9 +78,12 @@ uint16_t SenseAirComponent::senseair_checksum_(uint8_t *ptr, uint8_t length) { } void SenseAirComponent::background_calibration() { + // Responses are just echoes but must be read to clear the buffer ESP_LOGD(TAG, "SenseAir Starting background calibration"); - this->senseair_write_command_(SENSEAIR_COMMAND_CLEAR_ACK_REGISTER, nullptr, 0); - this->senseair_write_command_(SENSEAIR_COMMAND_BACKGROUND_CAL, nullptr, 0); + uint8_t command_length = sizeof(SENSEAIR_COMMAND_CLEAR_ACK_REGISTER) / sizeof(SENSEAIR_COMMAND_CLEAR_ACK_REGISTER[0]); + uint8_t response[command_length]; + this->senseair_write_command_(SENSEAIR_COMMAND_CLEAR_ACK_REGISTER, response, command_length); + this->senseair_write_command_(SENSEAIR_COMMAND_BACKGROUND_CAL, response, command_length); } void SenseAirComponent::background_calibration_result() { @@ -98,18 +101,25 @@ void SenseAirComponent::background_calibration_result() { return; } - ESP_LOGD(TAG, "SenseAir Result=%s (%02x%02x%02x)", response[2] == 2 ? "OK" : "NOT_OK", response[2], response[3], - response[4]); + // Check if 5th bit (register CI6) is set + ESP_LOGD(TAG, "SenseAir Result=%s (%02x%02x%02x %02x%02x %02x%02x)", (response[4] & 0b100000) != 0 ? "OK" : "NOT_OK", + response[0], response[1], response[2], response[3], response[4], response[5], response[6]); } void SenseAirComponent::abc_enable() { + // Response is just an echo but must be read to clear the buffer ESP_LOGD(TAG, "SenseAir Enabling automatic baseline calibration"); - this->senseair_write_command_(SENSEAIR_COMMAND_ABC_ENABLE, nullptr, 0); + uint8_t command_length = sizeof(SENSEAIR_COMMAND_ABC_ENABLE) / sizeof(SENSEAIR_COMMAND_ABC_ENABLE[0]); + uint8_t response[command_length]; + this->senseair_write_command_(SENSEAIR_COMMAND_ABC_ENABLE, response, command_length); } void SenseAirComponent::abc_disable() { + // Response is just an echo but must be read to clear the buffer ESP_LOGD(TAG, "SenseAir Disabling automatic baseline calibration"); - this->senseair_write_command_(SENSEAIR_COMMAND_ABC_DISABLE, nullptr, 0); + uint8_t command_length = sizeof(SENSEAIR_COMMAND_ABC_DISABLE) / sizeof(SENSEAIR_COMMAND_ABC_DISABLE[0]); + uint8_t response[command_length]; + this->senseair_write_command_(SENSEAIR_COMMAND_ABC_DISABLE, response, command_length); } void SenseAirComponent::abc_get_period() { From ed593544d896df1a830cd7320432ddcde9ddcbec Mon Sep 17 00:00:00 2001 From: Silvio <4004968+s1lvi0@users.noreply.github.com> Date: Wed, 22 Sep 2021 12:03:42 +0200 Subject: [PATCH 088/207] Add support for Daly Smart BMS (#2156) * Add support for Daly Smart BMS * Fix clang-format and python lint * Fix const declaration * Add code owner * Fix malloc with std::vector * Fix with suggestions * Revert "Fix with suggestions" This reverts commit bc618f20cf83e3df903fdbbca8d2529d946264b0. * Fix last commit * Fix Python Lint * Fix typo * Use std::vector instead pointer and fix loop * Fix typo * Add test configuration to test3.yaml * Fix test3.yaml * Fix uart in test3.yaml --- CODEOWNERS | 1 + esphome/components/daly_bms/__init__.py | 27 +++ esphome/components/daly_bms/binary_sensor.py | 49 +++++ esphome/components/daly_bms/daly_bms.cpp | 181 +++++++++++++++++ esphome/components/daly_bms/daly_bms.h | 83 ++++++++ esphome/components/daly_bms/sensor.py | 192 +++++++++++++++++++ esphome/components/daly_bms/text_sensor.py | 39 ++++ tests/test3.yaml | 44 +++++ 8 files changed, 616 insertions(+) create mode 100644 esphome/components/daly_bms/__init__.py create mode 100644 esphome/components/daly_bms/binary_sensor.py create mode 100644 esphome/components/daly_bms/daly_bms.cpp create mode 100644 esphome/components/daly_bms/daly_bms.h create mode 100644 esphome/components/daly_bms/sensor.py create mode 100644 esphome/components/daly_bms/text_sensor.py diff --git a/CODEOWNERS b/CODEOWNERS index adf96b7382..2577e0d706 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -39,6 +39,7 @@ esphome/components/coolix/* @glmnet esphome/components/cover/* @esphome/core esphome/components/cs5460a/* @balrog-kun esphome/components/ct_clamp/* @jesserockz +esphome/components/daly_bms/* @s1lvi0 esphome/components/debug/* @OttoWinter esphome/components/dfplayer/* @glmnet esphome/components/dht/* @OttoWinter diff --git a/esphome/components/daly_bms/__init__.py b/esphome/components/daly_bms/__init__.py new file mode 100644 index 0000000000..45b8f98f0c --- /dev/null +++ b/esphome/components/daly_bms/__init__.py @@ -0,0 +1,27 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import uart +from esphome.const import CONF_ID + +CODEOWNERS = ["@s1lvi0"] +DEPENDENCIES = ["uart"] +AUTO_LOAD = ["sensor", "text_sensor", "binary_sensor"] + +CONF_BMS_DALY_ID = "bms_daly_id" + +daly_bms = cg.esphome_ns.namespace("daly_bms") +DalyBmsComponent = daly_bms.class_( + "DalyBmsComponent", cg.PollingComponent, uart.UARTDevice +) + +CONFIG_SCHEMA = ( + cv.Schema({cv.GenerateID(): cv.declare_id(DalyBmsComponent)}) + .extend(uart.UART_DEVICE_SCHEMA) + .extend(cv.polling_component_schema("30s")) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await uart.register_uart_device(var, config) diff --git a/esphome/components/daly_bms/binary_sensor.py b/esphome/components/daly_bms/binary_sensor.py new file mode 100644 index 0000000000..23330cd945 --- /dev/null +++ b/esphome/components/daly_bms/binary_sensor.py @@ -0,0 +1,49 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import binary_sensor +from esphome.const import CONF_ID +from . import DalyBmsComponent, CONF_BMS_DALY_ID + +CONF_CHARGING_MOS_ENABLED = "charging_mos_enabled" +CONF_DISCHARGING_MOS_ENABLED = "discharging_mos_enabled" + +TYPES = [ + CONF_CHARGING_MOS_ENABLED, + CONF_DISCHARGING_MOS_ENABLED, +] + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(CONF_BMS_DALY_ID): cv.use_id(DalyBmsComponent), + cv.Optional( + CONF_CHARGING_MOS_ENABLED + ): binary_sensor.BINARY_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(binary_sensor.BinarySensor), + } + ), + cv.Optional( + CONF_DISCHARGING_MOS_ENABLED + ): binary_sensor.BINARY_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(binary_sensor.BinarySensor), + } + ), + } + ).extend(cv.COMPONENT_SCHEMA) +) + + +async def setup_conf(config, key, hub): + if key in config: + conf = config[key] + sens = cg.new_Pvariable(conf[CONF_ID]) + await binary_sensor.register_binary_sensor(sens, conf) + cg.add(getattr(hub, f"set_{key}_binary_sensor")(sens)) + + +async def to_code(config): + hub = await cg.get_variable(config[CONF_BMS_DALY_ID]) + for key in TYPES: + await setup_conf(config, key, hub) diff --git a/esphome/components/daly_bms/daly_bms.cpp b/esphome/components/daly_bms/daly_bms.cpp new file mode 100644 index 0000000000..19e8f12e1c --- /dev/null +++ b/esphome/components/daly_bms/daly_bms.cpp @@ -0,0 +1,181 @@ +#include "daly_bms.h" +#include "esphome/core/log.h" +#include + +namespace esphome { +namespace daly_bms { + +static const char *const TAG = "daly_bms"; + +static const uint8_t DALY_FRAME_SIZE = 13; +static const uint8_t DALY_TEMPERATURE_OFFSET = 40; +static const uint16_t DALY_CURRENT_OFFSET = 30000; + +static const uint8_t DALY_REQUEST_BATTERY_LEVEL = 0x90; +static const uint8_t DALY_REQUEST_MIN_MAX_VOLTAGE = 0x91; +static const uint8_t DALY_REQUEST_MIN_MAX_TEMPERATURE = 0x92; +static const uint8_t DALY_REQUEST_MOS = 0x93; +static const uint8_t DALY_REQUEST_STATUS = 0x94; +static const uint8_t DALY_REQUEST_TEMPERATURE = 0x96; + +void DalyBmsComponent::setup() {} + +void DalyBmsComponent::dump_config() { + ESP_LOGCONFIG(TAG, "Daly BMS:"); + this->check_uart_settings(9600); +} + +void DalyBmsComponent::update() { + this->request_data(DALY_REQUEST_BATTERY_LEVEL); + this->request_data(DALY_REQUEST_MIN_MAX_VOLTAGE); + this->request_data(DALY_REQUEST_MIN_MAX_TEMPERATURE); + this->request_data(DALY_REQUEST_MOS); + this->request_data(DALY_REQUEST_STATUS); + this->request_data(DALY_REQUEST_TEMPERATURE); + + std::vector get_battery_level_data; + int available_data = this->available(); + if (available_data >= DALY_FRAME_SIZE) { + get_battery_level_data.resize(available_data); + this->read_array(get_battery_level_data.data(), available_data); + this->decode_data(get_battery_level_data); + } +} + +float DalyBmsComponent::get_setup_priority() const { return setup_priority::DATA; } + +void DalyBmsComponent::request_data(uint8_t data_id) { + uint8_t request_message[DALY_FRAME_SIZE]; + + request_message[0] = 0xA5; // Start Flag + request_message[1] = 0x80; // Communication Module Address + request_message[2] = data_id; // Data ID + request_message[3] = 0x08; // Data Length (Fixed) + request_message[4] = 0x00; // Empty Data + request_message[5] = 0x00; // | + request_message[6] = 0x00; // | + request_message[7] = 0x00; // | + request_message[8] = 0x00; // | + request_message[9] = 0x00; // | + request_message[10] = 0x00; // | + request_message[11] = 0x00; // Empty Data + request_message[12] = (uint8_t)(request_message[0] + request_message[1] + request_message[2] + + request_message[3]); // Checksum (Lower byte of the other bytes sum) + + this->write_array(request_message, sizeof(request_message)); + this->flush(); +} + +void DalyBmsComponent::decode_data(std::vector data) { + auto it = data.begin(); + + while ((it = std::find(it, data.end(), 0xA5)) != data.end()) { + if (data.end() - it >= DALY_FRAME_SIZE && it[1] == 0x01) { + uint8_t checksum; + int sum = 0; + for (int i = 0; i < 12; i++) { + sum += it[i]; + } + checksum = sum; + + if (checksum == it[12]) { + switch (it[2]) { + case DALY_REQUEST_BATTERY_LEVEL: + if (this->voltage_sensor_) { + this->voltage_sensor_->publish_state((float) encode_uint16(it[4], it[5]) / 10); + } + if (this->current_sensor_) { + this->current_sensor_->publish_state(((float) (encode_uint16(it[8], it[9]) - DALY_CURRENT_OFFSET) / 10)); + } + if (this->battery_level_sensor_) { + this->battery_level_sensor_->publish_state((float) encode_uint16(it[10], it[11]) / 10); + } + break; + + case DALY_REQUEST_MIN_MAX_VOLTAGE: + if (this->max_cell_voltage_) { + this->max_cell_voltage_->publish_state((float) encode_uint16(it[4], it[5]) / 1000); + } + if (this->max_cell_voltage_number_) { + this->max_cell_voltage_number_->publish_state(it[6]); + } + if (this->min_cell_voltage_) { + this->min_cell_voltage_->publish_state((float) encode_uint16(it[7], it[8]) / 1000); + } + if (this->min_cell_voltage_number_) { + this->min_cell_voltage_number_->publish_state(it[9]); + } + break; + + case DALY_REQUEST_MIN_MAX_TEMPERATURE: + if (this->max_temperature_) { + this->max_temperature_->publish_state(it[4] - DALY_TEMPERATURE_OFFSET); + } + if (this->max_temperature_probe_number_) { + this->max_temperature_probe_number_->publish_state(it[5]); + } + if (this->min_temperature_) { + this->min_temperature_->publish_state(it[6] - DALY_TEMPERATURE_OFFSET); + } + if (this->min_temperature_probe_number_) { + this->min_temperature_probe_number_->publish_state(it[7]); + } + break; + + case DALY_REQUEST_MOS: + if (this->status_text_sensor_ != nullptr) { + switch (it[4]) { + case 0: + this->status_text_sensor_->publish_state("Stationary"); + break; + case 1: + this->status_text_sensor_->publish_state("Charging"); + break; + case 2: + this->status_text_sensor_->publish_state("Discharging"); + break; + default: + break; + } + } + if (this->charging_mos_enabled_) { + this->charging_mos_enabled_->publish_state(it[5]); + } + if (this->discharging_mos_enabled_) { + this->discharging_mos_enabled_->publish_state(it[6]); + } + if (this->remaining_capacity_) { + this->remaining_capacity_->publish_state((float) encode_uint32(it[8], it[9], it[10], it[11]) / 1000); + } + break; + + case DALY_REQUEST_STATUS: + if (this->cells_number_) { + this->cells_number_->publish_state(it[4]); + } + break; + + case DALY_REQUEST_TEMPERATURE: + if (it[4] == 1) { + if (this->temperature_1_sensor_) { + this->temperature_1_sensor_->publish_state(it[5] - DALY_TEMPERATURE_OFFSET); + } + if (this->temperature_2_sensor_) { + this->temperature_2_sensor_->publish_state(it[6] - DALY_TEMPERATURE_OFFSET); + } + } + break; + + default: + break; + } + } + std::advance(it, DALY_FRAME_SIZE); + } else { + std::advance(it, 1); + } + } +} + +} // namespace daly_bms +} // namespace esphome diff --git a/esphome/components/daly_bms/daly_bms.h b/esphome/components/daly_bms/daly_bms.h new file mode 100644 index 0000000000..e4f48776dd --- /dev/null +++ b/esphome/components/daly_bms/daly_bms.h @@ -0,0 +1,83 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/text_sensor/text_sensor.h" +#include "esphome/components/binary_sensor/binary_sensor.h" +#include "esphome/components/uart/uart.h" + +namespace esphome { +namespace daly_bms { + +class DalyBmsComponent : public PollingComponent, public uart::UARTDevice { + public: + DalyBmsComponent() = default; + + // SENSORS + void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; } + void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; } + void set_battery_level_sensor(sensor::Sensor *battery_level_sensor) { battery_level_sensor_ = battery_level_sensor; } + void set_max_cell_voltage_sensor(sensor::Sensor *max_cell_voltage) { max_cell_voltage_ = max_cell_voltage; } + void set_max_cell_voltage_number_sensor(sensor::Sensor *max_cell_voltage_number) { + max_cell_voltage_number_ = max_cell_voltage_number; + } + void set_min_cell_voltage_sensor(sensor::Sensor *min_cell_voltage) { min_cell_voltage_ = min_cell_voltage; } + void set_min_cell_voltage_number_sensor(sensor::Sensor *min_cell_voltage_number) { + min_cell_voltage_number_ = min_cell_voltage_number; + } + void set_max_temperature_sensor(sensor::Sensor *max_temperature) { max_temperature_ = max_temperature; } + void set_max_temperature_probe_number_sensor(sensor::Sensor *max_temperature_probe_number) { + max_temperature_probe_number_ = max_temperature_probe_number; + } + void set_min_temperature_sensor(sensor::Sensor *min_temperature) { min_temperature_ = min_temperature; } + void set_min_temperature_probe_number_sensor(sensor::Sensor *min_temperature_probe_number) { + min_temperature_probe_number_ = min_temperature_probe_number; + } + void set_remaining_capacity_sensor(sensor::Sensor *remaining_capacity) { remaining_capacity_ = remaining_capacity; } + void set_cells_number_sensor(sensor::Sensor *cells_number) { cells_number_ = cells_number; } + void set_temperature_1_sensor(sensor::Sensor *temperature_1_sensor) { temperature_1_sensor_ = temperature_1_sensor; } + void set_temperature_2_sensor(sensor::Sensor *temperature_2_sensor) { temperature_2_sensor_ = temperature_2_sensor; } + // TEXT_SENSORS + void set_status_text_sensor(text_sensor::TextSensor *status_text_sensor) { status_text_sensor_ = status_text_sensor; } + // BINARY_SENSORS + void set_charging_mos_enabled_binary_sensor(binary_sensor::BinarySensor *charging_mos_enabled) { + charging_mos_enabled_ = charging_mos_enabled; + } + void set_discharging_mos_enabled_binary_sensor(binary_sensor::BinarySensor *discharging_mos_enabled) { + discharging_mos_enabled_ = discharging_mos_enabled; + } + + void setup() override; + void dump_config() override; + void update() override; + + float get_setup_priority() const override; + + protected: + void request_data(uint8_t data_id); + void decode_data(std::vector data); + + sensor::Sensor *voltage_sensor_{nullptr}; + sensor::Sensor *current_sensor_{nullptr}; + sensor::Sensor *battery_level_sensor_{nullptr}; + sensor::Sensor *max_cell_voltage_{nullptr}; + sensor::Sensor *max_cell_voltage_number_{nullptr}; + sensor::Sensor *min_cell_voltage_{nullptr}; + sensor::Sensor *min_cell_voltage_number_{nullptr}; + sensor::Sensor *max_temperature_{nullptr}; + sensor::Sensor *max_temperature_probe_number_{nullptr}; + sensor::Sensor *min_temperature_{nullptr}; + sensor::Sensor *min_temperature_probe_number_{nullptr}; + sensor::Sensor *remaining_capacity_{nullptr}; + sensor::Sensor *cells_number_{nullptr}; + sensor::Sensor *temperature_1_sensor_{nullptr}; + sensor::Sensor *temperature_2_sensor_{nullptr}; + + text_sensor::TextSensor *status_text_sensor_{nullptr}; + + binary_sensor::BinarySensor *charging_mos_enabled_{nullptr}; + binary_sensor::BinarySensor *discharging_mos_enabled_{nullptr}; +}; + +} // namespace daly_bms +} // namespace esphome diff --git a/esphome/components/daly_bms/sensor.py b/esphome/components/daly_bms/sensor.py new file mode 100644 index 0000000000..1d0ee89914 --- /dev/null +++ b/esphome/components/daly_bms/sensor.py @@ -0,0 +1,192 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import sensor +from esphome.const import ( + CONF_VOLTAGE, + CONF_CURRENT, + CONF_BATTERY_LEVEL, + CONF_MAX_TEMPERATURE, + CONF_MIN_TEMPERATURE, + DEVICE_CLASS_VOLTAGE, + DEVICE_CLASS_CURRENT, + DEVICE_CLASS_BATTERY, + DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_EMPTY, + STATE_CLASS_MEASUREMENT, + STATE_CLASS_NONE, + UNIT_VOLT, + UNIT_AMPERE, + UNIT_PERCENT, + UNIT_CELSIUS, + UNIT_EMPTY, + ICON_FLASH, + ICON_PERCENT, + ICON_COUNTER, + ICON_THERMOMETER, + ICON_GAUGE, +) +from . import DalyBmsComponent, CONF_BMS_DALY_ID + +CONF_MAX_CELL_VOLTAGE = "max_cell_voltage" +CONF_MAX_CELL_VOLTAGE_NUMBER = "max_cell_voltage_number" +CONF_MIN_CELL_VOLTAGE = "min_cell_voltage" +CONF_MIN_CELL_VOLTAGE_NUMBER = "min_cell_voltage_number" +CONF_MAX_TEMPERATURE_PROBE_NUMBER = "max_temperature_probe_number" +CONF_MIN_TEMPERATURE_PROBE_NUMBER = "min_temperature_probe_number" +CONF_CELLS_NUMBER = "cells_number" + +CONF_REMAINING_CAPACITY = "remaining_capacity" +CONF_TEMPERATURE_1 = "temperature_1" +CONF_TEMPERATURE_2 = "temperature_2" + +ICON_CURRENT_DC = "mdi:current-dc" +ICON_BATTERY_OUTLINE = "mdi:battery-outline" +ICON_THERMOMETER_CHEVRON_UP = "mdi:thermometer-chevron-up" +ICON_THERMOMETER_CHEVRON_DOWN = "mdi:thermometer-chevron-down" +ICON_CAR_BATTERY = "mdi:car-battery" + +UNIT_AMPERE_HOUR = "Ah" + +TYPES = [ + CONF_VOLTAGE, + CONF_CURRENT, + CONF_BATTERY_LEVEL, + CONF_MAX_CELL_VOLTAGE, + CONF_MAX_CELL_VOLTAGE_NUMBER, + CONF_MIN_CELL_VOLTAGE, + CONF_MIN_CELL_VOLTAGE_NUMBER, + CONF_MAX_TEMPERATURE, + CONF_MAX_TEMPERATURE_PROBE_NUMBER, + CONF_MIN_TEMPERATURE, + CONF_MIN_TEMPERATURE_PROBE_NUMBER, + CONF_CELLS_NUMBER, + CONF_REMAINING_CAPACITY, + CONF_TEMPERATURE_1, + CONF_TEMPERATURE_2, +] + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(CONF_BMS_DALY_ID): cv.use_id(DalyBmsComponent), + cv.Optional(CONF_VOLTAGE): sensor.sensor_schema( + UNIT_VOLT, + ICON_FLASH, + 1, + DEVICE_CLASS_VOLTAGE, + STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_CURRENT): sensor.sensor_schema( + UNIT_AMPERE, + ICON_CURRENT_DC, + 1, + DEVICE_CLASS_CURRENT, + STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema( + UNIT_PERCENT, + ICON_PERCENT, + 1, + DEVICE_CLASS_BATTERY, + STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_MAX_CELL_VOLTAGE): sensor.sensor_schema( + UNIT_VOLT, + ICON_FLASH, + 2, + DEVICE_CLASS_VOLTAGE, + STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_MAX_CELL_VOLTAGE_NUMBER): sensor.sensor_schema( + UNIT_EMPTY, + ICON_COUNTER, + 0, + DEVICE_CLASS_EMPTY, + STATE_CLASS_NONE, + ), + cv.Optional(CONF_MIN_CELL_VOLTAGE): sensor.sensor_schema( + UNIT_VOLT, + ICON_FLASH, + 2, + DEVICE_CLASS_VOLTAGE, + STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_MIN_CELL_VOLTAGE_NUMBER): sensor.sensor_schema( + UNIT_EMPTY, + ICON_COUNTER, + 0, + DEVICE_CLASS_EMPTY, + STATE_CLASS_NONE, + ), + cv.Optional(CONF_MAX_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, + ICON_THERMOMETER_CHEVRON_UP, + 0, + DEVICE_CLASS_TEMPERATURE, + STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_MAX_TEMPERATURE_PROBE_NUMBER): sensor.sensor_schema( + UNIT_EMPTY, + ICON_COUNTER, + 0, + DEVICE_CLASS_EMPTY, + STATE_CLASS_NONE, + ), + cv.Optional(CONF_MIN_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, + ICON_THERMOMETER_CHEVRON_DOWN, + 0, + DEVICE_CLASS_TEMPERATURE, + STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_MIN_TEMPERATURE_PROBE_NUMBER): sensor.sensor_schema( + UNIT_EMPTY, + ICON_COUNTER, + 0, + DEVICE_CLASS_EMPTY, + STATE_CLASS_NONE, + ), + cv.Optional(CONF_REMAINING_CAPACITY): sensor.sensor_schema( + UNIT_AMPERE_HOUR, + ICON_GAUGE, + 2, + DEVICE_CLASS_VOLTAGE, + STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_CELLS_NUMBER): sensor.sensor_schema( + UNIT_EMPTY, + ICON_COUNTER, + 0, + DEVICE_CLASS_EMPTY, + STATE_CLASS_NONE, + ), + cv.Optional(CONF_TEMPERATURE_1): sensor.sensor_schema( + UNIT_CELSIUS, + ICON_THERMOMETER, + 0, + DEVICE_CLASS_TEMPERATURE, + STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_TEMPERATURE_2): sensor.sensor_schema( + UNIT_CELSIUS, + ICON_THERMOMETER, + 0, + DEVICE_CLASS_TEMPERATURE, + STATE_CLASS_MEASUREMENT, + ), + } + ).extend(cv.COMPONENT_SCHEMA) +) + + +async def setup_conf(config, key, hub): + if key in config: + conf = config[key] + sens = await sensor.new_sensor(conf) + cg.add(getattr(hub, f"set_{key}_sensor")(sens)) + + +async def to_code(config): + hub = await cg.get_variable(config[CONF_BMS_DALY_ID]) + for key in TYPES: + await setup_conf(config, key, hub) diff --git a/esphome/components/daly_bms/text_sensor.py b/esphome/components/daly_bms/text_sensor.py new file mode 100644 index 0000000000..de49a0b4b9 --- /dev/null +++ b/esphome/components/daly_bms/text_sensor.py @@ -0,0 +1,39 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import text_sensor +from esphome.const import CONF_ICON, CONF_ID, CONF_STATUS +from . import DalyBmsComponent, CONF_BMS_DALY_ID + +ICON_CAR_BATTERY = "mdi:car-battery" + +TYPES = [ + CONF_STATUS, +] + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(CONF_BMS_DALY_ID): cv.use_id(DalyBmsComponent), + cv.Optional(CONF_STATUS): text_sensor.TEXT_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(text_sensor.TextSensor), + cv.Optional(CONF_ICON, default=ICON_CAR_BATTERY): cv.icon, + } + ), + } + ).extend(cv.COMPONENT_SCHEMA) +) + + +async def setup_conf(config, key, hub): + if key in config: + conf = config[key] + sens = cg.new_Pvariable(conf[CONF_ID]) + await text_sensor.register_text_sensor(sens, conf) + cg.add(getattr(hub, f"set_{key}_text_sensor")(sens)) + + +async def to_code(config): + hub = await cg.get_variable(config[CONF_BMS_DALY_ID]) + for key in TYPES: + await setup_conf(config, key, hub) diff --git a/tests/test3.yaml b/tests/test3.yaml index de46cec99c..386775749d 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -273,6 +273,37 @@ adalight: sensor: + - platform: daly_bms + voltage: + name: "Battery Voltage" + current: + name: "Battery Current" + battery_level: + name: "Battery Level" + max_cell_voltage: + name: "Max Cell Voltage" + max_cell_voltage_number: + name: "Max Cell Voltage Number" + min_cell_voltage: + name: "Min Cell Voltage" + min_cell_voltage_number: + name: "Min Cell Voltage Number" + max_temperature: + name: "Max Temperature" + max_temperature_probe_number: + name: "Max Temperature Probe Number" + min_temperature: + name: "Min Temperature" + min_temperature_probe_number: + name: "Min Temperature Probe Number" + remaining_capacity: + name: "Remaining Capacity" + cells_number: + name: "Cells Number" + temperature_1: + name: "Temperature 1" + temperature_2: + name: "Temperature 2" - platform: apds9960 type: proximity name: APDS9960 Proximity @@ -621,6 +652,11 @@ mpr121: address: 0x5A binary_sensor: + - platform: daly_bms + charging_mos_enabled: + name: "Charging MOS" + discharging_mos_enabled: + name: "Discharging MOS" - platform: apds9960 direction: up name: APDS9960 Up @@ -701,6 +737,9 @@ status_led: pin: GPIO2 text_sensor: + - platform: daly_bms + status: + name: "BMS Status" - platform: version name: 'ESPHome Version' icon: mdi:icon @@ -1219,3 +1258,8 @@ fingerprint_grow: dsmr: decryption_key: 00112233445566778899aabbccddeeff uart_id: uart6 + +daly_bms: + update_interval: 20s + uart_id: uart1 + From f1364d4af4a72826a79f7427ec17449f5f9061a5 Mon Sep 17 00:00:00 2001 From: Robert Resch Date: Wed, 22 Sep 2021 12:12:55 +0200 Subject: [PATCH 089/207] Combine code of xiaomi_miscale and xiaomi_miscale2 (#2266) * Combine xiaomi_miscale and xiaomi_miscale2 * check if message contains impedance * auto detect scale version * remove xiaomi_miscale2 * fix lint errors * Apply suggestions from code review Co-authored-by: Oxan van Leeuwen * Apply suggestions from code review on old code * Fix clang-tidy warnings Co-authored-by: Oxan van Leeuwen --- .../esp32_ble_tracker/esp32_ble_tracker.cpp | 4 +- .../esp32_ble_tracker/esp32_ble_tracker.h | 4 +- esphome/components/xiaomi_miscale/sensor.py | 12 ++ .../xiaomi_miscale/xiaomi_miscale.cpp | 85 +++++++++++-- .../xiaomi_miscale/xiaomi_miscale.h | 6 + esphome/components/xiaomi_miscale2/sensor.py | 60 +--------- .../xiaomi_miscale2/xiaomi_miscale2.cpp | 112 ------------------ .../xiaomi_miscale2/xiaomi_miscale2.h | 40 ------- 8 files changed, 98 insertions(+), 225 deletions(-) delete mode 100644 esphome/components/xiaomi_miscale2/xiaomi_miscale2.cpp delete mode 100644 esphome/components/xiaomi_miscale2/xiaomi_miscale2.h diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index 5568884b9a..3ca250d52d 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -380,8 +380,8 @@ bool ESPBTUUID::operator==(const ESPBTUUID &uuid) const { } return false; } -esp_bt_uuid_t ESPBTUUID::get_uuid() { return this->uuid_; } -std::string ESPBTUUID::to_string() { +esp_bt_uuid_t ESPBTUUID::get_uuid() const { return this->uuid_; } +std::string ESPBTUUID::to_string() const { char sbuf[64]; switch (this->uuid_.len) { case ESP_UUID_LEN_16: diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index 40955d39cf..fc5498f91e 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -34,9 +34,9 @@ class ESPBTUUID { bool operator==(const ESPBTUUID &uuid) const; bool operator!=(const ESPBTUUID &uuid) const { return !(*this == uuid); } - esp_bt_uuid_t get_uuid(); + esp_bt_uuid_t get_uuid() const; - std::string to_string(); + std::string to_string() const; protected: esp_bt_uuid_t uuid_; diff --git a/esphome/components/xiaomi_miscale/sensor.py b/esphome/components/xiaomi_miscale/sensor.py index 3a112dfa34..517870cc01 100644 --- a/esphome/components/xiaomi_miscale/sensor.py +++ b/esphome/components/xiaomi_miscale/sensor.py @@ -8,6 +8,9 @@ from esphome.const import ( STATE_CLASS_MEASUREMENT, UNIT_KILOGRAM, ICON_SCALE_BATHROOM, + UNIT_OHM, + CONF_IMPEDANCE, + ICON_OMEGA, ) DEPENDENCIES = ["esp32_ble_tracker"] @@ -28,6 +31,12 @@ CONFIG_SCHEMA = ( accuracy_decimals=2, state_class=STATE_CLASS_MEASUREMENT, ), + cv.Optional(CONF_IMPEDANCE): sensor.sensor_schema( + unit_of_measurement=UNIT_OHM, + icon=ICON_OMEGA, + accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, + ), } ) .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) @@ -45,3 +54,6 @@ async def to_code(config): if CONF_WEIGHT in config: sens = await sensor.new_sensor(config[CONF_WEIGHT]) cg.add(var.set_weight(sens)) + if CONF_IMPEDANCE in config: + sens = await sensor.new_sensor(config[CONF_IMPEDANCE]) + cg.add(var.set_impedance(sens)) diff --git a/esphome/components/xiaomi_miscale/xiaomi_miscale.cpp b/esphome/components/xiaomi_miscale/xiaomi_miscale.cpp index 62378de72c..4587045136 100644 --- a/esphome/components/xiaomi_miscale/xiaomi_miscale.cpp +++ b/esphome/components/xiaomi_miscale/xiaomi_miscale.cpp @@ -11,6 +11,7 @@ static const char *const TAG = "xiaomi_miscale"; void XiaomiMiscale::dump_config() { ESP_LOGCONFIG(TAG, "Xiaomi Miscale"); LOG_SENSOR(" ", "Weight", this->weight_); + LOG_SENSOR(" ", "Impedance", this->impedance_); } bool XiaomiMiscale::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { @@ -26,14 +27,22 @@ bool XiaomiMiscale::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { if (!res.has_value()) { continue; } + if (!(parse_message(service_data.data, *res))) { continue; } + if (!(report_results(res, device.address_str()))) { continue; } + if (res->weight.has_value() && this->weight_ != nullptr) this->weight_->publish_state(*res->weight); + + if (res->version == 1 && this->impedance_ != nullptr) { + ESP_LOGW(TAG, "Impedance is only supported on version 2. Your scale was identified as verison 1."); + } else if (res->impedance.has_value() && this->impedance_ != nullptr) + this->impedance_->publish_state(*res->impedance); success = true; } @@ -42,8 +51,14 @@ bool XiaomiMiscale::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { optional XiaomiMiscale::parse_header(const esp32_ble_tracker::ServiceData &service_data) { ParseResult result; - if (!service_data.uuid.contains(0x1D, 0x18)) { - ESP_LOGVV(TAG, "parse_header(): no service data UUID magic bytes."); + if (service_data.uuid == esp32_ble_tracker::ESPBTUUID::from_uint16(0x181D) && service_data.data.size() == 10) { + result.version = 1; + } else if (service_data.uuid == esp32_ble_tracker::ESPBTUUID::from_uint16(0x181B) && service_data.data.size() == 13) { + result.version = 2; + } else { + ESP_LOGVV(TAG, + "parse_header(): Couldn't identify scale version or data size was not correct. UUID: %s, data_size: %d", + service_data.uuid.to_string().c_str(), service_data.data.size()); return {}; } @@ -51,7 +66,15 @@ optional XiaomiMiscale::parse_header(const esp32_ble_tracker::Servi } bool XiaomiMiscale::parse_message(const std::vector &message, ParseResult &result) { - // example 1d18 a2 6036 e307 07 11 0f1f11 + if (result.version == 1) { + return parse_message_V1(message, result); + } else { + return parse_message_V2(message, result); + } +} + +bool XiaomiMiscale::parse_message_V1(const std::vector &message, ParseResult &result) { + // message size is checked in parse_header // 1-2 Weight (MISCALE 181D) // 3-4 Years (MISCALE 181D) // 5 month (MISCALE 181D) @@ -61,21 +84,56 @@ bool XiaomiMiscale::parse_message(const std::vector &message, ParseResu // 9 second (MISCALE 181D) const uint8_t *data = message.data(); - const int data_length = 10; - - if (message.size() != data_length) { - ESP_LOGVV(TAG, "parse_message(): payload has wrong size (%d)!", message.size()); - return false; - } // weight, 2 bytes, 16-bit unsigned integer, 1 kg const int16_t weight = uint16_t(data[1]) | (uint16_t(data[2]) << 8); if (data[0] == 0x22 || data[0] == 0xa2) result.weight = weight * 0.01f / 2.0f; // unit 'kg' else if (data[0] == 0x12 || data[0] == 0xb2) - result.weight = weight * 0.01f * 0.6; // unit 'jin' + result.weight = weight * 0.01f * 0.6f; // unit 'jin' else if (data[0] == 0x03 || data[0] == 0xb3) - result.weight = weight * 0.01f * 0.453592; // unit 'lbs' + result.weight = weight * 0.01f * 0.453592f; // unit 'lbs' + + return true; +} + +bool XiaomiMiscale::parse_message_V2(const std::vector &message, ParseResult &result) { + // message size is checked in parse_header + // 2-3 Years (MISCALE 2 181B) + // 4 month (MISCALE 2 181B) + // 5 day (MISCALE 2 181B) + // 6 hour (MISCALE 2 181B) + // 7 minute (MISCALE 2 181B) + // 8 second (MISCALE 2 181B) + // 9-10 impedance (MISCALE 2 181B) + // 11-12 weight (MISCALE 2 181B) + + const uint8_t *data = message.data(); + + bool has_impedance = ((data[1] & (1 << 1)) != 0); + bool is_stabilized = ((data[1] & (1 << 5)) != 0); + bool load_removed = ((data[1] & (1 << 7)) != 0); + + if (!is_stabilized || load_removed) { + return false; + } + + // weight, 2 bytes, 16-bit unsigned integer, 1 kg + const int16_t weight = uint16_t(data[11]) | (uint16_t(data[12]) << 8); + if (data[0] == 0x02) + result.weight = weight * 0.01f / 2.0f; // unit 'kg' + else if (data[0] == 0x03) + result.weight = weight * 0.01f * 0.453592f; // unit 'lbs' + + if (has_impedance) { + // impedance, 2 bytes, 16-bit + const int16_t impedance = uint16_t(data[9]) | (uint16_t(data[10]) << 8); + result.impedance = impedance; + + if (impedance == 0 || impedance >= 3000) { + return false; + } + } return true; } @@ -86,11 +144,14 @@ bool XiaomiMiscale::report_results(const optional &result, const st return false; } - ESP_LOGD(TAG, "Got Xiaomi Miscale (%s):", address.c_str()); + ESP_LOGD(TAG, "Got Xiaomi Miscale v%d (%s):", result->version, address.c_str()); if (result->weight.has_value()) { ESP_LOGD(TAG, " Weight: %.2fkg", *result->weight); } + if (result->impedance.has_value()) { + ESP_LOGD(TAG, " Impedance: %.0fohm", *result->impedance); + } return true; } diff --git a/esphome/components/xiaomi_miscale/xiaomi_miscale.h b/esphome/components/xiaomi_miscale/xiaomi_miscale.h index 409fdeaf93..3c958afc03 100644 --- a/esphome/components/xiaomi_miscale/xiaomi_miscale.h +++ b/esphome/components/xiaomi_miscale/xiaomi_miscale.h @@ -10,7 +10,9 @@ namespace esphome { namespace xiaomi_miscale { struct ParseResult { + int version; optional weight; + optional impedance; }; class XiaomiMiscale : public Component, public esp32_ble_tracker::ESPBTDeviceListener { @@ -21,13 +23,17 @@ class XiaomiMiscale : public Component, public esp32_ble_tracker::ESPBTDeviceLis void dump_config() override; float get_setup_priority() const override { return setup_priority::DATA; } void set_weight(sensor::Sensor *weight) { weight_ = weight; } + void set_impedance(sensor::Sensor *impedance) { impedance_ = impedance; } protected: uint64_t address_; sensor::Sensor *weight_{nullptr}; + sensor::Sensor *impedance_{nullptr}; optional parse_header(const esp32_ble_tracker::ServiceData &service_data); bool parse_message(const std::vector &message, ParseResult &result); + bool parse_message_V1(const std::vector &message, ParseResult &result); + bool parse_message_V2(const std::vector &message, ParseResult &result); bool report_results(const optional &result, const std::string &address); }; diff --git a/esphome/components/xiaomi_miscale2/sensor.py b/esphome/components/xiaomi_miscale2/sensor.py index 7cc5984c62..de04e8171e 100644 --- a/esphome/components/xiaomi_miscale2/sensor.py +++ b/esphome/components/xiaomi_miscale2/sensor.py @@ -1,59 +1,5 @@ -import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components import sensor, esp32_ble_tracker -from esphome.const import ( - CONF_MAC_ADDRESS, - CONF_ID, - CONF_WEIGHT, - STATE_CLASS_MEASUREMENT, - UNIT_KILOGRAM, - ICON_SCALE_BATHROOM, - UNIT_OHM, - CONF_IMPEDANCE, - ICON_OMEGA, + +CONFIG_SCHEMA = cv.invalid( + "This platform has been combined into xiaomi_miscale. Use xiaomi_miscale instead." ) - -DEPENDENCIES = ["esp32_ble_tracker"] - -xiaomi_miscale2_ns = cg.esphome_ns.namespace("xiaomi_miscale2") -XiaomiMiscale2 = xiaomi_miscale2_ns.class_( - "XiaomiMiscale2", esp32_ble_tracker.ESPBTDeviceListener, cg.Component -) - -CONFIG_SCHEMA = ( - cv.Schema( - { - cv.GenerateID(): cv.declare_id(XiaomiMiscale2), - cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_WEIGHT): sensor.sensor_schema( - unit_of_measurement=UNIT_KILOGRAM, - icon=ICON_SCALE_BATHROOM, - accuracy_decimals=2, - state_class=STATE_CLASS_MEASUREMENT, - ), - cv.Optional(CONF_IMPEDANCE): sensor.sensor_schema( - unit_of_measurement=UNIT_OHM, - icon=ICON_OMEGA, - accuracy_decimals=0, - state_class=STATE_CLASS_MEASUREMENT, - ), - } - ) - .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) - .extend(cv.COMPONENT_SCHEMA) -) - - -async def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) - await cg.register_component(var, config) - await esp32_ble_tracker.register_ble_device(var, config) - - cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex)) - - if CONF_WEIGHT in config: - sens = await sensor.new_sensor(config[CONF_WEIGHT]) - cg.add(var.set_weight(sens)) - if CONF_IMPEDANCE in config: - sens = await sensor.new_sensor(config[CONF_IMPEDANCE]) - cg.add(var.set_impedance(sens)) diff --git a/esphome/components/xiaomi_miscale2/xiaomi_miscale2.cpp b/esphome/components/xiaomi_miscale2/xiaomi_miscale2.cpp deleted file mode 100644 index 9ae95c5f97..0000000000 --- a/esphome/components/xiaomi_miscale2/xiaomi_miscale2.cpp +++ /dev/null @@ -1,112 +0,0 @@ -#include "xiaomi_miscale2.h" -#include "esphome/core/log.h" - -#ifdef USE_ESP32 - -namespace esphome { -namespace xiaomi_miscale2 { - -static const char *const TAG = "xiaomi_miscale2"; - -void XiaomiMiscale2::dump_config() { - ESP_LOGCONFIG(TAG, "Xiaomi Miscale2"); - LOG_SENSOR(" ", "Weight", this->weight_); - LOG_SENSOR(" ", "Impedance", this->impedance_); -} - -bool XiaomiMiscale2::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { - if (device.address_uint64() != this->address_) { - ESP_LOGVV(TAG, "parse_device(): unknown MAC address."); - return false; - } - ESP_LOGVV(TAG, "parse_device(): MAC address %s found.", device.address_str().c_str()); - - bool success = false; - for (auto &service_data : device.get_service_datas()) { - auto res = parse_header(service_data); - if (!res.has_value()) { - continue; - } - if (!(parse_message(service_data.data, *res))) { - continue; - } - if (!(report_results(res, device.address_str()))) { - continue; - } - if (res->weight.has_value() && this->weight_ != nullptr) - this->weight_->publish_state(*res->weight); - if (res->impedance.has_value() && this->impedance_ != nullptr) - this->impedance_->publish_state(*res->impedance); - success = true; - } - - return success; -} - -optional XiaomiMiscale2::parse_header(const esp32_ble_tracker::ServiceData &service_data) { - ParseResult result; - if (!service_data.uuid.contains(0x1B, 0x18)) { - ESP_LOGVV(TAG, "parse_header(): no service data UUID magic bytes."); - return {}; - } - - return result; -} - -bool XiaomiMiscale2::parse_message(const std::vector &message, ParseResult &result) { - // 2-3 Years (MISCALE 2 181B) - // 4 month (MISCALE 2 181B) - // 5 day (MISCALE 2 181B) - // 6 hour (MISCALE 2 181B) - // 7 minute (MISCALE 2 181B) - // 8 second (MISCALE 2 181B) - // 9-10 impedance (MISCALE 2 181B) - // 11-12 weight (MISCALE 2 181B) - - const uint8_t *data = message.data(); - const int data_length = 13; - - if (message.size() != data_length) { - ESP_LOGVV(TAG, "parse_message(): payload has wrong size (%d)!", message.size()); - return false; - } - - bool is_stabilized = ((data[1] & (1 << 5)) != 0); - bool load_removed = ((data[1] & (1 << 7)) != 0); - - // weight, 2 bytes, 16-bit unsigned integer, 1 kg - const int16_t weight = uint16_t(data[11]) | (uint16_t(data[12]) << 8); - if (data[0] == 0x02) - result.weight = weight * 0.01f / 2.0f; // unit 'kg' - else if (data[0] == 0x03) - result.weight = weight * 0.01f * 0.453592; // unit 'lbs' - - // impedance, 2 bytes, 16-bit - const int16_t impedance = uint16_t(data[9]) | (uint16_t(data[10]) << 8); - result.impedance = impedance; - - return is_stabilized && !load_removed && impedance != 0 && impedance < 3000; -} - -bool XiaomiMiscale2::report_results(const optional &result, const std::string &address) { - if (!result.has_value()) { - ESP_LOGVV(TAG, "report_results(): no results available."); - return false; - } - - ESP_LOGD(TAG, "Got Xiaomi Miscale2 (%s):", address.c_str()); - - if (result->weight.has_value()) { - ESP_LOGD(TAG, " Weight: %.2fkg", *result->weight); - } - if (result->impedance.has_value()) { - ESP_LOGD(TAG, " Impedance: %.0fohm", *result->impedance); - } - - return true; -} - -} // namespace xiaomi_miscale2 -} // namespace esphome - -#endif diff --git a/esphome/components/xiaomi_miscale2/xiaomi_miscale2.h b/esphome/components/xiaomi_miscale2/xiaomi_miscale2.h deleted file mode 100644 index 9f7ebf6a1f..0000000000 --- a/esphome/components/xiaomi_miscale2/xiaomi_miscale2.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include "esphome/core/component.h" -#include "esphome/components/sensor/sensor.h" -#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" - -#ifdef USE_ESP32 - -namespace esphome { -namespace xiaomi_miscale2 { - -struct ParseResult { - optional weight; - optional impedance; -}; - -class XiaomiMiscale2 : public Component, public esp32_ble_tracker::ESPBTDeviceListener { - public: - void set_address(uint64_t address) { address_ = address; }; - - bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; - void dump_config() override; - float get_setup_priority() const override { return setup_priority::DATA; } - void set_weight(sensor::Sensor *weight) { weight_ = weight; } - void set_impedance(sensor::Sensor *impedance) { impedance_ = impedance; } - - protected: - uint64_t address_; - sensor::Sensor *weight_{nullptr}; - sensor::Sensor *impedance_{nullptr}; - - optional parse_header(const esp32_ble_tracker::ServiceData &service_data); - bool parse_message(const std::vector &message, ParseResult &result); - bool report_results(const optional &result, const std::string &address); -}; - -} // namespace xiaomi_miscale2 -} // namespace esphome - -#endif From 9fe7b0887416dd274f4acffa5b8fdfef720e01c9 Mon Sep 17 00:00:00 2001 From: Tommy van der Vorst Date: Wed, 22 Sep 2021 12:39:41 +0200 Subject: [PATCH 090/207] Add support for Waveshare 7.5 inch (C) bichromatic display (black-and-white only for now) (#1844) * Add support for Waveshare 7.5 inch (B) bichromatic display (black-and-white only for now) * Use drawing commands specific to bichromatic displays * Fix inaccurate comment * Fix merge error * Formatting Co-authored-by: Oxan van Leeuwen --- .../components/waveshare_epaper/display.py | 4 + .../waveshare_epaper/waveshare_epaper.cpp | 96 +++++++++++++++++++ .../waveshare_epaper/waveshare_epaper.h | 23 +++++ 3 files changed, 123 insertions(+) diff --git a/esphome/components/waveshare_epaper/display.py b/esphome/components/waveshare_epaper/display.py index e825456c36..64f5597a65 100644 --- a/esphome/components/waveshare_epaper/display.py +++ b/esphome/components/waveshare_epaper/display.py @@ -40,6 +40,9 @@ WaveshareEPaper5P8In = waveshare_epaper_ns.class_( WaveshareEPaper7P5In = waveshare_epaper_ns.class_( "WaveshareEPaper7P5In", WaveshareEPaper ) +WaveshareEPaper7P5InBC = waveshare_epaper_ns.class_( + "WaveshareEPaper7P5InBC", WaveshareEPaper +) WaveshareEPaper7P5InV2 = waveshare_epaper_ns.class_( "WaveshareEPaper7P5InV2", WaveshareEPaper ) @@ -66,6 +69,7 @@ MODELS = { "4.20in-bv2": ("b", WaveshareEPaper4P2InBV2), "5.83in": ("b", WaveshareEPaper5P8In), "7.50in": ("b", WaveshareEPaper7P5In), + "7.50in-bc": ("b", WaveshareEPaper7P5InBC), "7.50inv2": ("b", WaveshareEPaper7P5InV2), "2.13in-ttgo-dke": ("c", WaveshareEPaper2P13InDKE), } diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index c32e7d27a0..92fa289cfa 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -1081,6 +1081,102 @@ void WaveshareEPaper7P5InV2::dump_config() { LOG_UPDATE_INTERVAL(this); } +/* 7.50in-bc */ +void WaveshareEPaper7P5InBC::initialize() { + /* The command sequence is similar to the 7P5In display but differs in subtle ways + to allow for faster updates. */ + // COMMAND POWER SETTING + this->command(0x01); + this->data(0x37); + this->data(0x00); + + // COMMAND PANEL SETTING + this->command(0x00); + this->data(0xCF); + this->data(0x08); + + // COMMAND PLL CONTROL + this->command(0x30); + this->data(0x3A); + + // COMMAND VCM_DC_SETTING: all temperature range + this->command(0x82); + this->data(0x28); + + // COMMAND BOOSTER SOFT START + this->command(0x06); + this->data(0xC7); + this->data(0xCC); + this->data(0x15); + + // COMMAND VCOM AND DATA INTERVAL SETTING + this->command(0x50); + this->data(0x77); + + // COMMAND TCON SETTING + this->command(0x60); + this->data(0x22); + + // COMMAND FLASH CONTROL + this->command(0x65); + this->data(0x00); + + // COMMAND RESOLUTION SETTING + this->command(0x61); + this->data(0x02); // 640 >> 8 + this->data(0x80); + this->data(0x01); // 384 >> 8 + this->data(0x80); + + // COMMAND FLASH MODE + this->command(0xE5); + this->data(0x03); +} + +void HOT WaveshareEPaper7P5InBC::display() { + // COMMAND DATA START TRANSMISSION 1 + this->command(0x10); + this->start_data_(); + + for (size_t i = 0; i < this->get_buffer_length_(); i++) { + // A line of eight source pixels (each a bit in this byte) + uint8_t eight_pixels = this->buffer_[i]; + + for (uint8_t j = 0; j < 8; j += 2) { + /* For bichromatic displays, each byte represents two pixels. Each nibble encodes a pixel: 0=white, 3=black, + 4=color. Therefore, e.g. 0x44 = two adjacent color pixels, 0x33 is two adjacent black pixels, etc. If you want + to draw using the color pixels, change '0x30' with '0x40' and '0x03' with '0x04' below. */ + uint8_t left_nibble = (eight_pixels & 0x80) ? 0x30 : 0x00; + eight_pixels <<= 1; + uint8_t right_nibble = (eight_pixels & 0x80) ? 0x03 : 0x00; + eight_pixels <<= 1; + this->write_byte(left_nibble | right_nibble); + } + App.feed_wdt(); + } + this->end_data_(); + + // Unlike the 7P5In display, we send the "power on" command here rather than during initialization + // COMMAND POWER ON + this->command(0x04); + + // COMMAND DISPLAY REFRESH + this->command(0x12); +} + +int WaveshareEPaper7P5InBC::get_width_internal() { return 640; } + +int WaveshareEPaper7P5InBC::get_height_internal() { return 384; } + +void WaveshareEPaper7P5InBC::dump_config() { + LOG_DISPLAY("", "Waveshare E-Paper", this); + ESP_LOGCONFIG(TAG, " Model: 7.5in-bc"); + LOG_PIN(" Reset Pin: ", this->reset_pin_); + LOG_PIN(" DC Pin: ", this->dc_pin_); + LOG_PIN(" Busy Pin: ", this->busy_pin_); + LOG_UPDATE_INTERVAL(this); +} + static const uint8_t LUT_SIZE_TTGO_DKE_PART = 153; static const uint8_t PART_UPDATE_LUT_TTGO_DKE[LUT_SIZE_TTGO_DKE_PART] = { diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.h b/esphome/components/waveshare_epaper/waveshare_epaper.h index f7603c5af0..b50596643d 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.h +++ b/esphome/components/waveshare_epaper/waveshare_epaper.h @@ -278,6 +278,29 @@ class WaveshareEPaper7P5In : public WaveshareEPaper { int get_height_internal() override; }; +class WaveshareEPaper7P5InBC : public WaveshareEPaper { + public: + void initialize() override; + + void display() override; + + void dump_config() override; + + void deep_sleep() override { + // COMMAND POWER OFF + this->command(0x02); + this->wait_until_idle_(); + // COMMAND DEEP SLEEP + this->command(0x07); + this->data(0xA5); // check byte + } + + protected: + int get_width_internal() override; + + int get_height_internal() override; +}; + class WaveshareEPaper7P5InV2 : public WaveshareEPaper { public: void initialize() override; From 8e36e1b92e95a478888a21507def2eaee4bbfc9c Mon Sep 17 00:00:00 2001 From: Stanislav Meduna Date: Wed, 22 Sep 2021 12:43:17 +0200 Subject: [PATCH 091/207] ili9341: use larger SPI transfers (#1628) The original version uses write_byte to tranfer every byte of the display buffer which is quite extensive as every byte needs to be waited for in the SPI driver. This patch prepares transfers in 64-byte chunks. The result is a visible faster redraw of the display. Co-authored-by: Otto winter --- .../components/ili9341/ili9341_display.cpp | 55 +++++++++++++++---- esphome/components/ili9341/ili9341_display.h | 4 ++ 2 files changed, 49 insertions(+), 10 deletions(-) diff --git a/esphome/components/ili9341/ili9341_display.cpp b/esphome/components/ili9341/ili9341_display.cpp index b36d05c864..88f8bac272 100644 --- a/esphome/components/ili9341/ili9341_display.cpp +++ b/esphome/components/ili9341/ili9341_display.cpp @@ -93,12 +93,14 @@ void ILI9341Display::display_() { this->start_data_(); uint32_t start_pos = ((this->y_low_ * this->width_) + x_low_); for (uint16_t row = 0; row < h; row++) { - for (uint16_t col = 0; col < w; col++) { - uint32_t pos = start_pos + (row * width_) + col; + uint32_t pos = start_pos + (row * width_); + uint32_t rem = w; - uint16_t color = convert_to_16bit_color_(buffer_[pos]); - this->write_byte(color >> 8); - this->write_byte(color); + while (rem > 0) { + uint32_t sz = buffer_to_transfer_(pos, rem); + this->write_array(transfer_buffer_, 2 * sz); + pos += sz; + rem -= sz; } } this->end_data_(); @@ -140,16 +142,32 @@ void ILI9341Display::fill(Color color) { } void ILI9341Display::fill_internal_(Color color) { + if (color.raw_32 == COLOR_BLACK.raw_32) { + memset(transfer_buffer_, 0, sizeof(transfer_buffer_)); + } else { + uint8_t *dst = transfer_buffer_; + auto color565 = display::ColorUtil::color_to_565(color); + + while (dst < transfer_buffer_ + sizeof(transfer_buffer_)) { + *dst++ = (uint8_t)(color565 >> 8); + *dst++ = (uint8_t) color565; + } + } + + uint32_t rem = this->get_width_internal() * this->get_height_internal(); + this->set_addr_window_(0, 0, this->get_width_internal(), this->get_height_internal()); this->start_data_(); - auto color565 = display::ColorUtil::color_to_565(color); - for (uint32_t i = 0; i < (this->get_width_internal()) * (this->get_height_internal()); i++) { - this->write_byte(color565 >> 8); - this->write_byte(color565); - buffer_[i] = 0; + while (rem > 0) { + size_t sz = rem <= sizeof(transfer_buffer_) ? rem : sizeof(transfer_buffer_); + this->write_array(transfer_buffer_, sz); + rem -= sz; } + this->end_data_(); + + memset(buffer_, 0, (this->get_width_internal()) * (this->get_height_internal())); } void HOT ILI9341Display::draw_absolute_pixel_internal(int x, int y, Color color) { @@ -220,6 +238,23 @@ void ILI9341Display::invert_display_(bool invert) { this->command(invert ? ILI93 int ILI9341Display::get_width_internal() { return this->width_; } int ILI9341Display::get_height_internal() { return this->height_; } +uint32_t ILI9341Display::buffer_to_transfer_(uint32_t pos, uint32_t sz) { + uint8_t *src = buffer_ + pos; + uint8_t *dst = transfer_buffer_; + + if (sz > sizeof(transfer_buffer_) / 2) { + sz = sizeof(transfer_buffer_) / 2; + } + + for (uint32_t i = 0; i < sz; ++i) { + uint16_t color = convert_to_16bit_color_(*src++); + *dst++ = (uint8_t)(color >> 8); + *dst++ = (uint8_t) color; + } + + return sz; +} + // M5Stack display void ILI9341M5Stack::initialize() { this->init_lcd_(INITCMD_M5STACK); diff --git a/esphome/components/ili9341/ili9341_display.h b/esphome/components/ili9341/ili9341_display.h index 2b6ecc6871..d8c90c9d33 100644 --- a/esphome/components/ili9341/ili9341_display.h +++ b/esphome/components/ili9341/ili9341_display.h @@ -71,6 +71,10 @@ class ILI9341Display : public PollingComponent, void start_data_(); void end_data_(); + uint8_t transfer_buffer_[64]; + + uint32_t buffer_to_transfer_(uint32_t pos, uint32_t sz); + GPIOPin *reset_pin_{nullptr}; GPIOPin *led_pin_{nullptr}; GPIOPin *dc_pin_; From 654e31124ea4069545ea2768175552155f2b0d6e Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 22 Sep 2021 22:59:03 +1200 Subject: [PATCH 092/207] Correctly invert the float output state (#2368) --- esphome/components/output/float_output.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/esphome/components/output/float_output.cpp b/esphome/components/output/float_output.cpp index 5820a2d7cd..f120f86f1f 100644 --- a/esphome/components/output/float_output.cpp +++ b/esphome/components/output/float_output.cpp @@ -31,14 +31,13 @@ void FloatOutput::set_level(float state) { this->power_.unrequest(); } #endif + + if (!(state == 0.0f && this->zero_means_zero_)) // regardless of min_power_, 0.0 means off + state = (state * (this->max_power_ - this->min_power_)) + this->min_power_; + if (this->is_inverted()) state = 1.0f - state; - if (state == 0.0f && this->zero_means_zero_) { // regardless of min_power_, 0.0 means off - this->write_state(state); - return; - } - float adjusted_value = (state * (this->max_power_ - this->min_power_)) + this->min_power_; - this->write_state(adjusted_value); + this->write_state(state); } void FloatOutput::write_state(bool state) { this->set_level(state != this->inverted_ ? 1.0f : 0.0f); } From b20760c93c3f8874e0d73d515e0e88d04a7c87aa Mon Sep 17 00:00:00 2001 From: Stephen Tierney Date: Wed, 22 Sep 2021 21:24:19 +1000 Subject: [PATCH 093/207] Add support for LTR390 (#1505) * Add support for ltr390 * Fix linting errors * Fix more linting errors * Linting fixes continued * Linting forever * Another one * Fix regression and linting * Fix narrowing conversion * Add test and bugfix * Add codeowners * Update CODEOWNERS * Update sensor defs * Reformatted with black * Fixed device class import * Update CODEOWNERS * Update CODEOWNERS * Adding all config options As requested https://github.com/esphome/esphome/pull/1505#discussion_r597326897 * Moving test to different config file test1.yml runs out of memory * Update according to comments * Add safety clause to reading modes * Fix clang-tidy complaint * Revert change to i2c component * Fix for changes in dev * Revert "Revert change to i2c component" This reverts commit 2810df59e9c05311df6d32149ed79a393676503b. Co-authored-by: Otto winter Co-authored-by: Oxan van Leeuwen --- CODEOWNERS | 1 + esphome/components/i2c/i2c.h | 6 +- esphome/components/ltr390/__init__.py | 0 esphome/components/ltr390/ltr390.cpp | 166 ++++++++++++++++++++++++++ esphome/components/ltr390/ltr390.h | 93 +++++++++++++++ esphome/components/ltr390/sensor.py | 98 +++++++++++++++ tests/test2.yaml | 14 +++ 7 files changed, 376 insertions(+), 2 deletions(-) create mode 100644 esphome/components/ltr390/__init__.py create mode 100644 esphome/components/ltr390/ltr390.cpp create mode 100644 esphome/components/ltr390/ltr390.h create mode 100644 esphome/components/ltr390/sensor.py diff --git a/CODEOWNERS b/CODEOWNERS index 2577e0d706..92ee989309 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -74,6 +74,7 @@ esphome/components/json/* @OttoWinter esphome/components/ledc/* @OttoWinter esphome/components/light/* @esphome/core esphome/components/logger/* @esphome/core +esphome/components/ltr390/* @sjtrny esphome/components/max7219digit/* @rspaargaren esphome/components/mcp23008/* @jesserockz esphome/components/mcp23017/* @jesserockz diff --git a/esphome/components/i2c/i2c.h b/esphome/components/i2c/i2c.h index 71ab650e97..7ee4cdd811 100644 --- a/esphome/components/i2c/i2c.h +++ b/esphome/components/i2c/i2c.h @@ -13,8 +13,6 @@ namespace i2c { class I2CDevice; class I2CRegister { public: - I2CRegister(I2CDevice *parent, uint8_t a_register) : parent_(parent), register_(a_register) {} - I2CRegister &operator=(uint8_t value); I2CRegister &operator&=(uint8_t value); I2CRegister &operator|=(uint8_t value); @@ -24,6 +22,10 @@ class I2CRegister { uint8_t get() const; protected: + friend class I2CDevice; + + I2CRegister(I2CDevice *parent, uint8_t a_register) : parent_(parent), register_(a_register) {} + I2CDevice *parent_; uint8_t register_; }; diff --git a/esphome/components/ltr390/__init__.py b/esphome/components/ltr390/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/ltr390/ltr390.cpp b/esphome/components/ltr390/ltr390.cpp new file mode 100644 index 0000000000..36f3835724 --- /dev/null +++ b/esphome/components/ltr390/ltr390.cpp @@ -0,0 +1,166 @@ +#include "ltr390.h" +#include "esphome/core/hal.h" +#include "esphome/core/log.h" +#include + +namespace esphome { +namespace ltr390 { + +static const char *const TAG = "ltr390"; + +static const float GAINVALUES[5] = {1.0, 3.0, 6.0, 9.0, 18.0}; +static const float RESOLUTIONVALUE[6] = {4.0, 2.0, 1.0, 0.5, 0.25, 0.125}; +static const uint32_t MODEADDRESSES[2] = {0x0D, 0x10}; + +uint32_t little_endian_bytes_to_int(const uint8_t *buffer, uint8_t num_bytes) { + uint32_t value = 0; + + for (int i = 0; i < num_bytes; i++) { + value <<= 8; + value |= buffer[num_bytes - i - 1]; + } + + return value; +} + +optional LTR390Component::read_sensor_data_(LTR390MODE mode) { + const uint8_t num_bytes = 3; + uint8_t buffer[num_bytes]; + + // Wait until data available + const uint32_t now = millis(); + while (true) { + std::bitset<8> status = this->reg(LTR390_MAIN_STATUS).get(); + bool available = status[3]; + if (available) + break; + + if (millis() - now > 100) { + ESP_LOGW(TAG, "Sensor didn't return any data, aborting"); + return {}; + } + ESP_LOGD(TAG, "Waiting for data"); + delay(2); + } + + if (!this->read_bytes(MODEADDRESSES[mode], buffer, num_bytes)) { + ESP_LOGW(TAG, "Reading data from sensor failed!"); + return {}; + } + + return little_endian_bytes_to_int(buffer, num_bytes); +} + +void LTR390Component::read_als_() { + auto val = this->read_sensor_data_(LTR390_MODE_ALS); + if (!val.has_value()) + return; + uint32_t als = *val; + + if (this->light_sensor_ != nullptr) { + float lux = (0.6 * als) / (GAINVALUES[this->gain_] * RESOLUTIONVALUE[this->res_]) * this->wfac_; + this->light_sensor_->publish_state(lux); + } + + if (this->als_sensor_ != nullptr) { + this->als_sensor_->publish_state(als); + } +} + +void LTR390Component::read_uvs_() { + auto val = this->read_sensor_data_(LTR390_MODE_UVS); + if (!val.has_value()) + return; + uint32_t uv = *val; + + if (this->uvi_sensor_ != nullptr) { + this->uvi_sensor_->publish_state(uv / LTR390_SENSITIVITY * this->wfac_); + } + + if (this->uv_sensor_ != nullptr) { + this->uv_sensor_->publish_state(uv); + } +} + +void LTR390Component::read_mode_(int mode_index) { + // Set mode + LTR390MODE mode = std::get<0>(this->mode_funcs_[mode_index]); + + std::bitset<8> ctrl = this->reg(LTR390_MAIN_CTRL).get(); + ctrl[LTR390_CTRL_MODE] = mode; + this->reg(LTR390_MAIN_CTRL) = ctrl.to_ulong(); + + // After the sensor integration time do the following + this->set_timeout(((uint32_t) RESOLUTIONVALUE[this->res_]) * 100, [this, mode_index]() { + // Read from the sensor + std::get<1>(this->mode_funcs_[mode_index])(); + + // If there are more modes to read then begin the next + // otherwise stop + if (mode_index + 1 < this->mode_funcs_.size()) { + this->read_mode_(mode_index + 1); + } else { + this->reading_ = false; + } + }); +} + +void LTR390Component::setup() { + ESP_LOGCONFIG(TAG, "Setting up ltr390..."); + + // reset + std::bitset<8> ctrl = this->reg(LTR390_MAIN_CTRL).get(); + ctrl[LTR390_CTRL_RST] = true; + this->reg(LTR390_MAIN_CTRL) = ctrl.to_ulong(); + delay(10); + + // Enable + ctrl = this->reg(LTR390_MAIN_CTRL).get(); + ctrl[LTR390_CTRL_EN] = true; + this->reg(LTR390_MAIN_CTRL) = ctrl.to_ulong(); + + // check enabled + ctrl = this->reg(LTR390_MAIN_CTRL).get(); + bool enabled = ctrl[LTR390_CTRL_EN]; + + if (!enabled) { + ESP_LOGW(TAG, "Sensor didn't respond with enabled state"); + this->mark_failed(); + return; + } + + // Set gain + this->reg(LTR390_GAIN) = gain_; + + // Set resolution + uint8_t res = this->reg(LTR390_MEAS_RATE).get(); + // resolution is in bits 5-7 + res &= ~0b01110000; + res |= res << 4; + this->reg(LTR390_MEAS_RATE) = res; + + // Set sensor read state + this->reading_ = false; + + // If we need the light sensor then add to the list + if (this->light_sensor_ != nullptr || this->als_sensor_ != nullptr) { + this->mode_funcs_.emplace_back(LTR390_MODE_ALS, std::bind(<R390Component::read_als_, this)); + } + + // If we need the UV sensor then add to the list + if (this->uvi_sensor_ != nullptr || this->uv_sensor_ != nullptr) { + this->mode_funcs_.emplace_back(LTR390_MODE_UVS, std::bind(<R390Component::read_uvs_, this)); + } +} + +void LTR390Component::dump_config() { LOG_I2C_DEVICE(this); } + +void LTR390Component::update() { + if (!this->reading_ && !mode_funcs_.empty()) { + this->reading_ = true; + this->read_mode_(0); + } +} + +} // namespace ltr390 +} // namespace esphome diff --git a/esphome/components/ltr390/ltr390.h b/esphome/components/ltr390/ltr390.h new file mode 100644 index 0000000000..d607a3e55f --- /dev/null +++ b/esphome/components/ltr390/ltr390.h @@ -0,0 +1,93 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/optional.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/i2c/i2c.h" +#include + +namespace esphome { +namespace ltr390 { + +enum LTR390CTRL { + LTR390_CTRL_EN = 1, + LTR390_CTRL_MODE = 3, + LTR390_CTRL_RST = 4, +}; + +// enums from https://github.com/adafruit/Adafruit_LTR390/ + +static const uint8_t LTR390_MAIN_CTRL = 0x00; +static const uint8_t LTR390_MEAS_RATE = 0x04; +static const uint8_t LTR390_GAIN = 0x05; +static const uint8_t LTR390_PART_ID = 0x06; +static const uint8_t LTR390_MAIN_STATUS = 0x07; +static const float LTR390_SENSITIVITY = 2300.0; + +// Sensing modes +enum LTR390MODE { + LTR390_MODE_ALS, + LTR390_MODE_UVS, +}; + +// Sensor gain levels +enum LTR390GAIN { + LTR390_GAIN_1 = 0, + LTR390_GAIN_3, // Default + LTR390_GAIN_6, + LTR390_GAIN_9, + LTR390_GAIN_18, +}; + +// Sensor resolution +enum LTR390RESOLUTION { + LTR390_RESOLUTION_20BIT, + LTR390_RESOLUTION_19BIT, + LTR390_RESOLUTION_18BIT, // Default + LTR390_RESOLUTION_17BIT, + LTR390_RESOLUTION_16BIT, + LTR390_RESOLUTION_13BIT, +}; + +class LTR390Component : public PollingComponent, public i2c::I2CDevice { + public: + float get_setup_priority() const override { return setup_priority::DATA; } + void setup() override; + void dump_config() override; + void update() override; + + void set_gain_value(LTR390GAIN gain) { this->gain_ = gain; } + void set_res_value(LTR390RESOLUTION res) { this->res_ = res; } + void set_wfac_value(float wfac) { this->wfac_ = wfac; } + + void set_light_sensor(sensor::Sensor *light_sensor) { this->light_sensor_ = light_sensor; } + void set_als_sensor(sensor::Sensor *als_sensor) { this->als_sensor_ = als_sensor; } + void set_uvi_sensor(sensor::Sensor *uvi_sensor) { this->uvi_sensor_ = uvi_sensor; } + void set_uv_sensor(sensor::Sensor *uv_sensor) { this->uv_sensor_ = uv_sensor; } + + protected: + optional read_sensor_data_(LTR390MODE mode); + + void read_als_(); + void read_uvs_(); + + void read_mode_(int mode_index); + + bool reading_; + + // a list of modes and corresponding read functions + std::vector>> mode_funcs_; + + LTR390GAIN gain_; + LTR390RESOLUTION res_; + float wfac_; + + sensor::Sensor *light_sensor_{nullptr}; + sensor::Sensor *als_sensor_{nullptr}; + + sensor::Sensor *uvi_sensor_{nullptr}; + sensor::Sensor *uv_sensor_{nullptr}; +}; + +} // namespace ltr390 +} // namespace esphome diff --git a/esphome/components/ltr390/sensor.py b/esphome/components/ltr390/sensor.py new file mode 100644 index 0000000000..0e70f7bb1b --- /dev/null +++ b/esphome/components/ltr390/sensor.py @@ -0,0 +1,98 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import i2c, sensor +from esphome.const import ( + CONF_ID, + CONF_GAIN, + CONF_LIGHT, + CONF_RESOLUTION, + UNIT_LUX, + ICON_BRIGHTNESS_5, + DEVICE_CLASS_ILLUMINANCE, +) + +CODEOWNERS = ["@sjtrny"] +DEPENDENCIES = ["i2c"] + +ltr390_ns = cg.esphome_ns.namespace("ltr390") + +LTR390Component = ltr390_ns.class_( + "LTR390Component", cg.PollingComponent, i2c.I2CDevice +) + +CONF_AMBIENT_LIGHT = "ambient_light" +CONF_UV_INDEX = "uv_index" +CONF_UV = "uv" +CONF_WINDOW_CORRECTION_FACTOR = "window_correction_factor" + +UNIT_COUNTS = "#" +UNIT_UVI = "UVI" + +LTR390GAIN = ltr390_ns.enum("LTR390GAIN") +GAIN_OPTIONS = { + "X1": LTR390GAIN.LTR390_GAIN_1, + "X3": LTR390GAIN.LTR390_GAIN_3, + "X6": LTR390GAIN.LTR390_GAIN_6, + "X9": LTR390GAIN.LTR390_GAIN_9, + "X18": LTR390GAIN.LTR390_GAIN_18, +} + +LTR390RESOLUTION = ltr390_ns.enum("LTR390RESOLUTION") +RES_OPTIONS = { + 20: LTR390RESOLUTION.LTR390_RESOLUTION_20BIT, + 19: LTR390RESOLUTION.LTR390_RESOLUTION_19BIT, + 18: LTR390RESOLUTION.LTR390_RESOLUTION_18BIT, + 17: LTR390RESOLUTION.LTR390_RESOLUTION_17BIT, + 16: LTR390RESOLUTION.LTR390_RESOLUTION_16BIT, + 13: LTR390RESOLUTION.LTR390_RESOLUTION_13BIT, +} + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(LTR390Component), + cv.Optional(CONF_LIGHT): sensor.sensor_schema( + UNIT_LUX, ICON_BRIGHTNESS_5, 1, DEVICE_CLASS_ILLUMINANCE + ), + cv.Optional(CONF_AMBIENT_LIGHT): sensor.sensor_schema( + UNIT_COUNTS, ICON_BRIGHTNESS_5, 1, DEVICE_CLASS_ILLUMINANCE + ), + cv.Optional(CONF_UV_INDEX): sensor.sensor_schema( + UNIT_UVI, ICON_BRIGHTNESS_5, 5, DEVICE_CLASS_ILLUMINANCE + ), + cv.Optional(CONF_UV): sensor.sensor_schema( + UNIT_COUNTS, ICON_BRIGHTNESS_5, 1, DEVICE_CLASS_ILLUMINANCE + ), + cv.Optional(CONF_GAIN, default="X3"): cv.enum(GAIN_OPTIONS), + cv.Optional(CONF_RESOLUTION, default=18): cv.enum(RES_OPTIONS), + cv.Optional(CONF_WINDOW_CORRECTION_FACTOR, default=1.0): cv.float_range( + min=1.0 + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x53)), + cv.has_at_least_one_key(CONF_LIGHT, CONF_AMBIENT_LIGHT, CONF_UV_INDEX, CONF_UV), +) + +TYPES = { + CONF_LIGHT: "set_light_sensor", + CONF_AMBIENT_LIGHT: "set_als_sensor", + CONF_UV_INDEX: "set_uvi_sensor", + CONF_UV: "set_uv_sensor", +} + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await i2c.register_i2c_device(var, config) + + cg.add(var.set_gain_value(config[CONF_GAIN])) + cg.add(var.set_res_value(config[CONF_RESOLUTION])) + cg.add(var.set_wfac_value(config[CONF_WINDOW_CORRECTION_FACTOR])) + + for key, funcName in TYPES.items(): + if key in config: + sens = await sensor.new_sensor(config[key]) + cg.add(getattr(var, funcName)(sens)) diff --git a/tests/test2.yaml b/tests/test2.yaml index e6df4d513e..d0634e0f7b 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -238,6 +238,20 @@ sensor: name: 'Inkbird IBS-TH1 Humidity' battery_level: name: 'Inkbird IBS-TH1 Battery Level' + - platform: ltr390 + uv: + name: "LTR390 UV" + uv_index: + name: "LTR390 UVI" + light: + name: "LTR390 Light" + ambient_light: + name: "LTR390 ALS" + gain: "X3" + resolution: 18 + window_correction_factor: 1.0 + address: 0x53 + update_interval: 60s - platform: sgp40 name: 'Workshop VOC' update_interval: 5s From e32722db70fb6dbc2d44aaa80fa7607d59ae3bc9 Mon Sep 17 00:00:00 2001 From: Trevor North Date: Wed, 22 Sep 2021 12:29:05 +0100 Subject: [PATCH 094/207] Allow sloppy datapoint message length (#1982) This allows datapoint update messages to be handled even if the overall message is longer than required (likely that it contains trailing empty bytes). The specific type handling will read only the expected data lengths so we only need to hard bail if we have too little data not too much. --- esphome/components/tuya/tuya.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/tuya/tuya.cpp b/esphome/components/tuya/tuya.cpp index bbbc9274c3..d73ba50462 100644 --- a/esphome/components/tuya/tuya.cpp +++ b/esphome/components/tuya/tuya.cpp @@ -241,8 +241,10 @@ void Tuya::handle_datapoint_(const uint8_t *buffer, size_t len) { size_t data_size = (buffer[2] << 8) + buffer[3]; const uint8_t *data = buffer + 4; size_t data_len = len - 4; - if (data_size != data_len) { - ESP_LOGW(TAG, "Datapoint %u is not expected size (%zu != %zu)", datapoint.id, data_size, data_len); + if (data_size > data_len) { + ESP_LOGW(TAG, "Datapoint %u has extra bytes that will be ignored (%zu > %zu)", datapoint.id, data_size, data_len); + } else if (data_size < data_len) { + ESP_LOGW(TAG, "Datapoint %u is truncated and cannot be parsed (%zu < %zu)", datapoint.id, data_size, data_len); return; } datapoint.len = data_len; From fd836e982e31d8ff082a9f5c6847f217d4163b41 Mon Sep 17 00:00:00 2001 From: wifwucite <74489218+wifwucite@users.noreply.github.com> Date: Wed, 22 Sep 2021 13:42:58 +0200 Subject: [PATCH 095/207] Mqtt topics to support numeric fan speed (#1859) * numeric speed added * when dumping config for MQTT components log a note when skipped due to is_internal * added new topics to paython code validation/generation * reformatted with black * formatting corrected * use dump_config_ mechanism to skip internal components * use dump_config_ mechanism to skip internal components * style issues resolved * do_dump_config removed * formatting fixed * formatting fixed * Drop parent dump_config() calls Co-authored-by: Oxan van Leeuwen --- esphome/components/fan/__init__.py | 20 +++++++++++++ esphome/components/mqtt/mqtt_fan.cpp | 44 ++++++++++++++++++++++++++++ esphome/components/mqtt/mqtt_fan.h | 5 ++++ esphome/const.py | 2 ++ tests/test1.yaml | 2 ++ 5 files changed, 73 insertions(+) diff --git a/esphome/components/fan/__init__.py b/esphome/components/fan/__init__.py index 46ff0c2d53..f8772948fc 100644 --- a/esphome/components/fan/__init__.py +++ b/esphome/components/fan/__init__.py @@ -12,6 +12,8 @@ from esphome.const import ( CONF_OSCILLATION_COMMAND_TOPIC, CONF_OSCILLATION_STATE_TOPIC, CONF_SPEED, + CONF_SPEED_LEVEL_COMMAND_TOPIC, + CONF_SPEED_LEVEL_STATE_TOPIC, CONF_SPEED_COMMAND_TOPIC, CONF_SPEED_STATE_TOPIC, CONF_NAME, @@ -57,6 +59,12 @@ FAN_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend( cv.Optional(CONF_OSCILLATION_COMMAND_TOPIC): cv.All( cv.requires_component("mqtt"), cv.subscribe_topic ), + cv.Optional(CONF_SPEED_LEVEL_STATE_TOPIC): cv.All( + cv.requires_component("mqtt"), cv.publish_topic + ), + cv.Optional(CONF_SPEED_LEVEL_COMMAND_TOPIC): cv.All( + cv.requires_component("mqtt"), cv.subscribe_topic + ), cv.Optional(CONF_SPEED_STATE_TOPIC): cv.All( cv.requires_component("mqtt"), cv.publish_topic ), @@ -104,6 +112,18 @@ async def setup_fan_core_(var, config): config[CONF_OSCILLATION_COMMAND_TOPIC] ) ) + if CONF_SPEED_LEVEL_STATE_TOPIC in config: + cg.add( + mqtt_.set_custom_speed_level_state_topic( + config[CONF_SPEED_LEVEL_STATE_TOPIC] + ) + ) + if CONF_SPEED_LEVEL_COMMAND_TOPIC in config: + cg.add( + mqtt_.set_custom_speed_level_command_topic( + config[CONF_SPEED_LEVEL_COMMAND_TOPIC] + ) + ) if CONF_SPEED_STATE_TOPIC in config: cg.add(mqtt_.set_custom_speed_state_topic(config[CONF_SPEED_STATE_TOPIC])) if CONF_SPEED_COMMAND_TOPIC in config: diff --git a/esphome/components/mqtt/mqtt_fan.cpp b/esphome/components/mqtt/mqtt_fan.cpp index c8db5ecece..ed1ab605aa 100644 --- a/esphome/components/mqtt/mqtt_fan.cpp +++ b/esphome/components/mqtt/mqtt_fan.cpp @@ -16,6 +16,7 @@ MQTTFanComponent::MQTTFanComponent(FanState *state) : MQTTComponent(), state_(st FanState *MQTTFanComponent::get_state() const { return this->state_; } std::string MQTTFanComponent::component_type() const { return "fan"; } + void MQTTFanComponent::setup() { this->subscribe(this->get_command_topic_(), [this](const std::string &topic, const std::string &payload) { auto val = parse_on_off(payload.c_str()); @@ -64,6 +65,26 @@ void MQTTFanComponent::setup() { }); } + if (this->state_->get_traits().supports_speed()) { + this->subscribe(this->get_speed_level_command_topic(), + [this](const std::string &topic, const std::string &payload) { + optional speed_level_opt = parse_int(payload); + if (speed_level_opt.has_value()) { + const int speed_level = speed_level_opt.value(); + if (speed_level >= 0 && speed_level <= this->state_->get_traits().supported_speed_count()) { + ESP_LOGD(TAG, "New speed level %d", speed_level); + this->state_->make_call().set_speed(speed_level).perform(); + } else { + ESP_LOGW(TAG, "Invalid speed level %d", speed_level); + this->status_momentary_warning("speed", 5000); + } + } else { + ESP_LOGW(TAG, "Invalid speed level %s (int expected)", payload.c_str()); + this->status_momentary_warning("speed", 5000); + } + }); + } + if (this->state_->get_traits().supports_speed()) { this->subscribe(this->get_speed_command_topic(), [this](const std::string &topic, const std::string &payload) { this->state_->make_call() @@ -75,6 +96,22 @@ void MQTTFanComponent::setup() { auto f = std::bind(&MQTTFanComponent::publish_state, this); this->state_->add_on_state_callback([this, f]() { this->defer("send", f); }); } + +void MQTTFanComponent::dump_config() { + ESP_LOGCONFIG(TAG, "MQTT Fan '%s': ", this->state_->get_name().c_str()); + LOG_MQTT_COMPONENT(true, true); + if (this->state_->get_traits().supports_oscillation()) { + ESP_LOGCONFIG(TAG, " Oscillation State Topic: '%s'", this->get_oscillation_state_topic().c_str()); + ESP_LOGCONFIG(TAG, " Oscillation Command Topic: '%s'", this->get_oscillation_command_topic().c_str()); + } + if (this->state_->get_traits().supports_speed()) { + ESP_LOGCONFIG(TAG, " Speed Level State Topic: '%s'", this->get_speed_level_state_topic().c_str()); + ESP_LOGCONFIG(TAG, " Speed Level Command Topic: '%s'", this->get_speed_level_command_topic().c_str()); + ESP_LOGCONFIG(TAG, " Speed State Topic: '%s'", this->get_speed_state_topic().c_str()); + ESP_LOGCONFIG(TAG, " Speed Command Topic: '%s'", this->get_speed_command_topic().c_str()); + } +} + bool MQTTFanComponent::send_initial_state() { return this->publish_state(); } std::string MQTTFanComponent::friendly_name() const { return this->state_->get_name(); } void MQTTFanComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) { @@ -83,6 +120,8 @@ void MQTTFanComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryConfi root["oscillation_state_topic"] = this->get_oscillation_state_topic(); } if (this->state_->get_traits().supports_speed()) { + root["speed_level_command_topic"] = this->get_speed_level_command_topic(); + root["speed_level_state_topic"] = this->get_speed_level_state_topic(); root["speed_command_topic"] = this->get_speed_command_topic(); root["speed_state_topic"] = this->get_speed_state_topic(); } @@ -99,6 +138,11 @@ bool MQTTFanComponent::publish_state() { failed = failed || !success; } auto traits = this->state_->get_traits(); + if (traits.supports_speed()) { + std::string payload = to_string(this->state_->speed); + bool success = this->publish(this->get_speed_level_state_topic(), payload); + failed = failed || !success; + } if (traits.supports_speed()) { const char *payload; // NOLINTNEXTLINE(clang-diagnostic-deprecated-declarations) diff --git a/esphome/components/mqtt/mqtt_fan.h b/esphome/components/mqtt/mqtt_fan.h index 99d9c055cf..00263e13eb 100644 --- a/esphome/components/mqtt/mqtt_fan.h +++ b/esphome/components/mqtt/mqtt_fan.h @@ -17,6 +17,8 @@ class MQTTFanComponent : public mqtt::MQTTComponent { MQTT_COMPONENT_CUSTOM_TOPIC(oscillation, command) MQTT_COMPONENT_CUSTOM_TOPIC(oscillation, state) + MQTT_COMPONENT_CUSTOM_TOPIC(speed_level, command) + MQTT_COMPONENT_CUSTOM_TOPIC(speed_level, state) MQTT_COMPONENT_CUSTOM_TOPIC(speed, command) MQTT_COMPONENT_CUSTOM_TOPIC(speed, state) @@ -26,6 +28,9 @@ class MQTTFanComponent : public mqtt::MQTTComponent { // (In most use cases you won't need these) /// Setup the fan subscriptions and discovery. void setup() override; + + void dump_config() override; + /// Send the full current state to MQTT. bool send_initial_state() override; bool publish_state(); diff --git a/esphome/const.py b/esphome/const.py index f032cf0fc3..2cf261b4b5 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -597,6 +597,8 @@ CONF_SOURCE = "source" CONF_SPEED = "speed" CONF_SPEED_COMMAND_TOPIC = "speed_command_topic" CONF_SPEED_COUNT = "speed_count" +CONF_SPEED_LEVEL_COMMAND_TOPIC = "speed_level_command_topic" +CONF_SPEED_LEVEL_STATE_TOPIC = "speed_level_state_topic" CONF_SPEED_STATE_TOPIC = "speed_state_topic" CONF_SPI_ID = "spi_id" CONF_SPIKE_REJECTION = "spike_rejection" diff --git a/tests/test1.yaml b/tests/test1.yaml index c1ddf26488..0fb14fd34b 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1978,6 +1978,8 @@ fan: direction_output: gpio_26 oscillation_state_topic: oscillation/state/topic oscillation_command_topic: oscillation/command/topic + speed_level_state_topic: speed_level/state/topic + speed_level_command_topic: speed_level/command/topic speed_state_topic: speed/state/topic speed_command_topic: speed/command/topic on_speed_set: From 8bebf138ee7010a09c14ac547e1e74299d4f599d Mon Sep 17 00:00:00 2001 From: Gustavo Ambrozio Date: Wed, 22 Sep 2021 01:44:09 -1000 Subject: [PATCH 096/207] Wifi scan results (#1605) * adding a scan results wifi text sensor * Code comment * Adding scan results to test * Removing redundant call * linting * Better method to update wifi info Co-authored-by: Otto Winter * Getting loop back At least for now. * Trying out suggestion again * Applying cr suggestions Co-authored-by: Otto Winter --- esphome/components/wifi_info/text_sensor.py | 10 +++++++ .../wifi_info/wifi_info_text_sensor.cpp | 1 + .../wifi_info/wifi_info_text_sensor.h | 29 +++++++++++++++++++ esphome/const.py | 1 + tests/test1.yaml | 2 ++ 5 files changed, 43 insertions(+) diff --git a/esphome/components/wifi_info/text_sensor.py b/esphome/components/wifi_info/text_sensor.py index 50ec3eb272..1922502204 100644 --- a/esphome/components/wifi_info/text_sensor.py +++ b/esphome/components/wifi_info/text_sensor.py @@ -5,6 +5,7 @@ from esphome.const import ( CONF_BSSID, CONF_ID, CONF_IP_ADDRESS, + CONF_SCAN_RESULTS, CONF_SSID, CONF_MAC_ADDRESS, ) @@ -15,6 +16,9 @@ wifi_info_ns = cg.esphome_ns.namespace("wifi_info") IPAddressWiFiInfo = wifi_info_ns.class_( "IPAddressWiFiInfo", text_sensor.TextSensor, cg.Component ) +ScanResultsWiFiInfo = wifi_info_ns.class_( + "ScanResultsWiFiInfo", text_sensor.TextSensor, cg.PollingComponent +) SSIDWiFiInfo = wifi_info_ns.class_("SSIDWiFiInfo", text_sensor.TextSensor, cg.Component) BSSIDWiFiInfo = wifi_info_ns.class_( "BSSIDWiFiInfo", text_sensor.TextSensor, cg.Component @@ -30,6 +34,11 @@ CONFIG_SCHEMA = cv.Schema( cv.GenerateID(): cv.declare_id(IPAddressWiFiInfo), } ), + cv.Optional(CONF_SCAN_RESULTS): text_sensor.TEXT_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(ScanResultsWiFiInfo), + } + ).extend(cv.polling_component_schema("60s")), cv.Optional(CONF_SSID): text_sensor.TEXT_SENSOR_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(SSIDWiFiInfo), @@ -62,3 +71,4 @@ async def to_code(config): await setup_conf(config, CONF_SSID) await setup_conf(config, CONF_BSSID) await setup_conf(config, CONF_MAC_ADDRESS) + await setup_conf(config, CONF_SCAN_RESULTS) diff --git a/esphome/components/wifi_info/wifi_info_text_sensor.cpp b/esphome/components/wifi_info/wifi_info_text_sensor.cpp index 92e5d93a5a..0b73de68de 100644 --- a/esphome/components/wifi_info/wifi_info_text_sensor.cpp +++ b/esphome/components/wifi_info/wifi_info_text_sensor.cpp @@ -7,6 +7,7 @@ namespace wifi_info { static const char *const TAG = "wifi_info"; void IPAddressWiFiInfo::dump_config() { LOG_TEXT_SENSOR("", "WifiInfo IPAddress", this); } +void ScanResultsWiFiInfo::dump_config() { LOG_TEXT_SENSOR("", "WifiInfo Scan Results", this); } void SSIDWiFiInfo::dump_config() { LOG_TEXT_SENSOR("", "WifiInfo SSID", this); } void BSSIDWiFiInfo::dump_config() { LOG_TEXT_SENSOR("", "WifiInfo BSSID", this); } void MacAddressWifiInfo::dump_config() { LOG_TEXT_SENSOR("", "WifiInfo Mac Address", this); } diff --git a/esphome/components/wifi_info/wifi_info_text_sensor.h b/esphome/components/wifi_info/wifi_info_text_sensor.h index de1c7f71fc..b2f37de363 100644 --- a/esphome/components/wifi_info/wifi_info_text_sensor.h +++ b/esphome/components/wifi_info/wifi_info_text_sensor.h @@ -24,6 +24,35 @@ class IPAddressWiFiInfo : public Component, public text_sensor::TextSensor { network::IPAddress last_ip_; }; +class ScanResultsWiFiInfo : public PollingComponent, public text_sensor::TextSensor { + public: + void update() override { + std::string scan_results; + for (auto &scan : wifi::global_wifi_component->get_scan_result()) { + if (scan.get_is_hidden()) + continue; + + scan_results += scan.get_ssid(); + scan_results += ": "; + scan_results += esphome::to_string(scan.get_rssi()); + scan_results += "dB\n"; + } + + if (this->last_scan_results_ != scan_results) { + this->last_scan_results_ = scan_results; + // There's a limit of 255 characters per state. + // Longer states just don't get sent so we truncate it. + this->publish_state(scan_results.substr(0, 255)); + } + } + float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + std::string unique_id() override { return get_mac_address() + "-wifiinfo-scanresults"; } + void dump_config() override; + + protected: + std::string last_scan_results_; +}; + class SSIDWiFiInfo : public Component, public text_sensor::TextSensor { public: void loop() override { diff --git a/esphome/const.py b/esphome/const.py index 2cf261b4b5..a52085fbb7 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -558,6 +558,7 @@ CONF_SAFE_MODE = "safe_mode" CONF_SAMSUNG = "samsung" CONF_SATELLITES = "satellites" CONF_SCAN = "scan" +CONF_SCAN_RESULTS = "scan_results" CONF_SCL = "scl" CONF_SCL_PIN = "scl_pin" CONF_SDA = "sda" diff --git a/tests/test1.yaml b/tests/test1.yaml index 0fb14fd34b..5bec56c80f 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -2359,6 +2359,8 @@ text_sensor: name: Template Text Sensor id: ${textname}_text - platform: wifi_info + scan_results: + name: 'Scan Results' ip_address: name: 'IP Address' ssid: From 66761ff3400681828940e6b28c4466679bf045e3 Mon Sep 17 00:00:00 2001 From: ZJY <934526987@qq.com> Date: Wed, 22 Sep 2021 19:47:41 +0800 Subject: [PATCH 097/207] Add SSD1305 support to SSD1306 integration along with few new options (#1902) * Add serveral options for SSD1306 integration * Add SSD1305 support (SSD1305 is similar to SSD1306, it seems SSD1305 has brightness and color register but does not have charge pump) * Add some description when manipulating registers * Add flip, offset and invert option to get more compatibility with various display modules * Fix typo `setup_ssd1036' -> `setup_ssd1306' * Add SSD1306 brightness validation tip * Add more description, limit offset range * Changes according to linter * Fix test * Raise error instead of using warning * Fix wrong logic * Remove logger Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> * Remove logging import Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/ssd1306_base/__init__.py | 44 +++++++- .../components/ssd1306_base/ssd1306_base.cpp | 106 ++++++++++++++---- .../components/ssd1306_base/ssd1306_base.h | 16 +++ esphome/components/ssd1306_i2c/display.py | 4 +- .../components/ssd1306_i2c/ssd1306_i2c.cpp | 5 + esphome/components/ssd1306_spi/display.py | 4 +- .../components/ssd1306_spi/ssd1306_spi.cpp | 5 + tests/test1.yaml | 2 +- 8 files changed, 159 insertions(+), 27 deletions(-) diff --git a/esphome/components/ssd1306_base/__init__.py b/esphome/components/ssd1306_base/__init__.py index 9652d01efa..bc2e558f1b 100644 --- a/esphome/components/ssd1306_base/__init__.py +++ b/esphome/components/ssd1306_base/__init__.py @@ -8,12 +8,19 @@ from esphome.const import ( CONF_MODEL, CONF_RESET_PIN, CONF_BRIGHTNESS, + CONF_CONTRAST, + CONF_INVERT, ) ssd1306_base_ns = cg.esphome_ns.namespace("ssd1306_base") SSD1306 = ssd1306_base_ns.class_("SSD1306", cg.PollingComponent, display.DisplayBuffer) SSD1306Model = ssd1306_base_ns.enum("SSD1306Model") +CONF_FLIP_X = "flip_x" +CONF_FLIP_Y = "flip_y" +CONF_OFFSET_X = "offset_x" +CONF_OFFSET_Y = "offset_y" + MODELS = { "SSD1306_128X32": SSD1306Model.SSD1306_MODEL_128_32, "SSD1306_128X64": SSD1306Model.SSD1306_MODEL_128_64, @@ -23,21 +30,44 @@ MODELS = { "SH1106_128X64": SSD1306Model.SH1106_MODEL_128_64, "SH1106_96X16": SSD1306Model.SH1106_MODEL_96_16, "SH1106_64X48": SSD1306Model.SH1106_MODEL_64_48, + "SSD1305_128X32": SSD1306Model.SSD1305_MODEL_128_32, + "SSD1305_128X64": SSD1306Model.SSD1305_MODEL_128_64, } SSD1306_MODEL = cv.enum(MODELS, upper=True, space="_") + +def _validate(value): + model = value[CONF_MODEL] + if model not in ("SSD1305_128X32", "SSD1305_128X64"): + # Contrast is default value (1.0) while brightness is not + # Indicates user is using old `brightness` option + if value[CONF_BRIGHTNESS] != 1.0 and value[CONF_CONTRAST] == 1.0: + raise cv.Invalid( + "SSD1306/SH1106 no longer accepts brightness option, " + 'please use "contrast" instead.' + ) + + return value + + SSD1306_SCHEMA = display.FULL_DISPLAY_SCHEMA.extend( { cv.Required(CONF_MODEL): SSD1306_MODEL, cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage, + cv.Optional(CONF_CONTRAST, default=1.0): cv.percentage, cv.Optional(CONF_EXTERNAL_VCC): cv.boolean, + cv.Optional(CONF_FLIP_X, default=True): cv.boolean, + cv.Optional(CONF_FLIP_Y, default=True): cv.boolean, + cv.Optional(CONF_OFFSET_X, default=0): cv.int_range(min=0, max=15), + cv.Optional(CONF_OFFSET_Y, default=0): cv.int_range(min=0, max=15), + cv.Optional(CONF_INVERT, default=False): cv.boolean, } ).extend(cv.polling_component_schema("1s")) -async def setup_ssd1036(var, config): +async def setup_ssd1306(var, config): await cg.register_component(var, config) await display.register_display(var, config) @@ -47,8 +77,20 @@ async def setup_ssd1036(var, config): cg.add(var.set_reset_pin(reset)) if CONF_BRIGHTNESS in config: cg.add(var.init_brightness(config[CONF_BRIGHTNESS])) + if CONF_CONTRAST in config: + cg.add(var.init_contrast(config[CONF_CONTRAST])) if CONF_EXTERNAL_VCC in config: cg.add(var.set_external_vcc(config[CONF_EXTERNAL_VCC])) + if CONF_FLIP_X in config: + cg.add(var.init_flip_x(config[CONF_FLIP_X])) + if CONF_FLIP_Y in config: + cg.add(var.init_flip_y(config[CONF_FLIP_X])) + if CONF_OFFSET_X in config: + cg.add(var.init_offset_x(config[CONF_OFFSET_X])) + if CONF_OFFSET_Y in config: + cg.add(var.init_offset_y(config[CONF_OFFSET_Y])) + if CONF_INVERT in config: + cg.add(var.init_invert(config[CONF_INVERT])) if CONF_LAMBDA in config: lambda_ = await cg.process_lambda( config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void diff --git a/esphome/components/ssd1306_base/ssd1306_base.cpp b/esphome/components/ssd1306_base/ssd1306_base.cpp index d321933e8f..b1a2538ebd 100644 --- a/esphome/components/ssd1306_base/ssd1306_base.cpp +++ b/esphome/components/ssd1306_base/ssd1306_base.cpp @@ -8,12 +8,13 @@ namespace ssd1306_base { static const char *const TAG = "ssd1306"; static const uint8_t SSD1306_MAX_CONTRAST = 255; +static const uint8_t SSD1305_MAX_BRIGHTNESS = 255; static const uint8_t SSD1306_COMMAND_DISPLAY_OFF = 0xAE; static const uint8_t SSD1306_COMMAND_DISPLAY_ON = 0xAF; static const uint8_t SSD1306_COMMAND_SET_DISPLAY_CLOCK_DIV = 0xD5; static const uint8_t SSD1306_COMMAND_SET_MULTIPLEX = 0xA8; -static const uint8_t SSD1306_COMMAND_SET_DISPLAY_OFFSET = 0xD3; +static const uint8_t SSD1306_COMMAND_SET_DISPLAY_OFFSET_Y = 0xD3; static const uint8_t SSD1306_COMMAND_SET_START_LINE = 0x40; static const uint8_t SSD1306_COMMAND_CHARGE_PUMP = 0x8D; static const uint8_t SSD1306_COMMAND_MEMORY_MODE = 0x20; @@ -28,33 +29,60 @@ static const uint8_t SSD1306_COMMAND_DISPLAY_ALL_ON_RESUME = 0xA4; static const uint8_t SSD1306_COMMAND_DEACTIVATE_SCROLL = 0x2E; static const uint8_t SSD1306_COMMAND_COLUMN_ADDRESS = 0x21; static const uint8_t SSD1306_COMMAND_PAGE_ADDRESS = 0x22; +static const uint8_t SSD1306_COMMAND_NORMAL_DISPLAY = 0xA6; +static const uint8_t SSD1306_COMMAND_INVERSE_DISPLAY = 0xA7; -static const uint8_t SSD1306_NORMAL_DISPLAY = 0xA6; +static const uint8_t SSD1305_COMMAND_SET_BRIGHTNESS = 0x82; +static const uint8_t SSD1305_COMMAND_SET_AREA_COLOR = 0xD8; void SSD1306::setup() { this->init_internal_(this->get_buffer_length_()); + // Turn off display during initialization (0xAE) this->command(SSD1306_COMMAND_DISPLAY_OFF); - this->command(SSD1306_COMMAND_SET_DISPLAY_CLOCK_DIV); - this->command(0x80); // suggested ratio + // Set oscillator frequency to 4'b1000 with no clock division (0xD5) + this->command(SSD1306_COMMAND_SET_DISPLAY_CLOCK_DIV); + // Oscillator frequency <= 4'b1000, no clock division + this->command(0x80); + + // Enable low power display mode for SSD1305 (0xD8) + if (this->is_ssd1305_()) { + this->command(SSD1305_COMMAND_SET_AREA_COLOR); + this->command(0x05); + } + + // Set mux ratio to [Y pixels - 1] (0xA8) this->command(SSD1306_COMMAND_SET_MULTIPLEX); this->command(this->get_height_internal() - 1); - this->command(SSD1306_COMMAND_SET_DISPLAY_OFFSET); - this->command(0x00); // no offset - this->command(SSD1306_COMMAND_SET_START_LINE | 0x00); // start at line 0 - this->command(SSD1306_COMMAND_CHARGE_PUMP); - if (this->external_vcc_) - this->command(0x10); - else - this->command(0x14); + // Set Y offset (0xD3) + this->command(SSD1306_COMMAND_SET_DISPLAY_OFFSET_Y); + this->command(0x00 + this->offset_y_); + // Set start line at line 0 (0x40) + this->command(SSD1306_COMMAND_SET_START_LINE | 0x00); + // SSD1305 does not have charge pump + if (!this->is_ssd1305_()) { + // Enable charge pump (0x8D) + this->command(SSD1306_COMMAND_CHARGE_PUMP); + if (this->external_vcc_) + this->command(0x10); + else + this->command(0x14); + } + + // Set addressing mode to horizontal (0x20) this->command(SSD1306_COMMAND_MEMORY_MODE); this->command(0x00); - this->command(SSD1306_COMMAND_SEGRE_MAP | 0x01); - this->command(SSD1306_COMMAND_COM_SCAN_DEC); + // X flip mode (0xA0, 0xA1) + this->command(SSD1306_COMMAND_SEGRE_MAP | this->flip_x_); + + // Y flip mode (0xC0, 0xC8) + this->command(SSD1306_COMMAND_COM_SCAN_INC | (this->flip_y_ << 3)); + + // Set pin configuration (0xDA) this->command(SSD1306_COMMAND_SET_COM_PINS); switch (this->model_) { case SSD1306_MODEL_128_32: @@ -67,25 +95,37 @@ void SSD1306::setup() { case SH1106_MODEL_128_64: case SSD1306_MODEL_64_48: case SH1106_MODEL_64_48: + case SSD1305_MODEL_128_32: + case SSD1305_MODEL_128_64: this->command(0x12); break; } + // Pre-charge period (0xD9) this->command(SSD1306_COMMAND_SET_PRE_CHARGE); if (this->external_vcc_) this->command(0x22); else this->command(0xF1); + // Set V_COM (0xDB) this->command(SSD1306_COMMAND_SET_VCOM_DETECT); this->command(0x00); + // Display output follow RAM (0xA4) this->command(SSD1306_COMMAND_DISPLAY_ALL_ON_RESUME); - this->command(SSD1306_NORMAL_DISPLAY); + // Inverse display mode (0xA6, 0xA7) + this->command(SSD1306_COMMAND_NORMAL_DISPLAY | this->invert_); + + // Disable scrolling mode (0x2E) this->command(SSD1306_COMMAND_DEACTIVATE_SCROLL); - set_brightness(this->brightness_); + // Contrast and brighrness + // SSD1306 does not have brightness setting + set_contrast(this->contrast_); + if (this->is_ssd1305_()) + set_brightness(this->brightness_); this->fill(Color::BLACK); // clear display - ensures we do not see garbage at power-on this->display(); // ...write buffer, which actually clears the display's memory @@ -101,12 +141,12 @@ void SSD1306::display() { this->command(SSD1306_COMMAND_COLUMN_ADDRESS); switch (this->model_) { case SSD1306_MODEL_64_48: - this->command(0x20); - this->command(0x20 + this->get_width_internal() - 1); + this->command(0x20 + this->offset_x_); + this->command(0x20 + this->offset_x_ + this->get_width_internal() - 1); break; default: - this->command(0); // Page start address, 0 - this->command(this->get_width_internal() - 1); + this->command(0 + this->offset_x_); // Page start address, 0 + this->command(this->get_width_internal() + this->offset_x_ - 1); break; } @@ -122,16 +162,28 @@ bool SSD1306::is_sh1106_() const { return this->model_ == SH1106_MODEL_96_16 || this->model_ == SH1106_MODEL_128_32 || this->model_ == SH1106_MODEL_128_64; } +bool SSD1306::is_ssd1305_() const { + return this->model_ == SSD1305_MODEL_128_64 || this->model_ == SSD1305_MODEL_128_64; +} void SSD1306::update() { this->do_update_(); this->display(); } +void SSD1306::set_contrast(float contrast) { + // validation + this->contrast_ = clamp(contrast, 0.0F, 1.0F); + // now write the new contrast level to the display (0x81) + this->command(SSD1306_COMMAND_SET_CONTRAST); + this->command(int(SSD1306_MAX_CONTRAST * (this->contrast_))); +} void SSD1306::set_brightness(float brightness) { // validation + if (!this->is_ssd1305_()) + return; this->brightness_ = clamp(brightness, 0.0F, 1.0F); - // now write the new brightness level to the display - this->command(SSD1306_COMMAND_SET_CONTRAST); - this->command(int(SSD1306_MAX_CONTRAST * (this->brightness_))); + // now write the new brightness level to the display (0x82) + this->command(SSD1305_COMMAND_SET_BRIGHTNESS); + this->command(int(SSD1305_MAX_BRIGHTNESS * (this->brightness_))); } bool SSD1306::is_on() { return this->is_on_; } void SSD1306::turn_on() { @@ -146,9 +198,11 @@ int SSD1306::get_height_internal() { switch (this->model_) { case SSD1306_MODEL_128_32: case SH1106_MODEL_128_32: + case SSD1305_MODEL_128_32: return 32; case SSD1306_MODEL_128_64: case SH1106_MODEL_128_64: + case SSD1305_MODEL_128_64: return 64; case SSD1306_MODEL_96_16: case SH1106_MODEL_96_16: @@ -166,6 +220,8 @@ int SSD1306::get_width_internal() { case SH1106_MODEL_128_32: case SSD1306_MODEL_128_64: case SH1106_MODEL_128_64: + case SSD1305_MODEL_128_32: + case SSD1305_MODEL_128_64: return 128; case SSD1306_MODEL_96_16: case SH1106_MODEL_96_16: @@ -227,6 +283,10 @@ const char *SSD1306::model_str_() { return "SH1106 96x16"; case SH1106_MODEL_64_48: return "SH1106 64x48"; + case SSD1305_MODEL_128_32: + return "SSD1305 128x32"; + case SSD1305_MODEL_128_64: + return "SSD1305 128x32"; default: return "Unknown"; } diff --git a/esphome/components/ssd1306_base/ssd1306_base.h b/esphome/components/ssd1306_base/ssd1306_base.h index 2c54af7a67..54cb10d153 100644 --- a/esphome/components/ssd1306_base/ssd1306_base.h +++ b/esphome/components/ssd1306_base/ssd1306_base.h @@ -16,6 +16,8 @@ enum SSD1306Model { SH1106_MODEL_128_64, SH1106_MODEL_96_16, SH1106_MODEL_64_48, + SSD1305_MODEL_128_32, + SSD1305_MODEL_128_64, }; class SSD1306 : public PollingComponent, public display::DisplayBuffer { @@ -29,8 +31,15 @@ class SSD1306 : public PollingComponent, public display::DisplayBuffer { void set_model(SSD1306Model model) { this->model_ = model; } void set_reset_pin(GPIOPin *reset_pin) { this->reset_pin_ = reset_pin; } void set_external_vcc(bool external_vcc) { this->external_vcc_ = external_vcc; } + void init_contrast(float contrast) { this->contrast_ = contrast; } + void set_contrast(float contrast); void init_brightness(float brightness) { this->brightness_ = brightness; } void set_brightness(float brightness); + void init_flip_x(boolean flip_x) { this->flip_x_ = flip_x; } + void init_flip_y(boolean flip_y) { this->flip_y_ = flip_y; } + void init_offset_x(uint8_t offset_x) { this->offset_x_ = offset_x; } + void init_offset_y(uint8_t offset_y) { this->offset_y_ = offset_y; } + void init_invert(boolean invert) { this->invert_ = invert; } bool is_on(); void turn_on(); void turn_off(); @@ -43,6 +52,7 @@ class SSD1306 : public PollingComponent, public display::DisplayBuffer { void init_reset_(); bool is_sh1106_() const; + bool is_ssd1305_() const; void draw_absolute_pixel_internal(int x, int y, Color color) override; @@ -55,7 +65,13 @@ class SSD1306 : public PollingComponent, public display::DisplayBuffer { GPIOPin *reset_pin_{nullptr}; bool external_vcc_{false}; bool is_on_{false}; + float contrast_{1.0}; float brightness_{1.0}; + bool flip_x_{true}; + bool flip_y_{true}; + uint8_t offset_x_{0}; + uint8_t offset_y_{0}; + bool invert_{false}; }; } // namespace ssd1306_base diff --git a/esphome/components/ssd1306_i2c/display.py b/esphome/components/ssd1306_i2c/display.py index 4b51a90431..c51ab5f93e 100644 --- a/esphome/components/ssd1306_i2c/display.py +++ b/esphome/components/ssd1306_i2c/display.py @@ -1,6 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import ssd1306_base, i2c +from esphome.components.ssd1306_base import _validate from esphome.const import CONF_ID, CONF_LAMBDA, CONF_PAGES AUTO_LOAD = ["ssd1306_base"] @@ -18,10 +19,11 @@ CONFIG_SCHEMA = cv.All( .extend(cv.COMPONENT_SCHEMA) .extend(i2c.i2c_device_schema(0x3C)), cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), + _validate, ) async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) - await ssd1306_base.setup_ssd1036(var, config) + await ssd1306_base.setup_ssd1306(var, config) await i2c.register_i2c_device(var, config) diff --git a/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp b/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp index 45fda4870e..fddea25fc8 100644 --- a/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp +++ b/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp @@ -25,6 +25,11 @@ void I2CSSD1306::dump_config() { ESP_LOGCONFIG(TAG, " Model: %s", this->model_str_()); LOG_PIN(" Reset Pin: ", this->reset_pin_); ESP_LOGCONFIG(TAG, " External VCC: %s", YESNO(this->external_vcc_)); + ESP_LOGCONFIG(TAG, " Flip X: %s", YESNO(this->flip_x_)); + ESP_LOGCONFIG(TAG, " Flip Y: %s", YESNO(this->flip_y_)); + ESP_LOGCONFIG(TAG, " Offset X: %d", this->offset_x_); + ESP_LOGCONFIG(TAG, " Offset Y: %d", this->offset_y_); + ESP_LOGCONFIG(TAG, " Inverted Color: %s", YESNO(this->invert_)); LOG_UPDATE_INTERVAL(this); if (this->error_code_ == COMMUNICATION_FAILED) { diff --git a/esphome/components/ssd1306_spi/display.py b/esphome/components/ssd1306_spi/display.py index f7dd1553ba..0af1168bde 100644 --- a/esphome/components/ssd1306_spi/display.py +++ b/esphome/components/ssd1306_spi/display.py @@ -2,6 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import spi, ssd1306_base +from esphome.components.ssd1306_base import _validate from esphome.const import CONF_DC_PIN, CONF_ID, CONF_LAMBDA, CONF_PAGES AUTO_LOAD = ["ssd1306_base"] @@ -20,12 +21,13 @@ CONFIG_SCHEMA = cv.All( .extend(cv.COMPONENT_SCHEMA) .extend(spi.spi_device_schema()), cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), + _validate, ) async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) - await ssd1306_base.setup_ssd1036(var, config) + await ssd1306_base.setup_ssd1306(var, config) await spi.register_spi_device(var, config) dc = await cg.gpio_pin_expression(config[CONF_DC_PIN]) diff --git a/esphome/components/ssd1306_spi/ssd1306_spi.cpp b/esphome/components/ssd1306_spi/ssd1306_spi.cpp index 5ef25b8139..33d474a8ee 100644 --- a/esphome/components/ssd1306_spi/ssd1306_spi.cpp +++ b/esphome/components/ssd1306_spi/ssd1306_spi.cpp @@ -22,6 +22,11 @@ void SPISSD1306::dump_config() { LOG_PIN(" DC Pin: ", this->dc_pin_); LOG_PIN(" Reset Pin: ", this->reset_pin_); ESP_LOGCONFIG(TAG, " External VCC: %s", YESNO(this->external_vcc_)); + ESP_LOGCONFIG(TAG, " Flip X: %s", YESNO(this->flip_x_)); + ESP_LOGCONFIG(TAG, " Flip Y: %s", YESNO(this->flip_y_)); + ESP_LOGCONFIG(TAG, " Offset X: %d", this->offset_x_); + ESP_LOGCONFIG(TAG, " Offset Y: %d", this->offset_y_); + ESP_LOGCONFIG(TAG, " Inverted Color: %s", YESNO(this->invert_)); LOG_UPDATE_INTERVAL(this); } void SPISSD1306::command(uint8_t value) { diff --git a/tests/test1.yaml b/tests/test1.yaml index 5bec56c80f..6109e9f5c2 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -2072,7 +2072,7 @@ display: reset_pin: GPIO23 address: 0x3C id: display1 - brightness: 60% + contrast: 60% pages: - id: page1 lambda: |- From ed3ad615d8500fd4844db457926e6d6fbb000569 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Wed, 22 Sep 2021 14:07:39 +0200 Subject: [PATCH 098/207] Fix compilation due to incompatibility between #1237 and IDF changes (#2372) --- esphome/components/ssd1306_base/ssd1306_base.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/ssd1306_base/ssd1306_base.h b/esphome/components/ssd1306_base/ssd1306_base.h index 54cb10d153..09417a2c10 100644 --- a/esphome/components/ssd1306_base/ssd1306_base.h +++ b/esphome/components/ssd1306_base/ssd1306_base.h @@ -35,11 +35,11 @@ class SSD1306 : public PollingComponent, public display::DisplayBuffer { void set_contrast(float contrast); void init_brightness(float brightness) { this->brightness_ = brightness; } void set_brightness(float brightness); - void init_flip_x(boolean flip_x) { this->flip_x_ = flip_x; } - void init_flip_y(boolean flip_y) { this->flip_y_ = flip_y; } + void init_flip_x(bool flip_x) { this->flip_x_ = flip_x; } + void init_flip_y(bool flip_y) { this->flip_y_ = flip_y; } void init_offset_x(uint8_t offset_x) { this->offset_x_ = offset_x; } void init_offset_y(uint8_t offset_y) { this->offset_y_ = offset_y; } - void init_invert(boolean invert) { this->invert_ = invert; } + void init_invert(bool invert) { this->invert_ = invert; } bool is_on(); void turn_on(); void turn_off(); From 0406e271007c539de4d7decd7692dea8a1f046ed Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Wed, 22 Sep 2021 19:07:57 +0200 Subject: [PATCH 099/207] Don't generate IDs with the name of loaded integrations (#2373) --- esphome/core/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/core/__init__.py b/esphome/core/__init__.py index 9ec7fe358d..8021fc2f53 100644 --- a/esphome/core/__init__.py +++ b/esphome/core/__init__.py @@ -312,7 +312,7 @@ class ID: if self.id is None: base = str(self.type).replace("::", "_").lower() name = "".join(c for c in base if c.isalnum() or c == "_") - used = set(registered_ids) | set(RESERVED_IDS) + used = set(registered_ids) | set(RESERVED_IDS) | CORE.loaded_integrations self.id = ensure_unique_string(name, used) return self.id From 262d69308dc45f9ec133ad9aae532dad81efaa8f Mon Sep 17 00:00:00 2001 From: Martin <25747549+martgras@users.noreply.github.com> Date: Wed, 22 Sep 2021 19:08:42 +0200 Subject: [PATCH 100/207] fix i2c scanning eror for Arduino (#2364) --- esphome/components/i2c/i2c_bus_arduino.cpp | 5 ++--- esphome/components/i2c/i2c_bus_esp_idf.cpp | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/esphome/components/i2c/i2c_bus_arduino.cpp b/esphome/components/i2c/i2c_bus_arduino.cpp index aba412c3f7..40d8049617 100644 --- a/esphome/components/i2c/i2c_bus_arduino.cpp +++ b/esphome/components/i2c/i2c_bus_arduino.cpp @@ -33,9 +33,8 @@ void ArduinoI2CBus::dump_config() { if (this->scan_) { ESP_LOGI(TAG, "Scanning i2c bus for active devices..."); uint8_t found = 0; - for (uint8_t address = 1; address < 120; address++) { - auto err = readv(address, nullptr, 0); - + for (uint8_t address = 8; address < 120; address++) { + auto err = writev(address, nullptr, 0); if (err == ERROR_OK) { ESP_LOGI(TAG, "Found i2c device at address 0x%02X", address); found++; diff --git a/esphome/components/i2c/i2c_bus_esp_idf.cpp b/esphome/components/i2c/i2c_bus_esp_idf.cpp index 4b93b41877..8bf97b63ec 100644 --- a/esphome/components/i2c/i2c_bus_esp_idf.cpp +++ b/esphome/components/i2c/i2c_bus_esp_idf.cpp @@ -43,8 +43,8 @@ void IDFI2CBus::dump_config() { if (this->scan_) { ESP_LOGI(TAG, "Scanning i2c bus for active devices..."); uint8_t found = 0; - for (uint8_t address = 1; address < 120; address++) { - auto err = readv(address, nullptr, 0); + for (uint8_t address = 8; address < 120; address++) { + auto err = writev(address, nullptr, 0); if (err == ERROR_OK) { ESP_LOGI(TAG, "Found i2c device at address 0x%02X", address); From f463cd98f8543a36ec9925083561796b088d030d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Sep 2021 19:50:11 +0200 Subject: [PATCH 101/207] Bump tzlocal from 2.1 to 3.0 (#2294) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Otto winter --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e97c79985f..95ca95430d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ PyYAML==5.4.1 paho-mqtt==1.5.1 colorama==0.4.4 tornado==6.1 -tzlocal==2.1 +tzlocal==3.0 pytz==2021.1 pyserial==3.5 platformio==5.2.0 From edb557f79e1cf3fc33a93d8da59748ea2c097fb0 Mon Sep 17 00:00:00 2001 From: Philipp Riederer Date: Wed, 22 Sep 2021 19:50:19 +0200 Subject: [PATCH 102/207] ledc: do not try to write_state to an uninitialized output (#1732) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Philipp Tölke Co-authored-by: Otto winter --- esphome/components/ledc/ledc_output.cpp | 11 +++++++++++ esphome/components/ledc/ledc_output.h | 1 + 2 files changed, 12 insertions(+) diff --git a/esphome/components/ledc/ledc_output.cpp b/esphome/components/ledc/ledc_output.cpp index 77610a476f..21a747e34d 100644 --- a/esphome/components/ledc/ledc_output.cpp +++ b/esphome/components/ledc/ledc_output.cpp @@ -31,6 +31,11 @@ optional ledc_bit_depth_for_frequency(float frequency) { } void LEDCOutput::write_state(float state) { + if (!initialized_) { + ESP_LOGW(TAG, "LEDC output hasn't been initialized yet!"); + return; + } + if (this->pin_->is_inverted()) state = 1.0f - state; @@ -81,6 +86,7 @@ void LEDCOutput::setup() { chan_conf.duty = inverted_ == pin_->is_inverted() ? 0 : (1U << bit_depth_); chan_conf.hpoint = 0; ledc_channel_config(&chan_conf); + initialized_ = true; #endif } @@ -101,8 +107,13 @@ void LEDCOutput::update_frequency(float frequency) { this->frequency_ = frequency; #ifdef USE_ARDUINO ledcSetup(this->channel_, frequency, this->bit_depth_); + initialized_ = true; #endif // USE_ARDUINO #ifdef USE_ESP_IDF + if (!initialized_) { + ESP_LOGW(TAG, "LEDC output hasn't been initialized yet!"); + return; + } auto speed_mode = channel_ < 8 ? LEDC_HIGH_SPEED_MODE : LEDC_LOW_SPEED_MODE; auto timer_num = static_cast((channel_ % 8) / 2); diff --git a/esphome/components/ledc/ledc_output.h b/esphome/components/ledc/ledc_output.h index f810ce1e35..e02cefd170 100644 --- a/esphome/components/ledc/ledc_output.h +++ b/esphome/components/ledc/ledc_output.h @@ -36,6 +36,7 @@ class LEDCOutput : public output::FloatOutput, public Component { uint8_t bit_depth_{}; float frequency_{}; float duty_{0.0f}; + bool initialized_ = false; }; template class SetFrequencyAction : public Action { From b398d826c17adcb8ef9d6c330fca7d496cab8ba8 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Wed, 22 Sep 2021 20:07:43 +0200 Subject: [PATCH 103/207] Fix two i2c error code return errors (#2375) --- esphome/components/pn532_i2c/pn532_i2c.cpp | 4 +++- esphome/components/scd30/scd30.cpp | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/pn532_i2c/pn532_i2c.cpp b/esphome/components/pn532_i2c/pn532_i2c.cpp index ef7480ec25..e7c99e94b0 100644 --- a/esphome/components/pn532_i2c/pn532_i2c.cpp +++ b/esphome/components/pn532_i2c/pn532_i2c.cpp @@ -12,7 +12,9 @@ namespace pn532_i2c { static const char *const TAG = "pn532_i2c"; -bool PN532I2C::write_data(const std::vector &data) { return this->write(data.data(), data.size()); } +bool PN532I2C::write_data(const std::vector &data) { + return this->write(data.data(), data.size()) == i2c::ERROR_OK; +} bool PN532I2C::read_data(std::vector &data, uint8_t len) { delay(1); diff --git a/esphome/components/scd30/scd30.cpp b/esphome/components/scd30/scd30.cpp index 30775fdea4..d1246d9766 100644 --- a/esphome/components/scd30/scd30.cpp +++ b/esphome/components/scd30/scd30.cpp @@ -198,7 +198,7 @@ bool SCD30Component::write_command_(uint16_t command, uint16_t data) { raw[2] = data >> 8; raw[3] = data & 0xFF; raw[4] = sht_crc_(raw[2], raw[3]); - return this->write(raw, 5); + return this->write(raw, 5) == i2c::ERROR_OK; } uint8_t SCD30Component::sht_crc_(uint8_t data1, uint8_t data2) { From 5ddba719c56bc59a88f41c4d2ba896402774d5c1 Mon Sep 17 00:00:00 2001 From: Stijn Tintel Date: Wed, 22 Sep 2021 21:13:24 +0300 Subject: [PATCH 104/207] Fix ir_climate on ESP32-C3 (#2314) Co-authored-by: Otto winter --- esphome/components/remote_base/remote_base.cpp | 4 ++-- .../remote_transmitter/remote_transmitter_esp32.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/remote_base/remote_base.cpp b/esphome/components/remote_base/remote_base.cpp index a43f743ab6..a853c9849e 100644 --- a/esphome/components/remote_base/remote_base.cpp +++ b/esphome/components/remote_base/remote_base.cpp @@ -14,8 +14,8 @@ RemoteRMTChannel::RemoteRMTChannel(uint8_t mem_block_num) : mem_block_num_(mem_b } void RemoteRMTChannel::config_rmt(rmt_config_t &rmt) { - if (rmt_channel_t(int(this->channel_) + this->mem_block_num_) > RMT_CHANNEL_7) { - this->mem_block_num_ = int(RMT_CHANNEL_7) - int(this->channel_) + 1; + if (rmt_channel_t(int(this->channel_) + this->mem_block_num_) >= RMT_CHANNEL_MAX) { + this->mem_block_num_ = int(RMT_CHANNEL_MAX) - int(this->channel_); ESP_LOGW(TAG, "Not enough RMT memory blocks available, reduced to %i blocks.", this->mem_block_num_); } rmt.channel = this->channel_; diff --git a/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp b/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp index ff53d0be84..a1f7663a24 100644 --- a/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +++ b/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp @@ -92,7 +92,7 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen val = this->from_microseconds(static_cast(val)); do { - int32_t item = std::min(val, 32767); + int32_t item = std::min(val, int32_t(32767)); val -= item; if (rmt_i % 2 == 0) { From ea6a7a22ff451d164285b92574d4dd846f37d111 Mon Sep 17 00:00:00 2001 From: Paul Monigatti Date: Thu, 23 Sep 2021 20:45:41 +1200 Subject: [PATCH 105/207] Fix ESP8266 ADC (#2376) --- esphome/components/adc/adc_sensor.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esphome/components/adc/adc_sensor.cpp b/esphome/components/adc/adc_sensor.cpp index 9a05c1d66b..c8f8b0e0f6 100644 --- a/esphome/components/adc/adc_sensor.cpp +++ b/esphome/components/adc/adc_sensor.cpp @@ -1,9 +1,13 @@ #include "adc_sensor.h" #include "esphome/core/log.h" +#ifdef USE_ESP8266 #ifdef USE_ADC_SENSOR_VCC #include ADC_MODE(ADC_VCC) +#else +#include +#endif #endif namespace esphome { From 17dcba8f8ac932460ca87c68cfcd052958ed0305 Mon Sep 17 00:00:00 2001 From: Paul Monigatti Date: Thu, 23 Sep 2021 21:19:17 +1200 Subject: [PATCH 106/207] Fix: Pin flags code generation returning FLAG_NONE (#2377) Co-authored-by: Otto winter --- esphome/cpp_generator.py | 17 +++++++++++++++++ esphome/pins.py | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/esphome/cpp_generator.py b/esphome/cpp_generator.py index 691f45f91f..8460c1d462 100644 --- a/esphome/cpp_generator.py +++ b/esphome/cpp_generator.py @@ -310,6 +310,19 @@ class FloatLiteral(Literal): return f"{self.f}f" +class BinOpExpression(Expression): + __slots__ = ("op", "lhs", "rhs") + + def __init__(self, op: str, lhs: SafeExpType, rhs: SafeExpType): + # Remove every None on end + self.op = op + self.lhs = safe_exp(lhs) + self.rhs = safe_exp(rhs) + + def __str__(self): + return f"{self.lhs} {self.op} {self.rhs}" + + def safe_exp(obj: SafeExpType) -> Expression: """Try to convert obj to an expression by automatically converting native python types to expressions/literals. @@ -756,6 +769,10 @@ class MockObj(Expression): next_op = "->" return MockObj(f"{self.base}[{item}]", next_op) + def __or__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression("|", self, other) + return MockObj(op) + class MockObjEnum(MockObj): def __init__(self, *args, **kwargs): diff --git a/esphome/pins.py b/esphome/pins.py index ae762c1a1a..2b3adce86d 100644 --- a/esphome/pins.py +++ b/esphome/pins.py @@ -90,7 +90,7 @@ def gpio_flags_expr(mode): CONF_PULLDOWN: cg.gpio_Flags.FLAG_PULLDOWN, } active_flags = [v for k, v in FLAGS_MAPPING.items() if mode.get(k)] - if active_flags: + if not active_flags: return cg.gpio_Flags.FLAG_NONE return reduce(operator.or_, active_flags) From a27a8841913e1447d08b9c64c86c686780463842 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Thu, 23 Sep 2021 11:53:10 +0200 Subject: [PATCH 107/207] Add missing MockObj operators (#2378) --- esphome/cpp_generator.py | 183 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 176 insertions(+), 7 deletions(-) diff --git a/esphome/cpp_generator.py b/esphome/cpp_generator.py index 8460c1d462..cf357f2814 100644 --- a/esphome/cpp_generator.py +++ b/esphome/cpp_generator.py @@ -313,14 +313,26 @@ class FloatLiteral(Literal): class BinOpExpression(Expression): __slots__ = ("op", "lhs", "rhs") - def __init__(self, op: str, lhs: SafeExpType, rhs: SafeExpType): - # Remove every None on end - self.op = op + def __init__(self, lhs: SafeExpType, op: str, rhs: SafeExpType): self.lhs = safe_exp(lhs) + self.op = op self.rhs = safe_exp(rhs) def __str__(self): - return f"{self.lhs} {self.op} {self.rhs}" + # Surround with parentheses to ensure generated code has same + # order as python one + return f"({self.lhs} {self.op} {self.rhs})" + + +class UnaryOpExpression(Expression): + __slots__ = ("op", "exp") + + def __init__(self, op: str, exp: SafeExpType): + self.op = op + self.exp = safe_exp(exp) + + def __str__(self): + return f"({self.op}{self.exp})" def safe_exp(obj: SafeExpType) -> Expression: @@ -729,6 +741,7 @@ class MockObj(Expression): return MockObj(f"new {self.base}", "->") def template(self, *args: SafeExpType) -> "MockObj": + """Apply template parameters to this object.""" if len(args) != 1 or not isinstance(args[0], TemplateArguments): args = TemplateArguments(*args) else: @@ -749,6 +762,10 @@ class MockObj(Expression): return MockObjEnum(enum=name, is_class=is_class, base=self.base, op=self.op) def operator(self, name: str) -> "MockObj": + """Various other operations. + + Named operator because it's a C++ keyword and can't occur in valid code. + """ if name == "ref": return MockObj(f"{self.base} &", "") if name == "ptr": @@ -769,8 +786,160 @@ class MockObj(Expression): next_op = "->" return MockObj(f"{self.base}[{item}]", next_op) + def __lt__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(self, "<", other) + return MockObj(op) + + def __le__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(self, "<=", other) + return MockObj(op) + + def __eq__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(self, "==", other) + return MockObj(op) + + def __ne__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(self, "!=", other) + return MockObj(op) + + def __gt__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(self, ">", other) + return MockObj(op) + + def __ge__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(self, ">=", other) + return MockObj(op) + + def __add__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(self, "+", other) + return MockObj(op) + + def __sub__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(self, "-", other) + return MockObj(op) + + def __mul__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(self, "*", other) + return MockObj(op) + + def __truediv__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(self, "/", other) + return MockObj(op) + + def __mod__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(self, "%", other) + return MockObj(op) + + def __lshift__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(self, "<<", other) + return MockObj(op) + + def __rshift__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(self, ">>", other) + return MockObj(op) + + def __and__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(self, "&", other) + return MockObj(op) + + def __xor__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(self, "^", other) + return MockObj(op) + def __or__(self, other: SafeExpType) -> "MockObj": - op = BinOpExpression("|", self, other) + op = BinOpExpression(self, "|", other) + return MockObj(op) + + def __radd__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(other, "+", self) + return MockObj(op) + + def __rsub__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(other, "-", self) + return MockObj(op) + + def __rmul__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(other, "*", self) + return MockObj(op) + + def __rtruediv__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(other, "/", self) + return MockObj(op) + + def __rmod__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(other, "%", self) + return MockObj(op) + + def __rlshift__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(other, "<<", self) + return MockObj(op) + + def __rrshift__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(other, ">>", self) + return MockObj(op) + + def __rand__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(other, "&", self) + return MockObj(op) + + def __rxor__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(other, "^", self) + return MockObj(op) + + def __ror__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(other, "|", self) + return MockObj(op) + + def __iadd__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(self, "+=", other) + return MockObj(op) + + def __isub__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(self, "-=", other) + return MockObj(op) + + def __imul__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(self, "*=", other) + return MockObj(op) + + def __itruediv__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(self, "/=", other) + return MockObj(op) + + def __imod__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(self, "%=", other) + return MockObj(op) + + def __ilshift__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(self, "<<=", other) + return MockObj(op) + + def __irshift__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(self, ">>=", other) + return MockObj(op) + + def __iand__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(self, "&=", other) + return MockObj(op) + + def __ixor__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(self, "^=", other) + return MockObj(op) + + def __ior__(self, other: SafeExpType) -> "MockObj": + op = BinOpExpression(self, "|=", other) + return MockObj(op) + + def __neg__(self) -> "MockObj": + op = UnaryOpExpression("-", self) + return MockObj(op) + + def __pos__(self) -> "MockObj": + op = UnaryOpExpression("+", self) + return MockObj(op) + + def __invert__(self) -> "MockObj": + op = UnaryOpExpression("~", self) return MockObj(op) @@ -807,10 +976,10 @@ class MockObjClass(MockObj): self._parents += paren._parents def inherits_from(self, other: "MockObjClass") -> bool: - if self == other: + if str(self) == str(other): return True for parent in self._parents: - if parent == other: + if str(parent) == str(other): return True return False From 210a9a41621c3e391531d31d8c8822a4e9d79ec7 Mon Sep 17 00:00:00 2001 From: Christian Taedcke Date: Thu, 23 Sep 2021 18:24:29 +0200 Subject: [PATCH 108/207] Fix esp-idf pinmask bit-shift overflow (#2380) --- esphome/components/esp32/gpio_idf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/esp32/gpio_idf.h b/esphome/components/esp32/gpio_idf.h index 6a383afcae..a83c6bbc97 100644 --- a/esphome/components/esp32/gpio_idf.h +++ b/esphome/components/esp32/gpio_idf.h @@ -20,7 +20,7 @@ class IDFInternalGPIOPin : public InternalGPIOPin { } void pin_mode(gpio::Flags flags) override { gpio_config_t conf{}; - conf.pin_bit_mask = 1 << static_cast(pin_); + conf.pin_bit_mask = 1ULL << static_cast(pin_); conf.mode = flags_to_mode_(flags); conf.pull_up_en = flags & gpio::FLAG_PULLUP ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE; conf.pull_down_en = flags & gpio::FLAG_PULLDOWN ? GPIO_PULLDOWN_ENABLE : GPIO_PULLDOWN_DISABLE; From 963b28181fdbcc2c3e77dec5adc7b4c653a6bc7d Mon Sep 17 00:00:00 2001 From: Christian Taedcke Date: Thu, 23 Sep 2021 20:11:40 +0200 Subject: [PATCH 109/207] Always execute i2c bus recovery on setup (#2379) --- esphome/components/i2c/i2c_bus_arduino.cpp | 28 ++++++++++++++++ esphome/components/i2c/i2c_bus_arduino.h | 3 ++ esphome/components/i2c/i2c_bus_esp_idf.cpp | 38 ++++++++++++++++++++++ esphome/components/i2c/i2c_bus_esp_idf.h | 3 ++ 4 files changed, 72 insertions(+) diff --git a/esphome/components/i2c/i2c_bus_arduino.cpp b/esphome/components/i2c/i2c_bus_arduino.cpp index 40d8049617..b983fb7636 100644 --- a/esphome/components/i2c/i2c_bus_arduino.cpp +++ b/esphome/components/i2c/i2c_bus_arduino.cpp @@ -2,6 +2,7 @@ #include "i2c_bus_arduino.h" #include "esphome/core/log.h" +#include #include namespace esphome { @@ -10,6 +11,7 @@ namespace i2c { static const char *const TAG = "i2c.arduino"; void ArduinoI2CBus::setup() { + recover(); #ifdef USE_ESP32 static uint8_t next_bus_num = 0; if (next_bus_num == 0) @@ -90,6 +92,32 @@ ErrorCode ArduinoI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cn return ERROR_UNKNOWN; } +void ArduinoI2CBus::recover() { + // Perform I2C bus recovery, see + // https://www.analog.com/media/en/technical-documentation/application-notes/54305147357414AN686_0.pdf + // or see the linux kernel implementation, e.g. + // https://elixir.bootlin.com/linux/v5.14.6/source/drivers/i2c/i2c-core-base.c#L200 + + // try to get about 100kHz toggle frequency + const auto half_period_usec = 1000000 / 100000 / 2; + const auto recover_scl_periods = 9; + + // configure scl as output + pinMode(scl_pin_, OUTPUT); // NOLINT + + // set scl high + digitalWrite(scl_pin_, 1); // NOLINT + + // in total generate 9 falling-rising edges + for (auto i = 0; i < recover_scl_periods; i++) { + delayMicroseconds(half_period_usec); + digitalWrite(scl_pin_, 0); // NOLINT + delayMicroseconds(half_period_usec); + digitalWrite(scl_pin_, 1); // NOLINT + } + + delayMicroseconds(half_period_usec); +} } // namespace i2c } // namespace esphome diff --git a/esphome/components/i2c/i2c_bus_arduino.h b/esphome/components/i2c/i2c_bus_arduino.h index 220027b3d4..49be0c358c 100644 --- a/esphome/components/i2c/i2c_bus_arduino.h +++ b/esphome/components/i2c/i2c_bus_arduino.h @@ -22,6 +22,9 @@ class ArduinoI2CBus : public I2CBus, public Component { void set_scl_pin(uint8_t scl_pin) { scl_pin_ = scl_pin; } void set_frequency(uint32_t frequency) { frequency_ = frequency; } + private: + void recover(); + protected: TwoWire *wire_; bool scan_; diff --git a/esphome/components/i2c/i2c_bus_esp_idf.cpp b/esphome/components/i2c/i2c_bus_esp_idf.cpp index 8bf97b63ec..5ce5d40c00 100644 --- a/esphome/components/i2c/i2c_bus_esp_idf.cpp +++ b/esphome/components/i2c/i2c_bus_esp_idf.cpp @@ -1,6 +1,7 @@ #ifdef USE_ESP_IDF #include "i2c_bus_esp_idf.h" +#include "esphome/core/hal.h" #include "esphome/core/log.h" #include @@ -13,6 +14,8 @@ void IDFI2CBus::setup() { static i2c_port_t next_port = 0; port_ = next_port++; + recover(); + i2c_config_t conf{}; memset(&conf, 0, sizeof(conf)); conf.mode = I2C_MODE_MASTER; @@ -141,6 +144,41 @@ ErrorCode IDFI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cnt) { return ERROR_OK; } +void IDFI2CBus::recover() { + // Perform I2C bus recovery, see + // https://www.analog.com/media/en/technical-documentation/application-notes/54305147357414AN686_0.pdf + // or see the linux kernel implementation, e.g. + // https://elixir.bootlin.com/linux/v5.14.6/source/drivers/i2c/i2c-core-base.c#L200 + + // try to get about 100kHz toggle frequency + const auto half_period_usec = 1000000 / 100000 / 2; + const auto recover_scl_periods = 9; + const gpio_num_t scl_pin = static_cast(scl_pin_); + + // configure scl as output + gpio_config_t conf{}; + conf.pin_bit_mask = 1ULL << static_cast(scl_pin_); + conf.mode = GPIO_MODE_OUTPUT; + conf.pull_up_en = GPIO_PULLUP_DISABLE; + conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + conf.intr_type = GPIO_INTR_DISABLE; + + gpio_config(&conf); + + // set scl high + gpio_set_level(scl_pin, 1); + + // in total generate 9 falling-rising edges + for (auto i = 0; i < recover_scl_periods; i++) { + delayMicroseconds(half_period_usec); + gpio_set_level(scl_pin, 0); + delayMicroseconds(half_period_usec); + gpio_set_level(scl_pin, 1); + } + + delayMicroseconds(half_period_usec); +} + } // namespace i2c } // namespace esphome diff --git a/esphome/components/i2c/i2c_bus_esp_idf.h b/esphome/components/i2c/i2c_bus_esp_idf.h index c7e67145a3..9985e618f8 100644 --- a/esphome/components/i2c/i2c_bus_esp_idf.h +++ b/esphome/components/i2c/i2c_bus_esp_idf.h @@ -24,6 +24,9 @@ class IDFI2CBus : public I2CBus, public Component { void set_scl_pullup_enabled(bool scl_pullup_enabled) { scl_pullup_enabled_ = scl_pullup_enabled; } void set_frequency(uint32_t frequency) { frequency_ = frequency; } + private: + void recover(); + protected: i2c_port_t port_; bool scan_; From aea2491fa4f7e649241265ae0f2d21b697b48930 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Sep 2021 23:36:19 +0200 Subject: [PATCH 110/207] Bump voluptuous from 0.12.1 to 0.12.2 (#2381) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 95ca95430d..537a802e06 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -voluptuous==0.12.1 +voluptuous==0.12.2 PyYAML==5.4.1 paho-mqtt==1.5.1 colorama==0.4.4 From 52dd79691b82e32fd9e61ade165c551109de7af8 Mon Sep 17 00:00:00 2001 From: Maurice Makaay Date: Fri, 24 Sep 2021 14:15:22 +0200 Subject: [PATCH 111/207] Read unencrypted DSMR telegrams in chunks (#2382) Co-authored-by: Maurice Makaay --- esphome/components/dsmr/dsmr.cpp | 31 +++++++++++++++++++------------ esphome/components/dsmr/dsmr.h | 1 + 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/esphome/components/dsmr/dsmr.cpp b/esphome/components/dsmr/dsmr.cpp index 54c4343cfe..b798fe5d44 100644 --- a/esphome/components/dsmr/dsmr.cpp +++ b/esphome/components/dsmr/dsmr.cpp @@ -20,19 +20,22 @@ void Dsmr::loop() { } void Dsmr::receive_telegram_() { - while (available()) { + int count = MAX_BYTES_PER_LOOP; + while (available() && count-- > 0) { const char c = read(); - if (c == '/') { // header: forward slash + // Find a new telegram header, i.e. forward slash. + if (c == '/') { ESP_LOGV(TAG, "Header found"); header_found_ = true; footer_found_ = false; telegram_len_ = 0; } - if (!header_found_) continue; - if (telegram_len_ >= MAX_TELEGRAM_LENGTH) { // Buffer overflow + + // Check for buffer overflow. + if (telegram_len_ >= MAX_TELEGRAM_LENGTH) { header_found_ = false; footer_found_ = false; ESP_LOGE(TAG, "Error: Message larger than buffer"); @@ -45,18 +48,22 @@ void Dsmr::receive_telegram_() { while (c == '(' && (telegram_[telegram_len_ - 1] == '\n' || telegram_[telegram_len_ - 1] == '\r')) telegram_len_--; + // Store the byte in the buffer. telegram_[telegram_len_] = c; telegram_len_++; - if (c == '!') { // footer: exclamation mark + + // Check for a footer, i.e. exlamation mark, followed by a hex checksum. + if (c == '!') { ESP_LOGV(TAG, "Footer found"); footer_found_ = true; - } else { - if (footer_found_ && c == 10) { // last \n after footer - header_found_ = false; - // Parse message - if (parse_telegram()) - return; - } + continue; + } + // Check for the end of the hex checksum, i.e. a newline. + if (footer_found_ && c == '\n') { + header_found_ = false; + // Parse the telegram and publish sensor values. + if (parse_telegram()) + return; } } } diff --git a/esphome/components/dsmr/dsmr.h b/esphome/components/dsmr/dsmr.h index dfee3b338a..4f9a66b3d0 100644 --- a/esphome/components/dsmr/dsmr.h +++ b/esphome/components/dsmr/dsmr.h @@ -17,6 +17,7 @@ namespace esphome { namespace dsmr { static constexpr uint32_t MAX_TELEGRAM_LENGTH = 1500; +static constexpr uint32_t MAX_BYTES_PER_LOOP = 50; static constexpr uint32_t POLL_TIMEOUT = 1000; using namespace ::dsmr::fields; From aec02afcdc36d0c626db727d27dd46888c2578de Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Fri, 24 Sep 2021 18:02:28 +0200 Subject: [PATCH 112/207] Fix clang-tidy header filter (#2385) * Fix clang-tidy header filter * Allow private members * Fix clang-tidy detections * Run clang-format * Fix remaining detections * Fix graph * Run clang-format --- .clang-tidy | 12 +- .../airthings_wave_plus.cpp | 4 +- esphome/components/am43/am43.cpp | 6 +- esphome/components/am43/cover/am43_cover.cpp | 6 +- esphome/components/anova/anova.cpp | 4 +- esphome/components/anova/anova.h | 2 +- esphome/components/api/api_frame_helper.h | 9 +- esphome/components/api/api_noise_context.h | 2 +- esphome/components/api/api_server.h | 2 +- .../atc_mithermometer/atc_mithermometer.cpp | 12 +- .../atc_mithermometer/atc_mithermometer.h | 6 +- esphome/components/ble_client/automation.h | 10 +- esphome/components/ble_client/ble_client.cpp | 28 ++--- esphome/components/ble_client/ble_client.h | 13 ++- .../components/ble_client/sensor/automation.h | 5 +- .../ble_client/sensor/ble_sensor.cpp | 12 +- .../components/ble_client/sensor/ble_sensor.h | 4 +- .../ble_client/switch/ble_switch.cpp | 4 +- .../ble_presence/ble_presence_device.h | 4 +- esphome/components/ble_scanner/ble_scanner.h | 2 +- esphome/components/daly_bms/daly_bms.cpp | 18 +-- esphome/components/daly_bms/daly_bms.h | 4 +- esphome/components/esp32/gpio_arduino.cpp | 2 +- esphome/components/esp32/gpio_arduino.h | 2 +- esphome/components/esp32/gpio_idf.cpp | 2 +- esphome/components/esp32/gpio_idf.h | 13 ++- esphome/components/esp32_ble/ble.h | 2 + esphome/components/esp32_ble/queue.h | 26 +++-- .../esp32_ble_beacon/esp32_ble_beacon.h | 4 + .../esp32_ble_server/ble_characteristic.h | 4 +- .../components/esp32_ble_server/ble_server.h | 1 + .../esp32_ble_tracker/esp32_ble_tracker.cpp | 38 +++---- .../esp32_ble_tracker/esp32_ble_tracker.h | 25 +++-- esphome/components/esp32_ble_tracker/queue.h | 26 ++--- .../components/esp32_camera/esp32_camera.h | 1 + .../esp32_improv/esp32_improv_component.cpp | 4 +- .../esp32_improv/esp32_improv_component.h | 3 +- esphome/components/esp8266/gpio.cpp | 2 +- esphome/components/esp8266/gpio.h | 2 +- .../ethernet/ethernet_component.cpp | 28 ++--- .../components/ethernet/ethernet_component.h | 7 +- .../components/fastled_base/fastled_light.h | 104 +++++++++--------- esphome/components/graph/graph.cpp | 73 ++++++------ esphome/components/graph/graph.h | 29 ++--- esphome/components/i2c/i2c_bus_arduino.cpp | 4 +- esphome/components/i2c/i2c_bus_arduino.h | 2 +- esphome/components/i2c/i2c_bus_esp_idf.cpp | 4 +- esphome/components/i2c/i2c_bus_esp_idf.h | 2 +- .../inkbird_ibsth1_mini.cpp | 4 +- .../inkbird_ibsth1_mini/inkbird_ibsth1_mini.h | 2 +- .../components/inkbird_ibsth1_mini/sensor.py | 6 +- esphome/components/ledc/ledc_output.h | 1 + esphome/components/light/addressable_light.h | 2 +- .../light/addressable_light_wrapper.h | 2 +- esphome/components/light/esp_range_view.h | 2 + esphome/components/midea/appliance_base.h | 17 +-- .../pvvx_mithermometer/pvvx_mithermometer.cpp | 12 +- .../pvvx_mithermometer/pvvx_mithermometer.h | 6 +- .../components/remote_base/midea_protocol.h | 2 +- esphome/components/remote_base/remote_base.h | 4 +- .../remote_receiver/remote_receiver_esp32.cpp | 28 ++--- .../remote_transmitter/remote_transmitter.h | 2 +- .../remote_transmitter_esp32.cpp | 8 +- esphome/components/socket/headers.h | 27 +++-- esphome/components/t6615/t6615.h | 2 +- .../uart/uart_component_esp8266.cpp | 10 +- .../components/uart/uart_component_esp8266.h | 2 +- .../uart/uart_component_esp_idf.cpp | 4 +- .../components/uart/uart_component_esp_idf.h | 2 +- .../wifi_info/wifi_info_text_sensor.h | 2 +- .../xiaomi_miscale/xiaomi_miscale.cpp | 20 ++-- .../xiaomi_miscale/xiaomi_miscale.h | 10 +- esphome/core/gpio.h | 6 +- esphome/core/hal.h | 2 +- esphome/core/helpers.h | 4 +- script/clang-tidy | 3 +- 76 files changed, 404 insertions(+), 367 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 98a1568e5a..79276f81c3 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -135,10 +135,14 @@ CheckOptions: value: 'UPPER_CASE' - key: readability-identifier-naming.ParameterCase value: 'lower_case' - - key: readability-identifier-naming.PrivateMemberPrefix - value: 'NO_PRIVATE_MEMBERS_ALWAYS_USE_PROTECTED' - - key: readability-identifier-naming.PrivateMethodPrefix - value: 'NO_PRIVATE_METHODS_ALWAYS_USE_PROTECTED' + - key: readability-identifier-naming.PrivateMemberCase + value: 'lower_case' + - key: readability-identifier-naming.PrivateMemberSuffix + value: '_' + - key: readability-identifier-naming.PrivateMethodCase + value: 'lower_case' + - key: readability-identifier-naming.PrivateMethodSuffix + value: '_' - key: readability-identifier-naming.ClassMemberCase value: 'lower_case' - key: readability-identifier-naming.ClassMemberCase diff --git a/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp b/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp index a8153ae87a..0eaffbd889 100644 --- a/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp +++ b/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp @@ -31,7 +31,7 @@ void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt break; } this->handle_ = chr->handle; - this->node_state = esp32_ble_tracker::ClientState::Established; + this->node_state = esp32_ble_tracker::ClientState::ESTABLISHED; request_read_values_(); break; @@ -99,7 +99,7 @@ bool AirthingsWavePlus::is_valid_co2_value_(uint16_t co2) { return 0 <= co2 && c void AirthingsWavePlus::loop() {} void AirthingsWavePlus::update() { - if (this->node_state != esp32_ble_tracker::ClientState::Established) { + if (this->node_state != esp32_ble_tracker::ClientState::ESTABLISHED) { if (!parent()->enabled) { ESP_LOGW(TAG, "Reconnecting to device"); parent()->set_enabled(true); diff --git a/esphome/components/am43/am43.cpp b/esphome/components/am43/am43.cpp index 2130b334be..a62e3bb6df 100644 --- a/esphome/components/am43/am43.cpp +++ b/esphome/components/am43/am43.cpp @@ -31,7 +31,7 @@ void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_i } case ESP_GATTC_DISCONNECT_EVT: { this->logged_in_ = false; - this->node_state = espbt::ClientState::Idle; + this->node_state = espbt::ClientState::IDLE; if (this->battery_ != nullptr) this->battery_->publish_state(NAN); if (this->illuminance_ != nullptr) @@ -54,7 +54,7 @@ void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_i break; } case ESP_GATTC_REG_FOR_NOTIFY_EVT: { - this->node_state = espbt::ClientState::Established; + this->node_state = espbt::ClientState::ESTABLISHED; this->update(); break; } @@ -93,7 +93,7 @@ void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_i } void Am43::update() { - if (this->node_state != espbt::ClientState::Established) { + if (this->node_state != espbt::ClientState::ESTABLISHED) { ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->parent_->address_str().c_str()); return; } diff --git a/esphome/components/am43/cover/am43_cover.cpp b/esphome/components/am43/cover/am43_cover.cpp index fd337ba17b..274c527760 100644 --- a/esphome/components/am43/cover/am43_cover.cpp +++ b/esphome/components/am43/cover/am43_cover.cpp @@ -24,7 +24,7 @@ void Am43Component::setup() { } void Am43Component::loop() { - if (this->node_state == espbt::ClientState::Established && !this->logged_in_) { + if (this->node_state == espbt::ClientState::ESTABLISHED && !this->logged_in_) { auto packet = this->encoder_->get_send_pin_request(this->pin_); auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length, @@ -46,7 +46,7 @@ CoverTraits Am43Component::get_traits() { } void Am43Component::control(const CoverCall &call) { - if (this->node_state != espbt::ClientState::Established) { + if (this->node_state != espbt::ClientState::ESTABLISHED) { ESP_LOGW(TAG, "[%s] Cannot send cover control, not connected", this->get_name().c_str()); return; } @@ -98,7 +98,7 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ break; } case ESP_GATTC_REG_FOR_NOTIFY_EVT: { - this->node_state = espbt::ClientState::Established; + this->node_state = espbt::ClientState::ESTABLISHED; break; } case ESP_GATTC_NOTIFY_EVT: { diff --git a/esphome/components/anova/anova.cpp b/esphome/components/anova/anova.cpp index 8e0724ad13..5d9afddc74 100644 --- a/esphome/components/anova/anova.cpp +++ b/esphome/components/anova/anova.cpp @@ -72,7 +72,7 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_ break; } case ESP_GATTC_REG_FOR_NOTIFY_EVT: { - this->node_state = espbt::ClientState::Established; + this->node_state = espbt::ClientState::ESTABLISHED; this->current_request_ = 0; this->update(); break; @@ -129,7 +129,7 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_ void Anova::set_unit_of_measurement(const char *unit) { this->fahrenheit_ = !strncmp(unit, "f", 1); } void Anova::update() { - if (this->node_state != espbt::ClientState::Established) + if (this->node_state != espbt::ClientState::ESTABLISHED) return; if (this->current_request_ < 2) { diff --git a/esphome/components/anova/anova.h b/esphome/components/anova/anova.h index 554024e389..2e6910f326 100644 --- a/esphome/components/anova/anova.h +++ b/esphome/components/anova/anova.h @@ -27,7 +27,7 @@ class Anova : public climate::Climate, public esphome::ble_client::BLEClientNode esp_ble_gattc_cb_param_t *param) override; void dump_config() override; float get_setup_priority() const override { return setup_priority::DATA; } - climate::ClimateTraits traits() { + climate::ClimateTraits traits() override { auto traits = climate::ClimateTraits(); traits.set_supports_current_temperature(true); traits.set_supports_heat_mode(true); diff --git a/esphome/components/api/api_frame_helper.h b/esphome/components/api/api_frame_helper.h index 44df629b2f..7fdb26fd40 100644 --- a/esphome/components/api/api_frame_helper.h +++ b/esphome/components/api/api_frame_helper.h @@ -1,7 +1,8 @@ #pragma once #include -#include #include +#include +#include #include "esphome/core/defines.h" @@ -75,8 +76,8 @@ class APIFrameHelper { class APINoiseFrameHelper : public APIFrameHelper { public: APINoiseFrameHelper(std::unique_ptr socket, std::shared_ptr ctx) - : socket_(std::move(socket)), ctx_(ctx) {} - ~APINoiseFrameHelper(); + : socket_(std::move(socket)), ctx_(std::move(std::move(ctx))) {} + ~APINoiseFrameHelper() override; APIError init() override; APIError loop() override; APIError read_packet(ReadPacketBuffer *buffer) override; @@ -136,7 +137,7 @@ class APINoiseFrameHelper : public APIFrameHelper { class APIPlaintextFrameHelper : public APIFrameHelper { public: APIPlaintextFrameHelper(std::unique_ptr socket) : socket_(std::move(socket)) {} - ~APIPlaintextFrameHelper() = default; + ~APIPlaintextFrameHelper() override = default; APIError init() override; APIError loop() override; APIError read_packet(ReadPacketBuffer *buffer) override; diff --git a/esphome/components/api/api_noise_context.h b/esphome/components/api/api_noise_context.h index fba6b65a26..324e69d945 100644 --- a/esphome/components/api/api_noise_context.h +++ b/esphome/components/api/api_noise_context.h @@ -11,7 +11,7 @@ using psk_t = std::array; class APINoiseContext { public: - void set_psk(psk_t psk) { psk_ = std::move(psk); } + void set_psk(psk_t psk) { psk_ = psk; } const psk_t &get_psk() const { return psk_; } protected: diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index d659f24358..056d9f54f2 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -32,7 +32,7 @@ class APIServer : public Component, public Controller { void set_reboot_timeout(uint32_t reboot_timeout); #ifdef USE_API_NOISE - void set_noise_psk(psk_t psk) { noise_ctx_->set_psk(std::move(psk)); } + void set_noise_psk(psk_t psk) { noise_ctx_->set_psk(psk); } std::shared_ptr get_noise_ctx() { return noise_ctx_; } #endif // USE_API_NOISE diff --git a/esphome/components/atc_mithermometer/atc_mithermometer.cpp b/esphome/components/atc_mithermometer/atc_mithermometer.cpp index b04d634103..42c30598ad 100644 --- a/esphome/components/atc_mithermometer/atc_mithermometer.cpp +++ b/esphome/components/atc_mithermometer/atc_mithermometer.cpp @@ -25,14 +25,14 @@ bool ATCMiThermometer::parse_device(const esp32_ble_tracker::ESPBTDevice &device bool success = false; for (auto &service_data : device.get_service_datas()) { - auto res = parse_header(service_data); + auto res = parse_header_(service_data); if (!res.has_value()) { continue; } - if (!(parse_message(service_data.data, *res))) { + if (!(parse_message_(service_data.data, *res))) { continue; } - if (!(report_results(res, device.address_str()))) { + if (!(report_results_(res, device.address_str()))) { continue; } if (res->temperature.has_value() && this->temperature_ != nullptr) @@ -49,7 +49,7 @@ bool ATCMiThermometer::parse_device(const esp32_ble_tracker::ESPBTDevice &device return success; } -optional ATCMiThermometer::parse_header(const esp32_ble_tracker::ServiceData &service_data) { +optional ATCMiThermometer::parse_header_(const esp32_ble_tracker::ServiceData &service_data) { ParseResult result; if (!service_data.uuid.contains(0x1A, 0x18)) { ESP_LOGVV(TAG, "parse_header(): no service data UUID magic bytes."); @@ -68,7 +68,7 @@ optional ATCMiThermometer::parse_header(const esp32_ble_tracker::Se return result; } -bool ATCMiThermometer::parse_message(const std::vector &message, ParseResult &result) { +bool ATCMiThermometer::parse_message_(const std::vector &message, ParseResult &result) { // Byte 0-5 mac in correct order // Byte 6-7 Temperature in uint16 // Byte 8 Humidity in percent @@ -101,7 +101,7 @@ bool ATCMiThermometer::parse_message(const std::vector &message, ParseR return true; } -bool ATCMiThermometer::report_results(const optional &result, const std::string &address) { +bool ATCMiThermometer::report_results_(const optional &result, const std::string &address) { if (!result.has_value()) { ESP_LOGVV(TAG, "report_results(): no results available."); return false; diff --git a/esphome/components/atc_mithermometer/atc_mithermometer.h b/esphome/components/atc_mithermometer/atc_mithermometer.h index 291c1d96cd..ca079bf8c1 100644 --- a/esphome/components/atc_mithermometer/atc_mithermometer.h +++ b/esphome/components/atc_mithermometer/atc_mithermometer.h @@ -36,9 +36,9 @@ class ATCMiThermometer : public Component, public esp32_ble_tracker::ESPBTDevice sensor::Sensor *battery_level_{nullptr}; sensor::Sensor *battery_voltage_{nullptr}; - optional parse_header(const esp32_ble_tracker::ServiceData &service_data); - bool parse_message(const std::vector &message, ParseResult &result); - bool report_results(const optional &result, const std::string &address); + optional parse_header_(const esp32_ble_tracker::ServiceData &service_data); + bool parse_message_(const std::vector &message, ParseResult &result); + bool report_results_(const optional &result, const std::string &address); }; } // namespace atc_mithermometer diff --git a/esphome/components/ble_client/automation.h b/esphome/components/ble_client/automation.h index d015d4019c..6c374046ba 100644 --- a/esphome/components/ble_client/automation.h +++ b/esphome/components/ble_client/automation.h @@ -11,11 +11,12 @@ class BLEClientConnectTrigger : public Trigger<>, public BLEClientNode { public: explicit BLEClientConnectTrigger(BLEClient *parent) { parent->register_ble_node(this); } void loop() override {} - void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { + void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t *param) override { if (event == ESP_GATTC_OPEN_EVT && param->open.status == ESP_GATT_OK) this->trigger(); if (event == ESP_GATTC_SEARCH_CMPL_EVT) - this->node_state = espbt::ClientState::Established; + this->node_state = espbt::ClientState::ESTABLISHED; } }; @@ -23,11 +24,12 @@ class BLEClientDisconnectTrigger : public Trigger<>, public BLEClientNode { public: explicit BLEClientDisconnectTrigger(BLEClient *parent) { parent->register_ble_node(this); } void loop() override {} - void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { + void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t *param) override { if (event == ESP_GATTC_DISCONNECT_EVT && memcmp(param->disconnect.remote_bda, this->parent_->remote_bda, 6) == 0) this->trigger(); if (event == ESP_GATTC_SEARCH_CMPL_EVT) - this->node_state = espbt::ClientState::Established; + this->node_state = espbt::ClientState::ESTABLISHED; } }; diff --git a/esphome/components/ble_client/ble_client.cpp b/esphome/components/ble_client/ble_client.cpp index f584cdf79e..8ff516d735 100644 --- a/esphome/components/ble_client/ble_client.cpp +++ b/esphome/components/ble_client/ble_client.cpp @@ -17,12 +17,12 @@ void BLEClient::setup() { ESP_LOGE(TAG, "gattc app register failed. app_id=%d code=%d", this->app_id, ret); this->mark_failed(); } - this->set_states(espbt::ClientState::Idle); + this->set_states_(espbt::ClientState::IDLE); this->enabled = true; } void BLEClient::loop() { - if (this->state() == espbt::ClientState::Discovered) { + if (this->state() == espbt::ClientState::DISCOVERED) { this->connect(); } for (auto *node : this->nodes_) @@ -39,11 +39,11 @@ bool BLEClient::parse_device(const espbt::ESPBTDevice &device) { return false; if (device.address_uint64() != this->address) return false; - if (this->state() != espbt::ClientState::Idle) + if (this->state() != espbt::ClientState::IDLE) return false; ESP_LOGD(TAG, "Found device at MAC address [%s]", device.address_str().c_str()); - this->set_states(espbt::ClientState::Discovered); + this->set_states_(espbt::ClientState::DISCOVERED); auto addr = device.address_uint64(); this->remote_bda[0] = (addr >> 40) & 0xFF; @@ -69,7 +69,7 @@ std::string BLEClient::address_str() const { void BLEClient::set_enabled(bool enabled) { if (enabled == this->enabled) return; - if (!enabled && this->state() != espbt::ClientState::Idle) { + if (!enabled && this->state() != espbt::ClientState::IDLE) { ESP_LOGI(TAG, "[%s] Disabling BLE client.", this->address_str().c_str()); auto ret = esp_ble_gattc_close(this->gattc_if, this->conn_id); if (ret) { @@ -84,9 +84,9 @@ void BLEClient::connect() { auto ret = esp_ble_gattc_open(this->gattc_if, this->remote_bda, BLE_ADDR_TYPE_PUBLIC, true); if (ret) { ESP_LOGW(TAG, "esp_ble_gattc_open error, address=%s status=%d", this->address_str().c_str(), ret); - this->set_states(espbt::ClientState::Idle); + this->set_states_(espbt::ClientState::IDLE); } else { - this->set_states(espbt::ClientState::Connecting); + this->set_states_(espbt::ClientState::CONNECTING); } } @@ -97,7 +97,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es if (event != ESP_GATTC_REG_EVT && esp_gattc_if != ESP_GATT_IF_NONE && esp_gattc_if != this->gattc_if) return; - bool all_established = this->all_nodes_established(); + bool all_established = this->all_nodes_established_(); switch (event) { case ESP_GATTC_REG_EVT: { @@ -113,7 +113,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es ESP_LOGV(TAG, "[%s] ESP_GATTC_OPEN_EVT", this->address_str().c_str()); if (param->open.status != ESP_GATT_OK) { ESP_LOGW(TAG, "connect to %s failed, status=%d", this->address_str().c_str(), param->open.status); - this->set_states(espbt::ClientState::Idle); + this->set_states_(espbt::ClientState::IDLE); break; } this->conn_id = param->open.conn_id; @@ -126,7 +126,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es case ESP_GATTC_CFG_MTU_EVT: { if (param->cfg_mtu.status != ESP_GATT_OK) { ESP_LOGW(TAG, "cfg_mtu to %s failed, status %d", this->address_str().c_str(), param->cfg_mtu.status); - this->set_states(espbt::ClientState::Idle); + this->set_states_(espbt::ClientState::IDLE); break; } ESP_LOGV(TAG, "cfg_mtu status %d, mtu %d", param->cfg_mtu.status, param->cfg_mtu.mtu); @@ -141,7 +141,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es for (auto &svc : this->services_) delete svc; // NOLINT(cppcoreguidelines-owning-memory) this->services_.clear(); - this->set_states(espbt::ClientState::Idle); + this->set_states_(espbt::ClientState::IDLE); break; } case ESP_GATTC_SEARCH_RES_EVT: { @@ -160,8 +160,8 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es ESP_LOGI(TAG, " start_handle: 0x%x end_handle: 0x%x", svc->start_handle, svc->end_handle); svc->parse_characteristics(); } - this->set_states(espbt::ClientState::Connected); - this->set_state(espbt::ClientState::Established); + this->set_states_(espbt::ClientState::CONNECTED); + this->set_state(espbt::ClientState::ESTABLISHED); break; } case ESP_GATTC_REG_FOR_NOTIFY_EVT: { @@ -192,7 +192,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es node->gattc_event_handler(event, esp_gattc_if, param); // Delete characteristics after clients have used them to save RAM. - if (!all_established && this->all_nodes_established()) { + if (!all_established && this->all_nodes_established_()) { for (auto &svc : this->services_) delete svc; // NOLINT(cppcoreguidelines-owning-memory) this->services_.clear(); diff --git a/esphome/components/ble_client/ble_client.h b/esphome/components/ble_client/ble_client.h index a69460e8b6..4a17ccb79b 100644 --- a/esphome/components/ble_client/ble_client.h +++ b/esphome/components/ble_client/ble_client.h @@ -82,10 +82,11 @@ class BLEClient : public espbt::ESPBTClient, public Component { void dump_config() override; void loop() override; - void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); + void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t *param) override; bool parse_device(const espbt::ESPBTDevice &device) override; void on_scan_end() override {} - void connect(); + void connect() override; void set_address(uint64_t address) { this->address = address; } @@ -116,16 +117,16 @@ class BLEClient : public espbt::ESPBTClient, public Component { std::string address_str() const; protected: - void set_states(espbt::ClientState st) { + void set_states_(espbt::ClientState st) { this->set_state(st); for (auto &node : nodes_) node->node_state = st; } - bool all_nodes_established() { - if (this->state() != espbt::ClientState::Established) + bool all_nodes_established_() { + if (this->state() != espbt::ClientState::ESTABLISHED) return false; for (auto &node : nodes_) - if (node->node_state != espbt::ClientState::Established) + if (node->node_state != espbt::ClientState::ESTABLISHED) return false; return true; } diff --git a/esphome/components/ble_client/sensor/automation.h b/esphome/components/ble_client/sensor/automation.h index 2255a5ac55..2baaafe2ec 100644 --- a/esphome/components/ble_client/sensor/automation.h +++ b/esphome/components/ble_client/sensor/automation.h @@ -11,10 +11,11 @@ namespace ble_client { class BLESensorNotifyTrigger : public Trigger, public BLESensor { public: explicit BLESensorNotifyTrigger(BLESensor *sensor) { sensor_ = sensor; } - void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { + void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t *param) override { switch (event) { case ESP_GATTC_SEARCH_CMPL_EVT: { - this->sensor_->node_state = espbt::ClientState::Established; + this->sensor_->node_state = espbt::ClientState::ESTABLISHED; break; } case ESP_GATTC_NOTIFY_EVT: { diff --git a/esphome/components/ble_client/sensor/ble_sensor.cpp b/esphome/components/ble_client/sensor/ble_sensor.cpp index 4459163389..7a2e3ddc8b 100644 --- a/esphome/components/ble_client/sensor/ble_sensor.cpp +++ b/esphome/components/ble_client/sensor/ble_sensor.cpp @@ -71,7 +71,7 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga ESP_LOGW(TAG, "esp_ble_gattc_register_for_notify failed, status=%d", status); } } else { - this->node_state = espbt::ClientState::Established; + this->node_state = espbt::ClientState::ESTABLISHED; } break; } @@ -84,7 +84,7 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga } if (param->read.handle == this->handle) { this->status_clear_warning(); - this->publish_state(this->parse_data(param->read.value, param->read.value_len)); + this->publish_state(this->parse_data_(param->read.value, param->read.value_len)); } break; } @@ -93,11 +93,11 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga break; ESP_LOGV(TAG, "[%s] ESP_GATTC_NOTIFY_EVT: handle=0x%x, value=0x%x", this->get_name().c_str(), param->notify.handle, param->notify.value[0]); - this->publish_state(this->parse_data(param->notify.value, param->notify.value_len)); + this->publish_state(this->parse_data_(param->notify.value, param->notify.value_len)); break; } case ESP_GATTC_REG_FOR_NOTIFY_EVT: { - this->node_state = espbt::ClientState::Established; + this->node_state = espbt::ClientState::ESTABLISHED; break; } default: @@ -105,7 +105,7 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga } } -float BLESensor::parse_data(uint8_t *value, uint16_t value_len) { +float BLESensor::parse_data_(uint8_t *value, uint16_t value_len) { if (this->data_to_value_func_.has_value()) { std::vector data(value, value + value_len); return (*this->data_to_value_func_)(data); @@ -115,7 +115,7 @@ float BLESensor::parse_data(uint8_t *value, uint16_t value_len) { } void BLESensor::update() { - if (this->node_state != espbt::ClientState::Established) { + if (this->node_state != espbt::ClientState::ESTABLISHED) { ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->get_name().c_str()); return; } diff --git a/esphome/components/ble_client/sensor/ble_sensor.h b/esphome/components/ble_client/sensor/ble_sensor.h index 52c9e9d5ca..d9f310b575 100644 --- a/esphome/components/ble_client/sensor/ble_sensor.h +++ b/esphome/components/ble_client/sensor/ble_sensor.h @@ -32,13 +32,13 @@ class BLESensor : public sensor::Sensor, public PollingComponent, public BLEClie void set_descr_uuid16(uint16_t uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); } void set_descr_uuid32(uint32_t uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); } void set_descr_uuid128(uint8_t *uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_raw(uuid); } - void set_data_to_value(data_to_value_t &&lambda_) { this->data_to_value_func_ = lambda_; } + void set_data_to_value(data_to_value_t &&lambda) { this->data_to_value_func_ = lambda; } void set_enable_notify(bool notify) { this->notify_ = notify; } uint16_t handle; protected: uint32_t hash_base() override; - float parse_data(uint8_t *value, uint16_t value_len); + float parse_data_(uint8_t *value, uint16_t value_len); optional data_to_value_func_{}; bool notify_; espbt::ESPBTUUID service_uuid_; diff --git a/esphome/components/ble_client/switch/ble_switch.cpp b/esphome/components/ble_client/switch/ble_switch.cpp index 00593da9d6..6de5252404 100644 --- a/esphome/components/ble_client/switch/ble_switch.cpp +++ b/esphome/components/ble_client/switch/ble_switch.cpp @@ -21,10 +21,10 @@ void BLEClientSwitch::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_i this->publish_state(this->parent_->enabled); break; case ESP_GATTC_OPEN_EVT: - this->node_state = espbt::ClientState::Established; + this->node_state = espbt::ClientState::ESTABLISHED; break; case ESP_GATTC_DISCONNECT_EVT: - this->node_state = espbt::ClientState::Idle; + this->node_state = espbt::ClientState::IDLE; this->publish_state(this->parent_->enabled); break; default: diff --git a/esphome/components/ble_presence/ble_presence_device.h b/esphome/components/ble_presence/ble_presence_device.h index 40cda89e62..dcccf844d2 100644 --- a/esphome/components/ble_presence/ble_presence_device.h +++ b/esphome/components/ble_presence/ble_presence_device.h @@ -93,8 +93,8 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff, float get_setup_priority() const override { return setup_priority::DATA; } protected: - enum MATCH_TYPE { MATCH_BY_MAC_ADDRESS, MATCH_BY_SERVICE_UUID, MATCH_BY_IBEACON_UUID }; - MATCH_TYPE match_by_; + enum MatchType { MATCH_BY_MAC_ADDRESS, MATCH_BY_SERVICE_UUID, MATCH_BY_IBEACON_UUID }; + MatchType match_by_; bool found_{false}; diff --git a/esphome/components/ble_scanner/ble_scanner.h b/esphome/components/ble_scanner/ble_scanner.h index 542d5047ec..b330eff696 100644 --- a/esphome/components/ble_scanner/ble_scanner.h +++ b/esphome/components/ble_scanner/ble_scanner.h @@ -15,7 +15,7 @@ namespace ble_scanner { class BLEScanner : public text_sensor::TextSensor, public esp32_ble_tracker::ESPBTDeviceListener, public Component { public: bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override { - this->publish_state("{\"timestamp\":" + to_string(::time(NULL)) + + this->publish_state("{\"timestamp\":" + to_string(::time(nullptr)) + "," "\"address\":\"" + device.address_str() + diff --git a/esphome/components/daly_bms/daly_bms.cpp b/esphome/components/daly_bms/daly_bms.cpp index 19e8f12e1c..44c05f0686 100644 --- a/esphome/components/daly_bms/daly_bms.cpp +++ b/esphome/components/daly_bms/daly_bms.cpp @@ -26,25 +26,25 @@ void DalyBmsComponent::dump_config() { } void DalyBmsComponent::update() { - this->request_data(DALY_REQUEST_BATTERY_LEVEL); - this->request_data(DALY_REQUEST_MIN_MAX_VOLTAGE); - this->request_data(DALY_REQUEST_MIN_MAX_TEMPERATURE); - this->request_data(DALY_REQUEST_MOS); - this->request_data(DALY_REQUEST_STATUS); - this->request_data(DALY_REQUEST_TEMPERATURE); + this->request_data_(DALY_REQUEST_BATTERY_LEVEL); + this->request_data_(DALY_REQUEST_MIN_MAX_VOLTAGE); + this->request_data_(DALY_REQUEST_MIN_MAX_TEMPERATURE); + this->request_data_(DALY_REQUEST_MOS); + this->request_data_(DALY_REQUEST_STATUS); + this->request_data_(DALY_REQUEST_TEMPERATURE); std::vector get_battery_level_data; int available_data = this->available(); if (available_data >= DALY_FRAME_SIZE) { get_battery_level_data.resize(available_data); this->read_array(get_battery_level_data.data(), available_data); - this->decode_data(get_battery_level_data); + this->decode_data_(get_battery_level_data); } } float DalyBmsComponent::get_setup_priority() const { return setup_priority::DATA; } -void DalyBmsComponent::request_data(uint8_t data_id) { +void DalyBmsComponent::request_data_(uint8_t data_id) { uint8_t request_message[DALY_FRAME_SIZE]; request_message[0] = 0xA5; // Start Flag @@ -66,7 +66,7 @@ void DalyBmsComponent::request_data(uint8_t data_id) { this->flush(); } -void DalyBmsComponent::decode_data(std::vector data) { +void DalyBmsComponent::decode_data_(std::vector data) { auto it = data.begin(); while ((it = std::find(it, data.end(), 0xA5)) != data.end()) { diff --git a/esphome/components/daly_bms/daly_bms.h b/esphome/components/daly_bms/daly_bms.h index e4f48776dd..b5d4c8ae39 100644 --- a/esphome/components/daly_bms/daly_bms.h +++ b/esphome/components/daly_bms/daly_bms.h @@ -54,8 +54,8 @@ class DalyBmsComponent : public PollingComponent, public uart::UARTDevice { float get_setup_priority() const override; protected: - void request_data(uint8_t data_id); - void decode_data(std::vector data); + void request_data_(uint8_t data_id); + void decode_data_(std::vector data); sensor::Sensor *voltage_sensor_{nullptr}; sensor::Sensor *current_sensor_{nullptr}; diff --git a/esphome/components/esp32/gpio_arduino.cpp b/esphome/components/esp32/gpio_arduino.cpp index 11de1e13e6..c4bb21a0aa 100644 --- a/esphome/components/esp32/gpio_arduino.cpp +++ b/esphome/components/esp32/gpio_arduino.cpp @@ -21,7 +21,7 @@ ISRInternalGPIOPin ArduinoInternalGPIOPin::to_isr() const { return ISRInternalGPIOPin((void *) arg); } -void ArduinoInternalGPIOPin::attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const { +void ArduinoInternalGPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const { uint8_t arduino_mode = DISABLED; switch (type) { case gpio::INTERRUPT_RISING_EDGE: diff --git a/esphome/components/esp32/gpio_arduino.h b/esphome/components/esp32/gpio_arduino.h index a077723075..e88d39b1a8 100644 --- a/esphome/components/esp32/gpio_arduino.h +++ b/esphome/components/esp32/gpio_arduino.h @@ -23,7 +23,7 @@ class ArduinoInternalGPIOPin : public InternalGPIOPin { bool is_inverted() const override { return inverted_; } protected: - void attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const override; + void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override; uint8_t pin_; bool inverted_; diff --git a/esphome/components/esp32/gpio_idf.cpp b/esphome/components/esp32/gpio_idf.cpp index d662d5519a..478b28a89a 100644 --- a/esphome/components/esp32/gpio_idf.cpp +++ b/esphome/components/esp32/gpio_idf.cpp @@ -8,7 +8,7 @@ namespace esp32 { static const char *const TAG = "esp32"; -bool IDFInternalGPIOPin::isr_service_installed_ = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +bool IDFInternalGPIOPin::isr_service_installed = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) struct ISRPinArg { gpio_num_t pin; diff --git a/esphome/components/esp32/gpio_idf.h b/esphome/components/esp32/gpio_idf.h index a83c6bbc97..448151cd0f 100644 --- a/esphome/components/esp32/gpio_idf.h +++ b/esphome/components/esp32/gpio_idf.h @@ -21,7 +21,7 @@ class IDFInternalGPIOPin : public InternalGPIOPin { void pin_mode(gpio::Flags flags) override { gpio_config_t conf{}; conf.pin_bit_mask = 1ULL << static_cast(pin_); - conf.mode = flags_to_mode_(flags); + conf.mode = flags_to_mode(flags); conf.pull_up_en = flags & gpio::FLAG_PULLUP ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE; conf.pull_down_en = flags & gpio::FLAG_PULLDOWN ? GPIO_PULLDOWN_ENABLE : GPIO_PULLDOWN_DISABLE; conf.intr_type = GPIO_INTR_DISABLE; @@ -36,7 +36,7 @@ class IDFInternalGPIOPin : public InternalGPIOPin { bool is_inverted() const override { return inverted_; } protected: - static gpio_mode_t flags_to_mode_(gpio::Flags flags) { + static gpio_mode_t flags_to_mode(gpio::Flags flags) { flags = (gpio::Flags)(flags & ~(gpio::FLAG_PULLUP | gpio::FLAG_PULLDOWN)); if (flags == gpio::FLAG_NONE) { return GPIO_MODE_DISABLE; @@ -55,7 +55,7 @@ class IDFInternalGPIOPin : public InternalGPIOPin { return GPIO_MODE_DISABLE; } } - void attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const override { + void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override { gpio_int_type_t idf_type = GPIO_INTR_ANYEDGE; switch (type) { case gpio::INTERRUPT_RISING_EDGE: @@ -76,9 +76,9 @@ class IDFInternalGPIOPin : public InternalGPIOPin { } gpio_set_intr_type(pin_, idf_type); gpio_intr_enable(pin_); - if (!isr_service_installed_) { + if (!isr_service_installed) { gpio_install_isr_service(ESP_INTR_FLAG_LEVEL5); - isr_service_installed_ = true; + isr_service_installed = true; } gpio_isr_handler_add(pin_, func, arg); } @@ -87,7 +87,8 @@ class IDFInternalGPIOPin : public InternalGPIOPin { bool inverted_; gpio_drive_cap_t drive_strength_; gpio::Flags flags_; - static bool isr_service_installed_; + // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) + static bool isr_service_installed; }; } // namespace esp32 diff --git a/esphome/components/esp32_ble/ble.h b/esphome/components/esp32_ble/ble.h index 008eba3235..0477dee070 100644 --- a/esphome/components/esp32_ble/ble.h +++ b/esphome/components/esp32_ble/ble.h @@ -19,6 +19,7 @@ namespace esphome { namespace esp32_ble { +// NOLINTNEXTLINE(modernize-use-using) typedef struct { void *peer_device; bool connected; @@ -65,6 +66,7 @@ class ESP32BLE : public Component { BLEAdvertising *advertising_; }; +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) extern ESP32BLE *global_ble; } // namespace esp32_ble diff --git a/esphome/components/esp32_ble/queue.h b/esphome/components/esp32_ble/queue.h index 8fb2803237..8d05eca058 100644 --- a/esphome/components/esp32_ble/queue.h +++ b/esphome/components/esp32_ble/queue.h @@ -28,33 +28,33 @@ namespace esp32_ble { template class Queue { public: - Queue() { m = xSemaphoreCreateMutex(); } + Queue() { m_ = xSemaphoreCreateMutex(); } void push(T *element) { if (element == nullptr) return; - if (xSemaphoreTake(m, 5L / portTICK_PERIOD_MS)) { - q.push(element); - xSemaphoreGive(m); + if (xSemaphoreTake(m_, 5L / portTICK_PERIOD_MS)) { + q_.push(element); + xSemaphoreGive(m_); } } T *pop() { T *element = nullptr; - if (xSemaphoreTake(m, 5L / portTICK_PERIOD_MS)) { - if (!q.empty()) { - element = q.front(); - q.pop(); + if (xSemaphoreTake(m_, 5L / portTICK_PERIOD_MS)) { + if (!q_.empty()) { + element = q_.front(); + q_.pop(); } - xSemaphoreGive(m); + xSemaphoreGive(m_); } return element; } protected: - std::queue q; - SemaphoreHandle_t m; + std::queue q_; + SemaphoreHandle_t m_; }; // Received GAP, GATTC and GATTS events are only queued, and get processed in the main loop(). @@ -105,11 +105,13 @@ class BLEEvent { }; union { + // NOLINTNEXTLINE(readability-identifier-naming) struct gap_event { esp_gap_ble_cb_event_t gap_event; esp_ble_gap_cb_param_t gap_param; } gap; + // NOLINTNEXTLINE(readability-identifier-naming) struct gattc_event { esp_gattc_cb_event_t gattc_event; esp_gatt_if_t gattc_if; @@ -117,6 +119,7 @@ class BLEEvent { uint8_t data[64]; } gattc; + // NOLINTNEXTLINE(readability-identifier-naming) struct gatts_event { esp_gatts_cb_event_t gatts_event; esp_gatt_if_t gatts_if; @@ -124,6 +127,7 @@ class BLEEvent { uint8_t data[64]; } gatts; } event_; + // NOLINTNEXTLINE(readability-identifier-naming) enum ble_event_t : uint8_t { GAP, GATTC, diff --git a/esphome/components/esp32_ble_beacon/esp32_ble_beacon.h b/esphome/components/esp32_ble_beacon/esp32_ble_beacon.h index d0ef73899c..80ad2041f2 100644 --- a/esphome/components/esp32_ble_beacon/esp32_ble_beacon.h +++ b/esphome/components/esp32_ble_beacon/esp32_ble_beacon.h @@ -9,6 +9,7 @@ namespace esphome { namespace esp32_ble_beacon { +// NOLINTNEXTLINE(modernize-use-using) typedef struct { uint8_t flags[3]; uint8_t length; @@ -17,6 +18,7 @@ typedef struct { uint16_t beacon_type; } __attribute__((packed)) esp_ble_ibeacon_head_t; +// NOLINTNEXTLINE(modernize-use-using) typedef struct { uint8_t proximity_uuid[16]; uint16_t major; @@ -24,6 +26,7 @@ typedef struct { uint8_t measured_power; } __attribute__((packed)) esp_ble_ibeacon_vendor_t; +// NOLINTNEXTLINE(modernize-use-using) typedef struct { esp_ble_ibeacon_head_t ibeacon_head; esp_ble_ibeacon_vendor_t ibeacon_vendor; @@ -50,6 +53,7 @@ class ESP32BLEBeacon : public Component { uint16_t minor_{}; }; +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) extern ESP32BLEBeacon *global_esp32_ble_beacon; } // namespace esp32_ble_beacon diff --git a/esphome/components/esp32_ble_server/ble_characteristic.h b/esphome/components/esp32_ble_server/ble_characteristic.h index d2467dd176..d7af3a934a 100644 --- a/esphome/components/esp32_ble_server/ble_characteristic.h +++ b/esphome/components/esp32_ble_server/ble_characteristic.h @@ -24,7 +24,7 @@ class BLEService; class BLECharacteristic { public: - BLECharacteristic(const ESPBTUUID uuid, uint32_t properties); + BLECharacteristic(ESPBTUUID uuid, uint32_t properties); void set_value(const uint8_t *data, size_t length); void set_value(std::vector value); @@ -49,7 +49,7 @@ class BLECharacteristic { void do_create(BLEService *service); void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); - void on_write(const std::function &)> &&func) { this->on_write_ = std::move(func); } + void on_write(const std::function &)> &&func) { this->on_write_ = func; } void add_descriptor(BLEDescriptor *descriptor); diff --git a/esphome/components/esp32_ble_server/ble_server.h b/esphome/components/esp32_ble_server/ble_server.h index 7df44b4c61..9f7e8b8fc0 100644 --- a/esphome/components/esp32_ble_server/ble_server.h +++ b/esphome/components/esp32_ble_server/ble_server.h @@ -87,6 +87,7 @@ class BLEServer : public Component { } state_{INIT}; }; +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) extern BLEServer *global_ble_server; } // namespace esp32_ble_server diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index 3ca250d52d..9e987a994a 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -50,29 +50,29 @@ void ESP32BLETracker::setup() { return; } - global_esp32_ble_tracker->start_scan(true); + global_esp32_ble_tracker->start_scan_(true); } void ESP32BLETracker::loop() { BLEEvent *ble_event = this->ble_events_.pop(); while (ble_event != nullptr) { if (ble_event->type_) - this->real_gattc_event_handler(ble_event->event_.gattc.gattc_event, ble_event->event_.gattc.gattc_if, - &ble_event->event_.gattc.gattc_param); + this->real_gattc_event_handler_(ble_event->event_.gattc.gattc_event, ble_event->event_.gattc.gattc_if, + &ble_event->event_.gattc.gattc_param); else - this->real_gap_event_handler(ble_event->event_.gap.gap_event, &ble_event->event_.gap.gap_param); + this->real_gap_event_handler_(ble_event->event_.gap.gap_event, &ble_event->event_.gap.gap_param); delete ble_event; // NOLINT(cppcoreguidelines-owning-memory) ble_event = this->ble_events_.pop(); } bool connecting = false; for (auto *client : this->clients_) { - if (client->state() == ClientState::Connecting || client->state() == ClientState::Discovered) + if (client->state() == ClientState::CONNECTING || client->state() == ClientState::DISCOVERED) connecting = true; } if (!connecting && xSemaphoreTake(this->scan_end_lock_, 0L)) { xSemaphoreGive(this->scan_end_lock_); - global_esp32_ble_tracker->start_scan(false); + global_esp32_ble_tracker->start_scan_(false); } if (xSemaphoreTake(this->scan_result_lock_, 5L / portTICK_PERIOD_MS)) { @@ -94,7 +94,7 @@ void ESP32BLETracker::loop() { for (auto *client : this->clients_) if (client->parse_device(device)) { found = true; - if (client->state() == ClientState::Discovered) { + if (client->state() == ClientState::DISCOVERED) { esp_ble_gap_stop_scanning(); if (xSemaphoreTake(this->scan_end_lock_, 10L / portTICK_PERIOD_MS)) { xSemaphoreGive(this->scan_end_lock_); @@ -196,7 +196,7 @@ bool ESP32BLETracker::ble_setup() { return true; } -void ESP32BLETracker::start_scan(bool first) { +void ESP32BLETracker::start_scan_(bool first) { if (!xSemaphoreTake(this->scan_end_lock_, 0L)) { ESP_LOGW(TAG, "Cannot start scan!"); return; @@ -233,38 +233,38 @@ void ESP32BLETracker::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_ga global_esp32_ble_tracker->ble_events_.push(gap_event); } // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) -void ESP32BLETracker::real_gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { +void ESP32BLETracker::real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { switch (event) { case ESP_GAP_BLE_SCAN_RESULT_EVT: - global_esp32_ble_tracker->gap_scan_result(param->scan_rst); + global_esp32_ble_tracker->gap_scan_result_(param->scan_rst); break; case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: - global_esp32_ble_tracker->gap_scan_set_param_complete(param->scan_param_cmpl); + global_esp32_ble_tracker->gap_scan_set_param_complete_(param->scan_param_cmpl); break; case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: - global_esp32_ble_tracker->gap_scan_start_complete(param->scan_start_cmpl); + global_esp32_ble_tracker->gap_scan_start_complete_(param->scan_start_cmpl); break; case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: - global_esp32_ble_tracker->gap_scan_stop_complete(param->scan_stop_cmpl); + global_esp32_ble_tracker->gap_scan_stop_complete_(param->scan_stop_cmpl); break; default: break; } } -void ESP32BLETracker::gap_scan_set_param_complete(const esp_ble_gap_cb_param_t::ble_scan_param_cmpl_evt_param ¶m) { +void ESP32BLETracker::gap_scan_set_param_complete_(const esp_ble_gap_cb_param_t::ble_scan_param_cmpl_evt_param ¶m) { this->scan_set_param_failed_ = param.status; } -void ESP32BLETracker::gap_scan_start_complete(const esp_ble_gap_cb_param_t::ble_scan_start_cmpl_evt_param ¶m) { +void ESP32BLETracker::gap_scan_start_complete_(const esp_ble_gap_cb_param_t::ble_scan_start_cmpl_evt_param ¶m) { this->scan_start_failed_ = param.status; } -void ESP32BLETracker::gap_scan_stop_complete(const esp_ble_gap_cb_param_t::ble_scan_stop_cmpl_evt_param ¶m) { +void ESP32BLETracker::gap_scan_stop_complete_(const esp_ble_gap_cb_param_t::ble_scan_stop_cmpl_evt_param ¶m) { xSemaphoreGive(this->scan_end_lock_); } -void ESP32BLETracker::gap_scan_result(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) { +void ESP32BLETracker::gap_scan_result_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) { if (param.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT) { if (xSemaphoreTake(this->scan_result_lock_, 0L)) { if (this->scan_result_index_ < 16) { @@ -283,8 +283,8 @@ void ESP32BLETracker::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_i global_esp32_ble_tracker->ble_events_.push(gattc_event); } // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) -void ESP32BLETracker::real_gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, - esp_ble_gattc_cb_param_t *param) { +void ESP32BLETracker::real_gattc_event_handler_(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t *param) { for (auto *client : global_esp32_ble_tracker->clients_) { client->gattc_event_handler(event, gattc_if, param); } diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index fc5498f91e..71885a564f 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -135,15 +135,15 @@ class ESPBTDeviceListener { enum class ClientState { // Connection is idle, no device detected. - Idle, + IDLE, // Device advertisement found. - Discovered, + DISCOVERED, // Connection in progress. - Connecting, + CONNECTING, // Initial connection established. - Connected, + CONNECTED, // The client and sub-clients have completed setup. - Established, + ESTABLISHED, }; class ESPBTClient : public ESPBTDeviceListener { @@ -185,23 +185,23 @@ class ESP32BLETracker : public Component { /// The FreeRTOS task managing the bluetooth interface. static bool ble_setup(); /// Start a single scan by setting up the parameters and doing some esp-idf calls. - void start_scan(bool first); + void start_scan_(bool first); /// Callback that will handle all GAP events and redistribute them to other callbacks. static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); - void real_gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); + void real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); /// Called when a `ESP_GAP_BLE_SCAN_RESULT_EVT` event is received. - void gap_scan_result(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m); + void gap_scan_result_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m); /// Called when a `ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT` event is received. - void gap_scan_set_param_complete(const esp_ble_gap_cb_param_t::ble_scan_param_cmpl_evt_param ¶m); + void gap_scan_set_param_complete_(const esp_ble_gap_cb_param_t::ble_scan_param_cmpl_evt_param ¶m); /// Called when a `ESP_GAP_BLE_SCAN_START_COMPLETE_EVT` event is received. - void gap_scan_start_complete(const esp_ble_gap_cb_param_t::ble_scan_start_cmpl_evt_param ¶m); + void gap_scan_start_complete_(const esp_ble_gap_cb_param_t::ble_scan_start_cmpl_evt_param ¶m); /// Called when a `ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT` event is received. - void gap_scan_stop_complete(const esp_ble_gap_cb_param_t::ble_scan_stop_cmpl_evt_param ¶m); + void gap_scan_stop_complete_(const esp_ble_gap_cb_param_t::ble_scan_stop_cmpl_evt_param ¶m); int app_id_; /// Callback that will handle all GATTC events and redistribute them to other callbacks. static void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); - void real_gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); + void real_gattc_event_handler_(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); /// Vector of addresses that have already been printed in print_bt_device_info std::vector already_discovered_; @@ -225,6 +225,7 @@ class ESP32BLETracker : public Component { Queue ble_events_; }; +// NOLINTNEXTLINE extern ESP32BLETracker *global_esp32_ble_tracker; } // namespace esp32_ble_tracker diff --git a/esphome/components/esp32_ble_tracker/queue.h b/esphome/components/esp32_ble_tracker/queue.h index 3d38c17584..f09b2ca8d7 100644 --- a/esphome/components/esp32_ble_tracker/queue.h +++ b/esphome/components/esp32_ble_tracker/queue.h @@ -26,33 +26,33 @@ namespace esp32_ble_tracker { template class Queue { public: - Queue() { m = xSemaphoreCreateMutex(); } + Queue() { m_ = xSemaphoreCreateMutex(); } void push(T *element) { if (element == nullptr) return; - if (xSemaphoreTake(m, 5L / portTICK_PERIOD_MS)) { - q.push(element); - xSemaphoreGive(m); + if (xSemaphoreTake(m_, 5L / portTICK_PERIOD_MS)) { + q_.push(element); + xSemaphoreGive(m_); } } T *pop() { T *element = nullptr; - if (xSemaphoreTake(m, 5L / portTICK_PERIOD_MS)) { - if (!q.empty()) { - element = q.front(); - q.pop(); + if (xSemaphoreTake(m_, 5L / portTICK_PERIOD_MS)) { + if (!q_.empty()) { + element = q_.front(); + q_.pop(); } - xSemaphoreGive(m); + xSemaphoreGive(m_); } return element; } protected: - std::queue q; - SemaphoreHandle_t m; + std::queue q_; + SemaphoreHandle_t m_; }; // Received GAP and GATTC events are only queued, and get processed in the main loop(). @@ -87,12 +87,12 @@ class BLEEvent { }; union { - struct gap_event { + struct gap_event { // NOLINT(readability-identifier-naming) esp_gap_ble_cb_event_t gap_event; esp_ble_gap_cb_param_t gap_param; } gap; - struct gattc_event { + struct gattc_event { // NOLINT(readability-identifier-naming) esp_gattc_cb_event_t gattc_event; esp_gatt_if_t gattc_if; esp_ble_gattc_cb_param_t gattc_param; diff --git a/esphome/components/esp32_camera/esp32_camera.h b/esphome/components/esp32_camera/esp32_camera.h index 430391aa76..6246dc2f12 100644 --- a/esphome/components/esp32_camera/esp32_camera.h +++ b/esphome/components/esp32_camera/esp32_camera.h @@ -106,6 +106,7 @@ class ESP32Camera : public Component, public Nameable { uint32_t last_update_{0}; }; +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) extern ESP32Camera *global_esp32_camera; } // namespace esp32_camera diff --git a/esphome/components/esp32_improv/esp32_improv_component.cpp b/esphome/components/esp32_improv/esp32_improv_component.cpp index fc58fbd264..faa9ab7df6 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.cpp +++ b/esphome/components/esp32_improv/esp32_improv_component.cpp @@ -126,7 +126,7 @@ void ESP32ImprovComponent::loop() { std::string url = "https://my.home-assistant.io/redirect/config_flow_start?domain=esphome"; std::vector data = improv::build_rpc_response(improv::WIFI_SETTINGS, {url}); - this->send_response(data); + this->send_response_(data); this->set_timeout("end-service", 1000, [this] { this->service_->stop(); this->set_state_(improv::STATE_STOPPED); @@ -181,7 +181,7 @@ void ESP32ImprovComponent::set_error_(improv::Error error) { } } -void ESP32ImprovComponent::send_response(std::vector &response) { +void ESP32ImprovComponent::send_response_(std::vector &response) { this->rpc_response_->set_value(response); if (this->state_ != improv::STATE_STOPPED) this->rpc_response_->notify(); diff --git a/esphome/components/esp32_improv/esp32_improv_component.h b/esphome/components/esp32_improv/esp32_improv_component.h index af39ae4748..53cda5f399 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.h +++ b/esphome/components/esp32_improv/esp32_improv_component.h @@ -63,12 +63,13 @@ class ESP32ImprovComponent : public Component, public BLEServiceComponent { void set_state_(improv::State state); void set_error_(improv::Error error); - void send_response(std::vector &response); + void send_response_(std::vector &response); void process_incoming_data_(); void on_wifi_connect_timeout_(); bool check_identify_(); }; +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) extern ESP32ImprovComponent *global_improv_component; } // namespace esp32_improv diff --git a/esphome/components/esp8266/gpio.cpp b/esphome/components/esp8266/gpio.cpp index 4dbffa7f6c..cb703c18e1 100644 --- a/esphome/components/esp8266/gpio.cpp +++ b/esphome/components/esp8266/gpio.cpp @@ -20,7 +20,7 @@ ISRInternalGPIOPin ESP8266GPIOPin::to_isr() const { return ISRInternalGPIOPin((void *) arg); } -void ESP8266GPIOPin::attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const { +void ESP8266GPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const { uint8_t arduino_mode = 0; switch (type) { case gpio::INTERRUPT_RISING_EDGE: diff --git a/esphome/components/esp8266/gpio.h b/esphome/components/esp8266/gpio.h index 465d1099ae..0474d0baa6 100644 --- a/esphome/components/esp8266/gpio.h +++ b/esphome/components/esp8266/gpio.h @@ -25,7 +25,7 @@ class ESP8266GPIOPin : public InternalGPIOPin { bool is_inverted() const override { return inverted_; } protected: - void attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const override; + void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override; uint8_t pin_; bool inverted_; diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index d03211deaf..d55db0a7d8 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -45,11 +45,11 @@ void EthernetComponent::setup() { switch (this->type_) { case ETHERNET_TYPE_LAN8720: { - memcpy(&this->eth_config, &phy_lan8720_default_ethernet_config, sizeof(eth_config_t)); + memcpy(&this->eth_config_, &phy_lan8720_default_ethernet_config, sizeof(eth_config_t)); break; } case ETHERNET_TYPE_TLK110: { - memcpy(&this->eth_config, &phy_tlk110_default_ethernet_config, sizeof(eth_config_t)); + memcpy(&this->eth_config_, &phy_tlk110_default_ethernet_config, sizeof(eth_config_t)); break; } default: { @@ -58,20 +58,20 @@ void EthernetComponent::setup() { } } - this->eth_config.phy_addr = static_cast(this->phy_addr_); - this->eth_config.clock_mode = this->clk_mode_; - this->eth_config.gpio_config = EthernetComponent::eth_phy_config_gpio_; - this->eth_config.tcpip_input = tcpip_adapter_eth_input; + this->eth_config_.phy_addr = static_cast(this->phy_addr_); + this->eth_config_.clock_mode = this->clk_mode_; + this->eth_config_.gpio_config = EthernetComponent::eth_phy_config_gpio; + this->eth_config_.tcpip_input = tcpip_adapter_eth_input; if (this->power_pin_ != nullptr) { - this->orig_power_enable_fun_ = this->eth_config.phy_power_enable; - this->eth_config.phy_power_enable = EthernetComponent::eth_phy_power_enable_; + this->orig_power_enable_fun_ = this->eth_config_.phy_power_enable; + this->eth_config_.phy_power_enable = EthernetComponent::eth_phy_power_enable; } tcpipInit(); esp_err_t err; - err = esp_eth_init(&this->eth_config); + err = esp_eth_init(&this->eth_config_); ESPHL_ERROR_CHECK(err, "ETH init error"); err = esp_eth_enable(); ESPHL_ERROR_CHECK(err, "ETH enable error"); @@ -209,11 +209,11 @@ void EthernetComponent::start_connect_() { this->connect_begin_ = millis(); this->status_set_warning(); } -void EthernetComponent::eth_phy_config_gpio_() { +void EthernetComponent::eth_phy_config_gpio() { phy_rmii_configure_data_interface_pins(); phy_rmii_smi_configure_pins(global_eth_component->mdc_pin_, global_eth_component->mdio_pin_); } -void EthernetComponent::eth_phy_power_enable_(bool enable) { +void EthernetComponent::eth_phy_power_enable(bool enable) { global_eth_component->power_pin_->digital_write(enable); // power up takes some time, datasheet says max 300µs delay(1); @@ -242,9 +242,9 @@ void EthernetComponent::dump_connect_params_() { uint8_t mac[6]; esp_eth_get_mac(mac); ESP_LOGCONFIG(TAG, " MAC Address: %02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - ESP_LOGCONFIG(TAG, " Is Full Duplex: %s", YESNO(this->eth_config.phy_get_duplex_mode())); - ESP_LOGCONFIG(TAG, " Link Up: %s", YESNO(this->eth_config.phy_check_link())); - ESP_LOGCONFIG(TAG, " Link Speed: %u", this->eth_config.phy_get_speed_mode() ? 100 : 10); + ESP_LOGCONFIG(TAG, " Is Full Duplex: %s", YESNO(this->eth_config_.phy_get_duplex_mode())); + ESP_LOGCONFIG(TAG, " Link Up: %s", YESNO(this->eth_config_.phy_check_link())); + ESP_LOGCONFIG(TAG, " Link Speed: %u", this->eth_config_.phy_get_speed_mode() ? 100 : 10); } void EthernetComponent::set_phy_addr(uint8_t phy_addr) { this->phy_addr_ = phy_addr; } void EthernetComponent::set_power_pin(GPIOPin *power_pin) { this->power_pin_ = power_pin; } diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index 3e0c798a0c..abe1c62030 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -60,8 +60,8 @@ class EthernetComponent : public Component { void start_connect_(); void dump_connect_params_(); - static void eth_phy_config_gpio_(); - static void eth_phy_power_enable_(bool enable); + static void eth_phy_config_gpio(); + static void eth_phy_power_enable(bool enable); std::string use_address_; uint8_t phy_addr_{0}; @@ -76,10 +76,11 @@ class EthernetComponent : public Component { bool connected_{false}; EthernetComponentState state_{EthernetComponentState::STOPPED}; uint32_t connect_begin_; - eth_config_t eth_config; + eth_config_t eth_config_; eth_phy_power_enable_func orig_power_enable_fun_; }; +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) extern EthernetComponent *global_eth_component; } // namespace ethernet diff --git a/esphome/components/fastled_base/fastled_light.h b/esphome/components/fastled_base/fastled_light.h index 80840c3003..26f0f33d2a 100644 --- a/esphome/components/fastled_base/fastled_light.h +++ b/esphome/components/fastled_base/fastled_light.h @@ -44,33 +44,33 @@ class FastLEDLightOutput : public light::AddressableLight { CLEDController &add_leds(int num_leds) { switch (CHIPSET) { case LPD8806: { - static LPD8806Controller CONTROLLER; - return add_leds(&CONTROLLER, num_leds); + static LPD8806Controller controller; + return add_leds(&controller, num_leds); } case WS2801: { - static WS2801Controller CONTROLLER; - return add_leds(&CONTROLLER, num_leds); + static WS2801Controller controller; + return add_leds(&controller, num_leds); } case WS2803: { - static WS2803Controller CONTROLLER; - return add_leds(&CONTROLLER, num_leds); + static WS2803Controller controller; + return add_leds(&controller, num_leds); } case SM16716: { - static SM16716Controller CONTROLLER; - return add_leds(&CONTROLLER, num_leds); + static SM16716Controller controller; + return add_leds(&controller, num_leds); } case P9813: { - static P9813Controller CONTROLLER; - return add_leds(&CONTROLLER, num_leds); + static P9813Controller controller; + return add_leds(&controller, num_leds); } case DOTSTAR: case APA102: { - static APA102Controller CONTROLLER; - return add_leds(&CONTROLLER, num_leds); + static APA102Controller controller; + return add_leds(&controller, num_leds); } case SK9822: { - static SK9822Controller CONTROLLER; - return add_leds(&CONTROLLER, num_leds); + static SK9822Controller controller; + return add_leds(&controller, num_leds); } } } @@ -78,33 +78,33 @@ class FastLEDLightOutput : public light::AddressableLight { template CLEDController &add_leds(int num_leds) { switch (CHIPSET) { case LPD8806: { - static LPD8806Controller CONTROLLER; - return add_leds(&CONTROLLER, num_leds); + static LPD8806Controller controller; + return add_leds(&controller, num_leds); } case WS2801: { - static WS2801Controller CONTROLLER; - return add_leds(&CONTROLLER, num_leds); + static WS2801Controller controller; + return add_leds(&controller, num_leds); } case WS2803: { - static WS2803Controller CONTROLLER; - return add_leds(&CONTROLLER, num_leds); + static WS2803Controller controller; + return add_leds(&controller, num_leds); } case SM16716: { - static SM16716Controller CONTROLLER; - return add_leds(&CONTROLLER, num_leds); + static SM16716Controller controller; + return add_leds(&controller, num_leds); } case P9813: { - static P9813Controller CONTROLLER; - return add_leds(&CONTROLLER, num_leds); + static P9813Controller controller; + return add_leds(&controller, num_leds); } case DOTSTAR: case APA102: { - static APA102Controller CONTROLLER; - return add_leds(&CONTROLLER, num_leds); + static APA102Controller controller; + return add_leds(&controller, num_leds); } case SK9822: { - static SK9822Controller CONTROLLER; - return add_leds(&CONTROLLER, num_leds); + static SK9822Controller controller; + return add_leds(&controller, num_leds); } } } @@ -113,33 +113,33 @@ class FastLEDLightOutput : public light::AddressableLight { CLEDController &add_leds(int num_leds) { switch (CHIPSET) { case LPD8806: { - static LPD8806Controller CONTROLLER; - return add_leds(&CONTROLLER, num_leds); + static LPD8806Controller controller; + return add_leds(&controller, num_leds); } case WS2801: { - static WS2801Controller CONTROLLER; - return add_leds(&CONTROLLER, num_leds); + static WS2801Controller controller; + return add_leds(&controller, num_leds); } case WS2803: { - static WS2803Controller CONTROLLER; - return add_leds(&CONTROLLER, num_leds); + static WS2803Controller controller; + return add_leds(&controller, num_leds); } case SM16716: { - static SM16716Controller CONTROLLER; - return add_leds(&CONTROLLER, num_leds); + static SM16716Controller controller; + return add_leds(&controller, num_leds); } case P9813: { - static P9813Controller CONTROLLER; - return add_leds(&CONTROLLER, num_leds); + static P9813Controller controller; + return add_leds(&controller, num_leds); } case DOTSTAR: case APA102: { - static APA102Controller CONTROLLER; - return add_leds(&CONTROLLER, num_leds); + static APA102Controller controller; + return add_leds(&controller, num_leds); } case SK9822: { - static SK9822Controller CONTROLLER; - return add_leds(&CONTROLLER, num_leds); + static SK9822Controller controller; + return add_leds(&controller, num_leds); } } } @@ -147,30 +147,30 @@ class FastLEDLightOutput : public light::AddressableLight { #ifdef FASTLED_HAS_CLOCKLESS template class CHIPSET, uint8_t DATA_PIN, EOrder RGB_ORDER> CLEDController &add_leds(int num_leds) { - static CHIPSET CONTROLLER; - return add_leds(&CONTROLLER, num_leds); + static CHIPSET controller; + return add_leds(&controller, num_leds); } template class CHIPSET, uint8_t DATA_PIN> CLEDController &add_leds(int num_leds) { - static CHIPSET CONTROLLER; - return add_leds(&CONTROLLER, num_leds); + static CHIPSET controller; + return add_leds(&controller, num_leds); } template class CHIPSET, uint8_t DATA_PIN> CLEDController &add_leds(int num_leds) { - static CHIPSET CONTROLLER; - return add_leds(&CONTROLLER, num_leds); + static CHIPSET controller; + return add_leds(&controller, num_leds); } #endif template class CHIPSET, EOrder RGB_ORDER> CLEDController &add_leds(int num_leds) { - static CHIPSET CONTROLLER; - return add_leds(&CONTROLLER, num_leds); + static CHIPSET controller; + return add_leds(&controller, num_leds); } template class CHIPSET> CLEDController &add_leds(int num_leds) { - static CHIPSET CONTROLLER; - return add_leds(&CONTROLLER, num_leds); + static CHIPSET controller; + return add_leds(&controller, num_leds); } #ifdef FASTLED_HAS_BLOCKLESS diff --git a/esphome/components/graph/graph.cpp b/esphome/components/graph/graph.cpp index ff736b7cb7..a9daad4ab9 100644 --- a/esphome/components/graph/graph.cpp +++ b/esphome/components/graph/graph.cpp @@ -185,7 +185,7 @@ void GraphLegend::init(Graph *g) { for (auto *trace : g->traces_) { std::string txtstr = trace->get_name(); int fw, fos, fbl, fh; - this->font_label->measure(txtstr.c_str(), &fw, &fos, &fbl, &fh); + this->font_label_->measure(txtstr.c_str(), &fw, &fos, &fbl, &fh); if (fw > txtw) txtw = fw; if (fh > txth) @@ -201,7 +201,7 @@ void GraphLegend::init(Graph *g) { if (this->units_) { valstr += trace->sensor_->get_unit_of_measurement(); } - this->font_value->measure(valstr.c_str(), &fw, &fos, &fbl, &fh); + this->font_value_->measure(valstr.c_str(), &fw, &fos, &fbl, &fh); if (fw > valw) valw = fw; if (fh > valh) @@ -218,11 +218,11 @@ void GraphLegend::init(Graph *g) { uint16_t h = this->height_; DirectionType dir = this->direction_; ValuePositionType valpos = this->values_; - if (!this->font_value) { + if (!this->font_value_) { valpos = VALUE_POSITION_TYPE_NONE; } // Line sample always goes below text for compactness - this->yl = txth + (txth / 4) + lt / 2; + this->yl_ = txth + (txth / 4) + lt / 2; if (dir == DIRECTION_TYPE_AUTO) { dir = DIRECTION_TYPE_HORIZONTAL; // as default @@ -237,62 +237,62 @@ void GraphLegend::init(Graph *g) { } if (valpos == VALUE_POSITION_TYPE_BELOW) { - this->yv = txth + (txth / 4); + this->yv_ = txth + (txth / 4); if (this->lines_) - this->yv += txth / 4 + lt; + this->yv_ += txth / 4 + lt; } else if (valpos == VALUE_POSITION_TYPE_BESIDE) { - this->xv = (txtw + valw) / 2; + this->xv_ = (txtw + valw) / 2; } // If width or height is specified we divide evenly within, else we do tight-fit if (w == 0) { - this->x0 = txtw / 2; - this->xs = txtw; + this->x0_ = txtw / 2; + this->xs_ = txtw; if (valpos == VALUE_POSITION_TYPE_BELOW) { - this->xs = std::max(txtw, valw); + this->xs_ = std::max(txtw, valw); ; - this->x0 = this->xs / 2; + this->x0_ = this->xs_ / 2; } else if (valpos == VALUE_POSITION_TYPE_BESIDE) { - this->xs = txtw + valw; + this->xs_ = txtw + valw; } if (dir == DIRECTION_TYPE_VERTICAL) { - this->width_ = this->xs; + this->width_ = this->xs_; } else { - this->width_ = this->xs * n; + this->width_ = this->xs_ * n; } } else { - this->xs = w / n; - this->x0 = this->xs / 2; + this->xs_ = w / n; + this->x0_ = this->xs_ / 2; } if (h == 0) { - this->ys = txth; + this->ys_ = txth; if (valpos == VALUE_POSITION_TYPE_BELOW) { - this->ys = txth + txth / 2 + valh; + this->ys_ = txth + txth / 2 + valh; if (this->lines_) { - this->ys += lt; + this->ys_ += lt; } } else if (valpos == VALUE_POSITION_TYPE_BESIDE) { if (this->lines_) { - this->ys = std::max(txth + txth / 4 + lt + txth / 4, valh + valh / 4); + this->ys_ = std::max(txth + txth / 4 + lt + txth / 4, valh + valh / 4); } else { - this->ys = std::max(txth + txth / 4, valh + valh / 4); + this->ys_ = std::max(txth + txth / 4, valh + valh / 4); } - this->height_ = this->ys * n; + this->height_ = this->ys_ * n; } if (dir == DIRECTION_TYPE_HORIZONTAL) { - this->height_ = this->ys; + this->height_ = this->ys_; } else { - this->height_ = this->ys * n; + this->height_ = this->ys_ * n; } } else { - this->ys = h / n; + this->ys_ = h / n; } if (dir == DIRECTION_TYPE_HORIZONTAL) { - this->ys = 0; + this->ys_ = 0; } else { - this->xs = 0; + this->xs_ = 0; } } @@ -310,38 +310,39 @@ void Graph::draw_legend(display::DisplayBuffer *buff, uint16_t x_offset, uint16_ buff->vertical_line(x_offset + w - 1, y_offset, h, color); } - int x = x_offset + legend_->x0; + int x = x_offset + legend_->x0_; int y = y_offset; for (auto *trace : traces_) { std::string txtstr = trace->get_name(); ESP_LOGV(TAG, " %s", txtstr.c_str()); - buff->printf(x, y, legend_->font_label, trace->get_line_color(), TextAlign::TOP_CENTER, "%s", txtstr.c_str()); + buff->printf(x, y, legend_->font_label_, trace->get_line_color(), TextAlign::TOP_CENTER, "%s", txtstr.c_str()); if (legend_->lines_) { uint16_t thick = trace->get_line_thickness(); - for (int16_t i = 0; i < legend_->x0 * 4 / 3; i++) { + for (int16_t i = 0; i < legend_->x0_ * 4 / 3; i++) { uint8_t b = (i % (thick * LineType::PATTERN_LENGTH)) / thick; if (((uint8_t) trace->get_line_type() & (1 << b)) == (1 << b)) { - buff->vertical_line(x - legend_->x0 * 2 / 3 + i, y + legend_->yl - thick / 2, thick, trace->get_line_color()); + buff->vertical_line(x - legend_->x0_ * 2 / 3 + i, y + legend_->yl_ - thick / 2, thick, + trace->get_line_color()); } } } if (legend_->values_ != VALUE_POSITION_TYPE_NONE) { - int xv = x + legend_->xv; - int yv = y + legend_->yv; + int xv = x + legend_->xv_; + int yv = y + legend_->yv_; std::stringstream ss; ss << std::fixed << std::setprecision(trace->sensor_->get_accuracy_decimals()) << trace->sensor_->get_state(); std::string valstr = ss.str(); if (legend_->units_) { valstr += trace->sensor_->get_unit_of_measurement(); } - buff->printf(xv, yv, legend_->font_value, trace->get_line_color(), TextAlign::TOP_CENTER, "%s", valstr.c_str()); + buff->printf(xv, yv, legend_->font_value_, trace->get_line_color(), TextAlign::TOP_CENTER, "%s", valstr.c_str()); ESP_LOGV(TAG, " value: %s", valstr.c_str()); } - x += legend_->xs; - y += legend_->ys; + x += legend_->xs_; + y += legend_->ys_; } } diff --git a/esphome/components/graph/graph.h b/esphome/components/graph/graph.h index 8f6e74f67a..f935917c57 100644 --- a/esphome/components/graph/graph.h +++ b/esphome/components/graph/graph.h @@ -1,8 +1,9 @@ #pragma once -#include +#include "esphome/components/sensor/sensor.h" #include "esphome/core/color.h" #include "esphome/core/component.h" -#include "esphome/components/sensor/sensor.h" +#include +#include namespace esphome { @@ -43,8 +44,8 @@ enum ValuePositionType { class GraphLegend { public: void init(Graph *g); - void set_name_font(display::Font *font) { this->font_label = font; } - void set_value_font(display::Font *font) { this->font_value = font; } + void set_name_font(display::Font *font) { this->font_label_ = font; } + void set_value_font(display::Font *font) { this->font_value_ = font; } void set_width(uint32_t width) { this->width_ = width; } void set_height(uint32_t height) { this->height_ = height; } void set_border(bool val) { this->border_ = val; } @@ -61,8 +62,8 @@ class GraphLegend { ValuePositionType values_{VALUE_POSITION_TYPE_AUTO}; bool units_{true}; DirectionType direction_{DIRECTION_TYPE_AUTO}; - display::Font *font_label{nullptr}; - display::Font *font_value{nullptr}; + display::Font *font_label_{nullptr}; + display::Font *font_value_{nullptr}; // Calculated values Graph *parent_{nullptr}; // (x0) (xs,ys) (xs,ys) @@ -72,12 +73,12 @@ class GraphLegend { // (0,yl)| \-> VALUE1+units // v (top_center) // LINE_SAMPLE - int x0{0}; // X-offset to centre of label text - int xs{0}; // X spacing between labels - int ys{0}; // Y spacing between labels - int yl{0}; // Y spacing from label to line sample - int xv{0}; // X distance between label to value text - int yv{0}; // Y distance between label to value text + int x0_{0}; // X-offset to centre of label text + int xs_{0}; // X spacing between labels + int ys_{0}; // Y spacing between labels + int yl_{0}; // Y spacing from label to line sample + int xv_{0}; // X distance between label to value text + int yv_{0}; // Y distance between label to value text friend Graph; }; @@ -106,7 +107,7 @@ class HistoryData { class GraphTrace { public: void init(Graph *g); - void set_name(std::string name) { name_ = name; } + void set_name(std::string name) { name_ = std::move(name); } void set_sensor(sensor::Sensor *sensor) { sensor_ = sensor; } uint8_t get_line_thickness() { return this->line_thickness_; } void set_line_thickness(uint8_t val) { this->line_thickness_ = val; } @@ -114,7 +115,7 @@ class GraphTrace { void set_line_type(enum LineType val) { this->line_type_ = val; } Color get_line_color() { return this->line_color_; } void set_line_color(Color val) { this->line_color_ = val; } - const std::string get_name(void) { return name_; } + const std::string get_name() { return name_; } const HistoryData *get_tracedata() { return &data_; } protected: diff --git a/esphome/components/i2c/i2c_bus_arduino.cpp b/esphome/components/i2c/i2c_bus_arduino.cpp index b983fb7636..87dbcb66d8 100644 --- a/esphome/components/i2c/i2c_bus_arduino.cpp +++ b/esphome/components/i2c/i2c_bus_arduino.cpp @@ -11,7 +11,7 @@ namespace i2c { static const char *const TAG = "i2c.arduino"; void ArduinoI2CBus::setup() { - recover(); + recover_(); #ifdef USE_ESP32 static uint8_t next_bus_num = 0; if (next_bus_num == 0) @@ -92,7 +92,7 @@ ErrorCode ArduinoI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cn return ERROR_UNKNOWN; } -void ArduinoI2CBus::recover() { +void ArduinoI2CBus::recover_() { // Perform I2C bus recovery, see // https://www.analog.com/media/en/technical-documentation/application-notes/54305147357414AN686_0.pdf // or see the linux kernel implementation, e.g. diff --git a/esphome/components/i2c/i2c_bus_arduino.h b/esphome/components/i2c/i2c_bus_arduino.h index 49be0c358c..42589dcfb7 100644 --- a/esphome/components/i2c/i2c_bus_arduino.h +++ b/esphome/components/i2c/i2c_bus_arduino.h @@ -23,7 +23,7 @@ class ArduinoI2CBus : public I2CBus, public Component { void set_frequency(uint32_t frequency) { frequency_ = frequency; } private: - void recover(); + void recover_(); protected: TwoWire *wire_; diff --git a/esphome/components/i2c/i2c_bus_esp_idf.cpp b/esphome/components/i2c/i2c_bus_esp_idf.cpp index 5ce5d40c00..28e71ab2a0 100644 --- a/esphome/components/i2c/i2c_bus_esp_idf.cpp +++ b/esphome/components/i2c/i2c_bus_esp_idf.cpp @@ -14,7 +14,7 @@ void IDFI2CBus::setup() { static i2c_port_t next_port = 0; port_ = next_port++; - recover(); + recover_(); i2c_config_t conf{}; memset(&conf, 0, sizeof(conf)); @@ -144,7 +144,7 @@ ErrorCode IDFI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cnt) { return ERROR_OK; } -void IDFI2CBus::recover() { +void IDFI2CBus::recover_() { // Perform I2C bus recovery, see // https://www.analog.com/media/en/technical-documentation/application-notes/54305147357414AN686_0.pdf // or see the linux kernel implementation, e.g. diff --git a/esphome/components/i2c/i2c_bus_esp_idf.h b/esphome/components/i2c/i2c_bus_esp_idf.h index 9985e618f8..ba5fbf25c5 100644 --- a/esphome/components/i2c/i2c_bus_esp_idf.h +++ b/esphome/components/i2c/i2c_bus_esp_idf.h @@ -25,7 +25,7 @@ class IDFI2CBus : public I2CBus, public Component { void set_frequency(uint32_t frequency) { frequency_ = frequency; } private: - void recover(); + void recover_(); protected: i2c_port_t port_; diff --git a/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp b/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp index 177bc76072..c01fc274f4 100644 --- a/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp +++ b/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp @@ -8,7 +8,7 @@ namespace inkbird_ibsth1_mini { static const char *const TAG = "inkbird_ibsth1_mini"; -void InkbirdIBSTH1_MINI::dump_config() { +void InkbirdIbstH1Mini::dump_config() { ESP_LOGCONFIG(TAG, "Inkbird IBS TH1 MINI"); LOG_SENSOR(" ", "Temperature", this->temperature_); LOG_SENSOR(" ", "External Temperature", this->external_temperature_); @@ -16,7 +16,7 @@ void InkbirdIBSTH1_MINI::dump_config() { LOG_SENSOR(" ", "Battery Level", this->battery_level_); } -bool InkbirdIBSTH1_MINI::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { +bool InkbirdIbstH1Mini::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { // The below is based on my research and reverse engineering of a single device // It is entirely possible that some of that may be inaccurate or incomplete diff --git a/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.h b/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.h index 1b6be7afe0..bdca2d0cac 100644 --- a/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.h +++ b/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.h @@ -9,7 +9,7 @@ namespace esphome { namespace inkbird_ibsth1_mini { -class InkbirdIBSTH1_MINI : public Component, public esp32_ble_tracker::ESPBTDeviceListener { +class InkbirdIbstH1Mini : public Component, public esp32_ble_tracker::ESPBTDeviceListener { public: void set_address(uint64_t address) { address_ = address; } diff --git a/esphome/components/inkbird_ibsth1_mini/sensor.py b/esphome/components/inkbird_ibsth1_mini/sensor.py index a71921f8ed..0ab9f8b3e0 100644 --- a/esphome/components/inkbird_ibsth1_mini/sensor.py +++ b/esphome/components/inkbird_ibsth1_mini/sensor.py @@ -21,14 +21,14 @@ DEPENDENCIES = ["esp32_ble_tracker"] CONF_EXTERNAL_TEMPERATURE = "external_temperature" inkbird_ibsth1_mini_ns = cg.esphome_ns.namespace("inkbird_ibsth1_mini") -InkbirdUBSTH1_MINI = inkbird_ibsth1_mini_ns.class_( - "InkbirdIBSTH1_MINI", esp32_ble_tracker.ESPBTDeviceListener, cg.Component +InkbirdIbstH1Mini = inkbird_ibsth1_mini_ns.class_( + "InkbirdIbstH1Mini", esp32_ble_tracker.ESPBTDeviceListener, cg.Component ) CONFIG_SCHEMA = ( cv.Schema( { - cv.GenerateID(): cv.declare_id(InkbirdUBSTH1_MINI), + cv.GenerateID(): cv.declare_id(InkbirdIbstH1Mini), cv.Required(CONF_MAC_ADDRESS): cv.mac_address, cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( unit_of_measurement=UNIT_CELSIUS, diff --git a/esphome/components/ledc/ledc_output.h b/esphome/components/ledc/ledc_output.h index e02cefd170..a78bf440a9 100644 --- a/esphome/components/ledc/ledc_output.h +++ b/esphome/components/ledc/ledc_output.h @@ -10,6 +10,7 @@ namespace esphome { namespace ledc { +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) extern uint8_t next_ledc_channel; class LEDCOutput : public output::FloatOutput, public Component { diff --git a/esphome/components/light/addressable_light.h b/esphome/components/light/addressable_light.h index 97f4a4687d..fea7508515 100644 --- a/esphome/components/light/addressable_light.h +++ b/esphome/components/light/addressable_light.h @@ -80,7 +80,7 @@ class AddressableLight : public LightOutput, public Component { void mark_shown_() { #ifdef USE_POWER_SUPPLY - for (auto c : *this) { + for (const auto &c : *this) { if (c.get().is_on()) { this->power_.request(); return; diff --git a/esphome/components/light/addressable_light_wrapper.h b/esphome/components/light/addressable_light_wrapper.h index 813dd43313..cd5bcabd47 100644 --- a/esphome/components/light/addressable_light_wrapper.h +++ b/esphome/components/light/addressable_light_wrapper.h @@ -9,7 +9,7 @@ namespace light { class AddressableLightWrapper : public light::AddressableLight { public: explicit AddressableLightWrapper(light::LightState *light_state) : light_state_(light_state) { - this->wrapper_state_ = new uint8_t[5]; + this->wrapper_state_ = new uint8_t[5]; // NOLINT(cppcoreguidelines-owning-memory) } int32_t size() const override { return 1; } diff --git a/esphome/components/light/esp_range_view.h b/esphome/components/light/esp_range_view.h index f4a7980543..07d18af79f 100644 --- a/esphome/components/light/esp_range_view.h +++ b/esphome/components/light/esp_range_view.h @@ -18,6 +18,7 @@ class ESPRangeView : public ESPColorSettable { public: ESPRangeView(AddressableLight *parent, int32_t begin, int32_t end) : parent_(parent), begin_(begin), end_(end < begin ? begin : end) {} + ESPRangeView(const ESPRangeView &) = default; int32_t size() const { return this->end_ - this->begin_; } ESPColorView operator[](int32_t index) const; @@ -62,6 +63,7 @@ class ESPRangeView : public ESPColorSettable { class ESPRangeIterator { public: ESPRangeIterator(const ESPRangeView &range, int32_t i) : range_(range), i_(i) {} + ESPRangeIterator(const ESPRangeIterator &) = default; ESPRangeIterator operator++() { this->i_++; return *this; diff --git a/esphome/components/midea/appliance_base.h b/esphome/components/midea/appliance_base.h index 43dd2ffb32..88a722e389 100644 --- a/esphome/components/midea/appliance_base.h +++ b/esphome/components/midea/appliance_base.h @@ -31,9 +31,10 @@ class ApplianceBase : public Component, public uart::UARTDevice, public climate: ApplianceBase() { this->base_.setStream(this); this->base_.addOnStateCallback(std::bind(&ApplianceBase::on_status_change, this)); - dudanov::midea::ApplianceBase::setLogger([](int level, const char *tag, int line, String format, va_list args) { - esp_log_vprintf_(level, tag, line, format.c_str(), args); - }); + dudanov::midea::ApplianceBase::setLogger( + [](int level, const char *tag, int line, const String &format, va_list args) { + esp_log_vprintf_(level, tag, line, format.c_str(), args); + }); } bool can_proceed() override { return this->base_.getAutoconfStatus() != dudanov::midea::AutoconfStatus::AUTOCONF_PROGRESS; @@ -46,11 +47,11 @@ class ApplianceBase : public Component, public uart::UARTDevice, public climate: void set_request_attempts(uint32_t attempts) { this->base_.setNumAttempts(attempts); } void set_beeper_feedback(bool state) { this->base_.setBeeper(state); } void set_autoconf(bool value) { this->base_.setAutoconf(value); } - void set_supported_modes(std::set modes) { this->supported_modes_ = std::move(modes); } - void set_supported_swing_modes(std::set modes) { this->supported_swing_modes_ = std::move(modes); } - void set_supported_presets(std::set presets) { this->supported_presets_ = std::move(presets); } - void set_custom_presets(std::set presets) { this->supported_custom_presets_ = std::move(presets); } - void set_custom_fan_modes(std::set modes) { this->supported_custom_fan_modes_ = std::move(modes); } + void set_supported_modes(const std::set &modes) { this->supported_modes_ = modes; } + void set_supported_swing_modes(const std::set &modes) { this->supported_swing_modes_ = modes; } + void set_supported_presets(const std::set &presets) { this->supported_presets_ = presets; } + void set_custom_presets(const std::set &presets) { this->supported_custom_presets_ = presets; } + void set_custom_fan_modes(const std::set &modes) { this->supported_custom_fan_modes_ = modes; } virtual void on_status_change() = 0; #ifdef USE_REMOTE_TRANSMITTER void set_transmitter(remote_transmitter::RemoteTransmitterComponent *transmitter) { diff --git a/esphome/components/pvvx_mithermometer/pvvx_mithermometer.cpp b/esphome/components/pvvx_mithermometer/pvvx_mithermometer.cpp index ff9723ab2f..a41ad1bfcb 100644 --- a/esphome/components/pvvx_mithermometer/pvvx_mithermometer.cpp +++ b/esphome/components/pvvx_mithermometer/pvvx_mithermometer.cpp @@ -25,14 +25,14 @@ bool PVVXMiThermometer::parse_device(const esp32_ble_tracker::ESPBTDevice &devic bool success = false; for (auto &service_data : device.get_service_datas()) { - auto res = parse_header(service_data); + auto res = parse_header_(service_data); if (!res.has_value()) { continue; } - if (!(parse_message(service_data.data, *res))) { + if (!(parse_message_(service_data.data, *res))) { continue; } - if (!(report_results(res, device.address_str()))) { + if (!(report_results_(res, device.address_str()))) { continue; } if (res->temperature.has_value() && this->temperature_ != nullptr) @@ -49,7 +49,7 @@ bool PVVXMiThermometer::parse_device(const esp32_ble_tracker::ESPBTDevice &devic return success; } -optional PVVXMiThermometer::parse_header(const esp32_ble_tracker::ServiceData &service_data) { +optional PVVXMiThermometer::parse_header_(const esp32_ble_tracker::ServiceData &service_data) { ParseResult result; if (!service_data.uuid.contains(0x1A, 0x18)) { ESP_LOGVV(TAG, "parse_header(): no service data UUID magic bytes."); @@ -68,7 +68,7 @@ optional PVVXMiThermometer::parse_header(const esp32_ble_tracker::S return result; } -bool PVVXMiThermometer::parse_message(const std::vector &message, ParseResult &result) { +bool PVVXMiThermometer::parse_message_(const std::vector &message, ParseResult &result) { /* All data little endian uint8_t size; // = 19 @@ -109,7 +109,7 @@ bool PVVXMiThermometer::parse_message(const std::vector &message, Parse return true; } -bool PVVXMiThermometer::report_results(const optional &result, const std::string &address) { +bool PVVXMiThermometer::report_results_(const optional &result, const std::string &address) { if (!result.has_value()) { ESP_LOGVV(TAG, "report_results(): no results available."); return false; diff --git a/esphome/components/pvvx_mithermometer/pvvx_mithermometer.h b/esphome/components/pvvx_mithermometer/pvvx_mithermometer.h index bb67769d4f..ad8baed35f 100644 --- a/esphome/components/pvvx_mithermometer/pvvx_mithermometer.h +++ b/esphome/components/pvvx_mithermometer/pvvx_mithermometer.h @@ -36,9 +36,9 @@ class PVVXMiThermometer : public Component, public esp32_ble_tracker::ESPBTDevic sensor::Sensor *battery_level_{nullptr}; sensor::Sensor *battery_voltage_{nullptr}; - optional parse_header(const esp32_ble_tracker::ServiceData &service_data); - bool parse_message(const std::vector &message, ParseResult &result); - bool report_results(const optional &result, const std::string &address); + optional parse_header_(const esp32_ble_tracker::ServiceData &service_data); + bool parse_message_(const std::vector &message, ParseResult &result); + bool report_results_(const optional &result, const std::string &address); }; } // namespace pvvx_mithermometer diff --git a/esphome/components/remote_base/midea_protocol.h b/esphome/components/remote_base/midea_protocol.h index 12916bd44d..35ea23acfb 100644 --- a/esphome/components/remote_base/midea_protocol.h +++ b/esphome/components/remote_base/midea_protocol.h @@ -92,7 +92,7 @@ using MideaDumper = RemoteReceiverDumper; template class MideaAction : public RemoteTransmitterActionBase { TEMPLATABLE_VALUE(std::vector, code) - void set_code(std::vector code) { code_ = code; } + void set_code(const std::vector &code) { code_ = code; } void encode(RemoteTransmitData *dst, Ts... x) override { MideaData data = this->code_.value(x...); data.finalize(); diff --git a/esphome/components/remote_base/remote_base.h b/esphome/components/remote_base/remote_base.h index 68cf67d175..dd6f7c3482 100644 --- a/esphome/components/remote_base/remote_base.h +++ b/esphome/components/remote_base/remote_base.h @@ -161,11 +161,11 @@ class RemoteRMTChannel { void set_clock_divider(uint8_t clock_divider) { this->clock_divider_ = clock_divider; } protected: - uint32_t from_microseconds(uint32_t us) { + uint32_t from_microseconds_(uint32_t us) { const uint32_t ticks_per_ten_us = 80000000u / this->clock_divider_ / 100000u; return us * ticks_per_ten_us / 10; } - uint32_t to_microseconds(uint32_t ticks) { + uint32_t to_microseconds_(uint32_t ticks) { const uint32_t ticks_per_ten_us = 80000000u / this->clock_divider_ / 100000u; return (ticks * 10) / ticks_per_ten_us; } diff --git a/esphome/components/remote_receiver/remote_receiver_esp32.cpp b/esphome/components/remote_receiver/remote_receiver_esp32.cpp index bec2af6718..dde9b843c9 100644 --- a/esphome/components/remote_receiver/remote_receiver_esp32.cpp +++ b/esphome/components/remote_receiver/remote_receiver_esp32.cpp @@ -20,9 +20,9 @@ void RemoteReceiverComponent::setup() { rmt.rx_config.filter_en = false; } else { rmt.rx_config.filter_en = true; - rmt.rx_config.filter_ticks_thresh = this->from_microseconds(this->filter_us_); + rmt.rx_config.filter_ticks_thresh = this->from_microseconds_(this->filter_us_); } - rmt.rx_config.idle_threshold = this->from_microseconds(this->idle_us_); + rmt.rx_config.idle_threshold = this->from_microseconds_(this->idle_us_); esp_err_t error = rmt_config(&rmt); if (error != ESP_OK) { @@ -90,14 +90,14 @@ void RemoteReceiverComponent::decode_rmt_(rmt_item32_t *item, size_t len) { ESP_LOGVV(TAG, "START:"); for (size_t i = 0; i < len; i++) { if (item[i].level0) { - ESP_LOGVV(TAG, "%u A: ON %uus (%u ticks)", i, this->to_microseconds(item[i].duration0), item[i].duration0); + ESP_LOGVV(TAG, "%u A: ON %uus (%u ticks)", i, this->to_microseconds_(item[i].duration0), item[i].duration0); } else { - ESP_LOGVV(TAG, "%u A: OFF %uus (%u ticks)", i, this->to_microseconds(item[i].duration0), item[i].duration0); + ESP_LOGVV(TAG, "%u A: OFF %uus (%u ticks)", i, this->to_microseconds_(item[i].duration0), item[i].duration0); } if (item[i].level1) { - ESP_LOGVV(TAG, "%u B: ON %uus (%u ticks)", i, this->to_microseconds(item[i].duration1), item[i].duration1); + ESP_LOGVV(TAG, "%u B: ON %uus (%u ticks)", i, this->to_microseconds_(item[i].duration1), item[i].duration1); } else { - ESP_LOGVV(TAG, "%u B: OFF %uus (%u ticks)", i, this->to_microseconds(item[i].duration1), item[i].duration1); + ESP_LOGVV(TAG, "%u B: OFF %uus (%u ticks)", i, this->to_microseconds_(item[i].duration1), item[i].duration1); } } ESP_LOGVV(TAG, "\n"); @@ -111,16 +111,16 @@ void RemoteReceiverComponent::decode_rmt_(rmt_item32_t *item, size_t len) { } else { if (prev_length > 0) { if (prev_level) { - this->temp_.push_back(this->to_microseconds(prev_length) * multiplier); + this->temp_.push_back(this->to_microseconds_(prev_length) * multiplier); } else { - this->temp_.push_back(-int32_t(this->to_microseconds(prev_length)) * multiplier); + this->temp_.push_back(-int32_t(this->to_microseconds_(prev_length)) * multiplier); } } prev_level = bool(item[i].level0); prev_length = item[i].duration0; } - if (this->to_microseconds(prev_length) > this->idle_us_) { + if (this->to_microseconds_(prev_length) > this->idle_us_) { break; } @@ -131,24 +131,24 @@ void RemoteReceiverComponent::decode_rmt_(rmt_item32_t *item, size_t len) { } else { if (prev_length > 0) { if (prev_level) { - this->temp_.push_back(this->to_microseconds(prev_length) * multiplier); + this->temp_.push_back(this->to_microseconds_(prev_length) * multiplier); } else { - this->temp_.push_back(-int32_t(this->to_microseconds(prev_length)) * multiplier); + this->temp_.push_back(-int32_t(this->to_microseconds_(prev_length)) * multiplier); } } prev_level = bool(item[i].level1); prev_length = item[i].duration1; } - if (this->to_microseconds(prev_length) > this->idle_us_) { + if (this->to_microseconds_(prev_length) > this->idle_us_) { break; } } if (prev_length > 0) { if (prev_level) { - this->temp_.push_back(this->to_microseconds(prev_length) * multiplier); + this->temp_.push_back(this->to_microseconds_(prev_length) * multiplier); } else { - this->temp_.push_back(-int32_t(this->to_microseconds(prev_length)) * multiplier); + this->temp_.push_back(-int32_t(this->to_microseconds_(prev_length)) * multiplier); } } } diff --git a/esphome/components/remote_transmitter/remote_transmitter.h b/esphome/components/remote_transmitter/remote_transmitter.h index d05942de3b..733ac5e50d 100644 --- a/esphome/components/remote_transmitter/remote_transmitter.h +++ b/esphome/components/remote_transmitter/remote_transmitter.h @@ -35,7 +35,7 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, #endif #ifdef USE_ESP32 - void configure_rmt(); + void configure_rmt_(); uint32_t current_carrier_frequency_{UINT32_MAX}; bool initialized_{false}; diff --git a/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp b/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp index a1f7663a24..500d7193f3 100644 --- a/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +++ b/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp @@ -9,7 +9,7 @@ namespace remote_transmitter { static const char *const TAG = "remote_transmitter"; -void RemoteTransmitterComponent::setup() { this->configure_rmt(); } +void RemoteTransmitterComponent::setup() { this->configure_rmt_(); } void RemoteTransmitterComponent::dump_config() { ESP_LOGCONFIG(TAG, "Remote Transmitter..."); @@ -27,7 +27,7 @@ void RemoteTransmitterComponent::dump_config() { } } -void RemoteTransmitterComponent::configure_rmt() { +void RemoteTransmitterComponent::configure_rmt_() { rmt_config_t c{}; this->config_rmt(c); @@ -77,7 +77,7 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen if (this->current_carrier_frequency_ != this->temp_.get_carrier_frequency()) { this->current_carrier_frequency_ = this->temp_.get_carrier_frequency(); - this->configure_rmt(); + this->configure_rmt_(); } this->rmt_temp_.clear(); @@ -89,7 +89,7 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen bool level = val >= 0; if (!level) val = -val; - val = this->from_microseconds(static_cast(val)); + val = this->from_microseconds_(static_cast(val)); do { int32_t item = std::min(val, int32_t(32767)); diff --git a/esphome/components/socket/headers.h b/esphome/components/socket/headers.h index f9697fd421..a383c0071d 100644 --- a/esphome/components/socket/headers.h +++ b/esphome/components/socket/headers.h @@ -7,10 +7,10 @@ #ifdef USE_SOCKET_IMPL_LWIP_TCP #define LWIP_INTERNAL -#include #include "lwip/inet.h" -#include -#include +#include +#include +#include /* Address families. */ #define AF_UNSPEC 0 @@ -45,9 +45,10 @@ #define SOL_SOCKET 0xfff /* options for socket level */ -typedef uint8_t sa_family_t; -typedef uint16_t in_port_t; +using sa_family_t = uint8_t; +using in_port_t = uint16_t; +// NOLINTNEXTLINE(readability-identifier-naming) struct sockaddr_in { uint8_t sin_len; sa_family_t sin_family; @@ -57,6 +58,7 @@ struct sockaddr_in { char sin_zero[SIN_ZERO_LEN]; }; +// NOLINTNEXTLINE(readability-identifier-naming) struct sockaddr_in6 { uint8_t sin6_len; /* length of this structure */ sa_family_t sin6_family; /* AF_INET6 */ @@ -66,12 +68,14 @@ struct sockaddr_in6 { uint32_t sin6_scope_id; /* Set of interfaces for scope */ }; +// NOLINTNEXTLINE(readability-identifier-naming) struct sockaddr { uint8_t sa_len; sa_family_t sa_family; char sa_data[14]; }; +// NOLINTNEXTLINE(readability-identifier-naming) struct sockaddr_storage { uint8_t s2_len; sa_family_t ss_family; @@ -79,8 +83,9 @@ struct sockaddr_storage { uint32_t s2_data2[3]; uint32_t s2_data3[3]; }; -typedef uint32_t socklen_t; +using socklen_t = uint32_t; +// NOLINTNEXTLINE(readability-identifier-naming) struct iovec { void *iov_base; size_t iov_len; @@ -106,13 +111,13 @@ struct iovec { #ifdef USE_SOCKET_IMPL_BSD_SOCKETS -#include -#include +#include +#include #include +#include +#include #include #include -#include -#include #ifdef USE_ARDUINO // arduino-esp32 declares a global var called INADDR_NONE which is replaced @@ -121,7 +126,7 @@ struct iovec { #undef INADDR_NONE #endif // not defined for ESP32 -typedef uint32_t socklen_t; +using socklen_t = uint32_t; #define ESPHOME_INADDR_ANY ((uint32_t) 0x00000000UL) #define ESPHOME_INADDR_NONE ((uint32_t) 0xFFFFFFFFUL) diff --git a/esphome/components/t6615/t6615.h b/esphome/components/t6615/t6615.h index a075685023..fb53032e8d 100644 --- a/esphome/components/t6615/t6615.h +++ b/esphome/components/t6615/t6615.h @@ -35,7 +35,7 @@ class T6615Component : public PollingComponent, public uart::UARTDevice { void send_ppm_command_(); T6615Command command_ = T6615Command::NONE; - unsigned long command_time_ = 0; + uint32_t command_time_ = 0; sensor::Sensor *co2_sensor_{nullptr}; }; diff --git a/esphome/components/uart/uart_component_esp8266.cpp b/esphome/components/uart/uart_component_esp8266.cpp index 2188a4a4bc..973306cde2 100644 --- a/esphome/components/uart/uart_component_esp8266.cpp +++ b/esphome/components/uart/uart_component_esp8266.cpp @@ -13,7 +13,7 @@ namespace esphome { namespace uart { static const char *const TAG = "uart.arduino_esp8266"; -bool ESP8266UartComponent::serial0InUse = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +bool ESP8266UartComponent::serial0_in_use = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) uint32_t ESP8266UartComponent::get_config() { uint32_t config = 0; @@ -55,7 +55,7 @@ void ESP8266UartComponent::setup() { // is 1 we still want to use Serial. SerialConfig config = static_cast(get_config()); - if (!ESP8266UartComponent::serial0InUse && (tx_pin_ == nullptr || tx_pin_->get_pin() == 1) && + if (!ESP8266UartComponent::serial0_in_use && (tx_pin_ == nullptr || tx_pin_->get_pin() == 1) && (rx_pin_ == nullptr || rx_pin_->get_pin() == 3) #ifdef USE_LOGGER // we will use UART0 if logger isn't using it in swapped mode @@ -66,8 +66,8 @@ void ESP8266UartComponent::setup() { this->hw_serial_ = &Serial; this->hw_serial_->begin(this->baud_rate_, config); this->hw_serial_->setRxBufferSize(this->rx_buffer_size_); - ESP8266UartComponent::serial0InUse = true; - } else if (!ESP8266UartComponent::serial0InUse && (tx_pin_ == nullptr || tx_pin_->get_pin() == 15) && + ESP8266UartComponent::serial0_in_use = true; + } else if (!ESP8266UartComponent::serial0_in_use && (tx_pin_ == nullptr || tx_pin_->get_pin() == 15) && (rx_pin_ == nullptr || rx_pin_->get_pin() == 13) #ifdef USE_LOGGER // we will use UART0 swapped if logger isn't using it in regular mode @@ -79,7 +79,7 @@ void ESP8266UartComponent::setup() { this->hw_serial_->begin(this->baud_rate_, config); this->hw_serial_->setRxBufferSize(this->rx_buffer_size_); this->hw_serial_->swap(); - ESP8266UartComponent::serial0InUse = true; + ESP8266UartComponent::serial0_in_use = true; } else if ((tx_pin_ == nullptr || tx_pin_->get_pin() == 2) && (rx_pin_ == nullptr || rx_pin_->get_pin() == 8)) { this->hw_serial_ = &Serial1; this->hw_serial_->begin(this->baud_rate_, config); diff --git a/esphome/components/uart/uart_component_esp8266.h b/esphome/components/uart/uart_component_esp8266.h index 921d77e4f3..eed14f3265 100644 --- a/esphome/components/uart/uart_component_esp8266.h +++ b/esphome/components/uart/uart_component_esp8266.h @@ -70,7 +70,7 @@ class ESP8266UartComponent : public UARTComponent, public Component { ESP8266SoftwareSerial *sw_serial_{nullptr}; private: - static bool serial0InUse; + static bool serial0_in_use; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) }; } // namespace uart diff --git a/esphome/components/uart/uart_component_esp_idf.cpp b/esphome/components/uart/uart_component_esp_idf.cpp index 3d4a634a72..1cccd5821e 100644 --- a/esphome/components/uart/uart_component_esp_idf.cpp +++ b/esphome/components/uart/uart_component_esp_idf.cpp @@ -14,7 +14,7 @@ namespace esphome { namespace uart { static const char *const TAG = "uart.idf"; -uart_config_t IDFUARTComponent::get_config() { +uart_config_t IDFUARTComponent::get_config_() { uart_parity_t parity = UART_PARITY_DISABLE; if (this->parity_ == UART_CONFIG_PARITY_EVEN) parity = UART_PARITY_EVEN; @@ -70,7 +70,7 @@ void IDFUARTComponent::setup() { xSemaphoreTake(this->lock_, portMAX_DELAY); - uart_config_t uart_config = this->get_config(); + uart_config_t uart_config = this->get_config_(); esp_err_t err = uart_param_config(this->uart_num_, &uart_config); if (err != ESP_OK) { ESP_LOGW(TAG, "uart_param_config failed: %s", esp_err_to_name(err)); diff --git a/esphome/components/uart/uart_component_esp_idf.h b/esphome/components/uart/uart_component_esp_idf.h index 68cceafda2..27fb80d2cc 100644 --- a/esphome/components/uart/uart_component_esp_idf.h +++ b/esphome/components/uart/uart_component_esp_idf.h @@ -26,7 +26,7 @@ class IDFUARTComponent : public UARTComponent, public Component { protected: void check_logger_conflict() override; uart_port_t uart_num_; - uart_config_t get_config(); + uart_config_t get_config_(); SemaphoreHandle_t lock_; bool has_peek_{false}; diff --git a/esphome/components/wifi_info/wifi_info_text_sensor.h b/esphome/components/wifi_info/wifi_info_text_sensor.h index b2f37de363..5b54451ed0 100644 --- a/esphome/components/wifi_info/wifi_info_text_sensor.h +++ b/esphome/components/wifi_info/wifi_info_text_sensor.h @@ -13,7 +13,7 @@ class IPAddressWiFiInfo : public Component, public text_sensor::TextSensor { auto ip = wifi::global_wifi_component->wifi_sta_ip(); if (ip != this->last_ip_) { this->last_ip_ = ip; - this->publish_state(ip.str().c_str()); + this->publish_state(ip.str()); } } float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } diff --git a/esphome/components/xiaomi_miscale/xiaomi_miscale.cpp b/esphome/components/xiaomi_miscale/xiaomi_miscale.cpp index 4587045136..de77e6146b 100644 --- a/esphome/components/xiaomi_miscale/xiaomi_miscale.cpp +++ b/esphome/components/xiaomi_miscale/xiaomi_miscale.cpp @@ -23,16 +23,16 @@ bool XiaomiMiscale::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { bool success = false; for (auto &service_data : device.get_service_datas()) { - auto res = parse_header(service_data); + auto res = parse_header_(service_data); if (!res.has_value()) { continue; } - if (!(parse_message(service_data.data, *res))) { + if (!(parse_message_(service_data.data, *res))) { continue; } - if (!(report_results(res, device.address_str()))) { + if (!(report_results_(res, device.address_str()))) { continue; } @@ -49,7 +49,7 @@ bool XiaomiMiscale::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { return success; } -optional XiaomiMiscale::parse_header(const esp32_ble_tracker::ServiceData &service_data) { +optional XiaomiMiscale::parse_header_(const esp32_ble_tracker::ServiceData &service_data) { ParseResult result; if (service_data.uuid == esp32_ble_tracker::ESPBTUUID::from_uint16(0x181D) && service_data.data.size() == 10) { result.version = 1; @@ -65,15 +65,15 @@ optional XiaomiMiscale::parse_header(const esp32_ble_tracker::Servi return result; } -bool XiaomiMiscale::parse_message(const std::vector &message, ParseResult &result) { +bool XiaomiMiscale::parse_message_(const std::vector &message, ParseResult &result) { if (result.version == 1) { - return parse_message_V1(message, result); + return parse_message_v1_(message, result); } else { - return parse_message_V2(message, result); + return parse_message_v2_(message, result); } } -bool XiaomiMiscale::parse_message_V1(const std::vector &message, ParseResult &result) { +bool XiaomiMiscale::parse_message_v1_(const std::vector &message, ParseResult &result) { // message size is checked in parse_header // 1-2 Weight (MISCALE 181D) // 3-4 Years (MISCALE 181D) @@ -97,7 +97,7 @@ bool XiaomiMiscale::parse_message_V1(const std::vector &message, ParseR return true; } -bool XiaomiMiscale::parse_message_V2(const std::vector &message, ParseResult &result) { +bool XiaomiMiscale::parse_message_v2_(const std::vector &message, ParseResult &result) { // message size is checked in parse_header // 2-3 Years (MISCALE 2 181B) // 4 month (MISCALE 2 181B) @@ -138,7 +138,7 @@ bool XiaomiMiscale::parse_message_V2(const std::vector &message, ParseR return true; } -bool XiaomiMiscale::report_results(const optional &result, const std::string &address) { +bool XiaomiMiscale::report_results_(const optional &result, const std::string &address) { if (!result.has_value()) { ESP_LOGVV(TAG, "report_results(): no results available."); return false; diff --git a/esphome/components/xiaomi_miscale/xiaomi_miscale.h b/esphome/components/xiaomi_miscale/xiaomi_miscale.h index 3c958afc03..3e51405ddc 100644 --- a/esphome/components/xiaomi_miscale/xiaomi_miscale.h +++ b/esphome/components/xiaomi_miscale/xiaomi_miscale.h @@ -30,11 +30,11 @@ class XiaomiMiscale : public Component, public esp32_ble_tracker::ESPBTDeviceLis sensor::Sensor *weight_{nullptr}; sensor::Sensor *impedance_{nullptr}; - optional parse_header(const esp32_ble_tracker::ServiceData &service_data); - bool parse_message(const std::vector &message, ParseResult &result); - bool parse_message_V1(const std::vector &message, ParseResult &result); - bool parse_message_V2(const std::vector &message, ParseResult &result); - bool report_results(const optional &result, const std::string &address); + optional parse_header_(const esp32_ble_tracker::ServiceData &service_data); + bool parse_message_(const std::vector &message, ParseResult &result); + bool parse_message_v1_(const std::vector &message, ParseResult &result); + bool parse_message_v2_(const std::vector &message, ParseResult &result); + bool report_results_(const optional &result, const std::string &address); }; } // namespace xiaomi_miscale diff --git a/esphome/core/gpio.h b/esphome/core/gpio.h index 25d56b9020..1d3fb89805 100644 --- a/esphome/core/gpio.h +++ b/esphome/core/gpio.h @@ -6,7 +6,7 @@ namespace esphome { #define LOG_PIN(prefix, pin) \ if ((pin) != nullptr) { \ - ESP_LOGCONFIG(TAG, prefix "%s", pin->dump_summary().c_str()); \ + ESP_LOGCONFIG(TAG, prefix "%s", (pin)->dump_summary().c_str()); \ } // put GPIO flags in a namepsace to not pollute esphome namespace @@ -78,7 +78,7 @@ class ISRInternalGPIOPin { class InternalGPIOPin : public GPIOPin { public: template void attach_interrupt(void (*func)(T *), T *arg, gpio::InterruptType type) const { - this->attach_interrupt_(reinterpret_cast(func), arg, type); + this->attach_interrupt(reinterpret_cast(func), arg, type); } virtual void detach_interrupt() const = 0; @@ -92,7 +92,7 @@ class InternalGPIOPin : public GPIOPin { virtual bool is_inverted() const = 0; protected: - virtual void attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const = 0; + virtual void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const = 0; }; } // namespace esphome diff --git a/esphome/core/hal.h b/esphome/core/hal.h index 2843bb9b15..a86dbf2534 100644 --- a/esphome/core/hal.h +++ b/esphome/core/hal.h @@ -37,7 +37,7 @@ void yield(); uint32_t millis(); uint32_t micros(); void delay(uint32_t ms); -void delayMicroseconds(uint32_t us); +void delayMicroseconds(uint32_t us); // NOLINT(readability-identifier-naming) void __attribute__((noreturn)) arch_restart(); void arch_feed_wdt(); uint32_t arch_get_cpu_cycle_count(); diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 40d94005e7..86cd3b086e 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -333,10 +333,10 @@ template T *new_buffer(size_t length) { if (psramFound()) { buffer = (T *) ps_malloc(length); } else { - buffer = new T[length]; + buffer = new T[length]; // NOLINT(cppcoreguidelines-owning-memory) } #else - buffer = new T[length]; // NOLINT + buffer = new T[length]; // NOLINT(cppcoreguidelines-owning-memory) #endif return buffer; diff --git a/script/clang-tidy b/script/clang-tidy index 2612a18c1c..6e79059372 100755 --- a/script/clang-tidy +++ b/script/clang-tidy @@ -17,7 +17,7 @@ import pexpect sys.path.append(os.path.dirname(__file__)) from helpers import shlex_quote, get_output, filter_grep, \ - build_all_include, temp_header_file, git_ls_files, filter_changed, load_idedata + build_all_include, temp_header_file, git_ls_files, filter_changed, load_idedata, basepath def clang_options(idedata): @@ -89,6 +89,7 @@ def run_tidy(args, options, tmpdir, queue, lock, failed_files): invocation.append('-quiet') invocation.append(os.path.abspath(path)) + invocation.append(f"--header-filter={os.path.abspath(basepath)}/.*") invocation.append('--') invocation.extend(options) invocation_s = ' '.join(shlex_quote(x) for x in invocation) From 8503e08ee66f57b4e727550615f26af3e15ce9fe Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Sat, 25 Sep 2021 09:14:07 +0200 Subject: [PATCH 113/207] Fix InterruptLock on ESP-IDF (#2388) --- esphome/components/dallas/__init__.py | 18 ++++++++----- esphome/components/dht/dht.cpp | 39 +++++++++++++-------------- esphome/core/helpers.cpp | 2 +- 3 files changed, 31 insertions(+), 28 deletions(-) diff --git a/esphome/components/dallas/__init__.py b/esphome/components/dallas/__init__.py index 762bfdc3c3..2dbc69b8e2 100644 --- a/esphome/components/dallas/__init__.py +++ b/esphome/components/dallas/__init__.py @@ -11,13 +11,17 @@ dallas_ns = cg.esphome_ns.namespace("dallas") DallasComponent = dallas_ns.class_("DallasComponent", cg.PollingComponent) ESPOneWire = dallas_ns.class_("ESPOneWire") -CONFIG_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.declare_id(DallasComponent), - cv.GenerateID(CONF_ONE_WIRE_ID): cv.declare_id(ESPOneWire), - cv.Required(CONF_PIN): pins.internal_gpio_output_pin_schema, - } -).extend(cv.polling_component_schema("60s")) +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(DallasComponent), + cv.GenerateID(CONF_ONE_WIRE_ID): cv.declare_id(ESPOneWire), + cv.Required(CONF_PIN): pins.internal_gpio_output_pin_schema, + } + ).extend(cv.polling_component_schema("60s")), + # pin_mode call logs in esp-idf, but InterruptLock is active -> crash + cv.only_with_arduino, +) async def to_code(config): diff --git a/esphome/components/dht/dht.cpp b/esphome/components/dht/dht.cpp index 2539bfe5ee..2a4ccf1529 100644 --- a/esphome/components/dht/dht.cpp +++ b/esphome/components/dht/dht.cpp @@ -79,28 +79,27 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r int8_t i = 0; uint8_t data[5] = {0, 0, 0, 0, 0}; + this->pin_->digital_write(false); + this->pin_->pin_mode(gpio::FLAG_OUTPUT); + this->pin_->digital_write(false); + + if (this->model_ == DHT_MODEL_DHT11) { + delayMicroseconds(18000); + } else if (this->model_ == DHT_MODEL_SI7021) { + delayMicroseconds(500); + this->pin_->digital_write(true); + delayMicroseconds(40); + } else if (this->model_ == DHT_MODEL_DHT22_TYPE2) { + delayMicroseconds(2000); + } else if (this->model_ == DHT_MODEL_AM2302) { + delayMicroseconds(1000); + } else { + delayMicroseconds(800); + } + this->pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); + { InterruptLock lock; - - this->pin_->digital_write(false); - this->pin_->pin_mode(gpio::FLAG_OUTPUT); - this->pin_->digital_write(false); - - if (this->model_ == DHT_MODEL_DHT11) { - delayMicroseconds(18000); - } else if (this->model_ == DHT_MODEL_SI7021) { - delayMicroseconds(500); - this->pin_->digital_write(true); - delayMicroseconds(40); - } else if (this->model_ == DHT_MODEL_DHT22_TYPE2) { - delayMicroseconds(2000); - } else if (this->model_ == DHT_MODEL_AM2302) { - delayMicroseconds(1000); - } else { - delayMicroseconds(800); - } - this->pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); - // Host pull up 20-40us then DHT response 80us // Start waiting for initial rising edge at the center when we // expect the DHT response (30us+40us) diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index a90eb74be2..2b77c5827a 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -350,7 +350,7 @@ std::string hexencode(const uint8_t *data, uint32_t len) { IRAM_ATTR InterruptLock::InterruptLock() { xt_state_ = xt_rsil(15); } IRAM_ATTR InterruptLock::~InterruptLock() { xt_wsr_ps(xt_state_); } #endif -#ifdef USE_ESP32_FRAMEWORK_ARDUINO +#ifdef USE_ESP32 IRAM_ATTR InterruptLock::InterruptLock() { portDISABLE_INTERRUPTS(); } IRAM_ATTR InterruptLock::~InterruptLock() { portENABLE_INTERRUPTS(); } #endif From 278863d0272c3005b6e5b2f6c0ff3d1c297cb4e4 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Sat, 25 Sep 2021 09:16:32 +0200 Subject: [PATCH 114/207] Fix some issues with wifi driver after IDF refactor (#2387) --- .../wifi/wifi_component_esp32_arduino.cpp | 156 ++++++++++-------- .../wifi/wifi_component_esp8266.cpp | 14 +- .../wifi/wifi_component_esp_idf.cpp | 13 +- 3 files changed, 102 insertions(+), 81 deletions(-) diff --git a/esphome/components/wifi/wifi_component_esp32_arduino.cpp b/esphome/components/wifi/wifi_component_esp32_arduino.cpp index df2692ea81..a0f8a3d114 100644 --- a/esphome/components/wifi/wifi_component_esp32_arduino.cpp +++ b/esphome/components/wifi/wifi_component_esp32_arduino.cpp @@ -24,11 +24,7 @@ namespace wifi { static const char *const TAG = "wifi_esp32"; -static bool s_sta_connected = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -static bool s_sta_got_ip = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -static bool s_sta_connect_not_found = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -static bool s_sta_connect_error = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -static bool s_sta_connecting = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static bool s_sta_connecting = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) bool WiFiComponent::wifi_mode_(optional sta, optional ap) { uint8_t current_mode = WiFiClass::getMode(); @@ -122,8 +118,8 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { info.netmask.addr = static_cast(manual_ip->subnet); esp_err_t dhcp_stop_ret = tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA); - if (dhcp_stop_ret != ESP_OK) { - ESP_LOGV(TAG, "Stopping DHCP client failed! %d", dhcp_stop_ret); + if (dhcp_stop_ret != ESP_OK && dhcp_stop_ret != ESP_ERR_TCPIP_ADAPTER_DHCP_ALREADY_STOPPED) { + ESP_LOGV(TAG, "Stopping DHCP client failed! %s", esp_err_to_name(dhcp_stop_ret)); } esp_err_t wifi_set_info_ret = tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &info); @@ -280,18 +276,14 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { this->wifi_apply_hostname_(); + s_sta_connecting = true; + err = esp_wifi_connect(); if (err != ESP_OK) { ESP_LOGW(TAG, "esp_wifi_connect failed! %d", err); return false; } - s_sta_connecting = true; - s_sta_connected = false; - s_sta_got_ip = false; - s_sta_connect_error = false; - s_sta_connect_not_found = false; - return true; } const char *get_auth_mode_str(uint8_t mode) { @@ -402,35 +394,78 @@ const char *get_disconnect_reason_str(uint8_t reason) { return "Unspecified"; } } + #if ESP_IDF_VERSION_MAJOR >= 4 -void WiFiComponent::wifi_event_callback_(arduino_event_id_t event, arduino_event_info_t info) { -#else -void WiFiComponent::wifi_event_callback_(system_event_id_t event, system_event_info_t info) { -#endif + +#define ESPHOME_EVENT_ID_WIFI_READY ARDUINO_EVENT_WIFI_READY +#define ESPHOME_EVENT_ID_WIFI_SCAN_DONE ARDUINO_EVENT_WIFI_SCAN_DONE +#define ESPHOME_EVENT_ID_WIFI_STA_START ARDUINO_EVENT_WIFI_STA_START +#define ESPHOME_EVENT_ID_WIFI_STA_STOP ARDUINO_EVENT_WIFI_STA_STOP +#define ESPHOME_EVENT_ID_WIFI_STA_CONNECTED ARDUINO_EVENT_WIFI_STA_CONNECTED +#define ESPHOME_EVENT_ID_WIFI_STA_DISCONNECTED ARDUINO_EVENT_WIFI_STA_DISCONNECTED +#define ESPHOME_EVENT_ID_WIFI_STA_AUTHMODE_CHANGE ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE +#define ESPHOME_EVENT_ID_WIFI_STA_GOT_IP ARDUINO_EVENT_WIFI_STA_GOT_IP +#define ESPHOME_EVENT_ID_WIFI_STA_GOT_IP6 ARDUINO_EVENT_WIFI_STA_GOT_IP6 +#define ESPHOME_EVENT_ID_WIFI_STA_LOST_IP ARDUINO_EVENT_WIFI_STA_LOST_IP +#define ESPHOME_EVENT_ID_WIFI_AP_START ARDUINO_EVENT_WIFI_AP_START +#define ESPHOME_EVENT_ID_WIFI_AP_STOP ARDUINO_EVENT_WIFI_AP_STOP +#define ESPHOME_EVENT_ID_WIFI_AP_STACONNECTED ARDUINO_EVENT_WIFI_AP_STACONNECTED +#define ESPHOME_EVENT_ID_WIFI_AP_STADISCONNECTED ARDUINO_EVENT_WIFI_AP_STADISCONNECTED +#define ESPHOME_EVENT_ID_WIFI_AP_STAIPASSIGNED ARDUINO_EVENT_WIFI_AP_STAIPASSIGNED +#define ESPHOME_EVENT_ID_WIFI_AP_PROBEREQRECVED ARDUINO_EVENT_WIFI_AP_PROBEREQRECVED +#define ESPHOME_EVENT_ID_WIFI_AP_GOT_IP6 ARDUINO_EVENT_WIFI_AP_GOT_IP6 +using esphome_wifi_event_id_t = arduino_event_id_t; +using esphome_wifi_event_info_t = arduino_event_info_t; + +#else // ESP_IDF_VERSION_MAJOR >= 4 + +#define ESPHOME_EVENT_ID_WIFI_READY SYSTEM_EVENT_WIFI_READY +#define ESPHOME_EVENT_ID_WIFI_SCAN_DONE SYSTEM_EVENT_SCAN_DONE +#define ESPHOME_EVENT_ID_WIFI_STA_START SYSTEM_EVENT_STA_START +#define ESPHOME_EVENT_ID_WIFI_STA_STOP SYSTEM_EVENT_STA_STOP +#define ESPHOME_EVENT_ID_WIFI_STA_CONNECTED SYSTEM_EVENT_STA_CONNECTED +#define ESPHOME_EVENT_ID_WIFI_STA_DISCONNECTED SYSTEM_EVENT_STA_DISCONNECTED +#define ESPHOME_EVENT_ID_WIFI_STA_AUTHMODE_CHANGE SYSTEM_EVENT_STA_AUTHMODE_CHANGE +#define ESPHOME_EVENT_ID_WIFI_STA_GOT_IP SYSTEM_EVENT_STA_GOT_IP +#define ESPHOME_EVENT_ID_WIFI_STA_LOST_IP SYSTEM_EVENT_STA_LOST_IP +#define ESPHOME_EVENT_ID_WIFI_AP_START SYSTEM_EVENT_AP_START +#define ESPHOME_EVENT_ID_WIFI_AP_STOP SYSTEM_EVENT_AP_STOP +#define ESPHOME_EVENT_ID_WIFI_AP_STACONNECTED SYSTEM_EVENT_AP_STACONNECTED +#define ESPHOME_EVENT_ID_WIFI_AP_STADISCONNECTED SYSTEM_EVENT_AP_STADISCONNECTED +#define ESPHOME_EVENT_ID_WIFI_AP_STAIPASSIGNED SYSTEM_EVENT_AP_STAIPASSIGNED +#define ESPHOME_EVENT_ID_WIFI_AP_PROBEREQRECVED SYSTEM_EVENT_AP_PROBEREQRECVED +using esphome_wifi_event_id_t = system_event_id_t; +using esphome_wifi_event_info_t = system_event_info_t; + +#endif // !(ESP_IDF_VERSION_MAJOR >= 4) + +void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_wifi_event_info_t info) { switch (event) { - case SYSTEM_EVENT_WIFI_READY: { + case ESPHOME_EVENT_ID_WIFI_READY: { ESP_LOGV(TAG, "Event: WiFi ready"); break; } - case SYSTEM_EVENT_SCAN_DONE: { + case ESPHOME_EVENT_ID_WIFI_SCAN_DONE: { #if ESP_IDF_VERSION_MAJOR >= 4 auto it = info.wifi_scan_done; #else auto it = info.scan_done; #endif ESP_LOGV(TAG, "Event: WiFi Scan Done status=%u number=%u scan_id=%u", it.status, it.number, it.scan_id); + + this->wifi_scan_done_callback_(); break; } - case SYSTEM_EVENT_STA_START: { + case ESPHOME_EVENT_ID_WIFI_STA_START: { ESP_LOGV(TAG, "Event: WiFi STA start"); tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_STA, App.get_name().c_str()); break; } - case SYSTEM_EVENT_STA_STOP: { + case ESPHOME_EVENT_ID_WIFI_STA_STOP: { ESP_LOGV(TAG, "Event: WiFi STA stop"); break; } - case SYSTEM_EVENT_STA_CONNECTED: { + case ESPHOME_EVENT_ID_WIFI_STA_CONNECTED: { #if ESP_IDF_VERSION_MAJOR >= 4 auto it = info.wifi_sta_connected; #else @@ -441,10 +476,10 @@ void WiFiComponent::wifi_event_callback_(system_event_id_t event, system_event_i buf[it.ssid_len] = '\0'; ESP_LOGV(TAG, "Event: Connected ssid='%s' bssid=" LOG_SECRET("%s") " channel=%u, authmode=%s", buf, format_mac_addr(it.bssid).c_str(), it.channel, get_auth_mode_str(it.authmode)); - s_sta_connected = true; + break; } - case SYSTEM_EVENT_STA_DISCONNECTED: { + case ESPHOME_EVENT_ID_WIFI_STA_DISCONNECTED: { #if ESP_IDF_VERSION_MAJOR >= 4 auto it = info.wifi_sta_disconnected; #else @@ -455,17 +490,26 @@ void WiFiComponent::wifi_event_callback_(system_event_id_t event, system_event_i buf[it.ssid_len] = '\0'; if (it.reason == WIFI_REASON_NO_AP_FOUND) { ESP_LOGW(TAG, "Event: Disconnected ssid='%s' reason='Probe Request Unsuccessful'", buf); - s_sta_connect_not_found = true; } else { ESP_LOGW(TAG, "Event: Disconnected ssid='%s' bssid=" LOG_SECRET("%s") " reason='%s'", buf, format_mac_addr(it.bssid).c_str(), get_disconnect_reason_str(it.reason)); - s_sta_connect_error = true; } - s_sta_connected = false; + + uint8_t reason = it.reason; + if (reason == WIFI_REASON_AUTH_EXPIRE || reason == WIFI_REASON_BEACON_TIMEOUT || + reason == WIFI_REASON_NO_AP_FOUND || reason == WIFI_REASON_ASSOC_FAIL || + reason == WIFI_REASON_HANDSHAKE_TIMEOUT) { + err_t err = esp_wifi_disconnect(); + if (err != ESP_OK) { + ESP_LOGV(TAG, "Disconnect failed: %s", esp_err_to_name(err)); + } + this->error_from_callback_ = true; + } + s_sta_connecting = false; break; } - case SYSTEM_EVENT_STA_AUTHMODE_CHANGE: { + case ESPHOME_EVENT_ID_WIFI_STA_AUTHMODE_CHANGE: { #if ESP_IDF_VERSION_MAJOR >= 4 auto it = info.wifi_sta_authmode_change; #else @@ -487,27 +531,26 @@ void WiFiComponent::wifi_event_callback_(system_event_id_t event, system_event_i } break; } - case SYSTEM_EVENT_STA_GOT_IP: { + case ESPHOME_EVENT_ID_WIFI_STA_GOT_IP: { auto it = info.got_ip.ip_info; ESP_LOGV(TAG, "Event: Got IP static_ip=%s gateway=%s", format_ip4_addr(it.ip).c_str(), format_ip4_addr(it.gw).c_str()); - s_sta_got_ip = true; + s_sta_connecting = false; break; } - case SYSTEM_EVENT_STA_LOST_IP: { + case ESPHOME_EVENT_ID_WIFI_STA_LOST_IP: { ESP_LOGV(TAG, "Event: Lost IP"); - s_sta_got_ip = false; break; } - case SYSTEM_EVENT_AP_START: { + case ESPHOME_EVENT_ID_WIFI_AP_START: { ESP_LOGV(TAG, "Event: WiFi AP start"); break; } - case SYSTEM_EVENT_AP_STOP: { + case ESPHOME_EVENT_ID_WIFI_AP_STOP: { ESP_LOGV(TAG, "Event: WiFi AP stop"); break; } - case SYSTEM_EVENT_AP_STACONNECTED: { + case ESPHOME_EVENT_ID_WIFI_AP_STACONNECTED: { #if ESP_IDF_VERSION_MAJOR >= 4 auto it = info.wifi_sta_connected; auto &mac = it.bssid; @@ -518,7 +561,7 @@ void WiFiComponent::wifi_event_callback_(system_event_id_t event, system_event_i ESP_LOGV(TAG, "Event: AP client connected MAC=%s", format_mac_addr(mac).c_str()); break; } - case SYSTEM_EVENT_AP_STADISCONNECTED: { + case ESPHOME_EVENT_ID_WIFI_AP_STADISCONNECTED: { #if ESP_IDF_VERSION_MAJOR >= 4 auto it = info.wifi_sta_disconnected; auto &mac = it.bssid; @@ -529,11 +572,11 @@ void WiFiComponent::wifi_event_callback_(system_event_id_t event, system_event_i ESP_LOGV(TAG, "Event: AP client disconnected MAC=%s", format_mac_addr(mac).c_str()); break; } - case SYSTEM_EVENT_AP_STAIPASSIGNED: { + case ESPHOME_EVENT_ID_WIFI_AP_STAIPASSIGNED: { ESP_LOGV(TAG, "Event: AP client assigned IP"); break; } - case SYSTEM_EVENT_AP_PROBEREQRECVED: { + case ESPHOME_EVENT_ID_WIFI_AP_PROBEREQRECVED: { #if ESP_IDF_VERSION_MAJOR >= 4 auto it = info.wifi_ap_probereqrecved; #else @@ -545,31 +588,6 @@ void WiFiComponent::wifi_event_callback_(system_event_id_t event, system_event_i default: break; } - -#if ESP_IDF_VERSION_MAJOR >= 4 - if (event == ARDUINO_EVENT_WIFI_STA_DISCONNECTED) { - uint8_t reason = info.wifi_sta_disconnected.reason; -#else - if (event == SYSTEM_EVENT_STA_DISCONNECTED) { - uint8_t reason = info.disconnected.reason; -#endif - if (reason == WIFI_REASON_AUTH_EXPIRE || reason == WIFI_REASON_BEACON_TIMEOUT || - reason == WIFI_REASON_NO_AP_FOUND || reason == WIFI_REASON_ASSOC_FAIL || - reason == WIFI_REASON_HANDSHAKE_TIMEOUT) { - err_t err = esp_wifi_disconnect(); - if (err != ESP_OK) { - ESP_LOGV(TAG, "Disconnect failed: %s", esp_err_to_name(err)); - } - this->error_from_callback_ = true; - } - } -#if ESP_IDF_VERSION_MAJOR >= 4 - if (event == ARDUINO_EVENT_WIFI_SCAN_DONE) { -#else - if (event == SYSTEM_EVENT_SCAN_DONE) { -#endif - this->wifi_scan_done_callback_(); - } } void WiFiComponent::wifi_pre_setup_() { auto f = std::bind(&WiFiComponent::wifi_event_callback_, this, std::placeholders::_1, std::placeholders::_2); @@ -579,16 +597,14 @@ void WiFiComponent::wifi_pre_setup_() { this->wifi_mode_(false, false); } WiFiSTAConnectStatus WiFiComponent::wifi_sta_connect_status_() { - if (s_sta_connected && s_sta_got_ip) { + auto status = WiFiClass::status(); + if (status == WL_CONNECTED) { return WiFiSTAConnectStatus::CONNECTED; - } - if (s_sta_connect_error) { + } else if (status == WL_CONNECT_FAILED || status == WL_CONNECTION_LOST || status == WL_DISCONNECTED) { return WiFiSTAConnectStatus::ERROR_CONNECT_FAILED; - } - if (s_sta_connect_not_found) { + } else if (status == WL_NO_SSID_AVAIL) { return WiFiSTAConnectStatus::ERROR_NETWORK_NOT_FOUND; - } - if (s_sta_connecting) { + } else if (s_sta_connecting) { return WiFiSTAConnectStatus::CONNECTING; } return WiFiSTAConnectStatus::IDLE; diff --git a/esphome/components/wifi/wifi_component_esp8266.cpp b/esphome/components/wifi/wifi_component_esp8266.cpp index 5dcfe7a108..2021773209 100644 --- a/esphome/components/wifi/wifi_component_esp8266.cpp +++ b/esphome/components/wifi/wifi_component_esp8266.cpp @@ -309,6 +309,14 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { this->wifi_apply_hostname_(); + // Reset flags, do this _before_ wifi_station_connect as the callback method + // may be called from wifi_station_connect + s_sta_connecting = true; + s_sta_connected = false; + s_sta_got_ip = false; + s_sta_connect_error = false; + s_sta_connect_not_found = false; + ETS_UART_INTR_DISABLE(); ret = wifi_station_connect(); ETS_UART_INTR_ENABLE(); @@ -325,12 +333,6 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { } } - s_sta_connecting = true; - s_sta_connected = false; - s_sta_got_ip = false; - s_sta_connect_error = false; - s_sta_connect_not_found = false; - return true; } diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index 676b7f8fba..9ec3f80014 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -377,17 +377,20 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { } #endif // USE_WIFI_WPA2_EAP + // Reset flags, do this _before_ wifi_station_connect as the callback method + // may be called from wifi_station_connect + s_sta_connecting = true; + s_sta_connected = false; + s_sta_got_ip = false; + s_sta_connect_error = false; + s_sta_connect_not_found = false; + err = esp_wifi_connect(); if (err != ESP_OK) { ESP_LOGW(TAG, "esp_wifi_connect failed: %s", esp_err_to_name(err)); return false; } - s_sta_connecting = true; - s_sta_connected = false; - s_sta_got_ip = false; - s_sta_connect_error = false; - s_sta_connect_not_found = false; return true; } From d344b1ca0efcb93a74bb64adf5c5cb4e907c1f54 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Sat, 25 Sep 2021 10:04:57 +0200 Subject: [PATCH 115/207] Fix arduino esp32 wifi v2 (#2389) --- esphome/components/wifi/wifi_component_esp32_arduino.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/wifi/wifi_component_esp32_arduino.cpp b/esphome/components/wifi/wifi_component_esp32_arduino.cpp index a0f8a3d114..e1332e3181 100644 --- a/esphome/components/wifi/wifi_component_esp32_arduino.cpp +++ b/esphome/components/wifi/wifi_component_esp32_arduino.cpp @@ -600,7 +600,7 @@ WiFiSTAConnectStatus WiFiComponent::wifi_sta_connect_status_() { auto status = WiFiClass::status(); if (status == WL_CONNECTED) { return WiFiSTAConnectStatus::CONNECTED; - } else if (status == WL_CONNECT_FAILED || status == WL_CONNECTION_LOST || status == WL_DISCONNECTED) { + } else if (status == WL_CONNECT_FAILED || status == WL_CONNECTION_LOST) { return WiFiSTAConnectStatus::ERROR_CONNECT_FAILED; } else if (status == WL_NO_SSID_AVAIL) { return WiFiSTAConnectStatus::ERROR_NETWORK_NOT_FOUND; From 5342edf04af2476ba88eceadbe1a2e08682ab2f3 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Sat, 25 Sep 2021 10:05:32 +0200 Subject: [PATCH 116/207] Misc fixes for esp-idf (#2386) --- esphome/components/logger/logger.cpp | 13 ++++++------- esphome/core/config.py | 5 +++++ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/esphome/components/logger/logger.cpp b/esphome/components/logger/logger.cpp index 045f7059a9..4352b7e208 100644 --- a/esphome/components/logger/logger.cpp +++ b/esphome/components/logger/logger.cpp @@ -176,13 +176,12 @@ void Logger::pre_setup() { uart_num_ = UART_NUM_2; break; } - uart_config_t uart_config = { - .baud_rate = (int) baud_rate_, - .data_bits = UART_DATA_8_BITS, - .parity = UART_PARITY_DISABLE, - .stop_bits = UART_STOP_BITS_1, - .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, - }; + uart_config_t uart_config{}; + uart_config.baud_rate = (int) baud_rate_; + uart_config.data_bits = UART_DATA_8_BITS; + uart_config.parity = UART_PARITY_DISABLE; + uart_config.stop_bits = UART_STOP_BITS_1; + uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE; uart_param_config(uart_num_, &uart_config); const int uart_buffer_size = tx_buffer_size_; // Install UART driver using an event queue here diff --git a/esphome/core/config.py b/esphome/core/config.py index 71add56b13..bbdfcf124c 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -247,6 +247,11 @@ async def _add_automations(config): @coroutine_with_priority(100.0) async def to_code(config): cg.add_global(cg.global_ns.namespace("esphome").using) + # These can be used by user lambdas, put them to default scope + cg.add_global(cg.RawExpression("using std::isnan")) + cg.add_global(cg.RawExpression("using std::min")) + cg.add_global(cg.RawExpression("using std::max")) + cg.add( cg.App.pre_setup( config[CONF_NAME], From 95a6715b2b3ebc30d31430b24beb888583a1f913 Mon Sep 17 00:00:00 2001 From: rbaron Date: Sat, 25 Sep 2021 13:16:27 +0200 Subject: [PATCH 117/207] Adds light sensor support for b-parasites (#2391) --- esphome/components/b_parasite/b_parasite.cpp | 20 ++++++++++++++++++++ esphome/components/b_parasite/b_parasite.h | 2 ++ esphome/components/b_parasite/sensor.py | 10 ++++++++++ tests/test2.yaml | 2 ++ 4 files changed, 34 insertions(+) diff --git a/esphome/components/b_parasite/b_parasite.cpp b/esphome/components/b_parasite/b_parasite.cpp index bc1463fd1c..ee12226977 100644 --- a/esphome/components/b_parasite/b_parasite.cpp +++ b/esphome/components/b_parasite/b_parasite.cpp @@ -14,6 +14,7 @@ void BParasite::dump_config() { LOG_SENSOR(" ", "Temperature", this->temperature_); LOG_SENSOR(" ", "Humidity", this->humidity_); LOG_SENSOR(" ", "Soil Moisture", this->soil_moisture_); + LOG_SENSOR(" ", "Illuminance", this->illuminance_); } bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { @@ -36,6 +37,15 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { const auto &data = service_data.data; + const uint8_t protocol_version = data[0] >> 4; + if (protocol_version != 1) { + ESP_LOGE(TAG, "Unsupported protocol version: %u", protocol_version); + return false; + } + + // Some b-parasite versions have an (optional) illuminance sensor. + bool has_illuminance = data[0] & 0x1; + // Counter for deduplicating messages. uint8_t counter = data[1] & 0x0f; if (last_processed_counter_ == counter) { @@ -59,6 +69,9 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { uint16_t soil_moisture = data[8] << 8 | data[9]; float moisture_percent = (100.0f * soil_moisture) / (1 << 16); + // Ambient light in lux. + float illuminance = has_illuminance ? data[16] << 8 | data[17] : 0.0f; + if (battery_voltage_ != nullptr) { battery_voltage_->publish_state(battery_voltage); } @@ -71,6 +84,13 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { if (soil_moisture_ != nullptr) { soil_moisture_->publish_state(moisture_percent); } + if (illuminance_ != nullptr) { + if (has_illuminance) { + illuminance_->publish_state(illuminance); + } else { + ESP_LOGE(TAG, "No lux information is present in the BLE packet"); + } + } last_processed_counter_ = counter; return true; diff --git a/esphome/components/b_parasite/b_parasite.h b/esphome/components/b_parasite/b_parasite.h index bdd9a01b83..70ee4ab23c 100644 --- a/esphome/components/b_parasite/b_parasite.h +++ b/esphome/components/b_parasite/b_parasite.h @@ -22,6 +22,7 @@ class BParasite : public Component, public esp32_ble_tracker::ESPBTDeviceListene void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; } void set_humidity(sensor::Sensor *humidity) { humidity_ = humidity; } void set_soil_moisture(sensor::Sensor *soil_moisture) { soil_moisture_ = soil_moisture; } + void set_illuminance(sensor::Sensor *illuminance) { illuminance_ = illuminance; } protected: // The received advertisement packet contains an unsigned 4 bits wrap-around counter @@ -32,6 +33,7 @@ class BParasite : public Component, public esp32_ble_tracker::ESPBTDeviceListene sensor::Sensor *temperature_{nullptr}; sensor::Sensor *humidity_{nullptr}; sensor::Sensor *soil_moisture_{nullptr}; + sensor::Sensor *illuminance_{nullptr}; }; } // namespace b_parasite diff --git a/esphome/components/b_parasite/sensor.py b/esphome/components/b_parasite/sensor.py index 46ed64337f..d51c48c602 100644 --- a/esphome/components/b_parasite/sensor.py +++ b/esphome/components/b_parasite/sensor.py @@ -5,14 +5,17 @@ from esphome.const import ( CONF_BATTERY_VOLTAGE, CONF_HUMIDITY, CONF_ID, + CONF_ILLUMINANCE, CONF_MOISTURE, CONF_MAC_ADDRESS, CONF_TEMPERATURE, DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_ILLUMINANCE, DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT, UNIT_CELSIUS, + UNIT_LUX, UNIT_PERCENT, UNIT_VOLT, ) @@ -55,6 +58,12 @@ CONFIG_SCHEMA = ( device_class=DEVICE_CLASS_HUMIDITY, state_class=STATE_CLASS_MEASUREMENT, ), + cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema( + unit_of_measurement=UNIT_LUX, + accuracy_decimals=0, + device_class=DEVICE_CLASS_ILLUMINANCE, + state_class=STATE_CLASS_MEASUREMENT, + ), } ) .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) @@ -74,6 +83,7 @@ async def to_code(config): (CONF_HUMIDITY, var.set_humidity), (CONF_BATTERY_VOLTAGE, var.set_battery_voltage), (CONF_MOISTURE, var.set_soil_moisture), + (CONF_ILLUMINANCE, var.set_illuminance), ]: if config_key in config: sens = await sensor.new_sensor(config[config_key]) diff --git a/tests/test2.yaml b/tests/test2.yaml index d0634e0f7b..4541fba616 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -91,6 +91,8 @@ sensor: name: 'b-parasite Soil Moisture' battery_voltage: name: 'b-parasite Battery Voltage' + illuminance: + name: 'b-parasite Illuminance' - platform: senseair id: senseair0 co2: From bdcffc7ba91c32e33b77df1e22f8868a9421eb29 Mon Sep 17 00:00:00 2001 From: irtimaled Date: Sun, 26 Sep 2021 01:27:43 -0700 Subject: [PATCH 118/207] fix: Setting Tuya string DP value (#2394) --- esphome/components/tuya/tuya.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/tuya/tuya.cpp b/esphome/components/tuya/tuya.cpp index d73ba50462..4f65fa7118 100644 --- a/esphome/components/tuya/tuya.cpp +++ b/esphome/components/tuya/tuya.cpp @@ -479,7 +479,7 @@ void Tuya::set_string_datapoint_value(uint8_t datapoint_id, const std::string &v for (char const &c : value) { data.push_back(c); } - this->send_datapoint_command_(datapoint->id, datapoint->type, data); + this->send_datapoint_command_(datapoint_id, TuyaDatapointType::STRING, data); } void Tuya::set_enum_datapoint_value(uint8_t datapoint_id, uint8_t value) { From 7246f42a8ec4ba2c15ff930c69dacc4f174f6269 Mon Sep 17 00:00:00 2001 From: irtimaled Date: Sun, 26 Sep 2021 01:34:06 -0700 Subject: [PATCH 119/207] Tuya rgb support (#2278) Co-authored-by: Oxan van Leeuwen Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/rgbct/light.py | 2 +- esphome/components/rgbw/light.py | 10 ++- esphome/components/rgbww/light.py | 2 +- esphome/components/tuya/light/__init__.py | 12 ++- esphome/components/tuya/light/tuya_light.cpp | 91 ++++++++++++++------ esphome/components/tuya/light/tuya_light.h | 5 ++ esphome/const.py | 1 + esphome/core/helpers.cpp | 33 +++++++ esphome/core/helpers.h | 3 +- 9 files changed, 129 insertions(+), 30 deletions(-) diff --git a/esphome/components/rgbct/light.py b/esphome/components/rgbct/light.py index e525c207c7..0565057316 100644 --- a/esphome/components/rgbct/light.py +++ b/esphome/components/rgbct/light.py @@ -3,6 +3,7 @@ import esphome.config_validation as cv from esphome.components import light, output from esphome.const import ( CONF_BLUE, + CONF_COLOR_INTERLOCK, CONF_COLOR_TEMPERATURE, CONF_GREEN, CONF_RED, @@ -16,7 +17,6 @@ CODEOWNERS = ["@jesserockz"] rgbct_ns = cg.esphome_ns.namespace("rgbct") RGBCTLightOutput = rgbct_ns.class_("RGBCTLightOutput", light.LightOutput) -CONF_COLOR_INTERLOCK = "color_interlock" CONF_WHITE_BRIGHTNESS = "white_brightness" CONFIG_SCHEMA = cv.All( diff --git a/esphome/components/rgbw/light.py b/esphome/components/rgbw/light.py index de26edf7d5..f747580f61 100644 --- a/esphome/components/rgbw/light.py +++ b/esphome/components/rgbw/light.py @@ -1,11 +1,17 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import light, output -from esphome.const import CONF_BLUE, CONF_GREEN, CONF_RED, CONF_OUTPUT_ID, CONF_WHITE +from esphome.const import ( + CONF_BLUE, + CONF_COLOR_INTERLOCK, + CONF_GREEN, + CONF_RED, + CONF_OUTPUT_ID, + CONF_WHITE, +) rgbw_ns = cg.esphome_ns.namespace("rgbw") RGBWLightOutput = rgbw_ns.class_("RGBWLightOutput", light.LightOutput) -CONF_COLOR_INTERLOCK = "color_interlock" CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend( { diff --git a/esphome/components/rgbww/light.py b/esphome/components/rgbww/light.py index c0ce85e267..35f77b154b 100644 --- a/esphome/components/rgbww/light.py +++ b/esphome/components/rgbww/light.py @@ -3,6 +3,7 @@ import esphome.config_validation as cv from esphome.components import light, output from esphome.const import ( CONF_BLUE, + CONF_COLOR_INTERLOCK, CONF_CONSTANT_BRIGHTNESS, CONF_GREEN, CONF_RED, @@ -16,7 +17,6 @@ from esphome.const import ( rgbww_ns = cg.esphome_ns.namespace("rgbww") RGBWWLightOutput = rgbww_ns.class_("RGBWWLightOutput", light.LightOutput) -CONF_COLOR_INTERLOCK = "color_interlock" CONFIG_SCHEMA = cv.All( light.RGB_LIGHT_SCHEMA.extend( diff --git a/esphome/components/tuya/light/__init__.py b/esphome/components/tuya/light/__init__.py index f43cc570ca..6678fc47d8 100644 --- a/esphome/components/tuya/light/__init__.py +++ b/esphome/components/tuya/light/__init__.py @@ -10,6 +10,7 @@ from esphome.const import ( CONF_SWITCH_DATAPOINT, CONF_COLD_WHITE_COLOR_TEMPERATURE, CONF_WARM_WHITE_COLOR_TEMPERATURE, + CONF_COLOR_INTERLOCK, ) from .. import tuya_ns, CONF_TUYA_ID, Tuya @@ -20,6 +21,7 @@ CONF_MIN_VALUE_DATAPOINT = "min_value_datapoint" CONF_COLOR_TEMPERATURE_DATAPOINT = "color_temperature_datapoint" CONF_COLOR_TEMPERATURE_INVERT = "color_temperature_invert" CONF_COLOR_TEMPERATURE_MAX_VALUE = "color_temperature_max_value" +CONF_RGB_DATAPOINT = "rgb_datapoint" TuyaLight = tuya_ns.class_("TuyaLight", light.LightOutput, cg.Component) @@ -31,6 +33,8 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_DIMMER_DATAPOINT): cv.uint8_t, cv.Optional(CONF_MIN_VALUE_DATAPOINT): cv.uint8_t, cv.Optional(CONF_SWITCH_DATAPOINT): cv.uint8_t, + cv.Optional(CONF_RGB_DATAPOINT): cv.uint8_t, + cv.Optional(CONF_COLOR_INTERLOCK, default=False): cv.boolean, cv.Inclusive( CONF_COLOR_TEMPERATURE_DATAPOINT, "color_temperature" ): cv.uint8_t, @@ -52,7 +56,9 @@ CONFIG_SCHEMA = cv.All( ): cv.positive_time_period_milliseconds, } ).extend(cv.COMPONENT_SCHEMA), - cv.has_at_least_one_key(CONF_DIMMER_DATAPOINT, CONF_SWITCH_DATAPOINT), + cv.has_at_least_one_key( + CONF_DIMMER_DATAPOINT, CONF_SWITCH_DATAPOINT, CONF_RGB_DATAPOINT + ), ) @@ -67,6 +73,8 @@ async def to_code(config): cg.add(var.set_min_value_datapoint_id(config[CONF_MIN_VALUE_DATAPOINT])) if CONF_SWITCH_DATAPOINT in config: cg.add(var.set_switch_id(config[CONF_SWITCH_DATAPOINT])) + if CONF_RGB_DATAPOINT in config: + cg.add(var.set_rgb_id(config[CONF_RGB_DATAPOINT])) if CONF_COLOR_TEMPERATURE_DATAPOINT in config: cg.add(var.set_color_temperature_id(config[CONF_COLOR_TEMPERATURE_DATAPOINT])) cg.add(var.set_color_temperature_invert(config[CONF_COLOR_TEMPERATURE_INVERT])) @@ -87,5 +95,7 @@ async def to_code(config): config[CONF_COLOR_TEMPERATURE_MAX_VALUE] ) ) + + cg.add(var.set_color_interlock(config[CONF_COLOR_INTERLOCK])) paren = await cg.get_variable(config[CONF_TUYA_ID]) cg.add(var.set_tuya_parent(paren)) diff --git a/esphome/components/tuya/light/tuya_light.cpp b/esphome/components/tuya/light/tuya_light.cpp index 6f3adfcdfd..97f6de9bae 100644 --- a/esphome/components/tuya/light/tuya_light.cpp +++ b/esphome/components/tuya/light/tuya_light.cpp @@ -1,5 +1,6 @@ #include "esphome/core/log.h" #include "tuya_light.h" +#include "esphome/core/helpers.h" namespace esphome { namespace tuya { @@ -34,6 +35,18 @@ void TuyaLight::setup() { call.perform(); }); } + if (rgb_id_.has_value()) { + this->parent_->register_listener(*this->rgb_id_, [this](const TuyaDatapoint &datapoint) { + auto red = parse_hex(datapoint.value_string, 0, 2); + auto green = parse_hex(datapoint.value_string, 2, 2); + auto blue = parse_hex(datapoint.value_string, 4, 2); + if (red.has_value() && green.has_value() && blue.has_value()) { + auto call = this->state_->make_call(); + call.set_rgb(float(*red) / 255, float(*green) / 255, float(*blue) / 255); + call.perform(); + } + }); + } if (min_value_datapoint_id_.has_value()) { parent_->set_integer_datapoint_value(*this->min_value_datapoint_id_, this->min_value_); } @@ -45,14 +58,31 @@ void TuyaLight::dump_config() { ESP_LOGCONFIG(TAG, " Dimmer has datapoint ID %u", *this->dimmer_id_); if (this->switch_id_.has_value()) ESP_LOGCONFIG(TAG, " Switch has datapoint ID %u", *this->switch_id_); + if (this->rgb_id_.has_value()) + ESP_LOGCONFIG(TAG, " RGB has datapoint ID %u", *this->rgb_id_); } light::LightTraits TuyaLight::get_traits() { auto traits = light::LightTraits(); if (this->color_temperature_id_.has_value() && this->dimmer_id_.has_value()) { - traits.set_supported_color_modes({light::ColorMode::COLOR_TEMPERATURE}); + if (this->rgb_id_.has_value()) { + if (this->color_interlock_) + traits.set_supported_color_modes({light::ColorMode::RGB, light::ColorMode::COLOR_TEMPERATURE}); + else + traits.set_supported_color_modes( + {light::ColorMode::RGB_COLOR_TEMPERATURE, light::ColorMode::COLOR_TEMPERATURE}); + } else + traits.set_supported_color_modes({light::ColorMode::COLOR_TEMPERATURE}); traits.set_min_mireds(this->cold_white_temperature_); traits.set_max_mireds(this->warm_white_temperature_); + } else if (this->rgb_id_.has_value()) { + if (this->dimmer_id_.has_value()) { + if (this->color_interlock_) + traits.set_supported_color_modes({light::ColorMode::RGB, light::ColorMode::WHITE}); + else + traits.set_supported_color_modes({light::ColorMode::RGB_WHITE}); + } else + traits.set_supported_color_modes({light::ColorMode::RGB}); } else if (this->dimmer_id_.has_value()) { traits.set_supported_color_modes({light::ColorMode::BRIGHTNESS}); } else { @@ -64,38 +94,51 @@ light::LightTraits TuyaLight::get_traits() { void TuyaLight::setup_state(light::LightState *state) { state_ = state; } void TuyaLight::write_state(light::LightState *state) { - float brightness; - state->current_values_as_brightness(&brightness); + float red = 0.0f, green = 0.0f, blue = 0.0f; + float color_temperature = 0.0f, brightness = 0.0f; - if (brightness == 0.0f) { - // turning off, first try via switch (if exists), then dimmer - if (switch_id_.has_value()) { - parent_->set_boolean_datapoint_value(*this->switch_id_, false); - } else if (dimmer_id_.has_value()) { - parent_->set_integer_datapoint_value(*this->dimmer_id_, 0); + if (this->rgb_id_.has_value()) { + if (this->color_temperature_id_.has_value()) { + state->current_values_as_rgbct(&red, &green, &blue, &color_temperature, &brightness); + } else if (this->dimmer_id_.has_value()) { + state->current_values_as_rgbw(&red, &green, &blue, &brightness); + } else { + state->current_values_as_rgb(&red, &green, &blue); } - return; + } else if (this->color_temperature_id_.has_value()) { + state->current_values_as_ct(&color_temperature, &brightness); + } else { + state->current_values_as_brightness(&brightness); } - if (this->color_temperature_id_.has_value()) { - uint32_t color_temp_int = - static_cast(this->color_temperature_max_value_ * - (state->current_values.get_color_temperature() - this->cold_white_temperature_) / - (this->warm_white_temperature_ - this->cold_white_temperature_)); - if (this->color_temperature_invert_) { - color_temp_int = this->color_temperature_max_value_ - color_temp_int; + if (brightness > 0.0f || !color_interlock_) { + if (this->color_temperature_id_.has_value()) { + uint32_t color_temp_int = static_cast(color_temperature * this->color_temperature_max_value_); + if (this->color_temperature_invert_) { + color_temp_int = this->color_temperature_max_value_ - color_temp_int; + } + parent_->set_integer_datapoint_value(*this->color_temperature_id_, color_temp_int); + } + + if (this->dimmer_id_.has_value()) { + auto brightness_int = static_cast(brightness * this->max_value_); + brightness_int = std::max(brightness_int, this->min_value_); + + parent_->set_integer_datapoint_value(*this->dimmer_id_, brightness_int); } - parent_->set_integer_datapoint_value(*this->color_temperature_id_, color_temp_int); } - auto brightness_int = static_cast(brightness * this->max_value_); - brightness_int = std::max(brightness_int, this->min_value_); - - if (this->dimmer_id_.has_value()) { - parent_->set_integer_datapoint_value(*this->dimmer_id_, brightness_int); + if (brightness == 0.0f || !color_interlock_) { + if (this->rgb_id_.has_value()) { + char buffer[7]; + sprintf(buffer, "%02X%02X%02X", int(red * 255), int(green * 255), int(blue * 255)); + std::string value = buffer; + this->parent_->set_string_datapoint_value(*this->rgb_id_, value); + } } + if (this->switch_id_.has_value()) { - parent_->set_boolean_datapoint_value(*this->switch_id_, true); + parent_->set_boolean_datapoint_value(*this->switch_id_, state->current_values.is_on()); } } diff --git a/esphome/components/tuya/light/tuya_light.h b/esphome/components/tuya/light/tuya_light.h index 20753fa90b..de9ec5e45f 100644 --- a/esphome/components/tuya/light/tuya_light.h +++ b/esphome/components/tuya/light/tuya_light.h @@ -16,6 +16,7 @@ class TuyaLight : public Component, public light::LightOutput { this->min_value_datapoint_id_ = min_value_datapoint_id; } void set_switch_id(uint8_t switch_id) { this->switch_id_ = switch_id; } + void set_rgb_id(uint8_t rgb_id) { this->rgb_id_ = rgb_id; } void set_color_temperature_id(uint8_t color_temperature_id) { this->color_temperature_id_ = color_temperature_id; } void set_color_temperature_invert(bool color_temperature_invert) { this->color_temperature_invert_ = color_temperature_invert; @@ -32,6 +33,8 @@ class TuyaLight : public Component, public light::LightOutput { void set_warm_white_temperature(float warm_white_temperature) { this->warm_white_temperature_ = warm_white_temperature; } + void set_color_interlock(bool color_interlock) { color_interlock_ = color_interlock; } + light::LightTraits get_traits() override; void setup_state(light::LightState *state) override; void write_state(light::LightState *state) override; @@ -44,6 +47,7 @@ class TuyaLight : public Component, public light::LightOutput { optional dimmer_id_{}; optional min_value_datapoint_id_{}; optional switch_id_{}; + optional rgb_id_{}; optional color_temperature_id_{}; uint32_t min_value_ = 0; uint32_t max_value_ = 255; @@ -51,6 +55,7 @@ class TuyaLight : public Component, public light::LightOutput { float cold_white_temperature_; float warm_white_temperature_; bool color_temperature_invert_{false}; + bool color_interlock_{false}; light::LightState *state_{nullptr}; }; diff --git a/esphome/const.py b/esphome/const.py index a52085fbb7..054f032da4 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -111,6 +111,7 @@ CONF_COLD_WHITE_COLOR_TEMPERATURE = "cold_white_color_temperature" CONF_COLOR = "color" CONF_COLOR_BRIGHTNESS = "color_brightness" CONF_COLOR_CORRECT = "color_correct" +CONF_COLOR_INTERLOCK = "color_interlock" CONF_COLOR_MODE = "color_mode" CONF_COLOR_TEMPERATURE = "color_temperature" CONF_COLORS = "colors" diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 2b77c5827a..a190566bea 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -270,6 +270,39 @@ optional parse_int(const std::string &str) { return {}; return value; } + +optional parse_hex(const char chr) { + int out = chr; + if (out >= '0' && out <= '9') + return (out - '0'); + if (out >= 'A' && out <= 'F') + return (10 + (out - 'A')); + if (out >= 'a' && out <= 'f') + return (10 + (out - 'a')); + return {}; +} + +optional parse_hex(const std::string &str, size_t start, size_t length) { + if (str.length() < start) { + return {}; + } + size_t end = start + length; + if (str.length() < end) { + return {}; + } + int out = 0; + for (size_t i = start; i < end; i++) { + char chr = str[i]; + auto digit = parse_hex(chr); + if (!digit.has_value()) { + ESP_LOGW(TAG, "Can't convert '%s' to number, invalid character %c!", str.substr(start, length).c_str(), chr); + return {}; + } + out = (out << 4) | *digit; + } + return out; +} + uint32_t fnv1_hash(const std::string &str) { uint32_t hash = 2166136261UL; for (char c : str) { diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 86cd3b086e..8118585db5 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -43,7 +43,8 @@ std::string to_string(double val); std::string to_string(long double val); optional parse_float(const std::string &str); optional parse_int(const std::string &str); - +optional parse_hex(const std::string &str, size_t start, size_t length); +optional parse_hex(char chr); /// Sanitize the hostname by removing characters that are not in the allowlist and truncating it to 63 chars. std::string sanitize_hostname(const std::string &hostname); From 4d28afc153ba51b133e48e81d98694d3c47152ee Mon Sep 17 00:00:00 2001 From: WeekendWarrior1 Date: Mon, 27 Sep 2021 05:32:46 +1000 Subject: [PATCH 120/207] add fan.cycle_speed action (#2329) --- esphome/components/fan/__init__.py | 7 ++++++ esphome/components/fan/automation.h | 36 +++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/esphome/components/fan/__init__.py b/esphome/components/fan/__init__.py index f8772948fc..15895976d1 100644 --- a/esphome/components/fan/__init__.py +++ b/esphome/components/fan/__init__.py @@ -41,6 +41,7 @@ FAN_DIRECTION_ENUM = { TurnOnAction = fan_ns.class_("TurnOnAction", automation.Action) TurnOffAction = fan_ns.class_("TurnOffAction", automation.Action) ToggleAction = fan_ns.class_("ToggleAction", automation.Action) +CycleSpeedAction = fan_ns.class_("CycleSpeedAction", automation.Action) FanTurnOnTrigger = fan_ns.class_("FanTurnOnTrigger", automation.Trigger.template()) FanTurnOffTrigger = fan_ns.class_("FanTurnOffTrigger", automation.Trigger.template()) @@ -204,6 +205,12 @@ async def fan_turn_on_to_code(config, action_id, template_arg, args): return var +@automation.register_action("fan.cycle_speed", CycleSpeedAction, FAN_ACTION_SCHEMA) +async def fan_cycle_speed_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + return cg.new_Pvariable(action_id, template_arg, paren) + + @automation.register_condition( "fan.is_on", FanIsOnCondition, diff --git a/esphome/components/fan/automation.h b/esphome/components/fan/automation.h index 7ff7c720df..608f772b75 100644 --- a/esphome/components/fan/automation.h +++ b/esphome/components/fan/automation.h @@ -50,6 +50,42 @@ template class ToggleAction : public Action { FanState *state_; }; +template class CycleSpeedAction : public Action { + public: + explicit CycleSpeedAction(FanState *state) : state_(state) {} + + void play(Ts... x) override { + // check to see if fan supports speeds and is on + if (this->state_->get_traits().supported_speed_count()) { + if (this->state_->state) { + int speed = this->state_->speed + 1; + int supported_speed_count = this->state_->get_traits().supported_speed_count(); + if (speed > supported_speed_count) { + // was running at max speed, so turn off + speed = 1; + auto call = this->state_->turn_off(); + call.set_speed(speed); + call.perform(); + } else { + auto call = this->state_->turn_on(); + call.set_speed(speed); + call.perform(); + } + } else { + // fan was off, so set speed to 1 + auto call = this->state_->turn_on(); + call.set_speed(1); + call.perform(); + } + } else { + // fan doesn't support speed counts, so toggle + this->state_->toggle().perform(); + } + } + + FanState *state_; +}; + template class FanIsOnCondition : public Condition { public: explicit FanIsOnCondition(FanState *state) : state_(state) {} From 7672ba2c8d6d918a4b44220d2d02198332e6762a Mon Sep 17 00:00:00 2001 From: Martin <25747549+martgras@users.noreply.github.com> Date: Sun, 26 Sep 2021 22:27:24 +0200 Subject: [PATCH 121/207] Modbus controller (#1779) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 7 + esphome/components/modbus/__init__.py | 14 +- esphome/components/modbus/modbus.cpp | 126 +++- esphome/components/modbus/modbus.h | 20 +- .../components/modbus_controller/__init__.py | 114 ++++ .../binary_sensor/__init__.py | 81 +++ .../binary_sensor/modbus_binarysensor.cpp | 40 ++ .../binary_sensor/modbus_binarysensor.h | 43 ++ esphome/components/modbus_controller/const.py | 13 + .../modbus_controller/modbus_controller.cpp | 559 ++++++++++++++++++ .../modbus_controller/modbus_controller.h | 454 ++++++++++++++ .../modbus_controller/number/__init__.py | 157 +++++ .../number/modbus_number.cpp | 83 +++ .../modbus_controller/number/modbus_number.h | 48 ++ .../modbus_controller/output/__init__.py | 74 +++ .../output/modbus_output.cpp | 61 ++ .../modbus_controller/output/modbus_output.h | 45 ++ .../modbus_controller/sensor/__init__.py | 109 ++++ .../sensor/modbus_sensor.cpp | 36 ++ .../modbus_controller/sensor/modbus_sensor.h | 35 ++ .../modbus_controller/switch/__init__.py | 81 +++ .../switch/modbus_switch.cpp | 70 +++ .../modbus_controller/switch/modbus_switch.h | 44 ++ .../modbus_controller/text_sensor/__init__.py | 101 ++++ .../text_sensor/modbus_textsensor.cpp | 56 ++ .../text_sensor/modbus_textsensor.h | 52 ++ tests/test5.yaml | 16 + 27 files changed, 2505 insertions(+), 34 deletions(-) create mode 100644 esphome/components/modbus_controller/__init__.py create mode 100644 esphome/components/modbus_controller/binary_sensor/__init__.py create mode 100644 esphome/components/modbus_controller/binary_sensor/modbus_binarysensor.cpp create mode 100644 esphome/components/modbus_controller/binary_sensor/modbus_binarysensor.h create mode 100644 esphome/components/modbus_controller/const.py create mode 100644 esphome/components/modbus_controller/modbus_controller.cpp create mode 100644 esphome/components/modbus_controller/modbus_controller.h create mode 100644 esphome/components/modbus_controller/number/__init__.py create mode 100644 esphome/components/modbus_controller/number/modbus_number.cpp create mode 100644 esphome/components/modbus_controller/number/modbus_number.h create mode 100644 esphome/components/modbus_controller/output/__init__.py create mode 100644 esphome/components/modbus_controller/output/modbus_output.cpp create mode 100644 esphome/components/modbus_controller/output/modbus_output.h create mode 100644 esphome/components/modbus_controller/sensor/__init__.py create mode 100644 esphome/components/modbus_controller/sensor/modbus_sensor.cpp create mode 100644 esphome/components/modbus_controller/sensor/modbus_sensor.h create mode 100644 esphome/components/modbus_controller/switch/__init__.py create mode 100644 esphome/components/modbus_controller/switch/modbus_switch.cpp create mode 100644 esphome/components/modbus_controller/switch/modbus_switch.h create mode 100644 esphome/components/modbus_controller/text_sensor/__init__.py create mode 100644 esphome/components/modbus_controller/text_sensor/modbus_textsensor.cpp create mode 100644 esphome/components/modbus_controller/text_sensor/modbus_textsensor.h diff --git a/CODEOWNERS b/CODEOWNERS index 92ee989309..17825a9205 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -88,6 +88,13 @@ esphome/components/mcp9808/* @k7hpn esphome/components/mdns/* @esphome/core esphome/components/midea/* @dudanov esphome/components/mitsubishi/* @RubyBailey +esphome/components/modbus_controller/* @martgras +esphome/components/modbus_controller/binary_sensor/* @martgras +esphome/components/modbus_controller/number/* @martgras +esphome/components/modbus_controller/output/* @martgras +esphome/components/modbus_controller/sensor/* @martgras +esphome/components/modbus_controller/switch/* @martgras +esphome/components/modbus_controller/text_sensor/* @martgras esphome/components/network/* @esphome/core esphome/components/nextion/* @senexcrenshaw esphome/components/nextion/binary_sensor/* @senexcrenshaw diff --git a/esphome/components/modbus/__init__.py b/esphome/components/modbus/__init__.py index 6b454cbaf0..254322d097 100644 --- a/esphome/components/modbus/__init__.py +++ b/esphome/components/modbus/__init__.py @@ -2,7 +2,11 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.cpp_helpers import gpio_pin_expression from esphome.components import uart -from esphome.const import CONF_FLOW_CONTROL_PIN, CONF_ID, CONF_ADDRESS +from esphome.const import ( + CONF_FLOW_CONTROL_PIN, + CONF_ID, + CONF_ADDRESS, +) from esphome import pins DEPENDENCIES = ["uart"] @@ -13,11 +17,16 @@ ModbusDevice = modbus_ns.class_("ModbusDevice") MULTI_CONF = True CONF_MODBUS_ID = "modbus_id" +CONF_SEND_WAIT_TIME = "send_wait_time" + CONFIG_SCHEMA = ( cv.Schema( { cv.GenerateID(): cv.declare_id(Modbus), cv.Optional(CONF_FLOW_CONTROL_PIN): pins.gpio_output_pin_schema, + cv.Optional( + CONF_SEND_WAIT_TIME, default="250ms" + ): cv.positive_time_period_milliseconds, } ) .extend(cv.COMPONENT_SCHEMA) @@ -36,6 +45,9 @@ async def to_code(config): pin = await gpio_pin_expression(config[CONF_FLOW_CONTROL_PIN]) cg.add(var.set_flow_control_pin(pin)) + if CONF_SEND_WAIT_TIME in config: + cg.add(var.set_send_wait_time(config[CONF_SEND_WAIT_TIME])) + def modbus_device_schema(default_address): schema = { diff --git a/esphome/components/modbus/modbus.cpp b/esphome/components/modbus/modbus.cpp index 2d714e72a2..1f6d868baf 100644 --- a/esphome/components/modbus/modbus.cpp +++ b/esphome/components/modbus/modbus.cpp @@ -1,5 +1,6 @@ #include "modbus.h" #include "esphome/core/log.h" +#include "esphome/core/helpers.h" namespace esphome { namespace modbus { @@ -13,10 +14,15 @@ void Modbus::setup() { } void Modbus::loop() { const uint32_t now = millis(); + if (now - this->last_modbus_byte_ > 50) { this->rx_buffer_.clear(); this->last_modbus_byte_ = now; } + // stop blocking new send commands after send_wait_time_ ms regardless if a response has been received since then + if (now - this->last_send_ > send_wait_time_) { + waiting_for_response = 0; + } while (this->available()) { uint8_t byte; @@ -49,48 +55,66 @@ bool Modbus::parse_modbus_byte_(uint8_t byte) { size_t at = this->rx_buffer_.size(); this->rx_buffer_.push_back(byte); const uint8_t *raw = &this->rx_buffer_[0]; - + ESP_LOGV(TAG, "Modbus received Byte %d (0X%x)", byte, byte); // Byte 0: modbus address (match all) if (at == 0) return true; uint8_t address = raw[0]; - - // Byte 1: Function (msb indicates error) - if (at == 1) - return (byte & 0x80) != 0x80; - + uint8_t function_code = raw[1]; // Byte 2: Size (with modbus rtu function code 4/3) // See also https://en.wikipedia.org/wiki/Modbus if (at == 2) return true; uint8_t data_len = raw[2]; - // Byte 3..3+data_len-1: Data - if (at < 3 + data_len) + uint8_t data_offset = 3; + // the response for write command mirrors the requests and data startes at offset 2 instead of 3 for read commands + if (function_code == 0x5 || function_code == 0x06 || function_code == 0x10) { + data_offset = 2; + data_len = 4; + } + + // Error ( msb indicates error ) + // response format: Byte[0] = device address, Byte[1] function code | 0x80 , Byte[2] excpetion code, Byte[3-4] crc + if ((function_code & 0x80) == 0x80) { + data_offset = 2; + data_len = 1; + } + + // Byte data_offset..data_offset+data_len-1: Data + if (at < data_offset + data_len) return true; // Byte 3+data_len: CRC_LO (over all bytes) - if (at == 3 + data_len) + if (at == data_offset + data_len) return true; - // Byte 3+len+1: CRC_HI (over all bytes) - uint16_t computed_crc = crc16(raw, 3 + data_len); - uint16_t remote_crc = uint16_t(raw[3 + data_len]) | (uint16_t(raw[3 + data_len + 1]) << 8); + + // Byte data_offset+len+1: CRC_HI (over all bytes) + uint16_t computed_crc = crc16(raw, data_offset + data_len); + uint16_t remote_crc = uint16_t(raw[data_offset + data_len]) | (uint16_t(raw[data_offset + data_len + 1]) << 8); if (computed_crc != remote_crc) { ESP_LOGW(TAG, "Modbus CRC Check failed! %02X!=%02X", computed_crc, remote_crc); return false; } - std::vector data(this->rx_buffer_.begin() + 3, this->rx_buffer_.begin() + 3 + data_len); + waiting_for_response = 0; + std::vector data(this->rx_buffer_.begin() + data_offset, this->rx_buffer_.begin() + data_offset + data_len); bool found = false; for (auto *device : this->devices_) { if (device->address_ == address) { - device->on_modbus_data(data); + // Is it an error response? + if ((function_code & 0x80) == 0x80) { + ESP_LOGW(TAG, "Modbus error function code: 0x%X exception: %d", function_code, raw[2]); + device->on_modbus_error(function_code & 0x7F, raw[2]); + } else { + device->on_modbus_data(data); + } found = true; } } if (!found) { - ESP_LOGW(TAG, "Got Modbus frame from unknown address 0x%02X!", address); + ESP_LOGW(TAG, "Got Modbus frame from unknown address 0x%02X! ", address); } // return false to reset buffer @@ -100,31 +124,79 @@ bool Modbus::parse_modbus_byte_(uint8_t byte) { void Modbus::dump_config() { ESP_LOGCONFIG(TAG, "Modbus:"); LOG_PIN(" Flow Control Pin: ", this->flow_control_pin_); + ESP_LOGCONFIG(TAG, " Send Wait Time: %d ms", this->send_wait_time_); } float Modbus::get_setup_priority() const { // After UART bus return setup_priority::BUS - 1.0f; } -void Modbus::send(uint8_t address, uint8_t function, uint16_t start_address, uint16_t register_count) { - uint8_t frame[8]; - frame[0] = address; - frame[1] = function; - frame[2] = start_address >> 8; - frame[3] = start_address >> 0; - frame[4] = register_count >> 8; - frame[5] = register_count >> 0; - auto crc = crc16(frame, 6); - frame[6] = crc >> 0; - frame[7] = crc >> 8; + +void Modbus::send(uint8_t address, uint8_t function_code, uint16_t start_address, uint16_t number_of_entities, + uint8_t payload_len, const uint8_t *payload) { + static const size_t MAX_VALUES = 128; + + if (number_of_entities > MAX_VALUES) { + ESP_LOGE(TAG, "send too many values %d max=%zu", number_of_entities, MAX_VALUES); + return; + } + + std::vector data; + data.push_back(address); + data.push_back(function_code); + data.push_back(start_address >> 8); + data.push_back(start_address >> 0); + if (function_code != 0x5 && function_code != 0x6) { + data.push_back(number_of_entities >> 8); + data.push_back(number_of_entities >> 0); + } + + if (payload != nullptr) { + if (function_code == 0xF || function_code == 0x10) { // Write multiple + data.push_back(payload_len); // Byte count is required for write + } else { + payload_len = 2; // Write single register or coil + } + for (int i = 0; i < payload_len; i++) { + data.push_back(payload[i]); + } + } + + auto crc = crc16(data.data(), data.size()); + data.push_back(crc >> 0); + data.push_back(crc >> 8); if (this->flow_control_pin_ != nullptr) this->flow_control_pin_->digital_write(true); - this->write_array(frame, 8); + this->write_array(data); this->flush(); if (this->flow_control_pin_ != nullptr) this->flow_control_pin_->digital_write(false); + waiting_for_response = address; + last_send_ = millis(); + ESP_LOGV(TAG, "Modbus write: %s", hexencode(data).c_str()); +} + +// Helper function for lambdas +// Send raw command. Except CRC everything must be contained in payload +void Modbus::send_raw(const std::vector &payload) { + if (payload.empty()) { + return; + } + + if (this->flow_control_pin_ != nullptr) + this->flow_control_pin_->digital_write(true); + + auto crc = crc16(payload.data(), payload.size()); + this->write_array(payload); + this->write_byte(crc & 0xFF); + this->write_byte((crc >> 8) & 0xFF); + this->flush(); + if (this->flow_control_pin_ != nullptr) + this->flow_control_pin_->digital_write(false); + waiting_for_response = payload[0]; + last_send_ = millis(); } } // namespace modbus diff --git a/esphome/components/modbus/modbus.h b/esphome/components/modbus/modbus.h index 876c46b688..400e29e08b 100644 --- a/esphome/components/modbus/modbus.h +++ b/esphome/components/modbus/modbus.h @@ -22,17 +22,21 @@ class Modbus : public uart::UARTDevice, public Component { float get_setup_priority() const override; - void send(uint8_t address, uint8_t function, uint16_t start_address, uint16_t register_count); - + void send(uint8_t address, uint8_t function_code, uint16_t start_address, uint16_t number_of_entities, + uint8_t payload_len = 0, const uint8_t *payload = nullptr); + void send_raw(const std::vector &payload); void set_flow_control_pin(GPIOPin *flow_control_pin) { this->flow_control_pin_ = flow_control_pin; } + uint8_t waiting_for_response{0}; + void set_send_wait_time(uint16_t time_in_ms) { send_wait_time_ = time_in_ms; } protected: GPIOPin *flow_control_pin_{nullptr}; bool parse_modbus_byte_(uint8_t byte); - + uint16_t send_wait_time_{250}; std::vector rx_buffer_; uint32_t last_modbus_byte_{0}; + uint32_t last_send_{0}; std::vector devices_; }; @@ -43,10 +47,14 @@ class ModbusDevice { void set_parent(Modbus *parent) { parent_ = parent; } void set_address(uint8_t address) { address_ = address; } virtual void on_modbus_data(const std::vector &data) = 0; - - void send(uint8_t function, uint16_t start_address, uint16_t register_count) { - this->parent_->send(this->address_, function, start_address, register_count); + virtual void on_modbus_error(uint8_t function_code, uint8_t exception_code) {} + void send(uint8_t function, uint16_t start_address, uint16_t number_of_entities, uint8_t payload_len = 0, + const uint8_t *payload = nullptr) { + this->parent_->send(this->address_, function, start_address, number_of_entities, payload_len, payload); } + void send_raw(const std::vector &payload) { this->parent_->send_raw(payload); } + // If more than one device is connected block sending a new command before a response is received + bool waiting_for_response() { return parent_->waiting_for_response != 0; } protected: friend Modbus; diff --git a/esphome/components/modbus_controller/__init__.py b/esphome/components/modbus_controller/__init__.py new file mode 100644 index 0000000000..7a69029dab --- /dev/null +++ b/esphome/components/modbus_controller/__init__.py @@ -0,0 +1,114 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import modbus +from esphome.const import CONF_ID, CONF_ADDRESS +from esphome.cpp_helpers import logging +from .const import ( + CONF_COMMAND_THROTTLE, +) + +CODEOWNERS = ["@martgras"] + +AUTO_LOAD = ["modbus"] + +MULTI_CONF = True + +# pylint: disable=invalid-name +modbus_controller_ns = cg.esphome_ns.namespace("modbus_controller") +ModbusController = modbus_controller_ns.class_( + "ModbusController", cg.PollingComponent, modbus.ModbusDevice +) + +SensorItem = modbus_controller_ns.struct("SensorItem") + +ModbusFunctionCode_ns = modbus_controller_ns.namespace("ModbusFunctionCode") +ModbusFunctionCode = ModbusFunctionCode_ns.enum("ModbusFunctionCode") +MODBUS_FUNCTION_CODE = { + "read_coils": ModbusFunctionCode.READ_COILS, + "read_discrete_inputs": ModbusFunctionCode.READ_DISCRETE_INPUTS, + "read_holding_registers": ModbusFunctionCode.READ_HOLDING_REGISTERS, + "read_input_registers": ModbusFunctionCode.READ_INPUT_REGISTERS, + "write_single_coil": ModbusFunctionCode.WRITE_SINGLE_COIL, + "write_single_register": ModbusFunctionCode.WRITE_SINGLE_REGISTER, + "write_multiple_coils": ModbusFunctionCode.WRITE_MULTIPLE_COILS, + "write_multiple_registers": ModbusFunctionCode.WRITE_MULTIPLE_REGISTERS, +} + +ModbusRegisterType_ns = modbus_controller_ns.namespace("ModbusRegisterType") +ModbusRegisterType = ModbusRegisterType_ns.enum("ModbusRegisterType") +MODBUS_REGISTER_TYPE = { + "coil": ModbusRegisterType.COIL, + "discrete_input": ModbusRegisterType.DISCRETE, + "holding": ModbusRegisterType.HOLDING, + "read": ModbusRegisterType.READ, +} + +SensorValueType_ns = modbus_controller_ns.namespace("SensorValueType") +SensorValueType = SensorValueType_ns.enum("SensorValueType") +SENSOR_VALUE_TYPE = { + "RAW": SensorValueType.RAW, + "U_WORD": SensorValueType.U_WORD, + "S_WORD": SensorValueType.S_WORD, + "U_DWORD": SensorValueType.U_DWORD, + "U_DWORD_R": SensorValueType.U_DWORD_R, + "S_DWORD": SensorValueType.S_DWORD, + "S_DWORD_R": SensorValueType.S_DWORD_R, + "U_QWORD": SensorValueType.U_QWORD, + "U_QWORDU_R": SensorValueType.U_QWORD_R, + "S_QWORD": SensorValueType.S_QWORD, + "U_QWORD_R": SensorValueType.S_QWORD_R, + "FP32": SensorValueType.FP32, + "FP32_R": SensorValueType.FP32_R, +} + + +MULTI_CONF = True + +_LOGGER = logging.getLogger(__name__) + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(ModbusController), + cv.Optional( + CONF_COMMAND_THROTTLE, default="0ms" + ): cv.positive_time_period_milliseconds, + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(modbus.modbus_device_schema(0x01)) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID], config[CONF_COMMAND_THROTTLE]) + cg.add(var.set_command_throttle(config[CONF_COMMAND_THROTTLE])) + await register_modbus_device(var, config) + + +async def register_modbus_device(var, config): + cg.add(var.set_address(config[CONF_ADDRESS])) + await cg.register_component(var, config) + return await modbus.register_modbus_device(var, config) + + +def function_code_to_register(function_code): + FUNCTION_CODE_TYPE_MAP = { + "read_coils": ModbusRegisterType.COIL, + "read_discrete_inputs": ModbusRegisterType.DISCRETE, + "read_holding_registers": ModbusRegisterType.HOLDING, + "read_input_registers": ModbusRegisterType.READ, + "write_single_coil": ModbusRegisterType.COIL, + "write_single_register": ModbusRegisterType.HOLDING, + "write_multiple_coils": ModbusRegisterType.COIL, + "write_multiple_registers": ModbusRegisterType.HOLDING, + } + return FUNCTION_CODE_TYPE_MAP[function_code] + + +def find_by_value(dict, find_value): + for (key, value) in MODBUS_REGISTER_TYPE.items(): + print(find_value, value) + if find_value == value: + return key + return "not found" diff --git a/esphome/components/modbus_controller/binary_sensor/__init__.py b/esphome/components/modbus_controller/binary_sensor/__init__.py new file mode 100644 index 0000000000..d46ff71f2d --- /dev/null +++ b/esphome/components/modbus_controller/binary_sensor/__init__.py @@ -0,0 +1,81 @@ +from esphome.components import binary_sensor +import esphome.config_validation as cv +import esphome.codegen as cg + +from esphome.const import CONF_ADDRESS, CONF_ID, CONF_LAMBDA, CONF_OFFSET +from .. import ( + SensorItem, + modbus_controller_ns, + ModbusController, + MODBUS_REGISTER_TYPE, +) +from ..const import ( + CONF_BITMASK, + CONF_BYTE_OFFSET, + CONF_FORCE_NEW_RANGE, + CONF_MODBUS_CONTROLLER_ID, + CONF_REGISTER_TYPE, + CONF_SKIP_UPDATES, +) + +DEPENDENCIES = ["modbus_controller"] +CODEOWNERS = ["@martgras"] + + +ModbusBinarySensor = modbus_controller_ns.class_( + "ModbusBinarySensor", cg.Component, binary_sensor.BinarySensor, SensorItem +) + +CONFIG_SCHEMA = cv.All( + binary_sensor.BINARY_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(ModbusBinarySensor), + cv.GenerateID(CONF_MODBUS_CONTROLLER_ID): cv.use_id(ModbusController), + cv.Required(CONF_ADDRESS): cv.positive_int, + cv.Required(CONF_REGISTER_TYPE): cv.enum(MODBUS_REGISTER_TYPE), + cv.Optional(CONF_OFFSET, default=0): cv.positive_int, + cv.Optional(CONF_BYTE_OFFSET): cv.positive_int, + cv.Optional(CONF_BITMASK, default=0x1): cv.hex_uint32_t, + cv.Optional(CONF_SKIP_UPDATES, default=0): cv.positive_int, + cv.Optional(CONF_FORCE_NEW_RANGE, default=False): cv.boolean, + cv.Optional(CONF_LAMBDA): cv.returning_lambda, + } + ).extend(cv.COMPONENT_SCHEMA), +) + + +async def to_code(config): + byte_offset = 0 + if CONF_OFFSET in config: + byte_offset = config[CONF_OFFSET] + # A CONF_BYTE_OFFSET setting overrides CONF_OFFSET + if CONF_BYTE_OFFSET in config: + byte_offset = config[CONF_BYTE_OFFSET] + var = cg.new_Pvariable( + config[CONF_ID], + config[CONF_REGISTER_TYPE], + config[CONF_ADDRESS], + byte_offset, + config[CONF_BITMASK], + config[CONF_SKIP_UPDATES], + config[CONF_FORCE_NEW_RANGE], + ) + await cg.register_component(var, config) + await binary_sensor.register_binary_sensor(var, config) + + paren = await cg.get_variable(config[CONF_MODBUS_CONTROLLER_ID]) + cg.add(paren.add_sensor_item(var)) + if CONF_LAMBDA in config: + template_ = await cg.process_lambda( + config[CONF_LAMBDA], + [ + (ModbusBinarySensor.operator("ptr"), "item"), + (cg.float_, "x"), + ( + cg.std_vector.template(cg.uint8).operator("const").operator("ref"), + "data", + ), + ], + return_type=cg.optional.template(bool), + ) + cg.add(var.set_template(template_)) diff --git a/esphome/components/modbus_controller/binary_sensor/modbus_binarysensor.cpp b/esphome/components/modbus_controller/binary_sensor/modbus_binarysensor.cpp new file mode 100644 index 0000000000..81066b3f5c --- /dev/null +++ b/esphome/components/modbus_controller/binary_sensor/modbus_binarysensor.cpp @@ -0,0 +1,40 @@ +#include "modbus_binarysensor.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace modbus_controller { + +static const char *const TAG = "modbus_controller.binary_sensor"; + +void ModbusBinarySensor::dump_config() { LOG_BINARY_SENSOR("", "Modbus Controller Binary Sensor", this); } + +void ModbusBinarySensor::parse_and_publish(const std::vector &data) { + bool value; + + switch (this->register_type) { + case ModbusRegisterType::DISCRETE_INPUT: + value = coil_from_vector(this->offset, data); + break; + case ModbusRegisterType::COIL: + // offset for coil is the actual number of the coil not the byte offset + value = coil_from_vector(this->offset, data); + break; + default: + value = get_data(data, this->offset) & this->bitmask; + break; + } + // Is there a lambda registered + // call it with the pre converted value and the raw data array + if (this->transform_func_.has_value()) { + // the lambda can parse the response itself + auto val = (*this->transform_func_)(this, value, data); + if (val.has_value()) { + ESP_LOGV(TAG, "Value overwritten by lambda"); + value = val.value(); + } + } + this->publish_state(value); +} + +} // namespace modbus_controller +} // namespace esphome diff --git a/esphome/components/modbus_controller/binary_sensor/modbus_binarysensor.h b/esphome/components/modbus_controller/binary_sensor/modbus_binarysensor.h new file mode 100644 index 0000000000..c516d6b916 --- /dev/null +++ b/esphome/components/modbus_controller/binary_sensor/modbus_binarysensor.h @@ -0,0 +1,43 @@ +#pragma once + +#include "esphome/components/binary_sensor/binary_sensor.h" +#include "esphome/components/modbus_controller/modbus_controller.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace modbus_controller { + +class ModbusBinarySensor : public Component, public binary_sensor::BinarySensor, public SensorItem { + public: + ModbusBinarySensor(ModbusRegisterType register_type, uint16_t start_address, uint8_t offset, uint32_t bitmask, + uint8_t skip_updates, bool force_new_range) + : Component(), binary_sensor::BinarySensor() { + this->register_type = register_type; + this->start_address = start_address; + this->offset = offset; + this->bitmask = bitmask; + this->sensor_value_type = SensorValueType::BIT; + this->skip_updates = skip_updates; + this->force_new_range = force_new_range; + + if (register_type == ModbusRegisterType::COIL || register_type == ModbusRegisterType::DISCRETE_INPUT) + this->register_count = offset + 1; + else + this->register_count = 1; + } + + void parse_and_publish(const std::vector &data) override; + void set_state(bool state) { this->state = state; } + + void dump_config() override; + + using transform_func_t = + optional(ModbusBinarySensor *, bool, const std::vector &)>>; + void set_template(transform_func_t &&f) { this->transform_func_ = f; } + + protected: + transform_func_t transform_func_{nullopt}; +}; + +} // namespace modbus_controller +} // namespace esphome diff --git a/esphome/components/modbus_controller/const.py b/esphome/components/modbus_controller/const.py new file mode 100644 index 0000000000..3cd114e673 --- /dev/null +++ b/esphome/components/modbus_controller/const.py @@ -0,0 +1,13 @@ +CONF_BITMASK = "bitmask" +CONF_BYTE_OFFSET = "byte_offset" +CONF_COMMAND_THROTTLE = "command_throttle" +CONF_FORCE_NEW_RANGE = "force_new_range" +CONF_MODBUS_CONTROLLER_ID = "modbus_controller_id" +CONF_MODBUS_FUNCTIONCODE = "modbus_functioncode" +CONF_RAW_ENCODE = "raw_encode" +CONF_REGISTER_COUNT = "register_count" +CONF_REGISTER_TYPE = "register_type" +CONF_RESPONSE_SIZE = "response_size" +CONF_SKIP_UPDATES = "skip_updates" +CONF_VALUE_TYPE = "value_type" +CONF_WRITE_LAMBDA = "write_lambda" diff --git a/esphome/components/modbus_controller/modbus_controller.cpp b/esphome/components/modbus_controller/modbus_controller.cpp new file mode 100644 index 0000000000..70b5bf8eae --- /dev/null +++ b/esphome/components/modbus_controller/modbus_controller.cpp @@ -0,0 +1,559 @@ +#include "modbus_controller.h" +#include "esphome/core/application.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace modbus_controller { + +static const char *const TAG = "modbus_controller"; + +void ModbusController::setup() { + // Modbus::setup(); + this->create_register_ranges_(); +} + +/* + To work with the existing modbus class and avoid polling for responses a command queue is used. + send_next_command will submit the command at the top of the queue and set the corresponding callback + to handle the response from the device. + Once the response has been processed it is removed from the queue and the next command is sent +*/ +bool ModbusController::send_next_command_() { + uint32_t last_send = millis() - this->last_command_timestamp_; + + if ((last_send > this->command_throttle_) && !waiting_for_response() && !command_queue_.empty()) { + auto &command = command_queue_.front(); + + ESP_LOGV(TAG, "Sending next modbus command to device %d register 0x%02X count %d", this->address_, + command->register_address, command->register_count); + command->send(); + this->last_command_timestamp_ = millis(); + if (!command->on_data_func) { // No handler remove from queue directly after sending + command_queue_.pop_front(); + } + } + return (!command_queue_.empty()); +} + +// Queue incoming response +void ModbusController::on_modbus_data(const std::vector &data) { + auto ¤t_command = this->command_queue_.front(); + if (current_command != nullptr) { + // Move the commandItem to the response queue + current_command->payload = data; + this->incoming_queue_.push(std::move(current_command)); + ESP_LOGV(TAG, "Modbus response queued"); + command_queue_.pop_front(); + } +} + +// Dispatch the response to the registered handler +void ModbusController::process_modbus_data_(const ModbusCommandItem *response) { + ESP_LOGV(TAG, "Process modbus response for address 0x%X size: %zu", response->register_address, + response->payload.size()); + response->on_data_func(response->register_type, response->register_address, response->payload); +} + +void ModbusController::on_modbus_error(uint8_t function_code, uint8_t exception_code) { + ESP_LOGE(TAG, "Modbus error function code: 0x%X exception: %d ", function_code, exception_code); + // Remove pending command waiting for a response + auto ¤t_command = this->command_queue_.front(); + if (current_command != nullptr) { + ESP_LOGE(TAG, + "Modbus error - last command: function code=0x%X register adddress = 0x%X " + "registers count=%d " + "payload size=%zu", + function_code, current_command->register_address, current_command->register_count, + current_command->payload.size()); + command_queue_.pop_front(); + } +} + +void ModbusController::on_register_data(ModbusRegisterType register_type, uint16_t start_address, + const std::vector &data) { + ESP_LOGV(TAG, "data for register address : 0x%X : ", start_address); + + auto vec_it = find_if(begin(register_ranges_), end(register_ranges_), [=](RegisterRange const &r) { + return (r.start_address == start_address && r.register_type == register_type); + }); + + if (vec_it == register_ranges_.end()) { + ESP_LOGE(TAG, "Handle incoming data : No matching range for sensor found - start_address : 0x%X", start_address); + return; + } + auto map_it = sensormap_.find(vec_it->first_sensorkey); + if (map_it == sensormap_.end()) { + ESP_LOGE(TAG, "Handle incoming data : No sensor found in at start_address : 0x%X (0x%llX)", start_address, + vec_it->first_sensorkey); + return; + } + // loop through all sensors with the same start address + while (map_it != sensormap_.end() && map_it->second->start_address == start_address) { + if (map_it->second->register_type == register_type) { + map_it->second->parse_and_publish(data); + } + map_it++; + } +} + +void ModbusController::queue_command(const ModbusCommandItem &command) { + // check if this commmand is already qeued. + // not very effective but the queue is never really large + for (auto &item : command_queue_) { + if (item->register_address == command.register_address && item->register_count == command.register_count && + item->register_type == command.register_type) { + ESP_LOGW(TAG, "Duplicate modbus command found"); + // update the payload of the queued command + // replaces a previous command + item->payload = command.payload; + return; + } + } + command_queue_.push_back(make_unique(command)); +} + +void ModbusController::update_range_(RegisterRange &r) { + ESP_LOGV(TAG, "Range : %X Size: %x (%d) skip: %d", r.start_address, r.register_count, (int) r.register_type, + r.skip_updates_counter); + if (r.skip_updates_counter == 0) { + ModbusCommandItem command_item = + ModbusCommandItem::create_read_command(this, r.register_type, r.start_address, r.register_count); + queue_command(command_item); + r.skip_updates_counter = r.skip_updates; // reset counter to config value + } else { + r.skip_updates_counter--; + } +} +// +// Queue the modbus requests to be send. +// Once we get a response to the command it is removed from the queue and the next command is send +// +void ModbusController::update() { + if (!command_queue_.empty()) { + ESP_LOGV(TAG, "%zu modbus commands already in queue", command_queue_.size()); + } else { + ESP_LOGV(TAG, "Updating modbus component"); + } + + for (auto &r : this->register_ranges_) { + ESP_LOGVV(TAG, "Updating range 0x%X", r.start_address); + update_range_(r); + } +} + +// walk through the sensors and determine the registerranges to read +size_t ModbusController::create_register_ranges_() { + register_ranges_.clear(); + uint8_t n = 0; + if (sensormap_.empty()) { + return 0; + } + + auto ix = sensormap_.begin(); + auto prev = ix; + int total_register_count = 0; + uint16_t current_start_address = ix->second->start_address; + uint8_t buffer_offset = ix->second->offset; + uint8_t skip_updates = ix->second->skip_updates; + auto first_sensorkey = ix->second->getkey(); + total_register_count = 0; + while (ix != sensormap_.end()) { + ESP_LOGV(TAG, "Register: 0x%X %d %d 0x%llx (%d) buffer_offset = %d (0x%X) skip=%u", ix->second->start_address, + ix->second->register_count, ix->second->offset, ix->second->getkey(), total_register_count, buffer_offset, + buffer_offset, ix->second->skip_updates); + // if this is a sequential address based on number of registers and address of previous sensor + // convert to an offset to the previous sensor (address 0x101 becomes address 0x100 offset 2 bytes) + if (!ix->second->force_new_range && total_register_count >= 0 && + prev->second->register_type == ix->second->register_type && + prev->second->start_address + total_register_count == ix->second->start_address && + prev->second->start_address < ix->second->start_address) { + ix->second->start_address = prev->second->start_address; + ix->second->offset += prev->second->offset + prev->second->get_register_size(); + + // replace entry in sensormap_ + auto const value = ix->second; + sensormap_.erase(ix); + sensormap_.insert({value->getkey(), value}); + // move iterator back to new element + ix = sensormap_.find(value->getkey()); // next(prev, 1); + } + if (current_start_address != ix->second->start_address || + // ( prev->second->start_address + prev->second->offset != ix->second->start_address) || + ix->second->register_type != prev->second->register_type) { + // Difference doesn't match so we have a gap + if (n > 0) { + RegisterRange r; + r.start_address = current_start_address; + r.register_count = total_register_count; + if (prev->second->register_type == ModbusRegisterType::COIL || + prev->second->register_type == ModbusRegisterType::DISCRETE_INPUT) { + r.register_count = prev->second->offset + 1; + } + r.register_type = prev->second->register_type; + r.first_sensorkey = first_sensorkey; + r.skip_updates = skip_updates; + r.skip_updates_counter = 0; + ESP_LOGV(TAG, "Add range 0x%X %d skip:%d", r.start_address, r.register_count, r.skip_updates); + register_ranges_.push_back(r); + } + skip_updates = ix->second->skip_updates; + current_start_address = ix->second->start_address; + first_sensorkey = ix->second->getkey(); + total_register_count = ix->second->register_count; + buffer_offset = ix->second->offset; + n = 1; + } else { + n++; + if (ix->second->offset != prev->second->offset || n == 1) { + total_register_count += ix->second->register_count; + buffer_offset += ix->second->get_register_size(); + } + // use the lowest non zero value for the whole range + // Because zero is the default value for skip_updates it is excluded from getting the min value. + if (ix->second->skip_updates != 0) { + if (skip_updates != 0) { + skip_updates = std::min(skip_updates, ix->second->skip_updates); + } else { + skip_updates = ix->second->skip_updates; + } + } + } + prev = ix++; + } + // Add the last range + if (n > 0) { + RegisterRange r; + r.start_address = current_start_address; + // r.register_count = prev->second->offset>>1 + prev->second->get_register_size(); + r.register_count = total_register_count; + if (prev->second->register_type == ModbusRegisterType::COIL || + prev->second->register_type == ModbusRegisterType::DISCRETE_INPUT) { + r.register_count = prev->second->offset + 1; + } + r.register_type = prev->second->register_type; + r.first_sensorkey = first_sensorkey; + r.skip_updates = skip_updates; + r.skip_updates_counter = 0; + ESP_LOGV(TAG, "Add last range 0x%X %d skip:%d", r.start_address, r.register_count, r.skip_updates); + register_ranges_.push_back(r); + } + return register_ranges_.size(); +} + +void ModbusController::dump_config() { + ESP_LOGCONFIG(TAG, "ModbusController:"); + ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_); +#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE + ESP_LOGCONFIG(TAG, "sensormap"); + for (auto &it : sensormap_) { + ESP_LOGCONFIG("TAG", " Sensor 0x%llX start=0x%X count=%d size=%d", it.second->getkey(), it.second->start_address, + it.second->register_count, it.second->get_register_size()); + } +#endif +} + +void ModbusController::loop() { + // Incoming data to process? + if (!incoming_queue_.empty()) { + auto &message = incoming_queue_.front(); + if (message != nullptr) + process_modbus_data_(message.get()); + incoming_queue_.pop(); + + } else { + // all messages processed send pending commmands + send_next_command_(); + } +} + +void ModbusController::on_write_register_response(ModbusRegisterType register_type, uint16_t start_address, + const std::vector &data) { + ESP_LOGV(TAG, "Command ACK 0x%X %d ", get_data(data, 0), get_data(data, 1)); +} + +void ModbusController::dump_sensormap_() { + ESP_LOGV("modbuscontroller.h", "sensormap"); + for (auto &it : sensormap_) { + ESP_LOGV("modbuscontroller.h", " Sensor 0x%llX start=0x%X count=%d size=%d", it.second->getkey(), + it.second->start_address, it.second->register_count, it.second->get_register_size()); + } +} + +ModbusCommandItem ModbusCommandItem::create_read_command( + ModbusController *modbusdevice, ModbusRegisterType register_type, uint16_t start_address, uint16_t register_count, + std::function &data)> + &&handler) { + ModbusCommandItem cmd; + cmd.modbusdevice = modbusdevice; + cmd.register_type = register_type; + cmd.function_code = modbus_register_read_function(register_type); + cmd.register_address = start_address; + cmd.register_count = register_count; + cmd.on_data_func = std::move(handler); + return cmd; +} + +ModbusCommandItem ModbusCommandItem::create_read_command(ModbusController *modbusdevice, + ModbusRegisterType register_type, uint16_t start_address, + uint16_t register_count) { + ModbusCommandItem cmd; + cmd.modbusdevice = modbusdevice; + cmd.register_type = register_type; + cmd.function_code = modbus_register_read_function(register_type); + cmd.register_address = start_address; + cmd.register_count = register_count; + cmd.on_data_func = [modbusdevice](ModbusRegisterType register_type, uint16_t start_address, + const std::vector &data) { + modbusdevice->on_register_data(register_type, start_address, data); + }; + return cmd; +} + +ModbusCommandItem ModbusCommandItem::create_write_multiple_command(ModbusController *modbusdevice, + uint16_t start_address, uint16_t register_count, + const std::vector &values) { + ModbusCommandItem cmd; + cmd.modbusdevice = modbusdevice; + cmd.register_type = ModbusRegisterType::HOLDING; + cmd.function_code = ModbusFunctionCode::WRITE_MULTIPLE_REGISTERS; + cmd.register_address = start_address; + cmd.register_count = register_count; + cmd.on_data_func = [modbusdevice, cmd](ModbusRegisterType register_type, uint16_t start_address, + const std::vector &data) { + modbusdevice->on_write_register_response(cmd.register_type, start_address, data); + }; + for (auto v : values) { + cmd.payload.push_back((v / 256) & 0xFF); + cmd.payload.push_back(v & 0xFF); + } + return cmd; +} + +ModbusCommandItem ModbusCommandItem::create_write_single_coil(ModbusController *modbusdevice, uint16_t address, + bool value) { + ModbusCommandItem cmd; + cmd.modbusdevice = modbusdevice; + cmd.register_type = ModbusRegisterType::COIL; + cmd.function_code = ModbusFunctionCode::WRITE_SINGLE_COIL; + cmd.register_address = address; + cmd.register_count = 1; + cmd.on_data_func = [modbusdevice, cmd](ModbusRegisterType register_type, uint16_t start_address, + const std::vector &data) { + modbusdevice->on_write_register_response(cmd.register_type, start_address, data); + }; + cmd.payload.push_back(value ? 0xFF : 0); + cmd.payload.push_back(0); + return cmd; +} + +ModbusCommandItem ModbusCommandItem::create_write_multiple_coils(ModbusController *modbusdevice, uint16_t start_address, + const std::vector &values) { + ModbusCommandItem cmd; + cmd.modbusdevice = modbusdevice; + cmd.register_type = ModbusRegisterType::COIL; + cmd.function_code = ModbusFunctionCode::WRITE_MULTIPLE_COILS; + cmd.register_address = start_address; + cmd.register_count = values.size(); + cmd.on_data_func = [modbusdevice, cmd](ModbusRegisterType register_type, uint16_t start_address, + const std::vector &data) { + modbusdevice->on_write_register_response(cmd.register_type, start_address, data); + }; + + uint8_t bitmask = 0; + int bitcounter = 0; + for (auto coil : values) { + if (coil) { + bitmask |= (1 << bitcounter); + } + bitcounter++; + if (bitcounter % 8 == 0) { + cmd.payload.push_back(bitmask); + bitmask = 0; + } + } + // add remaining bits + if (bitcounter % 8) { + cmd.payload.push_back(bitmask); + } + return cmd; +} + +ModbusCommandItem ModbusCommandItem::create_write_single_command(ModbusController *modbusdevice, uint16_t start_address, + int16_t value) { + ModbusCommandItem cmd; + cmd.modbusdevice = modbusdevice; + cmd.register_type = ModbusRegisterType::HOLDING; + cmd.function_code = ModbusFunctionCode::WRITE_SINGLE_REGISTER; + cmd.register_address = start_address; + cmd.register_count = 1; // not used here anyways + cmd.on_data_func = [modbusdevice, cmd](ModbusRegisterType register_type, uint16_t start_address, + const std::vector &data) { + modbusdevice->on_write_register_response(cmd.register_type, start_address, data); + }; + cmd.payload.push_back((value / 256) & 0xFF); + cmd.payload.push_back((value % 256) & 0xFF); + return cmd; +} + +ModbusCommandItem ModbusCommandItem::create_custom_command( + ModbusController *modbusdevice, const std::vector &values, + std::function &data)> + &&handler) { + ModbusCommandItem cmd; + cmd.modbusdevice = modbusdevice; + cmd.function_code = ModbusFunctionCode::CUSTOM; + if (handler == nullptr) { + cmd.on_data_func = [](ModbusRegisterType, uint16_t, const std::vector &data) { + ESP_LOGI(TAG, "Custom Command sent"); + }; + } else { + cmd.on_data_func = handler; + } + cmd.payload = values; + + return cmd; +} + +bool ModbusCommandItem::send() { + if (this->function_code != ModbusFunctionCode::CUSTOM) { + modbusdevice->send(uint8_t(this->function_code), this->register_address, this->register_count, this->payload.size(), + this->payload.empty() ? nullptr : &this->payload[0]); + } else { + modbusdevice->send_raw(this->payload); + } + ESP_LOGV(TAG, "Command sent %d 0x%X %d", uint8_t(this->function_code), this->register_address, this->register_count); + return true; +} + +std::vector float_to_payload(float value, SensorValueType value_type) { + union { + float float_value; + uint32_t raw; + } raw_to_float; + + std::vector data; + int32_t val; + + switch (value_type) { + case SensorValueType::U_WORD: + case SensorValueType::S_WORD: + // cast truncates the float do some rounding here + data.push_back(lroundf(value) & 0xFFFF); + break; + case SensorValueType::U_DWORD: + case SensorValueType::S_DWORD: + val = lroundf(value); + data.push_back((val & 0xFFFF0000) >> 16); + data.push_back(val & 0xFFFF); + break; + case SensorValueType::U_DWORD_R: + case SensorValueType::S_DWORD_R: + val = lroundf(value); + data.push_back(val & 0xFFFF); + data.push_back((val & 0xFFFF0000) >> 16); + break; + case SensorValueType::FP32: + raw_to_float.float_value = value; + data.push_back((raw_to_float.raw & 0xFFFF0000) >> 16); + data.push_back(raw_to_float.raw & 0xFFFF); + break; + case SensorValueType::FP32_R: + raw_to_float.float_value = value; + data.push_back(raw_to_float.raw & 0xFFFF); + data.push_back((raw_to_float.raw & 0xFFFF0000) >> 16); + break; + default: + ESP_LOGE(TAG, "Invalid data type for modbus float to payload conversation"); + break; + } + return data; +} + +float payload_to_float(const std::vector &data, SensorValueType sensor_value_type, uint8_t offset, + uint32_t bitmask) { + union { + float float_value; + uint32_t raw; + } raw_to_float; + + int64_t value = 0; // int64_t because it can hold signed and unsigned 32 bits + float result = NAN; + + switch (sensor_value_type) { + case SensorValueType::U_WORD: + value = mask_and_shift_by_rightbit(get_data(data, offset), bitmask); // default is 0xFFFF ; + result = static_cast(value); + break; + case SensorValueType::U_DWORD: + value = get_data(data, offset); + value = mask_and_shift_by_rightbit((uint32_t) value, bitmask); + result = static_cast(value); + break; + case SensorValueType::U_DWORD_R: + value = get_data(data, offset); + value = static_cast(value & 0xFFFF) << 16 | (value & 0xFFFF0000) >> 16; + value = mask_and_shift_by_rightbit((uint32_t) value, bitmask); + result = static_cast(value); + break; + case SensorValueType::S_WORD: + value = mask_and_shift_by_rightbit(get_data(data, offset), + bitmask); // default is 0xFFFF ; + result = static_cast(value); + break; + case SensorValueType::S_DWORD: + value = mask_and_shift_by_rightbit(get_data(data, offset), bitmask); + result = static_cast(value); + break; + case SensorValueType::S_DWORD_R: { + value = get_data(data, offset); + // Currently the high word is at the low position + // the sign bit is therefore at low before the switch + uint32_t sign_bit = (value & 0x8000) << 16; + value = mask_and_shift_by_rightbit( + static_cast(((value & 0x7FFF) << 16 | (value & 0xFFFF0000) >> 16) | sign_bit), bitmask); + result = static_cast(value); + } break; + case SensorValueType::U_QWORD: + // Ignore bitmask for U_QWORD + value = get_data(data, offset); + result = static_cast(value); + break; + + case SensorValueType::S_QWORD: + // Ignore bitmask for S_QWORD + value = get_data(data, offset); + result = static_cast(value); + break; + case SensorValueType::U_QWORD_R: + // Ignore bitmask for U_QWORD + value = get_data(data, offset); + value = static_cast(value & 0xFFFF) << 48 | (value & 0xFFFF000000000000) >> 48 | + static_cast(value & 0xFFFF0000) << 32 | (value & 0x0000FFFF00000000) >> 32 | + static_cast(value & 0xFFFF00000000) << 16 | (value & 0x00000000FFFF0000) >> 16; + result = static_cast(value); + break; + + case SensorValueType::S_QWORD_R: + // Ignore bitmask for S_QWORD + value = get_data(data, offset); + result = static_cast(value); + break; + case SensorValueType::FP32: + raw_to_float.raw = get_data(data, offset); + ESP_LOGD(TAG, "FP32 = 0x%08X => %f", raw_to_float.raw, raw_to_float.float_value); + result = raw_to_float.float_value; + break; + case SensorValueType::FP32_R: { + auto tmp = get_data(data, offset); + raw_to_float.raw = static_cast(tmp & 0xFFFF) << 16 | (tmp & 0xFFFF0000) >> 16; + ESP_LOGD(TAG, "FP32_R = 0x%08X => %f", raw_to_float.raw, raw_to_float.float_value); + result = raw_to_float.float_value; + } break; + default: + break; + } + return result; +} + +} // namespace modbus_controller +} // namespace esphome diff --git a/esphome/components/modbus_controller/modbus_controller.h b/esphome/components/modbus_controller/modbus_controller.h new file mode 100644 index 0000000000..4b5f4337db --- /dev/null +++ b/esphome/components/modbus_controller/modbus_controller.h @@ -0,0 +1,454 @@ +#pragma once + +#include "esphome/core/component.h" + +#include "esphome/core/automation.h" +#include "esphome/components/modbus/modbus.h" + +#include +#include +#include +#include + +namespace esphome { +namespace modbus_controller { + +class ModbusController; + +enum class ModbusFunctionCode { + CUSTOM = 0x00, + READ_COILS = 0x01, + READ_DISCRETE_INPUTS = 0x02, + READ_HOLDING_REGISTERS = 0x03, + READ_INPUT_REGISTERS = 0x04, + WRITE_SINGLE_COIL = 0x05, + WRITE_SINGLE_REGISTER = 0x06, + READ_EXCEPTION_STATUS = 0x07, // not implemented + DIAGNOSTICS = 0x08, // not implemented + GET_COMM_EVENT_COUNTER = 0x0B, // not implemented + GET_COMM_EVENT_LOG = 0x0C, // not implemented + WRITE_MULTIPLE_COILS = 0x0F, + WRITE_MULTIPLE_REGISTERS = 0x10, + REPORT_SERVER_ID = 0x11, // not implemented + READ_FILE_RECORD = 0x14, // not implemented + WRITE_FILE_RECORD = 0x15, // not implemented + MASK_WRITE_REGISTER = 0x16, // not implemented + READ_WRITE_MULTIPLE_REGISTERS = 0x17, // not implemented + READ_FIFO_QUEUE = 0x18, // not implemented +}; + +enum class ModbusRegisterType : int { + CUSTOM = 0x0, + COIL = 0x01, + DISCRETE_INPUT = 0x02, + HOLDING = 0x03, + READ = 0x04, +}; + +enum class SensorValueType : uint8_t { + RAW = 0x00, // variable length + U_WORD = 0x1, // 1 Register unsigned + U_DWORD = 0x2, // 2 Registers unsigned + S_WORD = 0x3, // 1 Register signed + S_DWORD = 0x4, // 2 Registers signed + BIT = 0x5, + U_DWORD_R = 0x6, // 2 Registers unsigned + S_DWORD_R = 0x7, // 2 Registers unsigned + U_QWORD = 0x8, + S_QWORD = 0x9, + U_QWORD_R = 0xA, + S_QWORD_R = 0xB, + FP32 = 0xC, + FP32_R = 0xD +}; + +struct RegisterRange { + uint16_t start_address; + ModbusRegisterType register_type; + uint8_t register_count; + uint8_t skip_updates; // the config value + uint64_t first_sensorkey; + uint8_t skip_updates_counter; // the running value +} __attribute__((packed)); + +inline ModbusFunctionCode modbus_register_read_function(ModbusRegisterType reg_type) { + switch (reg_type) { + case ModbusRegisterType::COIL: + return ModbusFunctionCode::READ_COILS; + break; + case ModbusRegisterType::DISCRETE_INPUT: + return ModbusFunctionCode::READ_DISCRETE_INPUTS; + break; + case ModbusRegisterType::HOLDING: + return ModbusFunctionCode::READ_HOLDING_REGISTERS; + break; + case ModbusRegisterType::READ: + return ModbusFunctionCode::READ_INPUT_REGISTERS; + break; + default: + return ModbusFunctionCode::CUSTOM; + break; + } +} +inline ModbusFunctionCode modbus_register_write_function(ModbusRegisterType reg_type) { + switch (reg_type) { + case ModbusRegisterType::COIL: + return ModbusFunctionCode::WRITE_SINGLE_COIL; + break; + case ModbusRegisterType::DISCRETE_INPUT: + return ModbusFunctionCode::CUSTOM; + break; + case ModbusRegisterType::HOLDING: + return ModbusFunctionCode::READ_WRITE_MULTIPLE_REGISTERS; + break; + case ModbusRegisterType::READ: + return ModbusFunctionCode::CUSTOM; + break; + default: + return ModbusFunctionCode::CUSTOM; + break; + } +} + +/** All sensors are stored in a map + * to enable binary sensors for values encoded as bits in the same register the key of each sensor + * the key is a 64 bit integer that combines the register properties + * sensormap_ is sorted by this key. The key ensures the correct order when creating consequtive ranges + * Format: function_code (8 bit) | start address (16 bit)| offset (8bit)| bitmask (32 bit) + */ +inline uint64_t calc_key(ModbusRegisterType register_type, uint16_t start_address, uint8_t offset = 0, + uint32_t bitmask = 0) { + return uint64_t((uint16_t(register_type) << 24) + (uint32_t(start_address) << 8) + (offset & 0xFF)) << 32 | bitmask; +} +inline uint16_t register_from_key(uint64_t key) { return (key >> 40) & 0xFFFF; } + +inline uint8_t c_to_hex(char c) { return (c >= 'A') ? (c >= 'a') ? (c - 'a' + 10) : (c - 'A' + 10) : (c - '0'); } + +/** Get a byte from a hex string + * hex_byte_from_str("1122",1) returns uint_8 value 0x22 == 34 + * hex_byte_from_str("1122",0) returns 0x11 + * @param value string containing hex encoding + * @param position offset in bytes. Because each byte is encoded in 2 hex digits the position of the original byte in + * the hex string is byte_pos * 2 + * @return byte value + */ +inline uint8_t byte_from_hex_str(const std::string &value, uint8_t pos) { + if (value.length() < pos * 2 + 1) + return 0; + return (c_to_hex(value[pos * 2]) << 4) | c_to_hex(value[pos * 2 + 1]); +} + +/** Get a word from a hex string + * @param value string containing hex encoding + * @param position offset in bytes. Because each byte is encoded in 2 hex digits the position of the original byte in + * the hex string is byte_pos * 2 + * @return word value + */ +inline uint16_t word_from_hex_str(const std::string &value, uint8_t pos) { + return byte_from_hex_str(value, pos) << 8 | byte_from_hex_str(value, pos + 1); +} + +/** Get a dword from a hex string + * @param value string containing hex encoding + * @param position offset in bytes. Because each byte is encoded in 2 hex digits the position of the original byte in + * the hex string is byte_pos * 2 + * @return dword value + */ +inline uint32_t dword_from_hex_str(const std::string &value, uint8_t pos) { + return word_from_hex_str(value, pos) << 16 | word_from_hex_str(value, pos + 2); +} + +/** Get a qword from a hex string + * @param value string containing hex encoding + * @param position offset in bytes. Because each byte is encoded in 2 hex digits the position of the original byte in + * the hex string is byte_pos * 2 + * @return qword value + */ +inline uint64_t qword_from_hex_str(const std::string &value, uint8_t pos) { + return static_cast(dword_from_hex_str(value, pos)) << 32 | dword_from_hex_str(value, pos + 4); +} + +// Extract data from modbus response buffer +/** Extract data from modbus response buffer + * @param T one of supported integer data types int_8,int_16,int_32,int_64 + * @param data modbus response buffer (uint8_t) + * @param buffer_offset offset in bytes. + * @return value of type T extracted from buffer + */ +template T get_data(const std::vector &data, size_t buffer_offset) { + if (sizeof(T) == sizeof(uint8_t)) { + return T(data[buffer_offset]); + } + if (sizeof(T) == sizeof(uint16_t)) { + return T((uint16_t(data[buffer_offset + 0]) << 8) | (uint16_t(data[buffer_offset + 1]) << 0)); + } + + if (sizeof(T) == sizeof(uint32_t)) { + return get_data(data, buffer_offset) << 16 | get_data(data, (buffer_offset + 2)); + } + + if (sizeof(T) == sizeof(uint64_t)) { + return static_cast(get_data(data, buffer_offset)) << 32 | + (static_cast(get_data(data, buffer_offset + 4))); + } +} + +/** Extract coil data from modbus response buffer + * Responses for coil are packed into bytes . + * coil 3 is bit 3 of the first response byte + * coil 9 is bit 2 of the second response byte + * @param coil number of the cil + * @param data modbus response buffer (uint8_t) + * @return content of coil register + */ +inline bool coil_from_vector(int coil, const std::vector &data) { + auto data_byte = coil / 8; + return (data[data_byte] & (1 << (coil % 8))) > 0; +} + +/** Extract bits from value and shift right according to the bitmask + * if the bitmask is 0x00F0 we want the values frrom bit 5 - 8. + * the result is then shifted right by the postion if the first right set bit in the mask + * Usefull for modbus data where more than one value is packed in a 16 bit register + * Example: on Epever the "Length of night" register 0x9065 encodes values of the whole night length of time as + * D15 - D8 = hour, D7 - D0 = minute + * To get the hours use mask 0xFF00 and 0x00FF for the minute + * @param data an integral value between 16 aand 32 bits, + * @param bitmask the bitmask to apply + */ +template N mask_and_shift_by_rightbit(N data, uint32_t mask) { + auto result = (mask & data); + if (result == 0) { + return result; + } + for (int pos = 0; pos < sizeof(N) << 3; pos++) { + if ((mask & (1 << pos)) != 0) + return result >> pos; + } + return 0; +} + +/** convert float value to vector suitable for sending + * @param value float value to cconvert + * @param value_type defines if 16/32 or FP32 is used + * @return vector containing the modbus register words in correct order + */ +std::vector float_to_payload(float value, SensorValueType value_type); + +/** convert vector response payload to float + * @param value float value to cconvert + * @param sensor_value_type defines if 16/32/64 bits or FP32 is used + * @param offset offset to the data in data + * @param bitmask bitmask used for masking and shifting + * @return float version of the input + */ +float payload_to_float(const std::vector &data, SensorValueType sensor_value_type, uint8_t offset, + uint32_t bitmask); + +class ModbusController; + +struct SensorItem { + ModbusRegisterType register_type; + SensorValueType sensor_value_type; + uint16_t start_address; + uint32_t bitmask; + uint8_t offset; + uint8_t register_count; + uint8_t skip_updates; + bool force_new_range{false}; + + virtual void parse_and_publish(const std::vector &data) = 0; + + uint64_t getkey() const { return calc_key(register_type, start_address, offset, bitmask); } + + size_t virtual get_register_size() const { + size_t size = 0; + switch (sensor_value_type) { + case SensorValueType::BIT: + size = 1; + break; + case SensorValueType::U_WORD: + case SensorValueType::S_WORD: + size = 2; + break; + case SensorValueType::U_DWORD: + case SensorValueType::S_DWORD: + case SensorValueType::U_DWORD_R: + case SensorValueType::S_DWORD_R: + case SensorValueType::FP32: + case SensorValueType::FP32_R: + size = 4; + break; + case SensorValueType::U_QWORD: + case SensorValueType::U_QWORD_R: + case SensorValueType::S_QWORD: + case SensorValueType::S_QWORD_R: + size = 8; + break; + case SensorValueType::RAW: + size = this->register_count * 2; + } + return size; + } +}; + +struct ModbusCommandItem { + static const size_t MAX_PAYLOAD_BYTES = 240; + ModbusController *modbusdevice; + uint16_t register_address; + uint16_t register_count; + ModbusFunctionCode function_code; + ModbusRegisterType register_type; + std::function &data)> + on_data_func; + std::vector payload = {}; + bool send(); + + /// factory methods + /** Create modbus read command + * Function code 02-04 + * @param modbusdevice pointer to the device to execute the command + * @param function_code modbus function code for the read command + * @param start_address modbus address of the first register to read + * @param register_count number of registers to read + * @param handler function called when the response is received + * @return ModbusCommandItem with the prepared command + */ + static ModbusCommandItem create_read_command( + ModbusController *modbusdevice, ModbusRegisterType register_type, uint16_t start_address, uint16_t register_count, + std::function &data)> + &&handler); + /** Create modbus read command + * Function code 02-04 + * @param modbusdevice pointer to the device to execute the command + * @param function_code modbus function code for the read command + * @param start_address modbus address of the first register to read + * @param register_count number of registers to read + * @return ModbusCommandItem with the prepared command + */ + static ModbusCommandItem create_read_command(ModbusController *modbusdevice, ModbusRegisterType register_type, + uint16_t start_address, uint16_t register_count); + /** Create modbus read command + * Function code 02-04 + * @param modbusdevice pointer to the device to execute the command + * @param function_code modbus function code for the read command + * @param start_address modbus address of the first register to read + * @param register_count number of registers to read + * @param handler function called when the response is received + * @return ModbusCommandItem with the prepared command + */ + static ModbusCommandItem create_write_multiple_command(ModbusController *modbusdevice, uint16_t start_address, + uint16_t register_count, const std::vector &values); + /** Create modbus write multiple registers command + * Function 16 (10hex) Write Multiple Registers + * @param modbusdevice pointer to the device to execute the command + * @param start_address modbus address of the first register to read + * @param register_count number of registers to read + * @param values uint16_t array to be written to the registers + * @return ModbusCommandItem with the prepared command + */ + static ModbusCommandItem create_write_single_command(ModbusController *modbusdevice, uint16_t start_address, + int16_t value); + /** Create modbus write single registers command + * Function 05 (05hex) Write Single Coil + * @param modbusdevice pointer to the device to execute the command + * @param start_address modbus address of the first register to read + * @param value uint16_t data to be written to the registers + * @return ModbusCommandItem with the prepared command + */ + static ModbusCommandItem create_write_single_coil(ModbusController *modbusdevice, uint16_t address, bool value); + + /** Create modbus write multiple registers command + * Function 15 (0Fhex) Write Multiple Coils + * @param modbusdevice pointer to the device to execute the command + * @param start_address modbus address of the first register to read + * @param value bool vector of values to be written to the registers + * @return ModbusCommandItem with the prepared command + */ + static ModbusCommandItem create_write_multiple_coils(ModbusController *modbusdevice, uint16_t start_address, + const std::vector &values); + /** Create custom modbus command + * @param modbusdevice pointer to the device to execute the command + * @param values byte vector of data to be sent to the device. The compplete payload must be provided with the + * exception of the crc codess + * @param handler function called when the response is received. Default is just logging a response + * @return ModbusCommandItem with the prepared command + */ + static ModbusCommandItem create_custom_command( + ModbusController *modbusdevice, const std::vector &values, + std::function &data)> + &&handler = nullptr); +}; + +/** Modbus controller class. + * Each instance handles the modbus commuinication for all sensors with the same modbus address + * + * all sensor items (sensors, switches, binarysensor ...) are parsed in modbus address ranges. + * when esphome calls ModbusController::Update the commands for each range are created and sent + * Responses for the commands are dispatched to the modbus sensor items. + */ + +class ModbusController : public PollingComponent, public modbus::ModbusDevice { + public: + ModbusController(uint16_t throttle = 0) : modbus::ModbusDevice(), command_throttle_(throttle){}; + void dump_config() override; + void loop() override; + void setup() override; + void update() override; + + /// queues a modbus command in the send queue + void queue_command(const ModbusCommandItem &command); + /// Registers a sensor with the controller. Called by esphomes code generator + void add_sensor_item(SensorItem *item) { sensormap_[item->getkey()] = item; } + /// called when a modbus response was prased without errors + void on_modbus_data(const std::vector &data) override; + /// called when a modbus error response was received + void on_modbus_error(uint8_t function_code, uint8_t exception_code) override; + /// default delegate called by process_modbus_data when a response has retrieved from the incoming queue + void on_register_data(ModbusRegisterType register_type, uint16_t start_address, const std::vector &data); + /// default delegate called by process_modbus_data when a response for a write response has retrieved from the + /// incoming queue + void on_write_register_response(ModbusRegisterType register_type, uint16_t start_address, + const std::vector &data); + /// called by esphome generated code to set the command_throttle period + void set_command_throttle(uint16_t command_throttle) { this->command_throttle_ = command_throttle; } + + protected: + /// parse sensormap_ and create range of sequential addresses + size_t create_register_ranges_(); + /// submit the read command for the address range to the send queue + void update_range_(RegisterRange &r); + /// parse incoming modbus data + void process_modbus_data_(const ModbusCommandItem *response); + /// send the next modbus command from the send queue + bool send_next_command_(); + /// get the number of queued modbus commands (should be mostly empty) + size_t get_command_queue_length_() { return command_queue_.size(); } + /// dump the parsed sensormap for diagnostics + void dump_sensormap_(); + /// Collection of all sensors for this component + /// see calc_key how the key is contructed + std::map sensormap_; + /// Continous range of modbus registers + std::vector register_ranges_; + /// Hold the pending requests to be sent + std::list> command_queue_; + /// modbus response data waiting to get processed + std::queue> incoming_queue_; + /// when was the last send operation + uint32_t last_command_timestamp_; + /// min time in ms between sending modbus commands + uint16_t command_throttle_; +}; + +/** convert vector response payload to float + * @param value float value to cconvert + * @param item SensorItem object + * @return float version of the input + */ +inline float payload_to_float(const std::vector &data, const SensorItem &item) { + return payload_to_float(data, item.sensor_value_type, item.offset, item.bitmask); +} + +} // namespace modbus_controller +} // namespace esphome diff --git a/esphome/components/modbus_controller/number/__init__.py b/esphome/components/modbus_controller/number/__init__.py new file mode 100644 index 0000000000..c7919bb972 --- /dev/null +++ b/esphome/components/modbus_controller/number/__init__.py @@ -0,0 +1,157 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import number +from esphome.const import ( + CONF_ADDRESS, + CONF_ID, + CONF_LAMBDA, + CONF_MAX_VALUE, + CONF_MIN_VALUE, + CONF_MULTIPLY, + CONF_OFFSET, + CONF_STEP, +) + +from .. import ( + modbus_controller_ns, + ModbusController, + SENSOR_VALUE_TYPE, + SensorItem, +) + + +from ..const import ( + CONF_BITMASK, + CONF_BYTE_OFFSET, + CONF_FORCE_NEW_RANGE, + CONF_MODBUS_CONTROLLER_ID, + CONF_REGISTER_COUNT, + CONF_SKIP_UPDATES, + CONF_VALUE_TYPE, + CONF_WRITE_LAMBDA, +) + +DEPENDENCIES = ["modbus_controller"] +CODEOWNERS = ["@martgras"] + + +ModbusNumber = modbus_controller_ns.class_( + "ModbusNumber", cg.Component, number.Number, SensorItem +) + +TYPE_REGISTER_MAP = { + "RAW": 1, + "U_WORD": 1, + "S_WORD": 1, + "U_DWORD": 2, + "U_DWORD_R": 2, + "S_DWORD": 2, + "S_DWORD_R": 2, + "U_QWORD": 4, + "U_QWORDU_R": 4, + "S_QWORD": 4, + "U_QWORD_R": 4, + "FP32": 2, + "FP32_R": 2, +} + + +def validate_min_max(config): + if config[CONF_MAX_VALUE] <= config[CONF_MIN_VALUE]: + raise cv.Invalid("max_value must be greater than min_value") + if config[CONF_MIN_VALUE] < -16777215: + raise cv.Invalid("max_value must be greater than -16777215") + if config[CONF_MAX_VALUE] > 16777215: + raise cv.Invalid("max_value must not be greater than 16777215") + return config + + +CONFIG_SCHEMA = cv.All( + number.NUMBER_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(ModbusNumber), + cv.GenerateID(CONF_MODBUS_CONTROLLER_ID): cv.use_id(ModbusController), + cv.Required(CONF_ADDRESS): cv.positive_int, + cv.Optional(CONF_OFFSET, default=0): cv.positive_int, + cv.Optional(CONF_BYTE_OFFSET): cv.positive_int, + cv.Optional(CONF_BITMASK, default=0xFFFFFFFF): cv.hex_uint32_t, + cv.Optional(CONF_VALUE_TYPE, default="U_WORD"): cv.enum(SENSOR_VALUE_TYPE), + cv.Optional(CONF_REGISTER_COUNT, default=0): cv.positive_int, + cv.Optional(CONF_SKIP_UPDATES, default=0): cv.positive_int, + cv.Optional(CONF_FORCE_NEW_RANGE, default=False): cv.boolean, + cv.Optional(CONF_LAMBDA): cv.returning_lambda, + cv.Optional(CONF_WRITE_LAMBDA): cv.returning_lambda, + cv.GenerateID(): cv.declare_id(ModbusNumber), + # 24 bits are the maximum value for fp32 before precison is lost + # 0x00FFFFFF = 16777215 + cv.Optional(CONF_MAX_VALUE, default=16777215.0): cv.float_, + cv.Optional(CONF_MIN_VALUE, default=-16777215.0): cv.float_, + cv.Optional(CONF_STEP, default=1): cv.positive_float, + cv.Optional(CONF_MULTIPLY, default=1.0): cv.float_, + } + ).extend(cv.polling_component_schema("60s")), + validate_min_max, +) + + +async def to_code(config): + byte_offset = 0 + if CONF_OFFSET in config: + byte_offset = config[CONF_OFFSET] + # A CONF_BYTE_OFFSET setting overrides CONF_OFFSET + if CONF_BYTE_OFFSET in config: + byte_offset = config[CONF_BYTE_OFFSET] + value_type = config[CONF_VALUE_TYPE] + reg_count = config[CONF_REGISTER_COUNT] + if reg_count == 0: + reg_count = TYPE_REGISTER_MAP[value_type] + var = cg.new_Pvariable( + config[CONF_ID], + config[CONF_ADDRESS], + byte_offset, + config[CONF_BITMASK], + config[CONF_VALUE_TYPE], + reg_count, + config[CONF_SKIP_UPDATES], + config[CONF_FORCE_NEW_RANGE], + ) + + await cg.register_component(var, config) + await number.register_number( + var, + config, + min_value=config[CONF_MIN_VALUE], + max_value=config[CONF_MAX_VALUE], + step=config[CONF_STEP], + ) + + cg.add(var.set_write_multiply(config[CONF_MULTIPLY])) + parent = await cg.get_variable(config[CONF_MODBUS_CONTROLLER_ID]) + + cg.add(var.set_parent(parent)) + cg.add(parent.add_sensor_item(var)) + if CONF_LAMBDA in config: + template_ = await cg.process_lambda( + config[CONF_LAMBDA], + [ + (ModbusNumber.operator("ptr"), "item"), + (cg.float_, "x"), + ( + cg.std_vector.template(cg.uint8).operator("const").operator("ref"), + "data", + ), + ], + return_type=cg.optional.template(float), + ) + cg.add(var.set_template(template_)) + if CONF_WRITE_LAMBDA in config: + template_ = await cg.process_lambda( + config[CONF_WRITE_LAMBDA], + [ + (ModbusNumber.operator("ptr"), "item"), + (cg.float_, "x"), + (cg.std_vector.template(cg.uint16).operator("ref"), "payload"), + ], + return_type=cg.optional.template(float), + ) + cg.add(var.set_write_template(template_)) diff --git a/esphome/components/modbus_controller/number/modbus_number.cpp b/esphome/components/modbus_controller/number/modbus_number.cpp new file mode 100644 index 0000000000..95c6ac6f6a --- /dev/null +++ b/esphome/components/modbus_controller/number/modbus_number.cpp @@ -0,0 +1,83 @@ +#include +#include "modbus_number.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace modbus_controller { + +static const char *const TAG = "modbus.number"; + +void ModbusNumber::parse_and_publish(const std::vector &data) { + union { + float float_value; + uint32_t raw; + } raw_to_float; + + float result = payload_to_float(data, *this); + + // Is there a lambda registered + // call it with the pre converted value and the raw data array + if (this->transform_func_.has_value()) { + // the lambda can parse the response itself + auto val = (*this->transform_func_)(this, result, data); + if (val.has_value()) { + ESP_LOGV(TAG, "Value overwritten by lambda"); + result = val.value(); + } + } + ESP_LOGD(TAG, "Number new state : %.02f", result); + // this->sensor_->raw_state = result; + this->publish_state(result); +} + +void ModbusNumber::control(float value) { + union { + float float_value; + uint32_t raw; + } raw_to_float; + + std::vector data; + auto original_value = value; + // Is there are lambda configured? + if (this->write_transform_func_.has_value()) { + // data is passed by reference + // the lambda can fill the empty vector directly + // in that case the return value is ignored + auto val = (*this->write_transform_func_)(this, value, data); + if (val.has_value()) { + ESP_LOGV(TAG, "Value overwritten by lambda"); + value = val.value(); + } else { + ESP_LOGV(TAG, "Communication handled by lambda - exiting control"); + return; + } + } else { + value = multiply_by_ * value; + } + + // lambda didn't set payload + if (data.empty()) { + data = float_to_payload(value, this->sensor_value_type); + } + + ESP_LOGD(TAG, + "Updating register: connected Sensor=%s start address=0x%X register count=%d new value=%.02f (val=%.02f)", + this->get_name().c_str(), this->start_address, this->register_count, value, value); + + // Create and send the write command + auto write_cmd = ModbusCommandItem::create_write_multiple_command(parent_, this->start_address + this->offset, + this->register_count, data); + + // publish new value + write_cmd.on_data_func = [this, write_cmd, value](ModbusRegisterType register_type, uint16_t start_address, + const std::vector &data) { + // gets called when the write command is ack'd from the device + parent_->on_write_register_response(write_cmd.register_type, start_address, data); + this->publish_state(value); + }; + parent_->queue_command(write_cmd); +} +void ModbusNumber::dump_config() { LOG_NUMBER(TAG, "Modbus Number", this); } + +} // namespace modbus_controller +} // namespace esphome diff --git a/esphome/components/modbus_controller/number/modbus_number.h b/esphome/components/modbus_controller/number/modbus_number.h new file mode 100644 index 0000000000..0fd4e314bc --- /dev/null +++ b/esphome/components/modbus_controller/number/modbus_number.h @@ -0,0 +1,48 @@ +#pragma once + +#include "esphome/components/number/number.h" +#include "esphome/components/modbus_controller/modbus_controller.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace modbus_controller { + +using value_to_data_t = std::function(float); + +class ModbusNumber : public number::Number, public Component, public SensorItem { + public: + ModbusNumber(uint16_t start_address, uint8_t offset, uint32_t bitmask, SensorValueType value_type, int register_count, + uint8_t skip_updates, bool force_new_range) + : number::Number(), Component(), SensorItem() { + this->register_type = ModbusRegisterType::HOLDING; + this->start_address = start_address; + this->offset = offset; + this->bitmask = bitmask; + this->sensor_value_type = value_type; + this->register_count = register_count; + this->skip_updates = skip_updates; + this->force_new_range = force_new_range; + }; + + void dump_config() override; + void parse_and_publish(const std::vector &data) override; + float get_setup_priority() const override { return setup_priority::HARDWARE; } + void set_update_interval(int) {} + void set_parent(ModbusController *parent) { this->parent_ = parent; } + void set_write_multiply(float factor) { multiply_by_ = factor; } + + using transform_func_t = std::function(ModbusNumber *, float, const std::vector &)>; + using write_transform_func_t = std::function(ModbusNumber *, float, std::vector &)>; + void set_template(transform_func_t &&f) { this->transform_func_ = f; } + void set_write_template(write_transform_func_t &&f) { this->write_transform_func_ = f; } + + protected: + void control(float value) override; + optional transform_func_; + optional write_transform_func_; + ModbusController *parent_; + float multiply_by_{1.0}; +}; + +} // namespace modbus_controller +} // namespace esphome diff --git a/esphome/components/modbus_controller/output/__init__.py b/esphome/components/modbus_controller/output/__init__.py new file mode 100644 index 0000000000..9c41fc011c --- /dev/null +++ b/esphome/components/modbus_controller/output/__init__.py @@ -0,0 +1,74 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import output + +from esphome.const import ( + CONF_ADDRESS, + CONF_ID, + CONF_MULTIPLY, + CONF_OFFSET, +) + +from .. import ( + SensorItem, + modbus_controller_ns, + ModbusController, +) + +from ..const import ( + CONF_BYTE_OFFSET, + CONF_MODBUS_CONTROLLER_ID, + CONF_VALUE_TYPE, + CONF_WRITE_LAMBDA, +) +from ..sensor import SENSOR_VALUE_TYPE + +DEPENDENCIES = ["modbus_controller"] +CODEOWNERS = ["@martgras"] + + +ModbusOutput = modbus_controller_ns.class_( + "ModbusOutput", cg.Component, output.FloatOutput, SensorItem +) + +CONFIG_SCHEMA = cv.All( + output.FLOAT_OUTPUT_SCHEMA.extend( + { + cv.GenerateID(CONF_MODBUS_CONTROLLER_ID): cv.use_id(ModbusController), + cv.GenerateID(): cv.declare_id(ModbusOutput), + cv.Required(CONF_ADDRESS): cv.positive_int, + cv.Optional(CONF_OFFSET, default=0): cv.positive_int, + cv.Optional(CONF_BYTE_OFFSET): cv.positive_int, + cv.Optional(CONF_VALUE_TYPE, default="U_WORD"): cv.enum(SENSOR_VALUE_TYPE), + cv.Optional(CONF_WRITE_LAMBDA): cv.returning_lambda, + cv.Optional(CONF_MULTIPLY, default=1.0): cv.float_, + } + ), +) + + +async def to_code(config): + byte_offset = 0 + if CONF_OFFSET in config: + byte_offset = config[CONF_OFFSET] + # A CONF_BYTE_OFFSET setting overrides CONF_OFFSET + if CONF_BYTE_OFFSET in config: + byte_offset = config[CONF_BYTE_OFFSET] + var = cg.new_Pvariable( + config[CONF_ID], config[CONF_ADDRESS], byte_offset, config[CONF_VALUE_TYPE] + ) + await output.register_output(var, config) + cg.add(var.set_write_multiply(config[CONF_MULTIPLY])) + parent = await cg.get_variable(config[CONF_MODBUS_CONTROLLER_ID]) + cg.add(var.set_parent(parent)) + if CONF_WRITE_LAMBDA in config: + template_ = await cg.process_lambda( + config[CONF_WRITE_LAMBDA], + [ + (ModbusOutput.operator("ptr"), "item"), + (cg.float_, "x"), + (cg.std_vector.template(cg.uint16).operator("ref"), "payload"), + ], + return_type=cg.optional.template(float), + ) + cg.add(var.set_write_template(template_)) diff --git a/esphome/components/modbus_controller/output/modbus_output.cpp b/esphome/components/modbus_controller/output/modbus_output.cpp new file mode 100644 index 0000000000..f7d7c42342 --- /dev/null +++ b/esphome/components/modbus_controller/output/modbus_output.cpp @@ -0,0 +1,61 @@ +#include +#include "modbus_output.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace modbus_controller { + +static const char *const TAG = "modbus_controller.output"; + +void ModbusOutput::setup() {} + +/** Write a value to the device + * + */ +void ModbusOutput::write_state(float value) { + union { + float float_value; + uint32_t raw; + } raw_to_float; + + std::vector data; + auto original_value = value; + // Is there are lambda configured? + if (this->write_transform_func_.has_value()) { + // data is passed by reference + // the lambda can fill the empty vector directly + // in that case the return value is ignored + auto val = (*this->write_transform_func_)(this, value, data); + if (val.has_value()) { + ESP_LOGV(TAG, "Value overwritten by lambda"); + value = val.value(); + } else { + ESP_LOGV(TAG, "Communication handled by lambda - exiting control"); + return; + } + } else { + value = multiply_by_ * value; + } + // lambda didn't set payload + if (data.empty()) { + data = float_to_payload(value, this->sensor_value_type); + } + + ESP_LOGD(TAG, "Updating register: start address=0x%X register count=%d new value=%.02f (val=%.02f)", + this->start_address, this->register_count, value, original_value); + + // Create and send the write command + auto write_cmd = + ModbusCommandItem::create_write_multiple_command(parent_, this->start_address, this->register_count, data); + parent_->queue_command(write_cmd); +} + +void ModbusOutput::dump_config() { + ESP_LOGCONFIG(TAG, "Modbus Float Output:"); + LOG_FLOAT_OUTPUT(this); + ESP_LOGCONFIG(TAG, "Modbus device start address=0x%X register count=%d value type=%hhu", this->start_address, + this->register_count, this->sensor_value_type); +} + +} // namespace modbus_controller +} // namespace esphome diff --git a/esphome/components/modbus_controller/output/modbus_output.h b/esphome/components/modbus_controller/output/modbus_output.h new file mode 100644 index 0000000000..f46aef4683 --- /dev/null +++ b/esphome/components/modbus_controller/output/modbus_output.h @@ -0,0 +1,45 @@ +#pragma once + +#include "esphome/components/output/float_output.h" +#include "esphome/components/modbus_controller/modbus_controller.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace modbus_controller { + +using value_to_data_t = std::function(float); + +class ModbusOutput : public output::FloatOutput, public Component, public SensorItem { + public: + ModbusOutput(uint16_t start_address, uint8_t offset, SensorValueType value_type) + : output::FloatOutput(), Component() { + this->register_type = ModbusRegisterType::HOLDING; + this->start_address = start_address; + this->offset = offset; + this->bitmask = bitmask; + this->sensor_value_type = value_type; + this->skip_updates = 0; + this->start_address += offset; + this->offset = 0; + } + void setup() override; + void dump_config() override; + + void set_parent(ModbusController *parent) { this->parent_ = parent; } + void set_write_multiply(float factor) { multiply_by_ = factor; } + // Do nothing + void parse_and_publish(const std::vector &data) override{}; + + using write_transform_func_t = std::function(ModbusOutput *, float, std::vector &)>; + void set_write_template(write_transform_func_t &&f) { this->write_transform_func_ = f; } + + protected: + void write_state(float value) override; + optional write_transform_func_{nullopt}; + + ModbusController *parent_; + float multiply_by_{1.0}; +}; + +} // namespace modbus_controller +} // namespace esphome diff --git a/esphome/components/modbus_controller/sensor/__init__.py b/esphome/components/modbus_controller/sensor/__init__.py new file mode 100644 index 0000000000..687f3d82fb --- /dev/null +++ b/esphome/components/modbus_controller/sensor/__init__.py @@ -0,0 +1,109 @@ +from esphome.components import sensor +import esphome.config_validation as cv +import esphome.codegen as cg + +from esphome.const import CONF_ID, CONF_ADDRESS, CONF_LAMBDA, CONF_OFFSET +from .. import ( + SensorItem, + modbus_controller_ns, + ModbusController, + MODBUS_REGISTER_TYPE, + SENSOR_VALUE_TYPE, +) +from ..const import ( + CONF_BITMASK, + CONF_BYTE_OFFSET, + CONF_FORCE_NEW_RANGE, + CONF_MODBUS_CONTROLLER_ID, + CONF_REGISTER_COUNT, + CONF_REGISTER_TYPE, + CONF_SKIP_UPDATES, + CONF_VALUE_TYPE, +) + +DEPENDENCIES = ["modbus_controller"] +CODEOWNERS = ["@martgras"] + + +ModbusSensor = modbus_controller_ns.class_( + "ModbusSensor", cg.Component, sensor.Sensor, SensorItem +) + +TYPE_REGISTER_MAP = { + "RAW": 1, + "U_WORD": 1, + "S_WORD": 1, + "U_DWORD": 2, + "U_DWORD_R": 2, + "S_DWORD": 2, + "S_DWORD_R": 2, + "U_QWORD": 4, + "U_QWORDU_R": 4, + "S_QWORD": 4, + "U_QWORD_R": 4, + "FP32": 2, + "FP32_R": 2, +} + + +CONFIG_SCHEMA = cv.All( + sensor.SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(ModbusSensor), + cv.GenerateID(CONF_MODBUS_CONTROLLER_ID): cv.use_id(ModbusController), + cv.Required(CONF_ADDRESS): cv.positive_int, + cv.Required(CONF_REGISTER_TYPE): cv.enum(MODBUS_REGISTER_TYPE), + cv.Optional(CONF_OFFSET, default=0): cv.positive_int, + cv.Optional(CONF_BYTE_OFFSET): cv.positive_int, + cv.Optional(CONF_BITMASK, default=0xFFFFFFFF): cv.hex_uint32_t, + cv.Optional(CONF_VALUE_TYPE, default="U_WORD"): cv.enum(SENSOR_VALUE_TYPE), + cv.Optional(CONF_REGISTER_COUNT, default=0): cv.positive_int, + cv.Optional(CONF_SKIP_UPDATES, default=0): cv.positive_int, + cv.Optional(CONF_FORCE_NEW_RANGE, default=False): cv.boolean, + cv.Optional(CONF_LAMBDA): cv.returning_lambda, + } + ).extend(cv.COMPONENT_SCHEMA), +) + + +async def to_code(config): + byte_offset = 0 + if CONF_OFFSET in config: + byte_offset = config[CONF_OFFSET] + # A CONF_BYTE_OFFSET setting overrides CONF_OFFSET + if CONF_BYTE_OFFSET in config: + byte_offset = config[CONF_BYTE_OFFSET] + value_type = config[CONF_VALUE_TYPE] + reg_count = config[CONF_REGISTER_COUNT] + if reg_count == 0: + reg_count = TYPE_REGISTER_MAP[value_type] + var = cg.new_Pvariable( + config[CONF_ID], + config[CONF_REGISTER_TYPE], + config[CONF_ADDRESS], + byte_offset, + config[CONF_BITMASK], + config[CONF_VALUE_TYPE], + reg_count, + config[CONF_SKIP_UPDATES], + config[CONF_FORCE_NEW_RANGE], + ) + await cg.register_component(var, config) + await sensor.register_sensor(var, config) + + paren = await cg.get_variable(config[CONF_MODBUS_CONTROLLER_ID]) + cg.add(paren.add_sensor_item(var)) + if CONF_LAMBDA in config: + template_ = await cg.process_lambda( + config[CONF_LAMBDA], + [ + (ModbusSensor.operator("ptr"), "item"), + (cg.float_, "x"), + ( + cg.std_vector.template(cg.uint8).operator("const").operator("ref"), + "data", + ), + ], + return_type=cg.optional.template(float), + ) + cg.add(var.set_template(template_)) diff --git a/esphome/components/modbus_controller/sensor/modbus_sensor.cpp b/esphome/components/modbus_controller/sensor/modbus_sensor.cpp new file mode 100644 index 0000000000..dbd0525347 --- /dev/null +++ b/esphome/components/modbus_controller/sensor/modbus_sensor.cpp @@ -0,0 +1,36 @@ + +#include "modbus_sensor.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace modbus_controller { + +static const char *const TAG = "modbus_controller.sensor"; + +void ModbusSensor::dump_config() { LOG_SENSOR(TAG, "Modbus Controller Sensor", this); } + +void ModbusSensor::parse_and_publish(const std::vector &data) { + union { + float float_value; + uint32_t raw; + } raw_to_float; + + float result = payload_to_float(data, *this); + + // Is there a lambda registered + // call it with the pre converted value and the raw data array + if (this->transform_func_.has_value()) { + // the lambda can parse the response itself + auto val = (*this->transform_func_)(this, result, data); + if (val.has_value()) { + ESP_LOGV(TAG, "Value overwritten by lambda"); + result = val.value(); + } + } + ESP_LOGD(TAG, "Sensor new state: %.02f", result); + // this->sensor_->raw_state = result; + this->publish_state(result); +} + +} // namespace modbus_controller +} // namespace esphome diff --git a/esphome/components/modbus_controller/sensor/modbus_sensor.h b/esphome/components/modbus_controller/sensor/modbus_sensor.h new file mode 100644 index 0000000000..4f48c2a4dd --- /dev/null +++ b/esphome/components/modbus_controller/sensor/modbus_sensor.h @@ -0,0 +1,35 @@ +#pragma once + +#include "esphome/components/modbus_controller/modbus_controller.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace modbus_controller { + +class ModbusSensor : public Component, public sensor::Sensor, public SensorItem { + public: + ModbusSensor(ModbusRegisterType register_type, uint16_t start_address, uint8_t offset, uint32_t bitmask, + SensorValueType value_type, int register_count, uint8_t skip_updates, bool force_new_range) + : Component(), sensor::Sensor() { + this->register_type = register_type; + this->start_address = start_address; + this->offset = offset; + this->bitmask = bitmask; + this->sensor_value_type = value_type; + this->register_count = register_count; + this->skip_updates = skip_updates; + this->force_new_range = force_new_range; + } + + void parse_and_publish(const std::vector &data) override; + void dump_config() override; + using transform_func_t = std::function(ModbusSensor *, float, const std::vector &)>; + void set_template(transform_func_t &&f) { this->transform_func_ = f; } + + protected: + optional transform_func_{nullopt}; +}; + +} // namespace modbus_controller +} // namespace esphome diff --git a/esphome/components/modbus_controller/switch/__init__.py b/esphome/components/modbus_controller/switch/__init__.py new file mode 100644 index 0000000000..e03b0d37be --- /dev/null +++ b/esphome/components/modbus_controller/switch/__init__.py @@ -0,0 +1,81 @@ +from esphome.components import switch +import esphome.config_validation as cv +import esphome.codegen as cg + + +from esphome.const import CONF_ID, CONF_ADDRESS, CONF_LAMBDA, CONF_OFFSET +from .. import ( + MODBUS_REGISTER_TYPE, + SensorItem, + modbus_controller_ns, + ModbusController, +) +from ..const import ( + CONF_BITMASK, + CONF_BYTE_OFFSET, + CONF_FORCE_NEW_RANGE, + CONF_MODBUS_CONTROLLER_ID, + CONF_REGISTER_TYPE, +) + +DEPENDENCIES = ["modbus_controller"] +CODEOWNERS = ["@martgras"] + + +ModbusSwitch = modbus_controller_ns.class_( + "ModbusSwitch", cg.Component, switch.Switch, SensorItem +) + + +CONFIG_SCHEMA = cv.All( + switch.SWITCH_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(ModbusSwitch), + cv.GenerateID(CONF_MODBUS_CONTROLLER_ID): cv.use_id(ModbusController), + cv.Required(CONF_REGISTER_TYPE): cv.enum(MODBUS_REGISTER_TYPE), + cv.Required(CONF_ADDRESS): cv.positive_int, + cv.Optional(CONF_OFFSET, default=0): cv.positive_int, + cv.Optional(CONF_BYTE_OFFSET): cv.positive_int, + cv.Optional(CONF_BITMASK, default=0x1): cv.hex_uint32_t, + cv.Optional(CONF_FORCE_NEW_RANGE, default=False): cv.boolean, + cv.Optional(CONF_LAMBDA): cv.returning_lambda, + } + ).extend(cv.COMPONENT_SCHEMA), +) + + +async def to_code(config): + byte_offset = 0 + if CONF_OFFSET in config: + byte_offset = config[CONF_OFFSET] + # A CONF_BYTE_OFFSET setting overrides CONF_OFFSET + if CONF_BYTE_OFFSET in config: + byte_offset = config[CONF_BYTE_OFFSET] + var = cg.new_Pvariable( + config[CONF_ID], + config[CONF_REGISTER_TYPE], + config[CONF_ADDRESS], + byte_offset, + config[CONF_BITMASK], + config[CONF_FORCE_NEW_RANGE], + ) + await cg.register_component(var, config) + await switch.register_switch(var, config) + + paren = await cg.get_variable(config[CONF_MODBUS_CONTROLLER_ID]) + cg.add(paren.add_sensor_item(var)) + cg.add(var.set_parent(paren)) + if CONF_LAMBDA in config: + publish_template_ = await cg.process_lambda( + config[CONF_LAMBDA], + [ + (ModbusSwitch.operator("ptr"), "item"), + (bool, "x"), + ( + cg.std_vector.template(cg.uint8).operator("const").operator("ref"), + "data", + ), + ], + return_type=cg.optional.template(bool), + ) + cg.add(var.set_template(publish_template_)) diff --git a/esphome/components/modbus_controller/switch/modbus_switch.cpp b/esphome/components/modbus_controller/switch/modbus_switch.cpp new file mode 100644 index 0000000000..ce9557e6c4 --- /dev/null +++ b/esphome/components/modbus_controller/switch/modbus_switch.cpp @@ -0,0 +1,70 @@ + +#include "modbus_switch.h" +#include "esphome/core/log.h" +namespace esphome { +namespace modbus_controller { + +static const char *const TAG = "modbus_controller.switch"; + +void ModbusSwitch::setup() { + // value isn't required + // without it we crash on save + this->get_initial_state(); +} +void ModbusSwitch::dump_config() { LOG_SWITCH(TAG, "Modbus Controller Switch", this); } + +void ModbusSwitch::parse_and_publish(const std::vector &data) { + bool value = false; + switch (this->register_type) { + case ModbusRegisterType::DISCRETE_INPUT: + case ModbusRegisterType::COIL: + // offset for coil is the actual number of the coil not the byte offset + value = coil_from_vector(this->offset, data); + break; + default: + value = get_data(data, this->offset) & this->bitmask; + break; + } + + // Is there a lambda registered + // call it with the pre converted value and the raw data array + if (this->publish_transform_func_) { + // the lambda can parse the response itself + auto val = (*this->publish_transform_func_)(this, value, data); + if (val.has_value()) { + ESP_LOGV(TAG, "Value overwritten by lambda"); + value = val.value(); + } + } + + ESP_LOGV(TAG, "Publish '%s': new value = %s type = %d address = %X offset = %x", this->get_name().c_str(), + ONOFF(value), (int) this->register_type, this->start_address, this->offset); + this->publish_state(value); +} + +void ModbusSwitch::write_state(bool state) { + // This will be called every time the user requests a state change. + ModbusCommandItem cmd; + ESP_LOGV(TAG, "write_state '%s': new value = %s type = %d address = %X offset = %x", this->get_name().c_str(), + ONOFF(state), (int) this->register_type, this->start_address, this->offset); + switch (this->register_type) { + case ModbusRegisterType::COIL: + // offset for coil and discrete inputs is the coil/register number not bytes + cmd = ModbusCommandItem::create_write_single_coil(parent_, this->start_address + this->offset, state); + break; + case ModbusRegisterType::DISCRETE_INPUT: + cmd = ModbusCommandItem::create_write_single_command(parent_, this->start_address + this->offset, state); + break; + + default: + // since offset is in bytes and a register is 16 bits we get the start by adding offset/2 + cmd = ModbusCommandItem::create_write_single_command(parent_, this->start_address + this->offset / 2, + state ? 0xFFFF & this->bitmask : 0); + break; + } + this->parent_->queue_command(cmd); + publish_state(state); +} +// ModbusSwitch end +} // namespace modbus_controller +} // namespace esphome diff --git a/esphome/components/modbus_controller/switch/modbus_switch.h b/esphome/components/modbus_controller/switch/modbus_switch.h new file mode 100644 index 0000000000..a38668fabb --- /dev/null +++ b/esphome/components/modbus_controller/switch/modbus_switch.h @@ -0,0 +1,44 @@ +#pragma once + +#include "esphome/components/modbus_controller/modbus_controller.h" +#include "esphome/components/switch/switch.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace modbus_controller { + +class ModbusSwitch : public Component, public switch_::Switch, public SensorItem { + public: + ModbusSwitch(ModbusRegisterType register_type, uint16_t start_address, uint8_t offset, uint32_t bitmask, + bool force_new_range) + : Component(), switch_::Switch() { + this->register_type = register_type; + this->start_address = start_address; + this->offset = offset; + this->bitmask = bitmask; + this->sensor_value_type = SensorValueType::BIT; + this->skip_updates = 0; + this->register_count = 1; + if (register_type == ModbusRegisterType::HOLDING || register_type == ModbusRegisterType::COIL) { + this->start_address += offset; + this->offset = 0; + } + this->force_new_range = force_new_range; + }; + void setup() override; + void write_state(bool state) override; + void dump_config() override; + void set_state(bool state) { this->state = state; } + void parse_and_publish(const std::vector &data) override; + void set_parent(ModbusController *parent) { this->parent_ = parent; } + + using transform_func_t = std::function(ModbusSwitch *, bool, const std::vector &)>; + void set_template(transform_func_t &&f) { this->publish_transform_func_ = f; } + + protected: + ModbusController *parent_; + optional publish_transform_func_{nullopt}; +}; + +} // namespace modbus_controller +} // namespace esphome diff --git a/esphome/components/modbus_controller/text_sensor/__init__.py b/esphome/components/modbus_controller/text_sensor/__init__.py new file mode 100644 index 0000000000..2c02c86795 --- /dev/null +++ b/esphome/components/modbus_controller/text_sensor/__init__.py @@ -0,0 +1,101 @@ +from esphome.components import text_sensor +import esphome.config_validation as cv +import esphome.codegen as cg + + +from esphome.const import CONF_ID, CONF_ADDRESS, CONF_LAMBDA, CONF_OFFSET +from .. import ( + SensorItem, + modbus_controller_ns, + ModbusController, + MODBUS_REGISTER_TYPE, +) +from ..const import ( + CONF_BYTE_OFFSET, + CONF_FORCE_NEW_RANGE, + CONF_MODBUS_CONTROLLER_ID, + CONF_REGISTER_COUNT, + CONF_RESPONSE_SIZE, + CONF_SKIP_UPDATES, + CONF_RAW_ENCODE, + CONF_REGISTER_TYPE, +) + +DEPENDENCIES = ["modbus_controller"] +CODEOWNERS = ["@martgras"] + + +ModbusTextSensor = modbus_controller_ns.class_( + "ModbusTextSensor", cg.Component, text_sensor.TextSensor, SensorItem +) + +RawEncoding_ns = modbus_controller_ns.namespace("RawEncoding") +RawEncoding = RawEncoding_ns.enum("RawEncoding") +RAW_ENCODING = { + "NONE": RawEncoding.NONE, + "HEXBYTES": RawEncoding.HEXBYTES, + "COMMA": RawEncoding.COMMA, +} + +CONFIG_SCHEMA = cv.All( + text_sensor.TEXT_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(ModbusTextSensor), + cv.GenerateID(CONF_MODBUS_CONTROLLER_ID): cv.use_id(ModbusController), + cv.Required(CONF_REGISTER_TYPE): cv.enum(MODBUS_REGISTER_TYPE), + cv.Required(CONF_ADDRESS): cv.positive_int, + cv.Optional(CONF_OFFSET, default=0): cv.positive_int, + cv.Optional(CONF_BYTE_OFFSET): cv.positive_int, + cv.Optional(CONF_REGISTER_COUNT, default=0): cv.positive_int, + cv.Optional(CONF_RESPONSE_SIZE, default=2): cv.positive_int, + cv.Optional(CONF_RAW_ENCODE, default="NONE"): cv.enum(RAW_ENCODING), + cv.Optional(CONF_SKIP_UPDATES, default=0): cv.positive_int, + cv.Optional(CONF_FORCE_NEW_RANGE, default=False): cv.boolean, + cv.Optional(CONF_LAMBDA): cv.returning_lambda, + } + ).extend(cv.COMPONENT_SCHEMA), +) + + +async def to_code(config): + byte_offset = 0 + if CONF_OFFSET in config: + byte_offset = config[CONF_OFFSET] + # A CONF_BYTE_OFFSET setting overrides CONF_OFFSET + if CONF_BYTE_OFFSET in config: + byte_offset = config[CONF_BYTE_OFFSET] + response_size = config[CONF_RESPONSE_SIZE] + reg_count = config[CONF_REGISTER_COUNT] + if reg_count == 0: + reg_count = response_size / 2 + var = cg.new_Pvariable( + config[CONF_ID], + config[CONF_REGISTER_TYPE], + config[CONF_ADDRESS], + byte_offset, + reg_count, + config[CONF_RESPONSE_SIZE], + config[CONF_RAW_ENCODE], + config[CONF_SKIP_UPDATES], + config[CONF_FORCE_NEW_RANGE], + ) + + await cg.register_component(var, config) + await text_sensor.register_text_sensor(var, config) + + paren = await cg.get_variable(config[CONF_MODBUS_CONTROLLER_ID]) + cg.add(paren.add_sensor_item(var)) + if CONF_LAMBDA in config: + template_ = await cg.process_lambda( + config[CONF_LAMBDA], + [ + (ModbusTextSensor.operator("ptr"), "item"), + (cg.std_string.operator("const").operator("ref"), "x"), + ( + cg.std_vector.template(cg.uint8).operator("const").operator("ref"), + "data", + ), + ], + return_type=cg.optional.template(cg.std_string), + ) + cg.add(var.set_template(template_)) diff --git a/esphome/components/modbus_controller/text_sensor/modbus_textsensor.cpp b/esphome/components/modbus_controller/text_sensor/modbus_textsensor.cpp new file mode 100644 index 0000000000..a06d44e90b --- /dev/null +++ b/esphome/components/modbus_controller/text_sensor/modbus_textsensor.cpp @@ -0,0 +1,56 @@ + +#include "modbus_textsensor.h" +#include "esphome/core/log.h" +#include +#include + +namespace esphome { +namespace modbus_controller { + +static const char *const TAG = "modbus_controller.text_sensor"; + +void ModbusTextSensor::dump_config() { LOG_TEXT_SENSOR("", "Modbus Controller Text Sensor", this); } + +void ModbusTextSensor::parse_and_publish(const std::vector &data) { + std::ostringstream output; + uint8_t max_items = this->response_bytes_; + char buffer[4]; + bool add_comma = false; + for (auto b : data) { + switch (this->encode_) { + case RawEncoding::HEXBYTES: + sprintf(buffer, "%02x", b); + output << buffer; + break; + case RawEncoding::COMMA: + sprintf(buffer, add_comma ? ",%d" : "%d", b); + output << buffer; + add_comma = true; + break; + // Anything else no encoding + case RawEncoding::NONE: + default: + output << (char) b; + break; + } + if (--max_items == 0) { + break; + } + } + + auto result = output.str(); + // Is there a lambda registered + // call it with the pre converted value and the raw data array + if (this->transform_func_.has_value()) { + // the lambda can parse the response itself + auto val = (*this->transform_func_)(this, result, data); + if (val.has_value()) { + ESP_LOGV(TAG, "Value overwritten by lambda"); + result = val.value(); + } + } + this->publish_state(result); +} + +} // namespace modbus_controller +} // namespace esphome diff --git a/esphome/components/modbus_controller/text_sensor/modbus_textsensor.h b/esphome/components/modbus_controller/text_sensor/modbus_textsensor.h new file mode 100644 index 0000000000..28d0f0b241 --- /dev/null +++ b/esphome/components/modbus_controller/text_sensor/modbus_textsensor.h @@ -0,0 +1,52 @@ +#pragma once + +#include "esphome/components/modbus_controller/modbus_controller.h" +#include "esphome/components/text_sensor/text_sensor.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace modbus_controller { + +enum class RawEncoding { NONE = 0, HEXBYTES = 1, COMMA = 2 }; + +class ModbusTextSensor : public Component, public text_sensor::TextSensor, public SensorItem { + public: + ModbusTextSensor(ModbusRegisterType register_type, uint16_t start_address, uint8_t offset, uint8_t register_count, + uint16_t response_bytes, RawEncoding encode, uint8_t skip_updates, bool force_new_range) + : Component() { + this->register_type = register_type; + this->start_address = start_address; + this->offset = offset; + this->response_bytes_ = response_bytes; + this->register_count = register_count; + this->encode_ = encode; + this->skip_updates = skip_updates; + this->bitmask = 0xFFFFFFFF; + this->sensor_value_type = SensorValueType::RAW; + this->force_new_range = force_new_range; + } + size_t get_register_size() const override { + if (sensor_value_type == SensorValueType::RAW) { + return this->response_bytes_; + } else { + return SensorItem::get_register_size(); + } + } + + void dump_config() override; + + void parse_and_publish(const std::vector &data) override; + using transform_func_t = + std::function(ModbusTextSensor *, std::string, const std::vector &)>; + void set_template(transform_func_t &&f) { this->transform_func_ = f; } + + protected: + optional transform_func_{nullopt}; + + protected: + RawEncoding encode_; + uint16_t response_bytes_; +}; + +} // namespace modbus_controller +} // namespace esphome diff --git a/tests/test5.yaml b/tests/test5.yaml index f165617551..b22b19550e 100644 --- a/tests/test5.yaml +++ b/tests/test5.yaml @@ -36,6 +36,14 @@ i2c: modbus: uart_id: uart1 + flow_control_pin: 5 + id: mod_bus1 + +modbus_controller: + - id: modbus_controller_test + address: 0x2 + modbus_id: mod_bus1 + binary_sensor: - platform: gpio @@ -150,6 +158,14 @@ sensor: name: "SelecEM2M Maximum Demand Apparent Power" disabled_by_default: true + - id: battery_voltage + name: "Battery voltage2" + platform: modbus_controller + modbus_controller_id: modbus_controller_test + address: 0x331A + register_type: read + value_type: U_WORD + - platform: t6615 uart_id: uart2 co2: From 0d0954d74b3609615a583730479bdaa62113613c Mon Sep 17 00:00:00 2001 From: "Sergey V. DUDANOV" Date: Mon, 27 Sep 2021 00:32:33 +0400 Subject: [PATCH 122/207] Midea fix (#2395) --- esphome/components/midea/climate.py | 2 +- platformio.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/midea/climate.py b/esphome/components/midea/climate.py index 0d0bdce471..08e82025b6 100644 --- a/esphome/components/midea/climate.py +++ b/esphome/components/midea/climate.py @@ -282,4 +282,4 @@ async def to_code(config): if CONF_HUMIDITY_SETPOINT in config: sens = await sensor.new_sensor(config[CONF_HUMIDITY_SETPOINT]) cg.add(var.set_humidity_setpoint_sensor(sens)) - cg.add_library("dudanov/MideaUART", "1.1.5") + cg.add_library("dudanov/MideaUART", "1.1.8") diff --git a/platformio.ini b/platformio.ini index 1901f175af..6e605768fc 100644 --- a/platformio.ini +++ b/platformio.ini @@ -48,7 +48,7 @@ lib_deps = seeed-studio/Grove - Laser PM2.5 Sensor HM3301@1.0.3 ; hm3301 glmnet/Dsmr@0.5 ; dsmr rweather/Crypto@0.2.0 ; dsmr - dudanov/MideaUART@1.1.0 ; midea + dudanov/MideaUART@1.1.8 ; midea build_flags = ${common.build_flags} -DUSE_ARDUINO From 4c390d9f9f323c7af7093d77392d55d5fd46f354 Mon Sep 17 00:00:00 2001 From: JonasEr <8407728+JonasEr@users.noreply.github.com> Date: Sun, 26 Sep 2021 23:38:08 +0300 Subject: [PATCH 123/207] Extend nfc ndef records with Text (#2191) Co-authored-by: Oxan van Leeuwen --- esphome/components/nfc/ndef_message.cpp | 50 +++++++------- esphome/components/nfc/ndef_message.h | 4 +- esphome/components/nfc/ndef_record.cpp | 52 +++++--------- esphome/components/nfc/ndef_record.h | 76 ++++----------------- esphome/components/nfc/ndef_record_text.cpp | 40 +++++++++++ esphome/components/nfc/ndef_record_text.h | 41 +++++++++++ esphome/components/nfc/ndef_record_uri.cpp | 48 +++++++++++++ esphome/components/nfc/ndef_record_uri.h | 76 +++++++++++++++++++++ 8 files changed, 264 insertions(+), 123 deletions(-) create mode 100644 esphome/components/nfc/ndef_record_text.cpp create mode 100644 esphome/components/nfc/ndef_record_text.h create mode 100644 esphome/components/nfc/ndef_record_uri.cpp create mode 100644 esphome/components/nfc/ndef_record_uri.h diff --git a/esphome/components/nfc/ndef_message.cpp b/esphome/components/nfc/ndef_message.cpp index b1554f41ae..d8c940254e 100644 --- a/esphome/components/nfc/ndef_message.cpp +++ b/esphome/components/nfc/ndef_message.cpp @@ -10,16 +10,13 @@ NdefMessage::NdefMessage(std::vector &data) { uint8_t index = 0; while (index <= data.size()) { uint8_t tnf_byte = data[index++]; - bool me = tnf_byte & 0x40; - bool sr = tnf_byte & 0x10; - bool il = tnf_byte & 0x08; - uint8_t tnf = tnf_byte & 0x07; + bool me = tnf_byte & 0x40; // Message End bit (is set if this is the last record of the message) + bool sr = tnf_byte & 0x10; // Short record bit (is set if payload size is less or equal to 255 bytes) + bool il = tnf_byte & 0x08; // ID length bit (is set if ID Length field exists) + uint8_t tnf = tnf_byte & 0x07; // Type Name Format ESP_LOGVV(TAG, "me=%s, sr=%s, il=%s, tnf=%d", YESNO(me), YESNO(sr), YESNO(il), tnf); - auto record = make_unique(); - record->set_tnf(tnf); - uint8_t type_length = data[index++]; uint32_t payload_length = 0; if (sr) { @@ -38,28 +35,34 @@ NdefMessage::NdefMessage(std::vector &data) { ESP_LOGVV(TAG, "Lengths: type=%d, payload=%d, id=%d", type_length, payload_length, id_length); std::string type_str(data.begin() + index, data.begin() + index + type_length); - record->set_type(type_str); + index += type_length; + std::string id_str = ""; if (il) { - std::string id_str(data.begin() + index, data.begin() + index + id_length); - record->set_id(id_str); + id_str = std::string(data.begin() + index, data.begin() + index + id_length); index += id_length; } - uint8_t payload_identifier = 0x00; - if (type_str == "U") { - payload_identifier = data[index++]; - payload_length -= 1; + std::vector payload_data(data.begin() + index, data.begin() + index + payload_length); + + std::unique_ptr record; + + // Based on tnf and type, create a more specific NdefRecord object + // constructed from the payload data + if (tnf == TNF_WELL_KNOWN && type_str == "U") { + record = make_unique(payload_data); + } else if (tnf == TNF_WELL_KNOWN && type_str == "T") { + record = make_unique(payload_data); + } else { + // Could not recognize the record, so store as generic one. + record = make_unique(payload_data); + record->set_tnf(tnf); + record->set_type(type_str); } - std::string payload_str(data.begin() + index, data.begin() + index + payload_length); + record->set_id(id_str); - if (payload_identifier > 0x00 && payload_identifier <= PAYLOAD_IDENTIFIERS_COUNT) { - payload_str.insert(0, PAYLOAD_IDENTIFIERS[payload_identifier]); - } - - record->set_payload(payload_str); index += payload_length; ESP_LOGV(TAG, "Adding record type %s = %s", record->get_type().c_str(), record->get_payload().c_str()); @@ -82,13 +85,10 @@ bool NdefMessage::add_record(std::unique_ptr record) { bool NdefMessage::add_text_record(const std::string &text) { return this->add_text_record(text, "en"); }; bool NdefMessage::add_text_record(const std::string &text, const std::string &encoding) { - std::string payload = to_string(text.length()) + encoding + text; - return this->add_record(make_unique(TNF_WELL_KNOWN, "T", payload)); + return this->add_record(make_unique(encoding, text)); } -bool NdefMessage::add_uri_record(const std::string &uri) { - return this->add_record(make_unique(TNF_WELL_KNOWN, "U", uri)); -} +bool NdefMessage::add_uri_record(const std::string &uri) { return this->add_record(make_unique(uri)); } std::vector NdefMessage::encode() { std::vector data; diff --git a/esphome/components/nfc/ndef_message.h b/esphome/components/nfc/ndef_message.h index 20140e8c1a..5e44a06011 100644 --- a/esphome/components/nfc/ndef_message.h +++ b/esphome/components/nfc/ndef_message.h @@ -5,6 +5,8 @@ #include "esphome/core/helpers.h" #include "esphome/core/log.h" #include "ndef_record.h" +#include "ndef_record_text.h" +#include "ndef_record_uri.h" namespace esphome { namespace nfc { @@ -18,7 +20,7 @@ class NdefMessage { NdefMessage(const NdefMessage &msg) { records_.reserve(msg.records_.size()); for (const auto &r : msg.records_) { - records_.emplace_back(make_unique(*r)); + records_.emplace_back(r->clone()); } } diff --git a/esphome/components/nfc/ndef_record.cpp b/esphome/components/nfc/ndef_record.cpp index a75f5978ec..8a3a7d375d 100644 --- a/esphome/components/nfc/ndef_record.cpp +++ b/esphome/components/nfc/ndef_record.cpp @@ -5,40 +5,22 @@ namespace nfc { static const char *const TAG = "nfc.ndef_record"; -uint32_t NdefRecord::get_encoded_size() { - uint32_t size = 2; - if (this->payload_.length() > 255) { - size += 4; - } else { - size += 1; - } - if (this->id_.length()) { - size += 1; - } - size += (this->type_.length() + this->payload_.length() + this->id_.length()); - return size; +NdefRecord::NdefRecord(std::vector payload_data) { + this->payload_ = std::string(payload_data.begin(), payload_data.end()); } std::vector NdefRecord::encode(bool first, bool last) { std::vector data; - data.push_back(this->get_tnf_byte(first, last)); + // Get encoded payload, this is overriden by more specific record classes + std::vector payload_data = get_encoded_payload(); + + size_t payload_length = payload_data.size(); + + data.push_back(this->create_flag_byte(first, last, payload_length)); data.push_back(this->type_.length()); - uint8_t payload_prefix = 0x00; - uint8_t payload_prefix_length = 0x00; - for (uint8_t i = 1; i < PAYLOAD_IDENTIFIERS_COUNT; i++) { - std::string prefix = PAYLOAD_IDENTIFIERS[i]; - if (this->payload_.substr(0, prefix.length()).find(prefix) != std::string::npos) { - payload_prefix = i; - payload_prefix_length = prefix.length(); - break; - } - } - - uint32_t payload_length = this->payload_.length() - payload_prefix_length + 1; - if (payload_length <= 255) { data.push_back(payload_length); } else { @@ -58,25 +40,23 @@ std::vector NdefRecord::encode(bool first, bool last) { data.insert(data.end(), this->id_.begin(), this->id_.end()); } - data.push_back(payload_prefix); - - data.insert(data.end(), this->payload_.begin() + payload_prefix_length, this->payload_.end()); + data.insert(data.end(), payload_data.begin(), payload_data.end()); return data; } -uint8_t NdefRecord::get_tnf_byte(bool first, bool last) { - uint8_t value = this->tnf_; +uint8_t NdefRecord::create_flag_byte(bool first, bool last, size_t payload_size) { + uint8_t value = this->tnf_ & 0b00000111; if (first) { - value = value | 0x80; + value = value | 0x80; // Set MB bit } if (last) { - value = value | 0x40; + value = value | 0x40; // Set ME bit } - if (this->payload_.length() <= 255) { - value = value | 0x10; + if (payload_size <= 255) { + value = value | 0x10; // Set SR bit } if (this->id_.length()) { - value = value | 0x08; + value = value | 0x08; // Set IL bit } return value; }; diff --git a/esphome/components/nfc/ndef_record.h b/esphome/components/nfc/ndef_record.h index 680fe20986..4fab1c03e4 100644 --- a/esphome/components/nfc/ndef_record.h +++ b/esphome/components/nfc/ndef_record.h @@ -15,86 +15,40 @@ static const uint8_t TNF_UNKNOWN = 0x05; static const uint8_t TNF_UNCHANGED = 0x06; static const uint8_t TNF_RESERVED = 0x07; -static const uint8_t PAYLOAD_IDENTIFIERS_COUNT = 0x23; -static const char *const PAYLOAD_IDENTIFIERS[] = {"", - "http://www.", - "https://www.", - "http://", - "https://", - "tel:", - "mailto:", - "ftp://anonymous:anonymous@", - "ftp://ftp.", - "ftps://", - "sftp://", - "smb://", - "nfs://", - "ftp://", - "dav://", - "news:", - "telnet://", - "imap:", - "rtsp://", - "urn:", - "pop:", - "sip:", - "sips:", - "tftp:", - "btspp://", - "btl2cap://", - "btgoep://", - "tcpobex://", - "irdaobex://", - "file://", - "urn:epc:id:", - "urn:epc:tag:", - "urn:epc:pat:", - "urn:epc:raw:", - "urn:epc:", - "urn:nfc:"}; - class NdefRecord { public: NdefRecord(){}; - NdefRecord(uint8_t tnf, const std::string &type, const std::string &payload) { - this->tnf_ = tnf; - this->type_ = type; - this->set_payload(payload); - }; - NdefRecord(uint8_t tnf, const std::string &type, const std::string &payload, const std::string &id) { - this->tnf_ = tnf; - this->type_ = type; - this->set_payload(payload); - this->id_ = id; - }; - NdefRecord(const NdefRecord &rhs) { - this->tnf_ = rhs.tnf_; - this->type_ = rhs.type_; - this->payload_ = rhs.payload_; - this->payload_identifier_ = rhs.payload_identifier_; - this->id_ = rhs.id_; - }; + NdefRecord(std::vector payload_data); void set_tnf(uint8_t tnf) { this->tnf_ = tnf; }; void set_type(const std::string &type) { this->type_ = type; }; - void set_payload_identifier(uint8_t payload_identifier) { this->payload_identifier_ = payload_identifier; }; void set_payload(const std::string &payload) { this->payload_ = payload; }; void set_id(const std::string &id) { this->id_ = id; }; + NdefRecord(const NdefRecord &) = default; + virtual ~NdefRecord() {} + virtual std::unique_ptr clone() const { // To allow copying polymorphic classes + return make_unique(*this); + }; uint32_t get_encoded_size(); std::vector encode(bool first, bool last); - uint8_t get_tnf_byte(bool first, bool last); + + uint8_t create_flag_byte(bool first, bool last, size_t payload_size); const std::string &get_type() const { return this->type_; }; const std::string &get_id() const { return this->id_; }; - const std::string &get_payload() const { return this->payload_; }; + virtual const std::string &get_payload() const { return this->payload_; }; + + virtual std::vector get_encoded_payload() { + std::vector empty_payload; + return empty_payload; + }; protected: uint8_t tnf_; std::string type_; - uint8_t payload_identifier_; - std::string payload_; std::string id_; + std::string payload_; }; } // namespace nfc diff --git a/esphome/components/nfc/ndef_record_text.cpp b/esphome/components/nfc/ndef_record_text.cpp new file mode 100644 index 0000000000..80b0108b46 --- /dev/null +++ b/esphome/components/nfc/ndef_record_text.cpp @@ -0,0 +1,40 @@ +#include "ndef_record_text.h" +#include "ndef_record.h" + +namespace esphome { +namespace nfc { + +static const char *const TAG = "nfc.ndef_record_text"; + +NdefRecordText::NdefRecordText(const std::vector &payload) { + if (payload.empty()) { + ESP_LOGE(TAG, "Record payload too short"); + return; + } + + uint8_t language_code_length = payload[0] & 0b00111111; // Todo, make use of encoding bit? + + this->language_code_ = std::string(payload.begin() + 1, payload.begin() + 1 + language_code_length); + + this->text_ = std::string(payload.begin() + 1 + language_code_length, payload.end()); + + this->tnf_ = TNF_WELL_KNOWN; + + this->type_ = "T"; +} + +std::vector NdefRecordText::get_encoded_payload() { + std::vector data; + + uint8_t flag_byte = this->language_code_.length() & 0b00111111; // UTF8 assumed + + data.push_back(flag_byte); + + data.insert(data.end(), this->language_code_.begin(), this->language_code_.end()); + + data.insert(data.end(), this->text_.begin(), this->text_.end()); + return data; +} + +} // namespace nfc +} // namespace esphome diff --git a/esphome/components/nfc/ndef_record_text.h b/esphome/components/nfc/ndef_record_text.h new file mode 100644 index 0000000000..94375cc860 --- /dev/null +++ b/esphome/components/nfc/ndef_record_text.h @@ -0,0 +1,41 @@ +#pragma once + +#include "esphome/core/log.h" +#include "esphome/core/helpers.h" +#include "ndef_record.h" + +namespace esphome { +namespace nfc { + +class NdefRecordText : public NdefRecord { + public: + NdefRecordText(){}; + NdefRecordText(const std::vector &payload); + NdefRecordText(const std::string &language_code, const std::string &text) { + this->tnf_ = TNF_WELL_KNOWN; + this->type_ = "T"; + this->language_code_ = language_code; + this->text_ = text; + }; + NdefRecordText(const std::string &language_code, const std::string &text, const std::string &id) { + this->tnf_ = TNF_WELL_KNOWN; + this->type_ = "T"; + this->language_code_ = language_code; + this->text_ = text; + this->id_ = id; + }; + NdefRecordText(const NdefRecordText &) = default; + + std::unique_ptr clone() const override { return make_unique(*this); }; + + std::vector get_encoded_payload() override; + + const std::string &get_payload() const override { return this->text_; }; + + protected: + std::string text_; + std::string language_code_; +}; + +} // namespace nfc +} // namespace esphome diff --git a/esphome/components/nfc/ndef_record_uri.cpp b/esphome/components/nfc/ndef_record_uri.cpp new file mode 100644 index 0000000000..8fd043a1de --- /dev/null +++ b/esphome/components/nfc/ndef_record_uri.cpp @@ -0,0 +1,48 @@ +#include "ndef_record_uri.h" + +namespace esphome { +namespace nfc { + +static const char *const TAG = "nfc.ndef_record_uri"; + +NdefRecordUri::NdefRecordUri(const std::vector &payload) { + if (payload.empty()) { + ESP_LOGE(TAG, "Record payload too short"); + return; + } + + uint8_t payload_identifier = payload[0]; // First byte of payload is prefix code + + std::string uri(payload.begin() + 1, payload.end()); + + if (payload_identifier > 0x00 && payload_identifier <= PAYLOAD_IDENTIFIERS_COUNT) { + uri.insert(0, PAYLOAD_IDENTIFIERS[payload_identifier]); + } + + this->tnf_ = TNF_WELL_KNOWN; + this->type_ = "U"; + this->set_URI(uri); +} + +std::vector NdefRecordUri::get_encoded_payload() { + std::vector data; + + uint8_t payload_prefix = 0x00; + uint8_t payload_prefix_length = 0x00; + for (uint8_t i = 1; i < PAYLOAD_IDENTIFIERS_COUNT; i++) { + std::string prefix = PAYLOAD_IDENTIFIERS[i]; + if (this->URI_.substr(0, prefix.length()).find(prefix) != std::string::npos) { + payload_prefix = i; + payload_prefix_length = prefix.length(); + break; + } + } + + data.push_back(payload_prefix); + + data.insert(data.end(), this->URI_.begin() + payload_prefix_length, this->URI_.end()); + return data; +} + +} // namespace nfc +} // namespace esphome diff --git a/esphome/components/nfc/ndef_record_uri.h b/esphome/components/nfc/ndef_record_uri.h new file mode 100644 index 0000000000..75f9e19ee0 --- /dev/null +++ b/esphome/components/nfc/ndef_record_uri.h @@ -0,0 +1,76 @@ +#pragma once + +#include "esphome/core/log.h" +#include "esphome/core/helpers.h" +#include "ndef_record.h" + +namespace esphome { +namespace nfc { + +static const uint8_t PAYLOAD_IDENTIFIERS_COUNT = 0x23; +static const char *const PAYLOAD_IDENTIFIERS[] = {"", + "http://www.", + "https://www.", + "http://", + "https://", + "tel:", + "mailto:", + "ftp://anonymous:anonymous@", + "ftp://ftp.", + "ftps://", + "sftp://", + "smb://", + "nfs://", + "ftp://", + "dav://", + "news:", + "telnet://", + "imap:", + "rtsp://", + "urn:", + "pop:", + "sip:", + "sips:", + "tftp:", + "btspp://", + "btl2cap://", + "btgoep://", + "tcpobex://", + "irdaobex://", + "file://", + "urn:epc:id:", + "urn:epc:tag:", + "urn:epc:pat:", + "urn:epc:raw:", + "urn:epc:", + "urn:nfc:"}; + +class NdefRecordUri : public NdefRecord { + public: + NdefRecordUri(){}; + NdefRecordUri(const std::vector &payload); + NdefRecordUri(const std::string &URI) { + this->tnf_ = TNF_WELL_KNOWN; + this->type_ = "U"; + this->URI_ = URI; + }; + NdefRecordUri(const std::string &URI, const std::string &id) { + this->tnf_ = TNF_WELL_KNOWN; + this->type_ = "U"; + this->URI_ = URI; + this->id_ = id; + }; + NdefRecordUri(const NdefRecordUri &) = default; + std::unique_ptr clone() const override { return make_unique(*this); }; + + void set_URI(const std::string &URI) { this->URI_ = URI; }; + + std::vector get_encoded_payload() override; + const std::string &get_payload() const override { return this->URI_; }; + + protected: + std::string URI_; +}; + +} // namespace nfc +} // namespace esphome From 756c6721e9372667df7466570e170d59b69b1b11 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 27 Sep 2021 11:11:27 +1300 Subject: [PATCH 124/207] Fix NDEF URI casing (#2397) --- esphome/components/nfc/ndef_record_uri.cpp | 6 +++--- esphome/components/nfc/ndef_record_uri.h | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/esphome/components/nfc/ndef_record_uri.cpp b/esphome/components/nfc/ndef_record_uri.cpp index 8fd043a1de..9064f04f29 100644 --- a/esphome/components/nfc/ndef_record_uri.cpp +++ b/esphome/components/nfc/ndef_record_uri.cpp @@ -21,7 +21,7 @@ NdefRecordUri::NdefRecordUri(const std::vector &payload) { this->tnf_ = TNF_WELL_KNOWN; this->type_ = "U"; - this->set_URI(uri); + this->set_uri(uri); } std::vector NdefRecordUri::get_encoded_payload() { @@ -31,7 +31,7 @@ std::vector NdefRecordUri::get_encoded_payload() { uint8_t payload_prefix_length = 0x00; for (uint8_t i = 1; i < PAYLOAD_IDENTIFIERS_COUNT; i++) { std::string prefix = PAYLOAD_IDENTIFIERS[i]; - if (this->URI_.substr(0, prefix.length()).find(prefix) != std::string::npos) { + if (this->uri_.substr(0, prefix.length()).find(prefix) != std::string::npos) { payload_prefix = i; payload_prefix_length = prefix.length(); break; @@ -40,7 +40,7 @@ std::vector NdefRecordUri::get_encoded_payload() { data.push_back(payload_prefix); - data.insert(data.end(), this->URI_.begin() + payload_prefix_length, this->URI_.end()); + data.insert(data.end(), this->uri_.begin() + payload_prefix_length, this->uri_.end()); return data; } diff --git a/esphome/components/nfc/ndef_record_uri.h b/esphome/components/nfc/ndef_record_uri.h index 75f9e19ee0..4c21724c5c 100644 --- a/esphome/components/nfc/ndef_record_uri.h +++ b/esphome/components/nfc/ndef_record_uri.h @@ -49,27 +49,27 @@ class NdefRecordUri : public NdefRecord { public: NdefRecordUri(){}; NdefRecordUri(const std::vector &payload); - NdefRecordUri(const std::string &URI) { + NdefRecordUri(const std::string &uri) { this->tnf_ = TNF_WELL_KNOWN; this->type_ = "U"; - this->URI_ = URI; + this->uri_ = uri; }; - NdefRecordUri(const std::string &URI, const std::string &id) { + NdefRecordUri(const std::string &uri, const std::string &id) { this->tnf_ = TNF_WELL_KNOWN; this->type_ = "U"; - this->URI_ = URI; + this->uri_ = uri; this->id_ = id; }; NdefRecordUri(const NdefRecordUri &) = default; std::unique_ptr clone() const override { return make_unique(*this); }; - void set_URI(const std::string &URI) { this->URI_ = URI; }; + void set_uri(const std::string &uri) { this->uri_ = uri; }; std::vector get_encoded_payload() override; - const std::string &get_payload() const override { return this->URI_; }; + const std::string &get_payload() const override { return this->uri_; }; protected: - std::string URI_; + std::string uri_; }; } // namespace nfc From 97e76d64d60d2d46a4bd4a4b50d11c496d596138 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 27 Sep 2021 11:40:28 +0200 Subject: [PATCH 125/207] Re-enable TCP nodelay for ESP32 (#2390) --- esphome/components/api/api_frame_helper.cpp | 15 +++++++++++ .../components/socket/lwip_raw_tcp_impl.cpp | 27 ++++++++++--------- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/esphome/components/api/api_frame_helper.cpp b/esphome/components/api/api_frame_helper.cpp index 00f28457ae..4971272f41 100644 --- a/esphome/components/api/api_frame_helper.cpp +++ b/esphome/components/api/api_frame_helper.cpp @@ -127,6 +127,14 @@ APIError APINoiseFrameHelper::init() { return APIError::TCP_NONBLOCKING_FAILED; } + int enable = 1; + err = socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int)); + if (err != 0) { + state_ = State::FAILED; + HELPER_LOG("Setting nodelay failed with errno %d", errno); + return APIError::TCP_NODELAY_FAILED; + } + // init prologue prologue_.insert(prologue_.end(), PROLOGUE_INIT, PROLOGUE_INIT + strlen(PROLOGUE_INIT)); @@ -722,6 +730,13 @@ APIError APIPlaintextFrameHelper::init() { HELPER_LOG("Setting nonblocking failed with errno %d", errno); return APIError::TCP_NONBLOCKING_FAILED; } + int enable = 1; + err = socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int)); + if (err != 0) { + state_ = State::FAILED; + HELPER_LOG("Setting nodelay failed with errno %d", errno); + return APIError::TCP_NODELAY_FAILED; + } state_ = State::DATA; return APIError::OK; diff --git a/esphome/components/socket/lwip_raw_tcp_impl.cpp b/esphome/components/socket/lwip_raw_tcp_impl.cpp index 7521d1339f..54dfddac3f 100644 --- a/esphome/components/socket/lwip_raw_tcp_impl.cpp +++ b/esphome/components/socket/lwip_raw_tcp_impl.cpp @@ -257,7 +257,7 @@ class LWIPRawImpl : public Socket { errno = EINVAL; return -1; } - *reinterpret_cast(optval) = tcp_nagle_disabled(pcb_); + *reinterpret_cast(optval) = nodelay_; *optlen = 4; return 0; } @@ -286,11 +286,7 @@ class LWIPRawImpl : public Socket { return -1; } int val = *reinterpret_cast(optval); - if (val != 0) { - tcp_nagle_disable(pcb_); - } else { - tcp_nagle_enable(pcb_); - } + nodelay_ = val; return 0; } @@ -444,9 +440,11 @@ class LWIPRawImpl : public Socket { if (written == 0) // no need to output if nothing written return 0; - int err = internal_output(); - if (err == -1) - return -1; + if (nodelay_) { + int err = internal_output(); + if (err == -1) + return -1; + } return written; } ssize_t writev(const struct iovec *iov, int iovcnt) override { @@ -466,9 +464,11 @@ class LWIPRawImpl : public Socket { if (written == 0) // no need to output if nothing written return 0; - int err = internal_output(); - if (err == -1) - return -1; + if (nodelay_) { + int err = internal_output(); + if (err == -1) + return -1; + } return written; } int setblocking(bool blocking) override { @@ -550,6 +550,9 @@ class LWIPRawImpl : public Socket { bool rx_closed_ = false; pbuf *rx_buf_ = nullptr; size_t rx_buf_offset_ = 0; + // don't use lwip nodelay flag, it sometimes causes reconnect + // instead use it for determining whether to call lwip_output + bool nodelay_ = false; }; std::unique_ptr socket(int domain, int type, int protocol) { From 45940b051439cb0053889597643d3a5a62ab73ad Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 27 Sep 2021 19:10:53 +0200 Subject: [PATCH 126/207] Dashboard node import and render in browser (#2374) --- CODEOWNERS | 1 + .../components/dashboard_import/__init__.py | 45 ++++++++ .../dashboard_import/dashboard_import.cpp | 12 ++ .../dashboard_import/dashboard_import.h | 12 ++ esphome/components/mdns/mdns_component.cpp | 8 ++ esphome/core/defines.h | 2 + esphome/dashboard/dashboard.py | 104 ++++++++++++++---- esphome/zeroconf.py | 81 +++++++++++++- 8 files changed, 237 insertions(+), 28 deletions(-) create mode 100644 esphome/components/dashboard_import/__init__.py create mode 100644 esphome/components/dashboard_import/dashboard_import.cpp create mode 100644 esphome/components/dashboard_import/dashboard_import.h diff --git a/CODEOWNERS b/CODEOWNERS index 17825a9205..0b974b95e6 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -40,6 +40,7 @@ esphome/components/cover/* @esphome/core esphome/components/cs5460a/* @balrog-kun esphome/components/ct_clamp/* @jesserockz esphome/components/daly_bms/* @s1lvi0 +esphome/components/dashboard_import/* @esphome/core esphome/components/debug/* @OttoWinter esphome/components/dfplayer/* @glmnet esphome/components/dht/* @OttoWinter diff --git a/esphome/components/dashboard_import/__init__.py b/esphome/components/dashboard_import/__init__.py new file mode 100644 index 0000000000..2b884d3b9a --- /dev/null +++ b/esphome/components/dashboard_import/__init__.py @@ -0,0 +1,45 @@ +from pathlib import Path + +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components.packages import validate_source_shorthand +from esphome.yaml_util import dump + + +dashboard_import_ns = cg.esphome_ns.namespace("dashboard_import") + +# payload is in `esphomelib` mdns record, which only exists if api +# is enabled +DEPENDENCIES = ["api"] +CODEOWNERS = ["@esphome/core"] + + +def validate_import_url(value): + value = cv.string_strict(value) + value = cv.Length(max=255)(value) + # ignore result, only check if it's a valid shorthand + validate_source_shorthand(value) + return value + + +CONF_PACKAGE_IMPORT_URL = "package_import_url" +CONFIG_SCHEMA = cv.Schema( + { + cv.Required(CONF_PACKAGE_IMPORT_URL): validate_import_url, + } +) + + +async def to_code(config): + cg.add_define("USE_DASHBOARD_IMPORT") + cg.add(dashboard_import_ns.set_package_import_url(config[CONF_PACKAGE_IMPORT_URL])) + + +def import_config(path: str, name: str, project_name: str, import_url: str) -> None: + p = Path(path) + + if p.exists(): + raise FileExistsError + + config = {"substitutions": {"name": name}, "packages": {project_name: import_url}} + p.write_text(dump(config), encoding="utf8") diff --git a/esphome/components/dashboard_import/dashboard_import.cpp b/esphome/components/dashboard_import/dashboard_import.cpp new file mode 100644 index 0000000000..6875fd61a5 --- /dev/null +++ b/esphome/components/dashboard_import/dashboard_import.cpp @@ -0,0 +1,12 @@ +#include "dashboard_import.h" + +namespace esphome { +namespace dashboard_import { + +static std::string g_package_import_url; // NOLINT + +std::string get_package_import_url() { return g_package_import_url; } +void set_package_import_url(std::string url) { g_package_import_url = std::move(url); } + +} // namespace dashboard_import +} // namespace esphome diff --git a/esphome/components/dashboard_import/dashboard_import.h b/esphome/components/dashboard_import/dashboard_import.h new file mode 100644 index 0000000000..0ca2994aab --- /dev/null +++ b/esphome/components/dashboard_import/dashboard_import.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace esphome { +namespace dashboard_import { + +std::string get_package_import_url(); +void set_package_import_url(std::string url); + +} // namespace dashboard_import +} // namespace esphome diff --git a/esphome/components/mdns/mdns_component.cpp b/esphome/components/mdns/mdns_component.cpp index c742fe3948..372d980eb0 100644 --- a/esphome/components/mdns/mdns_component.cpp +++ b/esphome/components/mdns/mdns_component.cpp @@ -6,6 +6,9 @@ #ifdef USE_API #include "esphome/components/api/api_server.h" #endif +#ifdef USE_DASHBOARD_IMPORT +#include "esphome/components/dashboard_import/dashboard_import.h" +#endif namespace esphome { namespace mdns { @@ -42,6 +45,11 @@ std::vector MDNSComponent::compile_services_() { service.txt_records.push_back({"project_name", ESPHOME_PROJECT_NAME}); service.txt_records.push_back({"project_version", ESPHOME_PROJECT_VERSION}); #endif // ESPHOME_PROJECT_NAME + +#ifdef USE_DASHBOARD_IMPORT + service.txt_records.push_back({"package_import_url", dashboard_import::get_package_import_url()}); +#endif + res.push_back(service); } #endif // USE_API diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 62468dfbfe..1c3b17d071 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -67,3 +67,5 @@ // Disabled feature flags //#define USE_BSEC // Requires a library with proprietary license. + +#define USE_DASHBOARD_IMPORT diff --git a/esphome/dashboard/dashboard.py b/esphome/dashboard/dashboard.py index bfe6808729..492b86384d 100644 --- a/esphome/dashboard/dashboard.py +++ b/esphome/dashboard/dashboard.py @@ -41,7 +41,7 @@ from .util import password_hash # pylint: disable=unused-import, wrong-import-order from typing import Optional # noqa -from esphome.zeroconf import DashboardStatus, EsphomeZeroconf +from esphome.zeroconf import DashboardImportDiscovery, DashboardStatus, EsphomeZeroconf _LOGGER = logging.getLogger(__name__) @@ -154,9 +154,6 @@ def is_authenticated(request_handler): def bind_config(func): def decorator(self, *args, **kwargs): configuration = self.get_argument("configuration") - if not is_allowed(configuration): - self.set_status(500) - return None kwargs = kwargs.copy() kwargs["configuration"] = configuration return func(self, *args, **kwargs) @@ -363,8 +360,8 @@ class WizardRequestHandler(BaseHandler): from esphome import wizard kwargs = { - k: "".join(x.decode() for x in v) - for k, v in self.request.arguments.items() + k: v + for k, v in json.loads(self.request.body.decode()).items() if k in ("name", "platform", "board", "ssid", "psk", "password") } kwargs["ota_password"] = secrets.token_hex(16) @@ -374,6 +371,29 @@ class WizardRequestHandler(BaseHandler): self.finish() +class ImportRequestHandler(BaseHandler): + @authenticated + def post(self): + from esphome.components.dashboard_import import import_config + + args = json.loads(self.request.body.decode()) + try: + name = args["name"] + import_config( + settings.rel_path(f"{name}.yaml"), + name, + args["project_name"], + args["package_import_url"], + ) + except FileExistsError: + self.set_status(500) + self.write("File already exists") + return + + self.set_status(200) + self.finish() + + class DownloadBinaryRequestHandler(BaseHandler): @authenticated @bind_config @@ -469,15 +489,51 @@ class DashboardEntry: return self.storage.loaded_integrations +class ListDevicesHandler(BaseHandler): + @authenticated + def get(self): + entries = _list_dashboard_entries() + self.set_header("content-type", "application/json") + configured = {entry.name for entry in entries} + self.write( + json.dumps( + { + "configured": [ + { + "name": entry.name, + "configuration": entry.filename, + "loaded_integrations": entry.loaded_integrations, + "deployed_version": entry.update_old, + "current_version": entry.update_new, + "path": entry.path, + "comment": entry.comment, + "address": entry.address, + "target_platform": entry.target_platform, + } + for entry in entries + ], + "importable": [ + { + "name": res.device_name, + "package_import_url": res.package_import_url, + "project_name": res.project_name, + "project_version": res.project_version, + } + for res in IMPORT_RESULT.values() + if res.device_name not in configured + ], + } + ) + ) + + class MainRequestHandler(BaseHandler): @authenticated def get(self): begin = bool(self.get_argument("begin", False)) - entries = _list_dashboard_entries() self.render( get_template_path("index"), - entries=entries, begin=begin, **template_args(), login_enabled=settings.using_auth, @@ -495,6 +551,8 @@ def _ping_func(filename, address): class MDNSStatusThread(threading.Thread): def run(self): + global IMPORT_RESULT + zc = EsphomeZeroconf() def on_update(dat): @@ -502,17 +560,22 @@ class MDNSStatusThread(threading.Thread): PING_RESULT[key] = b stat = DashboardStatus(zc, on_update) + imports = DashboardImportDiscovery(zc) + stat.start() while not STOP_EVENT.is_set(): entries = _list_dashboard_entries() stat.request_query( {entry.filename: f"{entry.name}.local." for entry in entries} ) + IMPORT_RESULT = imports.import_state PING_REQUEST.wait() PING_REQUEST.clear() + stat.stop() stat.join() + imports.cancel() zc.close() @@ -567,10 +630,6 @@ class PingRequestHandler(BaseHandler): self.write(json.dumps(PING_RESULT)) -def is_allowed(configuration): - return os.path.sep not in configuration - - class InfoRequestHandler(BaseHandler): @authenticated @bind_config @@ -613,20 +672,18 @@ class DeleteRequestHandler(BaseHandler): def post(self, configuration=None): config_file = settings.rel_path(configuration) storage_path = ext_storage_path(settings.config_dir, configuration) - storage_json = StorageJSON.load(storage_path) - if storage_json is None: - self.set_status(500) - return - name = storage_json.name trash_path = trash_storage_path(settings.config_dir) mkdir_p(trash_path) shutil.move(config_file, os.path.join(trash_path, configuration)) - # Delete build folder (if exists) - build_folder = os.path.join(settings.config_dir, name) - if build_folder is not None: - shutil.rmtree(build_folder, os.path.join(trash_path, name)) + storage_json = StorageJSON.load(storage_path) + if storage_json is not None: + # Delete build folder (if exists) + name = storage_json.name + build_folder = os.path.join(settings.config_dir, name) + if build_folder is not None: + shutil.rmtree(build_folder, os.path.join(trash_path, name)) class UndoDeleteRequestHandler(BaseHandler): @@ -639,6 +696,7 @@ class UndoDeleteRequestHandler(BaseHandler): PING_RESULT = {} # type: dict +IMPORT_RESULT = {} STOP_EVENT = threading.Event() PING_REQUEST = threading.Event() @@ -808,8 +866,10 @@ def make_app(debug=get_bool_env(ENV_DEV)): (f"{rel}ping", PingRequestHandler), (f"{rel}delete", DeleteRequestHandler), (f"{rel}undo-delete", UndoDeleteRequestHandler), - (f"{rel}wizard.html", WizardRequestHandler), + (f"{rel}wizard", WizardRequestHandler), (f"{rel}static/(.*)", StaticFileHandler, {"path": get_static_path()}), + (f"{rel}devices", ListDevicesHandler), + (f"{rel}import", ImportRequestHandler), ], **app_settings, ) diff --git a/esphome/zeroconf.py b/esphome/zeroconf.py index e6853531f2..a19fc143ec 100644 --- a/esphome/zeroconf.py +++ b/esphome/zeroconf.py @@ -2,6 +2,8 @@ import socket import threading import time from typing import Dict, Optional +import logging +from dataclasses import dataclass from zeroconf import ( DNSAddress, @@ -10,11 +12,14 @@ from zeroconf import ( DNSQuestion, RecordUpdateListener, Zeroconf, + ServiceBrowser, ) +from zeroconf._services import ServiceStateChange _CLASS_IN = 1 _FLAGS_QR_QUERY = 0x0000 # query _TYPE_A = 1 +_LOGGER = logging.getLogger(__name__) class HostResolver(RecordUpdateListener): @@ -57,7 +62,7 @@ class HostResolver(RecordUpdateListener): return True -class DashboardStatus(RecordUpdateListener, threading.Thread): +class DashboardStatus(threading.Thread): PING_AFTER = 15 * 1000 # Send new mDNS request after 15 seconds OFFLINE_AFTER = PING_AFTER * 2 # Offline if no mDNS response after 30 seconds @@ -70,9 +75,6 @@ class DashboardStatus(RecordUpdateListener, threading.Thread): self.query_event = threading.Event() self.on_update = on_update - def update_record(self, zc: Zeroconf, now: float, record: DNSRecord) -> None: - pass - def request_query(self, hosts: Dict[str, str]) -> None: self.query_hosts = set(hosts.values()) self.key_to_host = hosts @@ -93,7 +95,6 @@ class DashboardStatus(RecordUpdateListener, threading.Thread): ) def run(self) -> None: - self.zc.add_listener(self, None) while not self.stop_event.is_set(): self.on_update( {key: self.host_status(host) for key, host in self.key_to_host.items()} @@ -110,7 +111,75 @@ class DashboardStatus(RecordUpdateListener, threading.Thread): self.zc.send(out) self.query_event.wait() self.query_event.clear() - self.zc.remove_listener(self) + + +ESPHOME_SERVICE_TYPE = "_esphomelib._tcp.local." +TXT_RECORD_PACKAGE_IMPORT_URL = b"package_import_url" +TXT_RECORD_PROJECT_NAME = b"project_name" +TXT_RECORD_PROJECT_VERSION = b"project_version" + + +@dataclass +class DiscoveredImport: + device_name: str + package_import_url: str + project_name: str + project_version: str + + +class DashboardImportDiscovery: + def __init__(self, zc: Zeroconf) -> None: + self.zc = zc + self.service_browser = ServiceBrowser( + self.zc, ESPHOME_SERVICE_TYPE, [self._on_update] + ) + self.import_state = {} + + def _on_update( + self, + zeroconf: Zeroconf, + service_type: str, + name: str, + state_change: ServiceStateChange, + ) -> None: + _LOGGER.debug( + "service_update: type=%s name=%s state_change=%s", + service_type, + name, + state_change, + ) + if service_type != ESPHOME_SERVICE_TYPE: + return + if state_change == ServiceStateChange.Removed: + self.import_state.pop(name, None) + + info = zeroconf.get_service_info(service_type, name) + _LOGGER.debug("-> resolved info: %s", info) + if info is None: + return + node_name = name[: -len(ESPHOME_SERVICE_TYPE) - 1] + required_keys = [ + TXT_RECORD_PACKAGE_IMPORT_URL, + TXT_RECORD_PROJECT_NAME, + TXT_RECORD_PROJECT_VERSION, + ] + if any(key not in info.properties for key in required_keys): + # Not a dashboard import device + return + + import_url = info.properties[TXT_RECORD_PACKAGE_IMPORT_URL].decode() + project_name = info.properties[TXT_RECORD_PROJECT_NAME].decode() + project_version = info.properties[TXT_RECORD_PROJECT_VERSION].decode() + + self.import_state[name] = DiscoveredImport( + device_name=node_name, + package_import_url=import_url, + project_name=project_name, + project_version=project_version, + ) + + def cancel(self) -> None: + self.service_browser.cancel() class EsphomeZeroconf(Zeroconf): From b2d516c70a5b2357cf0c7c7cf394269c631e6653 Mon Sep 17 00:00:00 2001 From: Christian Taedcke Date: Mon, 27 Sep 2021 21:53:05 +0200 Subject: [PATCH 127/207] ccs811: Skip reading data if it is not available (#2404) On bootup the ccs811 reports that no data is available. No error flag is set in that case. The current implementation ignores this, reads and publishes the invalid data, which is 0xFDFD for both tvoc and co2 in my case. This commit fixes this and does not read and publish invalid data. --- esphome/components/ccs811/ccs811.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/esphome/components/ccs811/ccs811.cpp b/esphome/components/ccs811/ccs811.cpp index 6bdb4739cf..11a66f5100 100644 --- a/esphome/components/ccs811/ccs811.cpp +++ b/esphome/components/ccs811/ccs811.cpp @@ -86,8 +86,11 @@ void CCS811Component::setup() { } } void CCS811Component::update() { - if (!this->status_has_data_()) + if (!this->status_has_data_()) { + ESP_LOGD(TAG, "Status indicates no data ready!"); this->status_set_warning(); + return; + } // page 12 - alg result data auto alg_data = this->read_bytes<4>(0x02); From 8c86a18dc6b3b8f62eed60f2310f638d2de313a5 Mon Sep 17 00:00:00 2001 From: Maurice Makaay Date: Mon, 27 Sep 2021 21:53:47 +0200 Subject: [PATCH 128/207] Add missing include for defines.h. (#2403) Co-authored-by: Maurice Makaay --- esphome/components/bme680_bsec/bme680_bsec.h | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/bme680_bsec/bme680_bsec.h b/esphome/components/bme680_bsec/bme680_bsec.h index 365aec725e..53bc5c3280 100644 --- a/esphome/components/bme680_bsec/bme680_bsec.h +++ b/esphome/components/bme680_bsec/bme680_bsec.h @@ -5,6 +5,7 @@ #include "esphome/components/text_sensor/text_sensor.h" #include "esphome/components/i2c/i2c.h" #include "esphome/core/preferences.h" +#include "esphome/core/defines.h" #include #ifdef USE_BSEC From 1ba560dc9e61ab3bbdc4d1ae35bcd98a0a6d1cdf Mon Sep 17 00:00:00 2001 From: irtimaled Date: Mon, 27 Sep 2021 12:54:51 -0700 Subject: [PATCH 129/207] fix: stop tuya light state getting reset (#2401) * fix: stop tuya light state getting reset * fix typo --- esphome/components/tuya/light/tuya_light.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/esphome/components/tuya/light/tuya_light.cpp b/esphome/components/tuya/light/tuya_light.cpp index 97f6de9bae..f75cc964aa 100644 --- a/esphome/components/tuya/light/tuya_light.cpp +++ b/esphome/components/tuya/light/tuya_light.cpp @@ -111,6 +111,11 @@ void TuyaLight::write_state(light::LightState *state) { state->current_values_as_brightness(&brightness); } + if (!state->current_values.is_on() && this->switch_id_.has_value()) { + parent_->set_boolean_datapoint_value(*this->switch_id_, false); + return; + } + if (brightness > 0.0f || !color_interlock_) { if (this->color_temperature_id_.has_value()) { uint32_t color_temp_int = static_cast(color_temperature * this->color_temperature_max_value_); @@ -138,7 +143,7 @@ void TuyaLight::write_state(light::LightState *state) { } if (this->switch_id_.has_value()) { - parent_->set_boolean_datapoint_value(*this->switch_id_, state->current_values.is_on()); + parent_->set_boolean_datapoint_value(*this->switch_id_, true); } } From e30f17f64f9d634984ed421f18dde71bae6d3884 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20P=C3=A9rez=20Ferro?= Date: Mon, 27 Sep 2021 22:22:45 +0200 Subject: [PATCH 130/207] Add Current based cover (#1439) * Adding first version of current_base cover. No Interlock yet. * simplifying code * Implementing malfunction protection * Adding test * Fixing too long lines * Fixing test sensor names * Adding missing id's in ade7953 tests * Adding code owners as requested * Fixing issue setting position when stop reached * Fixing issue setting position when stop reached * Black formatting * Fixing format issues * Fix for concurrent changes Co-authored-by: Oxan van Leeuwen --- CODEOWNERS | 1 + esphome/components/current_based/__init__.py | 1 + esphome/components/current_based/cover.py | 124 +++++++++ .../current_based/current_based_cover.cpp | 251 ++++++++++++++++++ .../current_based/current_based_cover.h | 95 +++++++ tests/test3.yaml | 28 ++ 6 files changed, 500 insertions(+) create mode 100644 esphome/components/current_based/__init__.py create mode 100644 esphome/components/current_based/cover.py create mode 100644 esphome/components/current_based/current_based_cover.cpp create mode 100644 esphome/components/current_based/current_based_cover.h diff --git a/CODEOWNERS b/CODEOWNERS index 0b974b95e6..3c3a1e04ee 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -39,6 +39,7 @@ esphome/components/coolix/* @glmnet esphome/components/cover/* @esphome/core esphome/components/cs5460a/* @balrog-kun esphome/components/ct_clamp/* @jesserockz +esphome/components/current_based/* @djwmarcx esphome/components/daly_bms/* @s1lvi0 esphome/components/dashboard_import/* @esphome/core esphome/components/debug/* @OttoWinter diff --git a/esphome/components/current_based/__init__.py b/esphome/components/current_based/__init__.py new file mode 100644 index 0000000000..e7f41154b7 --- /dev/null +++ b/esphome/components/current_based/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@djwmarcx"] diff --git a/esphome/components/current_based/cover.py b/esphome/components/current_based/cover.py new file mode 100644 index 0000000000..eb77a90aff --- /dev/null +++ b/esphome/components/current_based/cover.py @@ -0,0 +1,124 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import automation +from esphome.components import sensor, cover +from esphome.const import ( + CONF_CLOSE_ACTION, + CONF_CLOSE_DURATION, + CONF_ID, + CONF_OPEN_ACTION, + CONF_OPEN_DURATION, + CONF_STOP_ACTION, + CONF_MAX_DURATION, +) + + +CONF_OPEN_SENSOR = "open_sensor" +CONF_OPEN_MOVING_CURRENT_THRESHOLD = "open_moving_current_threshold" +CONF_OPEN_OBSTACLE_CURRENT_THRESHOLD = "open_obstacle_current_threshold" + +CONF_CLOSE_SENSOR = "close_sensor" +CONF_CLOSE_MOVING_CURRENT_THRESHOLD = "close_moving_current_threshold" +CONF_CLOSE_OBSTACLE_CURRENT_THRESHOLD = "close_obstacle_current_threshold" + +CONF_OBSTACLE_ROLLBACK = "obstacle_rollback" +CONF_MALFUNCTION_DETECTION = "malfunction_detection" +CONF_MALFUNCTION_ACTION = "malfunction_action" +CONF_START_SENSING_DELAY = "start_sensing_delay" + +current_based_ns = cg.esphome_ns.namespace("current_based") +CurrentBasedCover = current_based_ns.class_( + "CurrentBasedCover", cover.Cover, cg.Component +) + +CONFIG_SCHEMA = cover.COVER_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(CurrentBasedCover), + cv.Required(CONF_STOP_ACTION): automation.validate_automation(single=True), + cv.Required(CONF_OPEN_SENSOR): cv.use_id(sensor.Sensor), + cv.Required(CONF_OPEN_MOVING_CURRENT_THRESHOLD): cv.float_range( + min=0, min_included=False + ), + cv.Optional(CONF_OPEN_OBSTACLE_CURRENT_THRESHOLD): cv.float_range( + min=0, min_included=False + ), + cv.Required(CONF_OPEN_ACTION): automation.validate_automation(single=True), + cv.Required(CONF_OPEN_DURATION): cv.positive_time_period_milliseconds, + cv.Required(CONF_CLOSE_SENSOR): cv.use_id(sensor.Sensor), + cv.Required(CONF_CLOSE_MOVING_CURRENT_THRESHOLD): cv.float_range( + min=0, min_included=False + ), + cv.Optional(CONF_CLOSE_OBSTACLE_CURRENT_THRESHOLD): cv.float_range( + min=0, min_included=False + ), + cv.Required(CONF_CLOSE_ACTION): automation.validate_automation(single=True), + cv.Required(CONF_CLOSE_DURATION): cv.positive_time_period_milliseconds, + cv.Optional(CONF_OBSTACLE_ROLLBACK, default="10%"): cv.percentage, + cv.Optional(CONF_MAX_DURATION): cv.positive_time_period_milliseconds, + cv.Optional(CONF_MALFUNCTION_DETECTION, default=True): cv.boolean, + cv.Optional(CONF_MALFUNCTION_ACTION): automation.validate_automation( + single=True + ), + cv.Optional( + CONF_START_SENSING_DELAY, default="500ms" + ): cv.positive_time_period_milliseconds, + } +).extend(cv.COMPONENT_SCHEMA) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + yield cover.register_cover(var, config) + + yield automation.build_automation( + var.get_stop_trigger(), [], config[CONF_STOP_ACTION] + ) + + # OPEN + bin = yield cg.get_variable(config[CONF_OPEN_SENSOR]) + cg.add(var.set_open_sensor(bin)) + cg.add( + var.set_open_moving_current_threshold( + config[CONF_OPEN_MOVING_CURRENT_THRESHOLD] + ) + ) + if CONF_OPEN_OBSTACLE_CURRENT_THRESHOLD in config: + cg.add( + var.set_open_obstacle_current_threshold( + config[CONF_OPEN_OBSTACLE_CURRENT_THRESHOLD] + ) + ) + cg.add(var.set_open_duration(config[CONF_OPEN_DURATION])) + yield automation.build_automation( + var.get_open_trigger(), [], config[CONF_OPEN_ACTION] + ) + + # CLOSE + bin = yield cg.get_variable(config[CONF_CLOSE_SENSOR]) + cg.add(var.set_close_sensor(bin)) + cg.add( + var.set_close_moving_current_threshold( + config[CONF_CLOSE_MOVING_CURRENT_THRESHOLD] + ) + ) + if CONF_CLOSE_OBSTACLE_CURRENT_THRESHOLD in config: + cg.add( + var.set_close_obstacle_current_threshold( + config[CONF_CLOSE_OBSTACLE_CURRENT_THRESHOLD] + ) + ) + cg.add(var.set_close_duration(config[CONF_CLOSE_DURATION])) + yield automation.build_automation( + var.get_close_trigger(), [], config[CONF_CLOSE_ACTION] + ) + + cg.add(var.set_obstacle_rollback(config[CONF_OBSTACLE_ROLLBACK])) + if CONF_MAX_DURATION in config: + cg.add(var.set_max_duration(config[CONF_MAX_DURATION])) + cg.add(var.set_malfunction_detection(config[CONF_MALFUNCTION_DETECTION])) + if CONF_MALFUNCTION_ACTION in config: + yield automation.build_automation( + var.get_malfunction_trigger(), [], config[CONF_MALFUNCTION_ACTION] + ) + cg.add(var.set_start_sensing_delay(config[CONF_START_SENSING_DELAY])) diff --git a/esphome/components/current_based/current_based_cover.cpp b/esphome/components/current_based/current_based_cover.cpp new file mode 100644 index 0000000000..9f0a59377d --- /dev/null +++ b/esphome/components/current_based/current_based_cover.cpp @@ -0,0 +1,251 @@ +#include "current_based_cover.h" +#include "esphome/core/hal.h" +#include "esphome/core/log.h" +#include + +namespace esphome { +namespace current_based { + +static const char *const TAG = "current_based.cover"; + +using namespace esphome::cover; + +CoverTraits CurrentBasedCover::get_traits() { + auto traits = CoverTraits(); + traits.set_supports_position(true); + traits.set_is_assumed_state(false); + return traits; +} +void CurrentBasedCover::control(const CoverCall &call) { + if (call.get_stop()) { + this->direction_idle_(); + } + if (call.get_position().has_value()) { + auto pos = *call.get_position(); + if (pos == this->position) { + // already at target + } else { + auto op = pos < this->position ? COVER_OPERATION_CLOSING : COVER_OPERATION_OPENING; + this->target_position_ = pos; + this->start_direction_(op); + } + } +} +void CurrentBasedCover::setup() { + auto restore = this->restore_state_(); + if (restore.has_value()) { + restore->apply(this); + } else { + this->position = 0.5f; + } +} + +void CurrentBasedCover::loop() { + if (this->current_operation == COVER_OPERATION_IDLE) + return; + + const uint32_t now = millis(); + + if (this->current_operation == COVER_OPERATION_OPENING) { + if (this->malfunction_detection_ && this->is_closing_()) { // Malfunction + this->direction_idle_(); + this->malfunction_trigger_->trigger(); + ESP_LOGI(TAG, "'%s' - Malfunction detected during opening. Current flow detected in close circuit", + this->name_.c_str()); + } else if (this->is_opening_blocked_()) { // Blocked + ESP_LOGD(TAG, "'%s' - Obstacle detected during opening.", this->name_.c_str()); + this->direction_idle_(); + if (this->obstacle_rollback_ != 0) { + this->set_timeout("rollback", 300, [this]() { + ESP_LOGD(TAG, "'%s' - Rollback.", this->name_.c_str()); + this->target_position_ = clamp(this->position - this->obstacle_rollback_, 0.0F, 1.0F); + this->start_direction_(COVER_OPERATION_CLOSING); + }); + } + } else if (this->is_initial_delay_finished_() && !this->is_opening_()) { // End reached + auto dur = (now - this->start_dir_time_) / 1e3f; + ESP_LOGD(TAG, "'%s' - Open position reached. Took %.1fs.", this->name_.c_str(), dur); + this->direction_idle_(COVER_OPEN); + } + } else if (this->current_operation == COVER_OPERATION_CLOSING) { + if (this->malfunction_detection_ && this->is_opening_()) { // Malfunction + this->direction_idle_(); + this->malfunction_trigger_->trigger(); + ESP_LOGI(TAG, "'%s' - Malfunction detected during closing. Current flow detected in open circuit", + this->name_.c_str()); + } else if (this->is_closing_blocked_()) { // Blocked + ESP_LOGD(TAG, "'%s' - Obstacle detected during closing.", this->name_.c_str()); + this->direction_idle_(); + if (this->obstacle_rollback_ != 0) { + this->set_timeout("rollback", 300, [this]() { + ESP_LOGD(TAG, "'%s' - Rollback.", this->name_.c_str()); + this->target_position_ = clamp(this->position + this->obstacle_rollback_, 0.0F, 1.0F); + this->start_direction_(COVER_OPERATION_OPENING); + }); + } + } else if (this->is_initial_delay_finished_() && !this->is_closing_()) { // End reached + auto dur = (now - this->start_dir_time_) / 1e3f; + ESP_LOGD(TAG, "'%s' - Close position reached. Took %.1fs.", this->name_.c_str(), dur); + this->direction_idle_(COVER_CLOSED); + } + } else if (now - this->start_dir_time_ > this->max_duration_) { + ESP_LOGD(TAG, "'%s' - Max duration reached. Stopping cover.", this->name_.c_str()); + this->direction_idle_(); + } + + // Recompute position every loop cycle + this->recompute_position_(); + + if (this->current_operation != COVER_OPERATION_IDLE && this->is_at_target_()) { + this->direction_idle_(); + } + + // Send current position every second + if (this->current_operation != COVER_OPERATION_IDLE && now - this->last_publish_time_ > 1000) { + this->publish_state(false); + this->last_publish_time_ = now; + } +} + +void CurrentBasedCover::direction_idle_(float new_position) { + this->start_direction_(COVER_OPERATION_IDLE); + if (new_position != FLT_MAX) { + this->position = new_position; + } + this->publish_state(); +} + +void CurrentBasedCover::dump_config() { + LOG_COVER("", "Endstop Cover", this); + LOG_SENSOR(" ", "Open Sensor", this->open_sensor_); + ESP_LOGCONFIG(TAG, " Open moving current threshold: %.11fA", this->open_moving_current_threshold_); + if (this->open_obstacle_current_threshold_ != FLT_MAX) { + ESP_LOGCONFIG(TAG, " Open obstacle current threshold: %.11fA", this->open_obstacle_current_threshold_); + } + ESP_LOGCONFIG(TAG, " Open Duration: %.1fs", this->open_duration_ / 1e3f); + LOG_SENSOR(" ", "Close Sensor", this->close_sensor_); + ESP_LOGCONFIG(TAG, " Close moving current threshold: %.11fA", this->close_moving_current_threshold_); + if (this->close_obstacle_current_threshold_ != FLT_MAX) { + ESP_LOGCONFIG(TAG, " Close obstacle current threshold: %.11fA", this->close_obstacle_current_threshold_); + } + ESP_LOGCONFIG(TAG, " Close Duration: %.1fs", this->close_duration_ / 1e3f); + ESP_LOGCONFIG(TAG, "Obstacle Rollback: %.1f%%", this->obstacle_rollback_ * 100); + if (this->max_duration_ != UINT32_MAX) { + ESP_LOGCONFIG(TAG, "Maximun duration: %.1fs", this->max_duration_ / 1e3f); + } + ESP_LOGCONFIG(TAG, "Start sensing delay: %.1fs", this->start_sensing_delay_ / 1e3f); + ESP_LOGCONFIG(TAG, "Malfunction detection: %s", YESNO(this->malfunction_detection_)); +} + +float CurrentBasedCover::get_setup_priority() const { return setup_priority::DATA; } +void CurrentBasedCover::stop_prev_trigger_() { + if (this->prev_command_trigger_ != nullptr) { + this->prev_command_trigger_->stop_action(); + this->prev_command_trigger_ = nullptr; + } +} + +bool CurrentBasedCover::is_opening_() const { + return this->open_sensor_->get_state() > this->open_moving_current_threshold_; +} + +bool CurrentBasedCover::is_opening_blocked_() const { + if (this->open_obstacle_current_threshold_ == FLT_MAX) { + return false; + } + return this->open_sensor_->get_state() > this->open_obstacle_current_threshold_; +} + +bool CurrentBasedCover::is_closing_() const { + return this->close_sensor_->get_state() > this->close_moving_current_threshold_; +} + +bool CurrentBasedCover::is_closing_blocked_() const { + if (this->close_obstacle_current_threshold_ == FLT_MAX) { + return false; + } + return this->open_sensor_->get_state() > this->open_obstacle_current_threshold_; +} +bool CurrentBasedCover::is_initial_delay_finished_() const { + return millis() - this->start_dir_time_ > this->start_sensing_delay_; +} + +bool CurrentBasedCover::is_at_target_() const { + switch (this->current_operation) { + case COVER_OPERATION_OPENING: + if (this->target_position_ == COVER_OPEN) { + if (!this->is_initial_delay_finished_()) // During initial delay, state is assumed + return false; + return !this->is_opening_(); + } + return this->position >= this->target_position_; + case COVER_OPERATION_CLOSING: + if (this->target_position_ == COVER_CLOSED) { + if (!this->is_initial_delay_finished_()) // During initial delay, state is assumed + return false; + return !this->is_closing_(); + } + return this->position <= this->target_position_; + case COVER_OPERATION_IDLE: + default: + return true; + } +} +void CurrentBasedCover::start_direction_(CoverOperation dir) { + if (dir == this->current_operation) + return; + + this->recompute_position_(); + Trigger<> *trig; + switch (dir) { + case COVER_OPERATION_IDLE: + trig = this->stop_trigger_; + break; + case COVER_OPERATION_OPENING: + trig = this->open_trigger_; + break; + case COVER_OPERATION_CLOSING: + trig = this->close_trigger_; + break; + default: + return; + } + + this->current_operation = dir; + + this->stop_prev_trigger_(); + trig->trigger(); + this->prev_command_trigger_ = trig; + + const auto now = millis(); + this->start_dir_time_ = now; + this->last_recompute_time_ = now; +} +void CurrentBasedCover::recompute_position_() { + if (this->current_operation == COVER_OPERATION_IDLE) + return; + + float dir; + float action_dur; + switch (this->current_operation) { + case COVER_OPERATION_OPENING: + dir = 1.0F; + action_dur = this->open_duration_; + break; + case COVER_OPERATION_CLOSING: + dir = -1.0F; + action_dur = this->close_duration_; + break; + default: + return; + } + + const auto now = millis(); + this->position += dir * (now - this->last_recompute_time_) / action_dur; + this->position = clamp(this->position, 0.0F, 1.0F); + + this->last_recompute_time_ = now; +} + +} // namespace current_based +} // namespace esphome diff --git a/esphome/components/current_based/current_based_cover.h b/esphome/components/current_based/current_based_cover.h new file mode 100644 index 0000000000..220b770c05 --- /dev/null +++ b/esphome/components/current_based/current_based_cover.h @@ -0,0 +1,95 @@ +#pragma once + +#include "esphome/components/cover/cover.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/core/automation.h" +#include "esphome/core/component.h" +#include + +namespace esphome { +namespace current_based { + +class CurrentBasedCover : public cover::Cover, public Component { + public: + void setup() override; + void loop() override; + void dump_config() override; + float get_setup_priority() const override; + + Trigger<> *get_stop_trigger() const { return this->stop_trigger_; } + + Trigger<> *get_open_trigger() const { return this->open_trigger_; } + void set_open_sensor(sensor::Sensor *open_sensor) { this->open_sensor_ = open_sensor; } + void set_open_moving_current_threshold(float open_moving_current_threshold) { + this->open_moving_current_threshold_ = open_moving_current_threshold; + } + void set_open_obstacle_current_threshold(float open_obstacle_current_threshold) { + this->open_obstacle_current_threshold_ = open_obstacle_current_threshold; + } + void set_open_duration(uint32_t open_duration) { this->open_duration_ = open_duration; } + + Trigger<> *get_close_trigger() const { return this->close_trigger_; } + void set_close_sensor(sensor::Sensor *close_sensor) { this->close_sensor_ = close_sensor; } + void set_close_moving_current_threshold(float close_moving_current_threshold) { + this->close_moving_current_threshold_ = close_moving_current_threshold; + } + void set_close_obstacle_current_threshold(float close_obstacle_current_threshold) { + this->close_obstacle_current_threshold_ = close_obstacle_current_threshold; + } + void set_close_duration(uint32_t close_duration) { this->close_duration_ = close_duration; } + + void set_max_duration(uint32_t max_duration) { this->max_duration_ = max_duration; } + void set_obstacle_rollback(float obstacle_rollback) { this->obstacle_rollback_ = obstacle_rollback; } + + void set_malfunction_detection(bool malfunction_detection) { this->malfunction_detection_ = malfunction_detection; } + void set_start_sensing_delay(uint32_t start_sensing_delay) { this->start_sensing_delay_ = start_sensing_delay; } + + Trigger<> *get_malfunction_trigger() const { return this->malfunction_trigger_; } + + cover::CoverTraits get_traits() override; + + protected: + void control(const cover::CoverCall &call) override; + void stop_prev_trigger_(); + + bool is_at_target_() const; + bool is_opening_() const; + bool is_opening_blocked_() const; + bool is_closing_() const; + bool is_closing_blocked_() const; + bool is_initial_delay_finished_() const; + + void direction_idle_(float new_position = FLT_MAX); + void start_direction_(cover::CoverOperation dir); + + void recompute_position_(); + + Trigger<> *stop_trigger_{new Trigger<>()}; + + sensor::Sensor *open_sensor_{nullptr}; + Trigger<> *open_trigger_{new Trigger<>()}; + float open_moving_current_threshold_; + float open_obstacle_current_threshold_{FLT_MAX}; + uint32_t open_duration_; + + sensor::Sensor *close_sensor_{nullptr}; + Trigger<> *close_trigger_{new Trigger<>()}; + float close_moving_current_threshold_; + float close_obstacle_current_threshold_{FLT_MAX}; + uint32_t close_duration_; + + uint32_t max_duration_{UINT32_MAX}; + bool malfunction_detection_{true}; + Trigger<> *malfunction_trigger_{new Trigger<>()}; + uint32_t start_sensing_delay_; + float obstacle_rollback_; + + Trigger<> *prev_command_trigger_{nullptr}; + uint32_t last_recompute_time_{0}; + uint32_t start_dir_time_{0}; + uint32_t last_publish_time_{0}; + float target_position_{0}; +}; + +} // namespace current_based +} // namespace esphome diff --git a/tests/test3.yaml b/tests/test3.yaml index 386775749d..49f48f4cfa 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -426,14 +426,19 @@ sensor: irq_pin: GPIO16 voltage: name: ADE7953 Voltage + id: ade7953_voltage current_a: name: ADE7953 Current A + id: ade7953_current_a current_b: name: ADE7953 Current B + id: ade7953_current_b active_power_a: name: ADE7953 Active Power A + id: ade7953_active_power_a active_power_b: name: ADE7953 Active Power B + id: ade7953_active_power_b - platform: pzem004t uart_id: uart3 voltage: @@ -1021,6 +1026,29 @@ cover: close_action: - switch.turn_on: gpio_switch2 close_duration: 4.5min + - platform: current_based + name: "Current Based Cover" + open_sensor: ade7953_current_a + open_moving_current_threshold: 0.5 + open_obstacle_current_threshold: 0.8 + open_duration: 12s + open_action: + - switch.turn_on: gpio_switch1 + close_sensor: ade7953_current_b + close_moving_current_threshold: 0.5 + close_obstacle_current_threshold: 0.8 + close_duration: 10s + close_action: + - switch.turn_on: gpio_switch2 + stop_action: + - switch.turn_off: gpio_switch1 + - switch.turn_off: gpio_switch2 + obstacle_rollback: 30% + start_sensing_delay: 0.8s + malfunction_detection: true + malfunction_action: + then: + - logger.log: "Malfunction Detected" - platform: template name: Template Cover with Tilt tilt_lambda: 'return 0.5;' From 2eb5f89d82bb9ac61aebb096751005f9c5769c0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Mon, 27 Sep 2021 22:31:15 +0200 Subject: [PATCH 131/207] Add cover toggle support (#1809) * Add cover toggle support Step through open/stop/close/stop sequence with every toggle * Move the cover toggle logic to perform() * Add clang-tidy CI suggestion * Implement cover toggle action as cover trait * Handle toggle correctly if cover fully closed on POR * Fix CI finding * Add deprecated warning * Don't add already deprecated interface Co-authored-by: Oxan van Leeuwen * Don't add already deprecated interface Co-authored-by: Oxan van Leeuwen * Don't add already deprecated interface Co-authored-by: Oxan van Leeuwen Co-authored-by: Mueller, Daniel Co-authored-by: Oxan van Leeuwen --- esphome/components/cover/__init__.py | 7 +++++++ esphome/components/cover/automation.h | 10 ++++++++++ esphome/components/cover/cover.cpp | 20 +++++++++++++++++++ esphome/components/cover/cover.h | 6 +++++- esphome/components/cover/cover_traits.h | 3 +++ .../time_based/time_based_cover.cpp | 17 ++++++++++++++++ .../components/time_based/time_based_cover.h | 1 + tests/test3.yaml | 7 +++++++ 8 files changed, 70 insertions(+), 1 deletion(-) diff --git a/esphome/components/cover/__init__.py b/esphome/components/cover/__init__.py index 6fd6ac81b0..46b8906adb 100644 --- a/esphome/components/cover/__init__.py +++ b/esphome/components/cover/__init__.py @@ -59,6 +59,7 @@ validate_cover_operation = cv.enum(COVER_OPERATIONS, upper=True) OpenAction = cover_ns.class_("OpenAction", automation.Action) CloseAction = cover_ns.class_("CloseAction", automation.Action) StopAction = cover_ns.class_("StopAction", automation.Action) +ToggleAction = cover_ns.class_("ToggleAction", automation.Action) ControlAction = cover_ns.class_("ControlAction", automation.Action) CoverPublishAction = cover_ns.class_("CoverPublishAction", automation.Action) CoverIsOpenCondition = cover_ns.class_("CoverIsOpenCondition", Condition) @@ -119,6 +120,12 @@ async def cover_stop_to_code(config, action_id, template_arg, args): return cg.new_Pvariable(action_id, template_arg, paren) +@automation.register_action("cover.toggle", ToggleAction, COVER_ACTION_SCHEMA) +def cover_toggle_to_code(config, action_id, template_arg, args): + paren = yield cg.get_variable(config[CONF_ID]) + yield cg.new_Pvariable(action_id, template_arg, paren) + + COVER_CONTROL_ACTION_SCHEMA = cv.Schema( { cv.Required(CONF_ID): cv.use_id(Cover), diff --git a/esphome/components/cover/automation.h b/esphome/components/cover/automation.h index 0b364e1e09..79bca6826e 100644 --- a/esphome/components/cover/automation.h +++ b/esphome/components/cover/automation.h @@ -37,6 +37,16 @@ template class StopAction : public Action { Cover *cover_; }; +template class ToggleAction : public Action { + public: + explicit ToggleAction(Cover *cover) : cover_(cover) {} + + void play(Ts... x) override { this->cover_->make_call().set_command_toggle().perform(); } + + protected: + Cover *cover_; +}; + template class ControlAction : public Action { public: explicit ControlAction(Cover *cover) : cover_(cover) {} diff --git a/esphome/components/cover/cover.cpp b/esphome/components/cover/cover.cpp index e1ce211b64..863adb1d81 100644 --- a/esphome/components/cover/cover.cpp +++ b/esphome/components/cover/cover.cpp @@ -43,6 +43,8 @@ CoverCall &CoverCall::set_command(const char *command) { this->set_command_close(); } else if (strcasecmp(command, "STOP") == 0) { this->set_command_stop(); + } 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); } @@ -60,6 +62,10 @@ CoverCall &CoverCall::set_command_stop() { this->stop_ = true; return *this; } +CoverCall &CoverCall::set_command_toggle() { + this->toggle_ = true; + return *this; +} CoverCall &CoverCall::set_position(float position) { this->position_ = position; return *this; @@ -85,10 +91,14 @@ void CoverCall::perform() { if (this->tilt_.has_value()) { ESP_LOGD(TAG, " Tilt: %.0f%%", *this->tilt_ * 100.0f); } + if (this->toggle_.has_value()) { + ESP_LOGD(TAG, " Command: TOGGLE"); + } this->parent_->control(*this); } const optional &CoverCall::get_position() const { return this->position_; } const optional &CoverCall::get_tilt() const { return this->tilt_; } +const optional &CoverCall::get_toggle() const { return this->toggle_; } void CoverCall::validate_() { auto traits = this->parent_->get_traits(); if (this->position_.has_value()) { @@ -111,6 +121,12 @@ void CoverCall::validate_() { this->tilt_ = clamp(tilt, 0.0f, 1.0f); } } + if (this->toggle_.has_value()) { + if (!traits.get_supports_toggle()) { + ESP_LOGW(TAG, "'%s' - This cover device does not support toggle!", this->parent_->get_name().c_str()); + this->toggle_.reset(); + } + } if (this->stop_) { if (this->position_.has_value()) { ESP_LOGW(TAG, "Cannot set position when stopping a cover!"); @@ -120,6 +136,10 @@ void CoverCall::validate_() { ESP_LOGW(TAG, "Cannot set tilt when stopping a cover!"); this->tilt_.reset(); } + if (this->toggle_.has_value()) { + ESP_LOGW(TAG, "Cannot set toggle when stopping a cover!"); + this->toggle_.reset(); + } } } CoverCall &CoverCall::set_stop(bool stop) { diff --git a/esphome/components/cover/cover.h b/esphome/components/cover/cover.h index 72ec15a459..8f98a88a42 100644 --- a/esphome/components/cover/cover.h +++ b/esphome/components/cover/cover.h @@ -29,7 +29,7 @@ class CoverCall { public: CoverCall(Cover *parent); - /// Set the command as a string, "STOP", "OPEN", "CLOSE". + /// Set the command as a string, "STOP", "OPEN", "CLOSE", "TOGGLE". CoverCall &set_command(const char *command); /// Set the command to open the cover. CoverCall &set_command_open(); @@ -37,6 +37,8 @@ class CoverCall { CoverCall &set_command_close(); /// Set the command to stop the cover. CoverCall &set_command_stop(); + /// Set the command to toggle the cover. + CoverCall &set_command_toggle(); /// Set the call to a certain target position. CoverCall &set_position(float position); /// Set the call to a certain target tilt. @@ -50,6 +52,7 @@ class CoverCall { const optional &get_position() const; bool get_stop() const; const optional &get_tilt() const; + const optional &get_toggle() const; protected: void validate_(); @@ -58,6 +61,7 @@ class CoverCall { bool stop_{false}; optional position_{}; optional tilt_{}; + optional toggle_{}; }; /// Struct used to store the restored state of a cover diff --git a/esphome/components/cover/cover_traits.h b/esphome/components/cover/cover_traits.h index 2df4a0738e..fb30883f77 100644 --- a/esphome/components/cover/cover_traits.h +++ b/esphome/components/cover/cover_traits.h @@ -13,11 +13,14 @@ class CoverTraits { void set_supports_position(bool supports_position) { this->supports_position_ = supports_position; } bool get_supports_tilt() const { return this->supports_tilt_; } void set_supports_tilt(bool supports_tilt) { this->supports_tilt_ = supports_tilt; } + bool get_supports_toggle() const { return this->supports_toggle_; } + void set_supports_toggle(bool supports_toggle) { this->supports_toggle_ = supports_toggle; } protected: bool is_assumed_state_{false}; bool supports_position_{false}; bool supports_tilt_{false}; + bool supports_toggle_{false}; }; } // namespace cover diff --git a/esphome/components/time_based/time_based_cover.cpp b/esphome/components/time_based/time_based_cover.cpp index 3fa07167ca..522252e907 100644 --- a/esphome/components/time_based/time_based_cover.cpp +++ b/esphome/components/time_based/time_based_cover.cpp @@ -52,6 +52,7 @@ float TimeBasedCover::get_setup_priority() const { return setup_priority::DATA; CoverTraits TimeBasedCover::get_traits() { auto traits = CoverTraits(); traits.set_supports_position(true); + traits.set_supports_toggle(true); traits.set_is_assumed_state(this->assumed_state_); return traits; } @@ -60,6 +61,20 @@ void TimeBasedCover::control(const CoverCall &call) { this->start_direction_(COVER_OPERATION_IDLE); this->publish_state(); } + if (call.get_toggle().has_value()) { + if (this->current_operation != COVER_OPERATION_IDLE) { + this->start_direction_(COVER_OPERATION_IDLE); + this->publish_state(); + } else { + if (this->position == COVER_CLOSED || this->last_operation_ == COVER_OPERATION_CLOSING) { + this->target_position_ = COVER_OPEN; + this->start_direction_(COVER_OPERATION_OPENING); + } else { + this->target_position_ = COVER_CLOSED; + this->start_direction_(COVER_OPERATION_CLOSING); + } + } + } if (call.get_position().has_value()) { auto pos = *call.get_position(); if (pos == this->position) { @@ -105,9 +120,11 @@ void TimeBasedCover::start_direction_(CoverOperation dir) { trig = this->stop_trigger_; break; case COVER_OPERATION_OPENING: + this->last_operation_ = dir; trig = this->open_trigger_; break; case COVER_OPERATION_CLOSING: + this->last_operation_ = dir; trig = this->close_trigger_; break; default: diff --git a/esphome/components/time_based/time_based_cover.h b/esphome/components/time_based/time_based_cover.h index 6c48c26ed1..517ab77cb3 100644 --- a/esphome/components/time_based/time_based_cover.h +++ b/esphome/components/time_based/time_based_cover.h @@ -45,6 +45,7 @@ class TimeBasedCover : public cover::Cover, public Component { float target_position_{0}; bool has_built_in_endstop_{false}; bool assumed_state_{false}; + cover::CoverOperation last_operation_{cover::COVER_OPERATION_OPENING}; }; } // namespace time_based diff --git a/tests/test3.yaml b/tests/test3.yaml index 49f48f4cfa..b261d6cc8e 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -729,6 +729,12 @@ binary_sensor: id: r0_sensor name: 'R0 Sensor' component_name: page0.r0 + - platform: template + id: 'cover_toggle' + on_press: + then: + - cover.toggle: time_based_cover + globals: - id: my_global_string type: std::string @@ -1018,6 +1024,7 @@ cover: max_duration: 10min - platform: time_based name: Time Based Cover + id: time_based_cover stop_action: - switch.turn_on: gpio_switch1 open_action: From 5624fafb3a55662172a629bb09ea8a3ac105e8e5 Mon Sep 17 00:00:00 2001 From: 0hax <43876620+0hax@users.noreply.github.com> Date: Mon, 27 Sep 2021 22:32:08 +0200 Subject: [PATCH 132/207] Fix handling of timestamps in Teleinfo component. (#2392) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * teleinfo: avoid a buffer overflow. When reading tag or values, data is written to the buffer even if the size if bigger than the buffer. Add a new 'max_len' argument to get_field() to avoid this error. Signed-off-by: 0hax <0hax@protonmail.com> * teleinfo: read extra timestamp field for some tags. Some tags has an extra timestamp field that need to be read before the actual data. The code is inspired by Jpsy work: https://github.com/Jpsy/esphome/commit/29339c14f96ed7cf7a68911ca7d9bd5eb94955d6 Signed-off-by: 0hax <0hax@protonmail.com> * teleinfo: increase MAX_BUF_SIZE to suffice for 3-phase Linky in Standard mode. * teleinfo: handle DATE tag correctly. The DATE tag is special due its format and need to be handled separately. Fix from DrCoolzic. Signed-off-by: 0hax <0hax@protonmail.com> Co-authored-by: Jörg Wagner --- esphome/components/teleinfo/teleinfo.cpp | 37 +++++++++++++++++++++--- esphome/components/teleinfo/teleinfo.h | 4 ++- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/esphome/components/teleinfo/teleinfo.cpp b/esphome/components/teleinfo/teleinfo.cpp index 8240615cc5..badd66ae83 100644 --- a/esphome/components/teleinfo/teleinfo.cpp +++ b/esphome/components/teleinfo/teleinfo.cpp @@ -7,7 +7,7 @@ namespace teleinfo { static const char *const TAG = "teleinfo"; /* Helpers */ -static int get_field(char *dest, char *buf_start, char *buf_end, int sep) { +static int get_field(char *dest, char *buf_start, char *buf_end, int sep, int max_len) { char *field_end; int len; @@ -15,6 +15,8 @@ static int get_field(char *dest, char *buf_start, char *buf_end, int sep) { if (!field_end) return 0; len = field_end - buf_start; + if (len >= max_len) + return len; strncpy(dest, buf_start, len); dest[len] = '\0'; @@ -106,9 +108,22 @@ void TeleInfo::loop() { * 0xa | Tag | 0x9 | Data | 0x9 | CRC | 0xd * ^^^^^^^^^^^^^^^^^^^^^^^^^ * Checksum is computed on the above in standard mode. + * + * Note that some Tags may have a timestamp in Standard mode. In this case + * the group would looks like this: + * 0xa | Tag | 0x9 | Timestamp | 0x9 | Data | 0x9 | CRC | 0xd + * + * The DATE tag is a special case. The group looks like this + * 0xa | Tag | 0x9 | Timestamp | 0x9 | 0x9 | CRC | 0xd + * */ while ((buf_finger = static_cast(memchr(buf_finger, (int) 0xa, buf_index_ - 1))) && ((buf_finger - buf_) < buf_index_)) { + /* + * Make sure timesamp is nullified between each tag as some tags don't + * have a timestamp + */ + timestamp_[0] = '\0'; /* Point to the first char of the group after 0xa */ buf_finger += 1; @@ -123,7 +138,7 @@ void TeleInfo::loop() { continue; /* Get tag */ - field_len = get_field(tag_, buf_finger, grp_end, separator_); + field_len = get_field(tag_, buf_finger, grp_end, separator_, MAX_TAG_SIZE); if (!field_len || field_len >= MAX_TAG_SIZE) { ESP_LOGE(TAG, "Invalid tag."); break; @@ -132,8 +147,22 @@ void TeleInfo::loop() { /* Advance buf_finger to after the tag and the separator. */ buf_finger += field_len + 1; - /* Get value (after next separator) */ - field_len = get_field(val_, buf_finger, grp_end, separator_); + /* + * If there is two separators and the tag is not equal to "DATE", + * it means there is a timestamp to read first. + */ + if (std::count(buf_finger, grp_end, separator_) == 2 && strcmp(tag_, "DATE") != 0) { + field_len = get_field(timestamp_, buf_finger, grp_end, separator_, MAX_TIMESTAMP_SIZE); + if (!field_len || field_len >= MAX_TIMESTAMP_SIZE) { + ESP_LOGE(TAG, "Invalid Timestamp"); + break; + } + + /* Advance buf_finger to after the first data and the separator. */ + buf_finger += field_len + 1; + } + + field_len = get_field(val_, buf_finger, grp_end, separator_, MAX_VAL_SIZE); if (!field_len || field_len >= MAX_VAL_SIZE) { ESP_LOGE(TAG, "Invalid Value"); break; diff --git a/esphome/components/teleinfo/teleinfo.h b/esphome/components/teleinfo/teleinfo.h index f10024691e..2be34cfb78 100644 --- a/esphome/components/teleinfo/teleinfo.h +++ b/esphome/components/teleinfo/teleinfo.h @@ -11,7 +11,8 @@ namespace teleinfo { */ static const uint8_t MAX_TAG_SIZE = 64; static const uint16_t MAX_VAL_SIZE = 256; -static const uint16_t MAX_BUF_SIZE = 1024; +static const uint16_t MAX_BUF_SIZE = 2048; +static const uint16_t MAX_TIMESTAMP_SIZE = 14; class TeleInfoListener { public: @@ -36,6 +37,7 @@ class TeleInfo : public PollingComponent, public uart::UARTDevice { uint32_t buf_index_{0}; char tag_[MAX_TAG_SIZE]; char val_[MAX_VAL_SIZE]; + char timestamp_[MAX_TIMESTAMP_SIZE]; enum State { OFF, ON, From 6417d8132d392c9a43ec94aac661c569ad6fe146 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 27 Sep 2021 14:08:21 -0700 Subject: [PATCH 133/207] bump dashboard to 20210927.0 (#2405) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 537a802e06..7ee22f0415 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ pyserial==3.5 platformio==5.2.0 esptool==3.1 click==8.0.1 -esphome-dashboard==20210908.0 +esphome-dashboard==20210927.0 aioesphomeapi==9.1.1 # esp-idf requires this, but doesn't bundle it by default From 5596751c2c0a99d1c4df1d281d943007653424dd Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Mon, 27 Sep 2021 23:24:55 +0200 Subject: [PATCH 134/207] Add str_sprintf function that returns std::string (#2408) --- esphome/core/helpers.cpp | 15 +++++++++++++++ esphome/core/helpers.h | 3 +++ 2 files changed, 18 insertions(+) diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index a190566bea..0092d202c4 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -351,6 +351,21 @@ bool str_startswith(const std::string &full, const std::string &start) { return bool str_endswith(const std::string &full, const std::string &ending) { return full.rfind(ending) == (full.size() - ending.size()); } +std::string str_sprintf(const char *fmt, ...) { + std::string str; + va_list args; + + va_start(args, fmt); + size_t length = vsnprintf(nullptr, 0, fmt, args); + va_end(args); + + str.resize(length); + va_start(args, fmt); + vsnprintf(&str[0], length + 1, fmt, args); + va_end(args); + + return str; +} uint16_t encode_uint16(uint8_t msb, uint8_t lsb) { return (uint16_t(msb) << 8) | uint16_t(lsb); } std::array decode_uint16(uint16_t value) { diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 8118585db5..f5a7a197ca 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -59,6 +59,9 @@ bool str_equals_case_insensitive(const std::string &a, const std::string &b); bool str_startswith(const std::string &full, const std::string &start); bool str_endswith(const std::string &full, const std::string &ending); +/// sprintf-like function returning std::string instead of writing to char array. +std::string __attribute__((format(printf, 1, 2))) str_sprintf(const char *fmt, ...); + class HighFrequencyLoopRequester { public: void start(); From be965a60eba6bb769e2a5afdbc8eed132f077a59 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Tue, 28 Sep 2021 02:53:38 +0200 Subject: [PATCH 135/207] Merge pull request from GHSA-48mj-p7x2-5jfm * Move web_server auth to web_server_base * Fix * Fix * Add middleware system --- esphome/components/web_server/__init__.py | 8 +-- esphome/components/web_server/web_server.cpp | 7 -- esphome/components/web_server/web_server.h | 8 --- .../web_server_base/web_server_base.cpp | 11 +++ .../web_server_base/web_server_base.h | 72 +++++++++++++++++-- 5 files changed, 81 insertions(+), 25 deletions(-) diff --git a/esphome/components/web_server/__init__.py b/esphome/components/web_server/__init__.py index 7f17767657..240ba7c8a0 100644 --- a/esphome/components/web_server/__init__.py +++ b/esphome/components/web_server/__init__.py @@ -34,8 +34,8 @@ CONFIG_SCHEMA = cv.Schema( cv.Optional(CONF_JS_INCLUDE): cv.file_, cv.Optional(CONF_AUTH): cv.Schema( { - cv.Required(CONF_USERNAME): cv.string_strict, - cv.Required(CONF_PASSWORD): cv.string_strict, + cv.Required(CONF_USERNAME): cv.All(cv.string_strict, cv.Length(min=1)), + cv.Required(CONF_PASSWORD): cv.All(cv.string_strict, cv.Length(min=1)), } ), cv.GenerateID(CONF_WEB_SERVER_BASE_ID): cv.use_id( @@ -57,8 +57,8 @@ async def to_code(config): cg.add(var.set_css_url(config[CONF_CSS_URL])) cg.add(var.set_js_url(config[CONF_JS_URL])) if CONF_AUTH in config: - cg.add(var.set_username(config[CONF_AUTH][CONF_USERNAME])) - cg.add(var.set_password(config[CONF_AUTH][CONF_PASSWORD])) + cg.add(paren.set_auth_username(config[CONF_AUTH][CONF_USERNAME])) + cg.add(paren.set_auth_password(config[CONF_AUTH][CONF_PASSWORD])) if CONF_CSS_INCLUDE in config: cg.add_define("WEBSERVER_CSS_INCLUDE") path = CORE.relative_config_path(config[CONF_CSS_INCLUDE]) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 97777a8986..28e741cf24 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -158,9 +158,6 @@ void WebServer::setup() { void WebServer::dump_config() { ESP_LOGCONFIG(TAG, "Web Server:"); ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->base_->get_port()); - if (this->using_auth()) { - ESP_LOGCONFIG(TAG, " Basic authentication enabled"); - } } float WebServer::get_setup_priority() const { return setup_priority::WIFI - 1.0f; } @@ -764,10 +761,6 @@ bool WebServer::canHandle(AsyncWebServerRequest *request) { return false; } void WebServer::handleRequest(AsyncWebServerRequest *request) { - if (this->using_auth() && !request->authenticate(this->username_, this->password_)) { - return request->requestAuthentication(); - } - if (request->url() == "/") { this->handle_index_request(request); return; diff --git a/esphome/components/web_server/web_server.h b/esphome/components/web_server/web_server.h index 0eaa2e9a75..021d5a0646 100644 --- a/esphome/components/web_server/web_server.h +++ b/esphome/components/web_server/web_server.h @@ -32,10 +32,6 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { public: WebServer(web_server_base::WebServerBase *base) : base_(base) {} - void set_username(const char *username) { username_ = username; } - - void set_password(const char *password) { password_ = password; } - /** Set the URL to the CSS that's sent to each client. Defaults to * https://esphome.io/_static/webserver-v1.min.css * @@ -85,8 +81,6 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { void handle_js_request(AsyncWebServerRequest *request); #endif - bool using_auth() { return username_ != nullptr && password_ != nullptr; } - #ifdef USE_SENSOR void on_sensor_update(sensor::Sensor *obj, float state) override; /// Handle a sensor request under '/sensor/'. @@ -184,8 +178,6 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { protected: web_server_base::WebServerBase *base_; AsyncEventSource events_{"/events"}; - const char *username_{nullptr}; - const char *password_{nullptr}; const char *css_url_{nullptr}; const char *css_include_{nullptr}; const char *js_url_{nullptr}; diff --git a/esphome/components/web_server_base/web_server_base.cpp b/esphome/components/web_server_base/web_server_base.cpp index b0babcab46..3c269b28b8 100644 --- a/esphome/components/web_server_base/web_server_base.cpp +++ b/esphome/components/web_server_base/web_server_base.cpp @@ -17,6 +17,17 @@ namespace web_server_base { static const char *const TAG = "web_server_base"; +void WebServerBase::add_handler(AsyncWebHandler *handler) { + // remove all handlers + + if (!credentials_.username.empty()) { + handler = new internal::AuthMiddlewareHandler(handler, &credentials_); + } + this->handlers_.push_back(handler); + if (this->server_ != nullptr) + this->server_->addHandler(handler); +} + void report_ota_error() { StreamString ss; Update.printError(ss); diff --git a/esphome/components/web_server_base/web_server_base.h b/esphome/components/web_server_base/web_server_base.h index 4ef67f959c..7afc72b9d2 100644 --- a/esphome/components/web_server_base/web_server_base.h +++ b/esphome/components/web_server_base/web_server_base.h @@ -10,6 +10,68 @@ namespace esphome { namespace web_server_base { +namespace internal { + +class MiddlewareHandler : public AsyncWebHandler { + public: + MiddlewareHandler(AsyncWebHandler *next) : next_(next) {} + + bool canHandle(AsyncWebServerRequest *request) override { return next_->canHandle(request); } + void handleRequest(AsyncWebServerRequest *request) override { next_->handleRequest(request); } + void handleUpload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, + bool final) override { + next_->handleUpload(request, filename, index, data, len, final); + } + void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override { + next_->handleBody(request, data, len, index, total); + } + bool isRequestHandlerTrivial() override { return next_->isRequestHandlerTrivial(); } + + protected: + AsyncWebHandler *next_; +}; + +struct Credentials { + std::string username; + std::string password; +}; + +class AuthMiddlewareHandler : public MiddlewareHandler { + public: + AuthMiddlewareHandler(AsyncWebHandler *next, Credentials *credentials) + : MiddlewareHandler(next), credentials_(credentials) {} + + bool check_auth(AsyncWebServerRequest *request) { + bool success = request->authenticate(credentials_->username.c_str(), credentials_->password.c_str()); + if (!success) { + request->requestAuthentication(); + } + return success; + } + + void handleRequest(AsyncWebServerRequest *request) override { + if (!check_auth(request)) + return; + MiddlewareHandler::handleRequest(request); + } + void handleUpload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, + bool final) override { + if (!check_auth(request)) + return; + MiddlewareHandler::handleUpload(request, filename, index, data, len, final); + } + void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override { + if (!check_auth(request)) + return; + MiddlewareHandler::handleBody(request, data, len, index, total); + } + + protected: + Credentials *credentials_; +}; + +} // namespace internal + class WebServerBase : public Component { public: void init() { @@ -34,13 +96,10 @@ class WebServerBase : public Component { std::shared_ptr get_server() const { return server_; } float get_setup_priority() const override; - void add_handler(AsyncWebHandler *handler) { - // remove all handlers + void set_auth_username(std::string auth_username) { credentials_.username = auth_username; } + void set_auth_password(std::string auth_password) { credentials_.password = auth_password; } - this->handlers_.push_back(handler); - if (this->server_ != nullptr) - this->server_->addHandler(handler); - } + void add_handler(AsyncWebHandler *handler); void add_ota_handler(); @@ -54,6 +113,7 @@ class WebServerBase : public Component { uint16_t port_{80}; std::shared_ptr server_{nullptr}; std::vector handlers_; + internal::Credentials credentials_; }; class OTARequestHandler : public AsyncWebHandler { From 2234f6aacf8cc653307fed80f3750317a82c4f83 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 28 Sep 2021 15:33:30 +1300 Subject: [PATCH 136/207] Fix lint issues in web_server_base (#2409) --- esphome/components/web_server_base/web_server_base.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/esphome/components/web_server_base/web_server_base.h b/esphome/components/web_server_base/web_server_base.h index 7afc72b9d2..bc37337ca5 100644 --- a/esphome/components/web_server_base/web_server_base.h +++ b/esphome/components/web_server_base/web_server_base.h @@ -3,6 +3,7 @@ #ifdef USE_ARDUINO #include +#include #include "esphome/core/component.h" #include @@ -96,8 +97,8 @@ class WebServerBase : public Component { std::shared_ptr get_server() const { return server_; } float get_setup_priority() const override; - void set_auth_username(std::string auth_username) { credentials_.username = auth_username; } - void set_auth_password(std::string auth_password) { credentials_.password = auth_password; } + void set_auth_username(std::string auth_username) { credentials_.username = std::move(auth_username); } + void set_auth_password(std::string auth_password) { credentials_.password = std::move(auth_password); } void add_handler(AsyncWebHandler *handler); From 2b9054d3b2bedd6b3ed8e5cf87e0236b2acbf8eb Mon Sep 17 00:00:00 2001 From: Paul Monigatti Date: Wed, 29 Sep 2021 03:26:46 +1300 Subject: [PATCH 137/207] Initialised ESPPreferenceObject::backend_ to nullptr (#2411) --- esphome/core/preferences.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/core/preferences.h b/esphome/core/preferences.h index b3f4b77f78..ad45cd9684 100644 --- a/esphome/core/preferences.h +++ b/esphome/core/preferences.h @@ -30,7 +30,7 @@ class ESPPreferenceObject { } protected: - ESPPreferenceBackend *backend_; + ESPPreferenceBackend *backend_{nullptr}; }; class ESPPreferences { From af04f565cf203719f6340bcd263e50b856ae2a6f Mon Sep 17 00:00:00 2001 From: Stephen Tierney Date: Wed, 29 Sep 2021 06:10:25 +1000 Subject: [PATCH 138/207] Add support for SCD4X (#2217) * Initial commit * clang-format fixes * Update CODEOWNERS * clang-format fixes * Fix merge error * Fix missing return Co-authored-by: Otto winter --- CODEOWNERS | 1 + esphome/components/scd4x/__init__.py | 0 esphome/components/scd4x/scd4x.cpp | 259 +++++++++++++++++++++++++++ esphome/components/scd4x/scd4x.h | 53 ++++++ esphome/components/scd4x/sensor.py | 98 ++++++++++ tests/test1.yaml | 13 ++ 6 files changed, 424 insertions(+) create mode 100644 esphome/components/scd4x/__init__.py create mode 100644 esphome/components/scd4x/scd4x.cpp create mode 100644 esphome/components/scd4x/scd4x.h create mode 100644 esphome/components/scd4x/sensor.py diff --git a/CODEOWNERS b/CODEOWNERS index 3c3a1e04ee..2972b30b33 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -125,6 +125,7 @@ esphome/components/restart/* @esphome/core esphome/components/rf_bridge/* @jesserockz esphome/components/rgbct/* @jesserockz esphome/components/rtttl/* @glmnet +esphome/components/scd4x/* @sjtrny esphome/components/script/* @esphome/core esphome/components/sdm_meter/* @jesserockz @polyfaces esphome/components/sdp3x/* @Azimath diff --git a/esphome/components/scd4x/__init__.py b/esphome/components/scd4x/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/scd4x/scd4x.cpp b/esphome/components/scd4x/scd4x.cpp new file mode 100644 index 0000000000..c91fd5e882 --- /dev/null +++ b/esphome/components/scd4x/scd4x.cpp @@ -0,0 +1,259 @@ +#include "scd4x.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace scd4x { + +static const char *const TAG = "scd4x"; + +static const uint16_t SCD4X_CMD_GET_SERIAL_NUMBER = 0x3682; +static const uint16_t SCD4X_CMD_TEMPERATURE_OFFSET = 0x241d; +static const uint16_t SCD4X_CMD_ALTITUDE_COMPENSATION = 0x2427; +static const uint16_t SCD4X_CMD_AMBIENT_PRESSURE_COMPENSATION = 0xe000; +static const uint16_t SCD4X_CMD_AUTOMATIC_SELF_CALIBRATION = 0x2416; +static const uint16_t SCD4X_CMD_START_CONTINUOUS_MEASUREMENTS = 0x21b1; +static const uint16_t SCD4X_CMD_GET_DATA_READY_STATUS = 0xe4b8; +static const uint16_t SCD4X_CMD_READ_MEASUREMENT = 0xec05; +static const uint16_t SCD4X_CMD_PERFORM_FORCED_CALIBRATION = 0x362f; +static const uint16_t SCD4X_CMD_STOP_MEASUREMENTS = 0x3f86; + +static const float SCD4X_TEMPERATURE_OFFSET_MULTIPLIER = (1 << 16) / 175.0f; + +void SCD4XComponent::setup() { + ESP_LOGCONFIG(TAG, "Setting up scd4x..."); + + // the sensor needs 1000 ms to enter the idle state + this->set_timeout(1000, [this]() { + // Check if measurement is ready before reading the value + if (!this->write_command_(SCD4X_CMD_GET_DATA_READY_STATUS)) { + ESP_LOGE(TAG, "Failed to write data ready status command"); + this->mark_failed(); + return; + } + + uint16_t raw_read_status[1]; + if (!this->read_data_(raw_read_status, 1)) { + ESP_LOGE(TAG, "Failed to read data ready status"); + this->mark_failed(); + return; + } + + // In order to query the device periodic measurement must be ceased + if (raw_read_status[0]) { + ESP_LOGD(TAG, "Sensor has data available, stopping periodic measurement"); + if (!this->write_command_(SCD4X_CMD_STOP_MEASUREMENTS)) { + ESP_LOGE(TAG, "Failed to stop measurements"); + this->mark_failed(); + return; + } + } + + if (!this->write_command_(SCD4X_CMD_GET_SERIAL_NUMBER)) { + ESP_LOGE(TAG, "Failed to write get serial command"); + this->error_code_ = COMMUNICATION_FAILED; + this->mark_failed(); + return; + } + + uint16_t raw_serial_number[3]; + if (!this->read_data_(raw_serial_number, 3)) { + ESP_LOGE(TAG, "Failed to read serial number"); + this->error_code_ = SERIAL_NUMBER_IDENTIFICATION_FAILED; + this->mark_failed(); + return; + } + ESP_LOGD(TAG, "Serial number %02d.%02d.%02d", (uint16_t(raw_serial_number[0]) >> 8), + uint16_t(raw_serial_number[0] & 0xFF), (uint16_t(raw_serial_number[1]) >> 8)); + + if (!this->write_command_(SCD4X_CMD_TEMPERATURE_OFFSET, + (uint16_t)(temperature_offset_ * SCD4X_TEMPERATURE_OFFSET_MULTIPLIER))) { + ESP_LOGE(TAG, "Error setting temperature offset."); + this->error_code_ = MEASUREMENT_INIT_FAILED; + this->mark_failed(); + return; + } + + // If pressure compensation available use it + // else use altitude + if (ambient_pressure_compensation_) { + if (!this->write_command_(SCD4X_CMD_AMBIENT_PRESSURE_COMPENSATION, ambient_pressure_compensation_)) { + ESP_LOGE(TAG, "Error setting ambient pressure compensation."); + this->error_code_ = MEASUREMENT_INIT_FAILED; + this->mark_failed(); + return; + } + } else { + if (!this->write_command_(SCD4X_CMD_ALTITUDE_COMPENSATION, altitude_compensation_)) { + ESP_LOGE(TAG, "Error setting altitude compensation."); + this->error_code_ = MEASUREMENT_INIT_FAILED; + this->mark_failed(); + return; + } + } + + if (!this->write_command_(SCD4X_CMD_AUTOMATIC_SELF_CALIBRATION, enable_asc_ ? 1 : 0)) { + ESP_LOGE(TAG, "Error setting automatic self calibration."); + this->error_code_ = MEASUREMENT_INIT_FAILED; + this->mark_failed(); + return; + } + + // Finally start sensor measurements + if (!this->write_command_(SCD4X_CMD_START_CONTINUOUS_MEASUREMENTS)) { + ESP_LOGE(TAG, "Error starting continuous measurements."); + this->error_code_ = MEASUREMENT_INIT_FAILED; + this->mark_failed(); + return; + } + + initialized_ = true; + ESP_LOGD(TAG, "Sensor initialized"); + }); +} + +void SCD4XComponent::dump_config() { + ESP_LOGCONFIG(TAG, "scd4x:"); + LOG_I2C_DEVICE(this); + if (this->is_failed()) { + switch (this->error_code_) { + case COMMUNICATION_FAILED: + ESP_LOGW(TAG, "Communication failed! Is the sensor connected?"); + break; + case MEASUREMENT_INIT_FAILED: + ESP_LOGW(TAG, "Measurement Initialization failed!"); + break; + case SERIAL_NUMBER_IDENTIFICATION_FAILED: + ESP_LOGW(TAG, "Unable to read sensor firmware version"); + break; + default: + ESP_LOGW(TAG, "Unknown setup error!"); + break; + } + } + ESP_LOGCONFIG(TAG, " Automatic self calibration: %s", ONOFF(this->enable_asc_)); + if (this->ambient_pressure_compensation_) { + ESP_LOGCONFIG(TAG, " Altitude compensation disabled"); + ESP_LOGCONFIG(TAG, " Ambient pressure compensation: %dmBar", this->ambient_pressure_); + } else { + ESP_LOGCONFIG(TAG, " Ambient pressure compensation disabled"); + ESP_LOGCONFIG(TAG, " Altitude compensation: %dm", this->altitude_compensation_); + } + ESP_LOGCONFIG(TAG, " Temperature offset: %.2f °C", this->temperature_offset_); + LOG_UPDATE_INTERVAL(this); + LOG_SENSOR(" ", "CO2", this->co2_sensor_); + LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); + LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); +} + +void SCD4XComponent::update() { + if (!initialized_) { + return; + } + + // Check if data is ready + if (!this->write_command_(SCD4X_CMD_GET_DATA_READY_STATUS)) { + this->status_set_warning(); + return; + } + + uint16_t raw_read_status[1]; + if (!this->read_data_(raw_read_status, 1) || raw_read_status[0] == 0x00) { + this->status_set_warning(); + ESP_LOGW(TAG, "Data not ready yet!"); + return; + } + + if (!this->write_command_(SCD4X_CMD_READ_MEASUREMENT)) { + ESP_LOGW(TAG, "Error reading measurement!"); + this->status_set_warning(); + return; + } + + // Read off sensor data + uint16_t raw_data[3]; + if (!this->read_data_(raw_data, 3)) { + this->status_set_warning(); + return; + } + + if (this->co2_sensor_ != nullptr) + this->co2_sensor_->publish_state(raw_data[0]); + + if (this->temperature_sensor_ != nullptr) { + const float temperature = -45.0f + (175.0f * (raw_data[1])) / (1 << 16); + this->temperature_sensor_->publish_state(temperature); + } + + if (this->humidity_sensor_ != nullptr) { + const float humidity = (100.0f * raw_data[2]) / (1 << 16); + this->humidity_sensor_->publish_state(humidity); + } + + this->status_clear_warning(); +} + +uint8_t SCD4XComponent::sht_crc_(uint8_t data1, uint8_t data2) { + uint8_t bit; + uint8_t crc = 0xFF; + + crc ^= data1; + for (bit = 8; bit > 0; --bit) { + if (crc & 0x80) + crc = (crc << 1) ^ 0x131; + else + crc = (crc << 1); + } + + crc ^= data2; + for (bit = 8; bit > 0; --bit) { + if (crc & 0x80) + crc = (crc << 1) ^ 0x131; + else + crc = (crc << 1); + } + + return crc; +} + +bool SCD4XComponent::read_data_(uint16_t *data, uint8_t len) { + const uint8_t num_bytes = len * 3; + std::vector buf(num_bytes); + + if (this->read(buf.data(), num_bytes) != i2c::ERROR_OK) { + return false; + } + + for (uint8_t i = 0; i < len; i++) { + const uint8_t j = 3 * i; + uint8_t crc = sht_crc_(buf[j], buf[j + 1]); + if (crc != buf[j + 2]) { + ESP_LOGE(TAG, "CRC8 Checksum invalid! 0x%02X != 0x%02X", buf[j + 2], crc); + return false; + } + data[i] = (buf[j] << 8) | buf[j + 1]; + } + return true; +} + +bool SCD4XComponent::write_command_(uint16_t command) { + const uint8_t num_bytes = 2; + uint8_t buffer[num_bytes]; + + buffer[0] = (command >> 8); + buffer[1] = command & 0xff; + + return this->write(buffer, num_bytes) == i2c::ERROR_OK; +} + +bool SCD4XComponent::write_command_(uint16_t command, uint16_t data) { + uint8_t raw[5]; + raw[0] = command >> 8; + raw[1] = command & 0xFF; + raw[2] = data >> 8; + raw[3] = data & 0xFF; + raw[4] = sht_crc_(raw[2], raw[3]); + return this->write(raw, 5) == i2c::ERROR_OK; +} + +} // namespace scd4x +} // namespace esphome diff --git a/esphome/components/scd4x/scd4x.h b/esphome/components/scd4x/scd4x.h new file mode 100644 index 0000000000..3c428b8623 --- /dev/null +++ b/esphome/components/scd4x/scd4x.h @@ -0,0 +1,53 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace scd4x { + +enum ERRORCODE { COMMUNICATION_FAILED, SERIAL_NUMBER_IDENTIFICATION_FAILED, MEASUREMENT_INIT_FAILED, UNKNOWN }; + +class SCD4XComponent : public PollingComponent, public i2c::I2CDevice { + public: + float get_setup_priority() const override { return setup_priority::DATA; } + void setup() override; + void dump_config() override; + void update() override; + + void set_automatic_self_calibration(bool asc) { enable_asc_ = asc; } + void set_altitude_compensation(uint16_t altitude) { altitude_compensation_ = altitude; } + void set_ambient_pressure_compensation(float pressure) { + ambient_pressure_compensation_ = true; + ambient_pressure_ = (uint16_t)(pressure * 1000); + } + void set_temperature_offset(float offset) { temperature_offset_ = offset; }; + + void set_co2_sensor(sensor::Sensor *co2) { co2_sensor_ = co2; } + void set_temperature_sensor(sensor::Sensor *temperature) { temperature_sensor_ = temperature; }; + void set_humidity_sensor(sensor::Sensor *humidity) { humidity_sensor_ = humidity; } + + protected: + uint8_t sht_crc_(uint8_t data1, uint8_t data2); + bool read_data_(uint16_t *data, uint8_t len); + bool write_command_(uint16_t command); + bool write_command_(uint16_t command, uint16_t data); + + ERRORCODE error_code_; + + bool initialized_{false}; + + float temperature_offset_; + uint16_t altitude_compensation_; + bool ambient_pressure_compensation_; + uint16_t ambient_pressure_; + bool enable_asc_; + + sensor::Sensor *co2_sensor_{nullptr}; + sensor::Sensor *temperature_sensor_{nullptr}; + sensor::Sensor *humidity_sensor_{nullptr}; +}; + +} // namespace scd4x +} // namespace esphome diff --git a/esphome/components/scd4x/sensor.py b/esphome/components/scd4x/sensor.py new file mode 100644 index 0000000000..0b1a960f6f --- /dev/null +++ b/esphome/components/scd4x/sensor.py @@ -0,0 +1,98 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import i2c, sensor + +from esphome.const import ( + CONF_ID, + CONF_CO2, + CONF_HUMIDITY, + CONF_TEMPERATURE, + DEVICE_CLASS_CARBON_DIOXIDE, + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_TEMPERATURE, + STATE_CLASS_MEASUREMENT, + UNIT_PARTS_PER_MILLION, + ICON_MOLECULE_CO2, + ICON_THERMOMETER, + ICON_WATER_PERCENT, + UNIT_CELSIUS, + UNIT_PERCENT, +) + +CODEOWNERS = ["@sjtrny"] +DEPENDENCIES = ["i2c"] + +scd4x_ns = cg.esphome_ns.namespace("scd4x") +SCD4XComponent = scd4x_ns.class_("SCD4XComponent", cg.PollingComponent, i2c.I2CDevice) + +CONF_AUTOMATIC_SELF_CALIBRATION = "automatic_self_calibration" +CONF_ALTITUDE_COMPENSATION = "altitude_compensation" +CONF_AMBIENT_PRESSURE_COMPENSATION = "ambient_pressure_compensation" +CONF_TEMPERATURE_OFFSET = "temperature_offset" + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(SCD4XComponent), + cv.Optional(CONF_CO2): sensor.sensor_schema( + unit_of_measurement=UNIT_PARTS_PER_MILLION, + icon=ICON_MOLECULE_CO2, + accuracy_decimals=0, + device_class=DEVICE_CLASS_CARBON_DIOXIDE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + icon=ICON_THERMOMETER, + accuracy_decimals=2, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( + unit_of_measurement=UNIT_PERCENT, + icon=ICON_WATER_PERCENT, + accuracy_decimals=2, + device_class=DEVICE_CLASS_HUMIDITY, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_AUTOMATIC_SELF_CALIBRATION, default=True): cv.boolean, + cv.Optional(CONF_ALTITUDE_COMPENSATION, default="0m"): cv.All( + cv.float_with_unit("altitude", "(m|m a.s.l.|MAMSL|MASL)"), + cv.int_range(min=0, max=0xFFFF, max_included=False), + ), + cv.Optional(CONF_AMBIENT_PRESSURE_COMPENSATION): cv.pressure, + cv.Optional(CONF_TEMPERATURE_OFFSET, default="4°C"): cv.temperature, + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x62)) +) + +SENSOR_MAP = { + CONF_CO2: "set_co2_sensor", + CONF_TEMPERATURE: "set_temperature_sensor", + CONF_HUMIDITY: "set_humidity_sensor", +} + +SETTING_MAP = { + CONF_AUTOMATIC_SELF_CALIBRATION: "set_automatic_self_calibration", + CONF_ALTITUDE_COMPENSATION: "set_altitude_compensation", + CONF_AMBIENT_PRESSURE_COMPENSATION: "set_ambient_pressure_compensation", + CONF_TEMPERATURE_OFFSET: "set_temperature_offset", +} + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await i2c.register_i2c_device(var, config) + + for key, funcName in SETTING_MAP.items(): + if key in config: + cg.add(getattr(var, funcName)(config[key])) + + for key, funcName in SENSOR_MAP.items(): + + if key in config: + sens = await sensor.new_sensor(config[key]) + cg.add(getattr(var, funcName)(sens)) diff --git a/tests/test1.yaml b/tests/test1.yaml index 6109e9f5c2..77f7da2b08 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -811,6 +811,19 @@ sensor: ambient_pressure_compensation: 961mBar temperature_offset: 4.2C i2c_id: i2c_bus + - platform: scd4x + co2: + name: "SCD4X CO2" + temperature: + name: "SCD4X Temperature" + humidity: + name: "SCD4X Humidity" + update_interval: 15s + automatic_self_calibration: true + altitude_compensation: 10m + ambient_pressure_compensation: 961mBar + temperature_offset: 4.2C + i2c_id: i2c_bus - platform: sgp30 eco2: name: 'Workshop eCO2' From c39ac9edfebead943cc51fb6ac4fe9b039b5a5c4 Mon Sep 17 00:00:00 2001 From: irtimaled Date: Tue, 28 Sep 2021 13:19:17 -0700 Subject: [PATCH 139/207] Support HSV-based color support on tuya light (#2400) * fix: stop tuya light state getting reset * fix typo * Support for HSV color in Tuya * Clamp formatting --- esphome/components/tuya/light/__init__.py | 11 +++- esphome/components/tuya/light/tuya_light.cpp | 33 ++++++++-- esphome/components/tuya/light/tuya_light.h | 2 + esphome/core/helpers.cpp | 63 ++++++++++++++++++++ esphome/core/helpers.h | 5 ++ 5 files changed, 107 insertions(+), 7 deletions(-) diff --git a/esphome/components/tuya/light/__init__.py b/esphome/components/tuya/light/__init__.py index 6678fc47d8..b983e3f84e 100644 --- a/esphome/components/tuya/light/__init__.py +++ b/esphome/components/tuya/light/__init__.py @@ -22,6 +22,7 @@ CONF_COLOR_TEMPERATURE_DATAPOINT = "color_temperature_datapoint" CONF_COLOR_TEMPERATURE_INVERT = "color_temperature_invert" CONF_COLOR_TEMPERATURE_MAX_VALUE = "color_temperature_max_value" CONF_RGB_DATAPOINT = "rgb_datapoint" +CONF_HSV_DATAPOINT = "hsv_datapoint" TuyaLight = tuya_ns.class_("TuyaLight", light.LightOutput, cg.Component) @@ -33,7 +34,8 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_DIMMER_DATAPOINT): cv.uint8_t, cv.Optional(CONF_MIN_VALUE_DATAPOINT): cv.uint8_t, cv.Optional(CONF_SWITCH_DATAPOINT): cv.uint8_t, - cv.Optional(CONF_RGB_DATAPOINT): cv.uint8_t, + cv.Exclusive(CONF_RGB_DATAPOINT, "color"): cv.uint8_t, + cv.Exclusive(CONF_HSV_DATAPOINT, "color"): cv.uint8_t, cv.Optional(CONF_COLOR_INTERLOCK, default=False): cv.boolean, cv.Inclusive( CONF_COLOR_TEMPERATURE_DATAPOINT, "color_temperature" @@ -57,7 +59,10 @@ CONFIG_SCHEMA = cv.All( } ).extend(cv.COMPONENT_SCHEMA), cv.has_at_least_one_key( - CONF_DIMMER_DATAPOINT, CONF_SWITCH_DATAPOINT, CONF_RGB_DATAPOINT + CONF_DIMMER_DATAPOINT, + CONF_SWITCH_DATAPOINT, + CONF_RGB_DATAPOINT, + CONF_HSV_DATAPOINT, ), ) @@ -75,6 +80,8 @@ async def to_code(config): cg.add(var.set_switch_id(config[CONF_SWITCH_DATAPOINT])) if CONF_RGB_DATAPOINT in config: cg.add(var.set_rgb_id(config[CONF_RGB_DATAPOINT])) + elif CONF_HSV_DATAPOINT in config: + cg.add(var.set_hsv_id(config[CONF_HSV_DATAPOINT])) if CONF_COLOR_TEMPERATURE_DATAPOINT in config: cg.add(var.set_color_temperature_id(config[CONF_COLOR_TEMPERATURE_DATAPOINT])) cg.add(var.set_color_temperature_invert(config[CONF_COLOR_TEMPERATURE_INVERT])) diff --git a/esphome/components/tuya/light/tuya_light.cpp b/esphome/components/tuya/light/tuya_light.cpp index f75cc964aa..133ee1e557 100644 --- a/esphome/components/tuya/light/tuya_light.cpp +++ b/esphome/components/tuya/light/tuya_light.cpp @@ -46,6 +46,19 @@ void TuyaLight::setup() { call.perform(); } }); + } else if (hsv_id_.has_value()) { + this->parent_->register_listener(*this->hsv_id_, [this](const TuyaDatapoint &datapoint) { + auto hue = parse_hex(datapoint.value_string, 0, 4); + auto saturation = parse_hex(datapoint.value_string, 4, 4); + auto value = parse_hex(datapoint.value_string, 8, 4); + if (hue.has_value() && saturation.has_value() && value.has_value()) { + float red, green, blue; + hsv_to_rgb(*hue, float(*saturation) / 1000, float(*value) / 1000, red, green, blue); + auto call = this->state_->make_call(); + call.set_rgb(red, green, blue); + call.perform(); + } + }); } if (min_value_datapoint_id_.has_value()) { parent_->set_integer_datapoint_value(*this->min_value_datapoint_id_, this->min_value_); @@ -60,12 +73,14 @@ void TuyaLight::dump_config() { ESP_LOGCONFIG(TAG, " Switch has datapoint ID %u", *this->switch_id_); if (this->rgb_id_.has_value()) ESP_LOGCONFIG(TAG, " RGB has datapoint ID %u", *this->rgb_id_); + else if (this->hsv_id_.has_value()) + ESP_LOGCONFIG(TAG, " HSV has datapoint ID %u", *this->hsv_id_); } light::LightTraits TuyaLight::get_traits() { auto traits = light::LightTraits(); if (this->color_temperature_id_.has_value() && this->dimmer_id_.has_value()) { - if (this->rgb_id_.has_value()) { + if (this->rgb_id_.has_value() || this->hsv_id_.has_value()) { if (this->color_interlock_) traits.set_supported_color_modes({light::ColorMode::RGB, light::ColorMode::COLOR_TEMPERATURE}); else @@ -75,7 +90,7 @@ light::LightTraits TuyaLight::get_traits() { traits.set_supported_color_modes({light::ColorMode::COLOR_TEMPERATURE}); traits.set_min_mireds(this->cold_white_temperature_); traits.set_max_mireds(this->warm_white_temperature_); - } else if (this->rgb_id_.has_value()) { + } else if (this->rgb_id_.has_value() || this->hsv_id_.has_value()) { if (this->dimmer_id_.has_value()) { if (this->color_interlock_) traits.set_supported_color_modes({light::ColorMode::RGB, light::ColorMode::WHITE}); @@ -97,7 +112,7 @@ void TuyaLight::write_state(light::LightState *state) { float red = 0.0f, green = 0.0f, blue = 0.0f; float color_temperature = 0.0f, brightness = 0.0f; - if (this->rgb_id_.has_value()) { + if (this->rgb_id_.has_value() || this->hsv_id_.has_value()) { if (this->color_temperature_id_.has_value()) { state->current_values_as_rgbct(&red, &green, &blue, &color_temperature, &brightness); } else if (this->dimmer_id_.has_value()) { @@ -137,8 +152,16 @@ void TuyaLight::write_state(light::LightState *state) { if (this->rgb_id_.has_value()) { char buffer[7]; sprintf(buffer, "%02X%02X%02X", int(red * 255), int(green * 255), int(blue * 255)); - std::string value = buffer; - this->parent_->set_string_datapoint_value(*this->rgb_id_, value); + std::string rgb_value = buffer; + this->parent_->set_string_datapoint_value(*this->rgb_id_, rgb_value); + } else if (this->hsv_id_.has_value()) { + int hue; + float saturation, value; + rgb_to_hsv(red, green, blue, hue, saturation, value); + char buffer[13]; + sprintf(buffer, "%04X%04X%04X", hue, int(saturation * 1000), int(value * 1000)); + std::string hsv_value = buffer; + this->parent_->set_string_datapoint_value(*this->hsv_id_, hsv_value); } } diff --git a/esphome/components/tuya/light/tuya_light.h b/esphome/components/tuya/light/tuya_light.h index de9ec5e45f..3d9f25271c 100644 --- a/esphome/components/tuya/light/tuya_light.h +++ b/esphome/components/tuya/light/tuya_light.h @@ -17,6 +17,7 @@ class TuyaLight : public Component, public light::LightOutput { } void set_switch_id(uint8_t switch_id) { this->switch_id_ = switch_id; } void set_rgb_id(uint8_t rgb_id) { this->rgb_id_ = rgb_id; } + void set_hsv_id(uint8_t hsv_id) { this->hsv_id_ = hsv_id; } void set_color_temperature_id(uint8_t color_temperature_id) { this->color_temperature_id_ = color_temperature_id; } void set_color_temperature_invert(bool color_temperature_invert) { this->color_temperature_invert_ = color_temperature_invert; @@ -48,6 +49,7 @@ class TuyaLight : public Component, public light::LightOutput { optional min_value_datapoint_id_{}; optional switch_id_{}; optional rgb_id_{}; + optional hsv_id_{}; optional color_temperature_id_{}; uint32_t min_value_ = 0; uint32_t max_value_ = 255; diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 0092d202c4..731ee6c8f5 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -394,6 +394,69 @@ std::string hexencode(const uint8_t *data, uint32_t len) { return res; } +void rgb_to_hsv(float red, float green, float blue, int &hue, float &saturation, float &value) { + float max_color_value = std::max(std::max(red, green), blue); + float min_color_value = std::min(std::min(red, green), blue); + float delta = max_color_value - min_color_value; + + if (delta == 0) + hue = 0; + else if (max_color_value == red) + hue = int(fmod(((60 * ((green - blue) / delta)) + 360), 360)); + else if (max_color_value == green) + hue = int(fmod(((60 * ((blue - red) / delta)) + 120), 360)); + else if (max_color_value == blue) + hue = int(fmod(((60 * ((red - green) / delta)) + 240), 360)); + + if (max_color_value == 0) + saturation = 0; + else + saturation = delta / max_color_value; + + value = max_color_value; +} + +void hsv_to_rgb(int hue, float saturation, float value, float &red, float &green, float &blue) { + float chroma = value * saturation; + float hue_prime = fmod(hue / 60.0, 6); + float intermediate = chroma * (1 - fabs(fmod(hue_prime, 2) - 1)); + float delta = value - chroma; + + if (0 <= hue_prime && hue_prime < 1) { + red = chroma; + green = intermediate; + blue = 0; + } else if (1 <= hue_prime && hue_prime < 2) { + red = intermediate; + green = chroma; + blue = 0; + } else if (2 <= hue_prime && hue_prime < 3) { + red = 0; + green = chroma; + blue = intermediate; + } else if (3 <= hue_prime && hue_prime < 4) { + red = 0; + green = intermediate; + blue = chroma; + } else if (4 <= hue_prime && hue_prime < 5) { + red = intermediate; + green = 0; + blue = chroma; + } else if (5 <= hue_prime && hue_prime < 6) { + red = chroma; + green = 0; + blue = intermediate; + } else { + red = 0; + green = 0; + blue = 0; + } + + red += delta; + green += delta; + blue += delta; +} + #ifdef USE_ESP8266 IRAM_ATTR InterruptLock::InterruptLock() { xt_state_ = xt_rsil(15); } IRAM_ATTR InterruptLock::~InterruptLock() { xt_wsr_ps(xt_state_); } diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index f5a7a197ca..24b55eade0 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -149,6 +149,11 @@ std::array decode_uint16(uint16_t value); /// Encode a 32-bit unsigned integer given four bytes in MSB -> LSB order uint32_t encode_uint32(uint8_t msb, uint8_t byte2, uint8_t byte3, uint8_t lsb); +/// Convert RGB floats (0-1) to hue (0-360) & saturation/value percentage (0-1) +void rgb_to_hsv(float red, float green, float blue, int &hue, float &saturation, float &value); +/// Convert hue (0-360) & saturation/value percentage (0-1) to RGB floats (0-1) +void hsv_to_rgb(int hue, float saturation, float value, float &red, float &green, float &blue); + /*** * An interrupt helper class. * From c26ea7e4e093cbedc77ca5cd3fc39e062d37a3de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Tue, 28 Sep 2021 23:02:13 +0200 Subject: [PATCH 140/207] Tuya: add cover component (#2279) --- esphome/components/tuya/cover/__init__.py | 54 ++++++++++++++++++ esphome/components/tuya/cover/tuya_cover.cpp | 58 ++++++++++++++++++++ esphome/components/tuya/cover/tuya_cover.h | 33 +++++++++++ tests/test4.yaml | 5 ++ 4 files changed, 150 insertions(+) create mode 100644 esphome/components/tuya/cover/__init__.py create mode 100644 esphome/components/tuya/cover/tuya_cover.cpp create mode 100644 esphome/components/tuya/cover/tuya_cover.h diff --git a/esphome/components/tuya/cover/__init__.py b/esphome/components/tuya/cover/__init__.py new file mode 100644 index 0000000000..7816f39bf8 --- /dev/null +++ b/esphome/components/tuya/cover/__init__.py @@ -0,0 +1,54 @@ +from esphome.components import cover +import esphome.config_validation as cv +import esphome.codegen as cg +from esphome.const import ( + CONF_OUTPUT_ID, + CONF_MIN_VALUE, + CONF_MAX_VALUE, +) +from .. import tuya_ns, CONF_TUYA_ID, Tuya + +DEPENDENCIES = ["tuya"] + +CONF_POSITION_DATAPOINT = "position_datapoint" +CONF_INVERT_POSITION = "invert_position" + +TuyaCover = tuya_ns.class_("TuyaCover", cover.Cover, cg.Component) + + +def validate_range(config): + if config[CONF_MIN_VALUE] > config[CONF_MAX_VALUE]: + raise cv.Invalid( + "min_value({}) cannot be greater than max_value({})".format( + config[CONF_MIN_VALUE], config[CONF_MAX_VALUE] + ) + ) + return config + + +CONFIG_SCHEMA = cv.All( + cover.COVER_SCHEMA.extend( + { + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(TuyaCover), + cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya), + cv.Required(CONF_POSITION_DATAPOINT): cv.uint8_t, + cv.Optional(CONF_MIN_VALUE, default=0): cv.int_, + cv.Optional(CONF_MAX_VALUE, default=100): cv.int_, + cv.Optional(CONF_INVERT_POSITION, default=False): cv.boolean, + }, + ).extend(cv.COMPONENT_SCHEMA), + validate_range, +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_OUTPUT_ID]) + await cg.register_component(var, config) + await cover.register_cover(var, config) + + cg.add(var.set_position_id(config[CONF_POSITION_DATAPOINT])) + cg.add(var.set_min_value(config[CONF_MIN_VALUE])) + cg.add(var.set_max_value(config[CONF_MAX_VALUE])) + cg.add(var.set_invert_position(config[CONF_INVERT_POSITION])) + paren = await cg.get_variable(config[CONF_TUYA_ID]) + cg.add(var.set_tuya_parent(paren)) diff --git a/esphome/components/tuya/cover/tuya_cover.cpp b/esphome/components/tuya/cover/tuya_cover.cpp new file mode 100644 index 0000000000..7da1312938 --- /dev/null +++ b/esphome/components/tuya/cover/tuya_cover.cpp @@ -0,0 +1,58 @@ +#include "esphome/core/log.h" +#include "tuya_cover.h" + +namespace esphome { +namespace tuya { + +static const char *const TAG = "tuya.cover"; + +void TuyaCover::setup() { + this->value_range_ = this->max_value_ - this->min_value_; + if (this->position_id_.has_value()) { + this->parent_->register_listener(*this->position_id_, [this](const TuyaDatapoint &datapoint) { + auto pos = float(datapoint.value_uint - this->min_value_) / this->value_range_; + if (this->invert_position_) + pos = 1.0f - pos; + this->position = pos; + this->publish_state(); + }); + } +} + +void TuyaCover::control(const cover::CoverCall &call) { + if (call.get_stop()) { + auto pos = this->position; + if (this->invert_position_) + pos = 1.0f - pos; + auto position_int = static_cast(pos * this->value_range_); + position_int = position_int + this->min_value_; + + parent_->set_integer_datapoint_value(*this->position_id_, position_int); + } + if (call.get_position().has_value()) { + auto pos = *call.get_position(); + if (this->invert_position_) + pos = 1.0f - pos; + auto position_int = static_cast(pos * this->value_range_); + position_int = position_int + this->min_value_; + + parent_->set_integer_datapoint_value(*this->position_id_, position_int); + } + + this->publish_state(); +} + +void TuyaCover::dump_config() { + ESP_LOGCONFIG(TAG, "Tuya Cover:"); + if (this->position_id_.has_value()) + ESP_LOGCONFIG(TAG, " Position has datapoint ID %u", *this->position_id_); +} + +cover::CoverTraits TuyaCover::get_traits() { + auto traits = cover::CoverTraits(); + traits.set_supports_position(true); + return traits; +} + +} // namespace tuya +} // namespace esphome diff --git a/esphome/components/tuya/cover/tuya_cover.h b/esphome/components/tuya/cover/tuya_cover.h new file mode 100644 index 0000000000..b62e58dc1b --- /dev/null +++ b/esphome/components/tuya/cover/tuya_cover.h @@ -0,0 +1,33 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/tuya/tuya.h" +#include "esphome/components/cover/cover.h" + +namespace esphome { +namespace tuya { + +class TuyaCover : public cover::Cover, public Component { + public: + void setup() override; + void dump_config() override; + void set_position_id(uint8_t dimmer_id) { this->position_id_ = dimmer_id; } + void set_tuya_parent(Tuya *parent) { this->parent_ = parent; } + void set_min_value(uint32_t min_value) { min_value_ = min_value; } + void set_max_value(uint32_t max_value) { max_value_ = max_value; } + void set_invert_position(bool invert_position) { invert_position_ = invert_position; } + + protected: + void control(const cover::CoverCall &call) override; + cover::CoverTraits get_traits() override; + + Tuya *parent_; + optional position_id_{}; + uint32_t min_value_ = 0; + uint32_t max_value_ = 100; + uint32_t value_range_; + bool invert_position_ = false; +}; + +} // namespace tuya +} // namespace esphome diff --git a/tests/test4.yaml b/tests/test4.yaml index a205f0802e..4f2025ad74 100644 --- a/tests/test4.yaml +++ b/tests/test4.yaml @@ -358,6 +358,11 @@ light: warm_white_color_temperature: 500 mireds gamma_correct: 1 +cover: + - platform: tuya + id: tuya_cover + position_datapoint: 2 + display: - platform: addressable_light id: led_matrix_32x8_display From 855c98d81500448a311263d51250f0babf8aaedb Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Tue, 28 Sep 2021 23:15:52 +0200 Subject: [PATCH 141/207] Fix tuya cover lint checks (#2414) --- esphome/components/tuya/cover/__init__.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/esphome/components/tuya/cover/__init__.py b/esphome/components/tuya/cover/__init__.py index 7816f39bf8..5a654841f7 100644 --- a/esphome/components/tuya/cover/__init__.py +++ b/esphome/components/tuya/cover/__init__.py @@ -19,9 +19,7 @@ TuyaCover = tuya_ns.class_("TuyaCover", cover.Cover, cg.Component) def validate_range(config): if config[CONF_MIN_VALUE] > config[CONF_MAX_VALUE]: raise cv.Invalid( - "min_value({}) cannot be greater than max_value({})".format( - config[CONF_MIN_VALUE], config[CONF_MAX_VALUE] - ) + f"min_value ({config[CONF_MIN_VALUE]}) cannot be greater than max_value ({config[CONF_MAX_VALUE]})" ) return config From 7af1c044939ea54f7fc1e74b15bb2fac9d75a11a Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Tue, 28 Sep 2021 23:16:00 +0200 Subject: [PATCH 142/207] Bump debian base to 5.1.0 / 20210902 (#2413) --- docker/Dockerfile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 9c3e864e43..e66c3e1d95 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -5,12 +5,12 @@ # One of "docker", "hassio" ARG BASEIMGTYPE=docker -FROM ghcr.io/hassio-addons/debian-base/amd64:5.0.0 AS base-hassio-amd64 -FROM ghcr.io/hassio-addons/debian-base/aarch64:5.0.0 AS base-hassio-arm64 -FROM ghcr.io/hassio-addons/debian-base/armv7:5.0.0 AS base-hassio-armv7 -FROM debian:bullseye-20210816-slim AS base-docker-amd64 -FROM debian:bullseye-20210816-slim AS base-docker-arm64 -FROM debian:bullseye-20210816-slim AS base-docker-armv7 +FROM ghcr.io/hassio-addons/debian-base/amd64:5.1.0 AS base-hassio-amd64 +FROM ghcr.io/hassio-addons/debian-base/aarch64:5.1.0 AS base-hassio-arm64 +FROM ghcr.io/hassio-addons/debian-base/armv7:5.1.0 AS base-hassio-armv7 +FROM debian:bullseye-20210902-slim AS base-docker-amd64 +FROM debian:bullseye-20210902-slim AS base-docker-arm64 +FROM debian:bullseye-20210902-slim AS base-docker-armv7 # Use TARGETARCH/TARGETVARIANT defined by docker # https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope From 505d1d78fb29c55b024d4d0b7eea5a02e3af7bce Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 29 Sep 2021 12:19:19 +1300 Subject: [PATCH 143/207] Remove default initializations from tuya cover (#2415) --- esphome/components/tuya/cover/tuya_cover.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/tuya/cover/tuya_cover.h b/esphome/components/tuya/cover/tuya_cover.h index b62e58dc1b..c3b0c3e069 100644 --- a/esphome/components/tuya/cover/tuya_cover.h +++ b/esphome/components/tuya/cover/tuya_cover.h @@ -23,10 +23,10 @@ class TuyaCover : public cover::Cover, public Component { Tuya *parent_; optional position_id_{}; - uint32_t min_value_ = 0; - uint32_t max_value_ = 100; + uint32_t min_value_; + uint32_t max_value_; uint32_t value_range_; - bool invert_position_ = false; + bool invert_position_; }; } // namespace tuya From 4f5e4f3b86d0d2069b51757025b0c585bf33fc27 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Wed, 29 Sep 2021 23:21:52 +0200 Subject: [PATCH 144/207] Move #ifdef to after header include (#2417) defines.h needs to be included first. Fixes esphome/issues#2490. --- esphome/components/nextion/nextion_upload.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/nextion/nextion_upload.cpp b/esphome/components/nextion/nextion_upload.cpp index 9a748277d8..cebdbec31a 100644 --- a/esphome/components/nextion/nextion_upload.cpp +++ b/esphome/components/nextion/nextion_upload.cpp @@ -1,6 +1,7 @@ +#include "nextion.h" + #ifdef USE_NEXTION_TFT_UPLOAD -#include "nextion.h" #include "esphome/core/application.h" #include "esphome/core/macros.h" #include "esphome/core/util.h" From 3dfc8d42915f5224a9ac13b6f4be5e4d5828cbac Mon Sep 17 00:00:00 2001 From: WeekendWarrior1 Date: Thu, 30 Sep 2021 07:25:06 +1000 Subject: [PATCH 145/207] String manipulation filters for text sensors (#2393) * initial text sensor filter POC * fixed verbose logging * add append, prepend, substitute filters * add to lower, get to upper working without dummy * clang lint * more linting... * std::move append and prepend filters * fix verbose filter::input logging * value.c_str() in input print * lambda filter verbose log fix * correct log tag, neaten to upper and to lower * add on_raw_value automation/trigger --- esphome/components/text_sensor/__init__.py | 97 +++++++++++++++ esphome/components/text_sensor/automation.h | 7 ++ esphome/components/text_sensor/filter.cpp | 74 ++++++++++++ esphome/components/text_sensor/filter.h | 112 ++++++++++++++++++ .../components/text_sensor/text_sensor.cpp | 59 ++++++++- esphome/components/text_sensor/text_sensor.h | 29 ++++- 6 files changed, 373 insertions(+), 5 deletions(-) create mode 100644 esphome/components/text_sensor/filter.cpp create mode 100644 esphome/components/text_sensor/filter.h diff --git a/esphome/components/text_sensor/__init__.py b/esphome/components/text_sensor/__init__.py index d06f12de0e..cdb4b85e9a 100644 --- a/esphome/components/text_sensor/__init__.py +++ b/esphome/components/text_sensor/__init__.py @@ -4,16 +4,22 @@ from esphome import automation from esphome.components import mqtt from esphome.const import ( CONF_DISABLED_BY_DEFAULT, + CONF_FILTERS, CONF_ICON, CONF_ID, CONF_INTERNAL, CONF_ON_VALUE, + CONF_ON_RAW_VALUE, CONF_TRIGGER_ID, CONF_MQTT_ID, CONF_NAME, CONF_STATE, + CONF_FROM, + CONF_TO, ) from esphome.core import CORE, coroutine_with_priority +from esphome.util import Registry + IS_PLATFORM_COMPONENT = True @@ -25,6 +31,9 @@ TextSensorPtr = TextSensor.operator("ptr") TextSensorStateTrigger = text_sensor_ns.class_( "TextSensorStateTrigger", automation.Trigger.template(cg.std_string) ) +TextSensorStateRawTrigger = text_sensor_ns.class_( + "TextSensorStateRawTrigger", automation.Trigger.template(cg.std_string) +) TextSensorPublishAction = text_sensor_ns.class_( "TextSensorPublishAction", automation.Action ) @@ -32,21 +41,101 @@ TextSensorStateCondition = text_sensor_ns.class_( "TextSensorStateCondition", automation.Condition ) +FILTER_REGISTRY = Registry() +validate_filters = cv.validate_registry("filter", FILTER_REGISTRY) + +# Filters +Filter = text_sensor_ns.class_("Filter") +LambdaFilter = text_sensor_ns.class_("LambdaFilter", Filter) +ToUpperFilter = text_sensor_ns.class_("ToUpperFilter", Filter) +ToLowerFilter = text_sensor_ns.class_("ToLowerFilter", Filter) +AppendFilter = text_sensor_ns.class_("AppendFilter", Filter) +PrependFilter = text_sensor_ns.class_("PrependFilter", Filter) +SubstituteFilter = text_sensor_ns.class_("SubstituteFilter", Filter) + + +@FILTER_REGISTRY.register("lambda", LambdaFilter, cv.returning_lambda) +async def lambda_filter_to_code(config, filter_id): + lambda_ = await cg.process_lambda( + config, [(cg.std_string, "x")], return_type=cg.optional.template(cg.std_string) + ) + return cg.new_Pvariable(filter_id, lambda_) + + +@FILTER_REGISTRY.register("to_upper", ToUpperFilter, {}) +async def to_upper_filter_to_code(config, filter_id): + return cg.new_Pvariable(filter_id) + + +@FILTER_REGISTRY.register("to_lower", ToLowerFilter, {}) +async def to_lower_filter_to_code(config, filter_id): + return cg.new_Pvariable(filter_id) + + +@FILTER_REGISTRY.register("append", AppendFilter, cv.string) +async def append_filter_to_code(config, filter_id): + return cg.new_Pvariable(filter_id, config) + + +@FILTER_REGISTRY.register("prepend", PrependFilter, cv.string) +async def prepend_filter_to_code(config, filter_id): + return cg.new_Pvariable(filter_id, config) + + +def validate_substitute(value): + if isinstance(value, dict): + return cv.Schema( + { + cv.Required(CONF_FROM): cv.string, + cv.Required(CONF_TO): cv.string, + } + )(value) + value = cv.string(value) + if "->" not in value: + raise cv.Invalid("Substitute mapping must contain '->'") + a, b = value.split("->", 1) + a, b = a.strip(), b.strip() + return validate_substitute({CONF_FROM: cv.string(a), CONF_TO: cv.string(b)}) + + +@FILTER_REGISTRY.register( + "substitute", + SubstituteFilter, + cv.All(cv.ensure_list(validate_substitute), cv.Length(min=2)), +) +async def substitute_filter_to_code(config, filter_id): + from_strings = [conf[CONF_FROM] for conf in config] + to_strings = [conf[CONF_TO] for conf in config] + return cg.new_Pvariable(filter_id, from_strings, to_strings) + + icon = cv.icon TEXT_SENSOR_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend( { cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTTextSensor), cv.Optional(CONF_ICON): icon, + cv.Optional(CONF_FILTERS): validate_filters, cv.Optional(CONF_ON_VALUE): automation.validate_automation( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TextSensorStateTrigger), } ), + cv.Optional(CONF_ON_RAW_VALUE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + TextSensorStateRawTrigger + ), + } + ), } ) +async def build_filters(config): + return await cg.build_registry_list(FILTER_REGISTRY, config) + + async def setup_text_sensor_core_(var, config): cg.add(var.set_name(config[CONF_NAME])) cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT])) @@ -55,10 +144,18 @@ async def setup_text_sensor_core_(var, config): if CONF_ICON in config: cg.add(var.set_icon(config[CONF_ICON])) + if config.get(CONF_FILTERS): # must exist and not be empty + filters = await build_filters(config[CONF_FILTERS]) + cg.add(var.set_filters(filters)) + for conf in config.get(CONF_ON_VALUE, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation(trigger, [(cg.std_string, "x")], conf) + for conf in config.get(CONF_ON_RAW_VALUE, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [(cg.std_string, "x")], conf) + if CONF_MQTT_ID in config: mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var) await mqtt.register_mqtt_component(mqtt_, config) diff --git a/esphome/components/text_sensor/automation.h b/esphome/components/text_sensor/automation.h index d82fd27c1f..dd02aeaf3b 100644 --- a/esphome/components/text_sensor/automation.h +++ b/esphome/components/text_sensor/automation.h @@ -16,6 +16,13 @@ class TextSensorStateTrigger : public Trigger { } }; +class TextSensorStateRawTrigger : public Trigger { + public: + explicit TextSensorStateRawTrigger(TextSensor *parent) { + parent->add_on_raw_state_callback([this](std::string value) { this->trigger(std::move(value)); }); + } +}; + template class TextSensorStateCondition : public Condition { public: explicit TextSensorStateCondition(TextSensor *parent) : parent_(parent) {} diff --git a/esphome/components/text_sensor/filter.cpp b/esphome/components/text_sensor/filter.cpp new file mode 100644 index 0000000000..14df6238ff --- /dev/null +++ b/esphome/components/text_sensor/filter.cpp @@ -0,0 +1,74 @@ +#include "filter.h" +#include "text_sensor.h" +#include "esphome/core/log.h" +#include "esphome/core/hal.h" + +namespace esphome { +namespace text_sensor { + +static const char *const TAG = "text_sensor.filter"; + +// Filter +void Filter::input(const std::string &value) { + ESP_LOGVV(TAG, "Filter(%p)::input(%s)", this, value.c_str()); + optional out = this->new_value(value); + if (out.has_value()) + this->output(*out); +} +void Filter::output(const std::string &value) { + if (this->next_ == nullptr) { + ESP_LOGVV(TAG, "Filter(%p)::output(%s) -> SENSOR", this, value.c_str()); + this->parent_->internal_send_state_to_frontend(value); + } else { + ESP_LOGVV(TAG, "Filter(%p)::output(%s) -> %p", this, value.c_str(), this->next_); + this->next_->input(value); + } +} +void Filter::initialize(TextSensor *parent, Filter *next) { + ESP_LOGVV(TAG, "Filter(%p)::initialize(parent=%p next=%p)", this, parent, next); + this->parent_ = parent; + this->next_ = next; +} + +// LambdaFilter +LambdaFilter::LambdaFilter(lambda_filter_t lambda_filter) : lambda_filter_(std::move(lambda_filter)) {} +const lambda_filter_t &LambdaFilter::get_lambda_filter() const { return this->lambda_filter_; } +void LambdaFilter::set_lambda_filter(const lambda_filter_t &lambda_filter) { this->lambda_filter_ = lambda_filter; } + +optional LambdaFilter::new_value(std::string value) { + auto it = this->lambda_filter_(value); + ESP_LOGVV(TAG, "LambdaFilter(%p)::new_value(%s) -> %s", this, value.c_str(), it.value_or("").c_str()); + return it; +} + +// ToUpperFilter +optional ToUpperFilter::new_value(std::string value) { + for (char &c : value) + c = ::toupper(c); + return value; +} + +// ToLowerFilter +optional ToLowerFilter::new_value(std::string value) { + for (char &c : value) + c = ::toupper(c); + return value; +} + +// Append +optional AppendFilter::new_value(std::string value) { return value + this->suffix_; } + +// Prepend +optional PrependFilter::new_value(std::string value) { return this->prefix_ + value; } + +// Substitute +optional SubstituteFilter::new_value(std::string value) { + std::size_t pos; + for (int i = 0; i < this->from_strings_.size(); i++) + while ((pos = value.find(this->from_strings_[i])) != std::string::npos) + value.replace(pos, this->from_strings_[i].size(), this->to_strings_[i]); + return value; +} + +} // namespace text_sensor +} // namespace esphome diff --git a/esphome/components/text_sensor/filter.h b/esphome/components/text_sensor/filter.h new file mode 100644 index 0000000000..6a1d9ab04e --- /dev/null +++ b/esphome/components/text_sensor/filter.h @@ -0,0 +1,112 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/helpers.h" +#include +#include + +namespace esphome { +namespace text_sensor { + +class TextSensor; + +/** Apply a filter to text sensor values such as to_upper. + * + * This class is purposefully kept quite simple, since more complicated + * filters should really be done with the filter sensor in Home Assistant. + */ +class Filter { + public: + /** This will be called every time the filter receives a new value. + * + * It can return an empty optional to indicate that the filter chain + * should stop, otherwise the value in the filter will be passed down + * the chain. + * + * @param value The new value. + * @return An optional string, the new value that should be pushed out. + */ + virtual optional new_value(std::string value); + + /// Initialize this filter, please note this can be called more than once. + virtual void initialize(TextSensor *parent, Filter *next); + + void input(const std::string &value); + + void output(const std::string &value); + + protected: + friend TextSensor; + + Filter *next_{nullptr}; + TextSensor *parent_{nullptr}; +}; + +using lambda_filter_t = std::function(std::string)>; + +/** This class allows for creation of simple template filters. + * + * The constructor accepts a lambda of the form std::string -> optional. + * It will be called with each new value in the filter chain and returns the modified + * value that shall be passed down the filter chain. Returning an empty Optional + * means that the value shall be discarded. + */ +class LambdaFilter : public Filter { + public: + explicit LambdaFilter(lambda_filter_t lambda_filter); + + optional new_value(std::string value) override; + + const lambda_filter_t &get_lambda_filter() const; + void set_lambda_filter(const lambda_filter_t &lambda_filter); + + protected: + lambda_filter_t lambda_filter_; +}; + +/// A simple filter that converts all text to uppercase +class ToUpperFilter : public Filter { + public: + optional new_value(std::string value) override; +}; + +/// A simple filter that converts all text to lowercase +class ToLowerFilter : public Filter { + public: + optional new_value(std::string value) override; +}; + +/// A simple filter that adds a string to the end of another string +class AppendFilter : public Filter { + public: + AppendFilter(std::string suffix) : suffix_(std::move(suffix)) {} + optional new_value(std::string value) override; + + protected: + std::string suffix_; +}; + +/// A simple filter that adds a string to the start of another string +class PrependFilter : public Filter { + public: + PrependFilter(std::string prefix) : prefix_(std::move(prefix)) {} + optional new_value(std::string value) override; + + protected: + std::string prefix_; +}; + +/// A simple filter that replaces a substring with another substring +class SubstituteFilter : public Filter { + public: + SubstituteFilter(std::vector from_strings, std::vector to_strings) + : from_strings_(std::move(from_strings)), to_strings_(std::move(to_strings)) {} + optional new_value(std::string value) override; + + protected: + std::vector from_strings_; + std::vector to_strings_; +}; + +} // namespace text_sensor +} // namespace esphome diff --git a/esphome/components/text_sensor/text_sensor.cpp b/esphome/components/text_sensor/text_sensor.cpp index 8738860d55..774f3a8cb6 100644 --- a/esphome/components/text_sensor/text_sensor.cpp +++ b/esphome/components/text_sensor/text_sensor.cpp @@ -10,21 +10,72 @@ TextSensor::TextSensor() : TextSensor("") {} TextSensor::TextSensor(const std::string &name) : Nameable(name) {} void TextSensor::publish_state(const std::string &state) { - this->state = state; + this->raw_state = state; + this->raw_callback_.call(state); + + ESP_LOGV(TAG, "'%s': Received new state %s", this->name_.c_str(), state.c_str()); + + if (this->filter_list_ == nullptr) { + this->internal_send_state_to_frontend(state); + } else { + this->filter_list_->input(state); + } +} + +void TextSensor::add_filter(Filter *filter) { + // inefficient, but only happens once on every sensor setup and nobody's going to have massive amounts of + // filters + ESP_LOGVV(TAG, "TextSensor(%p)::add_filter(%p)", this, filter); + if (this->filter_list_ == nullptr) { + this->filter_list_ = filter; + } else { + Filter *last_filter = this->filter_list_; + while (last_filter->next_ != nullptr) + last_filter = last_filter->next_; + last_filter->initialize(this, filter); + } + filter->initialize(this, nullptr); +} +void TextSensor::add_filters(const std::vector &filters) { + for (Filter *filter : filters) { + this->add_filter(filter); + } +} +void TextSensor::set_filters(const std::vector &filters) { + this->clear_filters(); + this->add_filters(filters); +} +void TextSensor::clear_filters() { + if (this->filter_list_ != nullptr) { + ESP_LOGVV(TAG, "TextSensor(%p)::clear_filters()", this); + } + this->filter_list_ = nullptr; +} + +void TextSensor::add_on_state_callback(std::function callback) { + this->callback_.add(std::move(callback)); +} +void TextSensor::add_on_raw_state_callback(std::function callback) { + this->raw_callback_.add(std::move(callback)); +} + +std::string TextSensor::get_state() const { return this->state; } +std::string TextSensor::get_raw_state() const { return this->raw_state; } +void TextSensor::internal_send_state_to_frontend(const std::string &state) { + this->state = this->raw_state; this->has_state_ = true; ESP_LOGD(TAG, "'%s': Sending state '%s'", this->name_.c_str(), state.c_str()); this->callback_.call(state); } + void TextSensor::set_icon(const std::string &icon) { this->icon_ = icon; } -void TextSensor::add_on_state_callback(std::function callback) { - this->callback_.add(std::move(callback)); -} std::string TextSensor::get_icon() { if (this->icon_.has_value()) return *this->icon_; return this->icon(); } std::string TextSensor::icon() { return ""; } + std::string TextSensor::unique_id() { return ""; } bool TextSensor::has_state() { return this->has_state_; } uint32_t TextSensor::hash_base() { return 334300109UL; } diff --git a/esphome/components/text_sensor/text_sensor.h b/esphome/components/text_sensor/text_sensor.h index 5293f0d216..7804deedb6 100644 --- a/esphome/components/text_sensor/text_sensor.h +++ b/esphome/components/text_sensor/text_sensor.h @@ -2,6 +2,7 @@ #include "esphome/core/component.h" #include "esphome/core/helpers.h" +#include "esphome/components/text_sensor/filter.h" namespace esphome { namespace text_sensor { @@ -22,13 +23,33 @@ class TextSensor : public Nameable { explicit TextSensor(); explicit TextSensor(const std::string &name); + /// Getter-syntax for .state. + std::string get_state() const; + /// Getter-syntax for .raw_state + std::string get_raw_state() const; + void publish_state(const std::string &state); void set_icon(const std::string &icon); + /// Add a filter to the filter chain. Will be appended to the back. + void add_filter(Filter *filter); + + /// Add a list of vectors to the back of the filter chain. + void add_filters(const std::vector &filters); + + /// Clear the filters and replace them by filters. + void set_filters(const std::vector &filters); + + /// Clear the entire filter chain. + void clear_filters(); + void add_on_state_callback(std::function callback); + /// Add a callback that will be called every time the sensor sends a raw value. + void add_on_raw_state_callback(std::function callback); std::string state; + std::string raw_state; // ========== INTERNAL METHODS ========== // (In most use cases you won't need these) @@ -40,10 +61,16 @@ class TextSensor : public Nameable { bool has_state(); + void internal_send_state_to_frontend(const std::string &state); + protected: uint32_t hash_base() override; - CallbackManager callback_; + CallbackManager raw_callback_; ///< Storage for raw state callbacks. + CallbackManager callback_; ///< Storage for filtered state callbacks. + + Filter *filter_list_{nullptr}; ///< Store all active filters. + optional icon_; bool has_state_{false}; }; From 946db3fd506509368a7d18aa070eef133ffaeb3f Mon Sep 17 00:00:00 2001 From: Andy Allsopp Date: Thu, 30 Sep 2021 12:03:30 +0100 Subject: [PATCH 146/207] Add viewport meta tag to web server layout (#2419) * Update web_server.cpp Added viewport meta tag to web_server.cpp in order to better control layout on mobile browsers. Adds 70 characters. Vastly improves accessibility on mobile devices. * Update web_server.cpp split line to meet clang format requirement. * Update web_server.cpp Reworked line break for clangtidy --- esphome/components/web_server/web_server.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 28e741cf24..d72262b4d3 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -164,7 +164,9 @@ float WebServer::get_setup_priority() const { return setup_priority::WIFI - 1.0f void WebServer::handle_index_request(AsyncWebServerRequest *request) { AsyncResponseStream *stream = request->beginResponseStream("text/html"); std::string title = App.get_name() + " Web Server"; - stream->print(F("")); + stream->print(F("<!DOCTYPE html><html lang=\"en\"><head><meta charset=UTF-8>" + "<meta name=\"viewport\" content=\"width=device-width, " + "initial-scale=1.0\"><title>")); stream->print(title.c_str()); stream->print(F("")); #ifdef WEBSERVER_CSS_INCLUDE From 0e4f1ac40d58a17af878e7c4bf6ae0bb14ceb543 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Thu, 30 Sep 2021 16:24:02 +0200 Subject: [PATCH 147/207] Fix default environment for clang-tidy (#2420) * Drop unnecessary platformio call from script/lint-cpp * Default environment for clang-tidy to esp32-tidy --- script/clang-tidy | 2 +- script/lint-cpp | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/script/clang-tidy b/script/clang-tidy index 6e79059372..87ba1c84b5 100755 --- a/script/clang-tidy +++ b/script/clang-tidy @@ -122,7 +122,7 @@ def main(): parser.add_argument('-j', '--jobs', type=int, default=multiprocessing.cpu_count(), help='number of tidy instances to be run in parallel.') - parser.add_argument('-e', '--environment', default='esp8266-tidy', + parser.add_argument('-e', '--environment', default='esp32-tidy', help='the PlatformIO environment to run against (esp8266-tidy or esp32-tidy)') parser.add_argument('files', nargs='*', default=[], help='files to be processed (regex on path)') diff --git a/script/lint-cpp b/script/lint-cpp index 170d61d539..ac03ca0f23 100755 --- a/script/lint-cpp +++ b/script/lint-cpp @@ -3,9 +3,6 @@ set -e cd "$(dirname "$0")/.." -if [[ ! -e ".gcc-flags.json" ]]; then - pio init --ide atom -fi set -x From 5b0fbbaada91766de1c4ee950378a82bc8116149 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Thu, 30 Sep 2021 16:25:08 +0200 Subject: [PATCH 148/207] Replace std::move() with const references where possible (#2421) * Replace std::move() with const references where possible * Fix formatting --- esphome/components/http_request/http_request.h | 2 +- esphome/components/pipsolar/output/pipsolar_output.h | 2 +- esphome/components/pipsolar/switch/pipsolar_switch.h | 4 ++-- esphome/components/rf_bridge/rf_bridge.h | 3 +-- esphome/components/select/automation.h | 2 +- esphome/components/sim800l/sim800l.h | 2 +- esphome/components/template/select/template_select.h | 2 +- esphome/components/text_sensor/automation.h | 4 ++-- 8 files changed, 10 insertions(+), 11 deletions(-) diff --git a/esphome/components/http_request/http_request.h b/esphome/components/http_request/http_request.h index 511096e7fa..9cc027b58d 100644 --- a/esphome/components/http_request/http_request.h +++ b/esphome/components/http_request/http_request.h @@ -40,7 +40,7 @@ class HttpRequestComponent : public Component { void set_method(const char *method) { this->method_ = method; } void set_useragent(const char *useragent) { this->useragent_ = useragent; } void set_timeout(uint16_t timeout) { this->timeout_ = timeout; } - void set_body(std::string body) { this->body_ = std::move(body); } + void set_body(const std::string &body) { this->body_ = body; } void set_headers(std::list
headers) { this->headers_ = std::move(headers); } void send(const std::vector &response_triggers); void close(); diff --git a/esphome/components/pipsolar/output/pipsolar_output.h b/esphome/components/pipsolar/output/pipsolar_output.h index 932efe01c2..fe783cf034 100644 --- a/esphome/components/pipsolar/output/pipsolar_output.h +++ b/esphome/components/pipsolar/output/pipsolar_output.h @@ -13,7 +13,7 @@ class PipsolarOutput : public output::FloatOutput { public: PipsolarOutput() {} void set_parent(Pipsolar *parent) { this->parent_ = parent; } - void set_set_command(std::string command) { this->set_command_ = std::move(command); }; + void set_set_command(const std::string &command) { this->set_command_ = command; }; void set_possible_values(std::vector possible_values) { this->possible_values_ = std::move(possible_values); } void set_value(float value) { this->write_state(value); }; diff --git a/esphome/components/pipsolar/switch/pipsolar_switch.h b/esphome/components/pipsolar/switch/pipsolar_switch.h index 3fe4c7dfa1..11ff6c853a 100644 --- a/esphome/components/pipsolar/switch/pipsolar_switch.h +++ b/esphome/components/pipsolar/switch/pipsolar_switch.h @@ -10,8 +10,8 @@ class Pipsolar; class PipsolarSwitch : public switch_::Switch, public Component { public: void set_parent(Pipsolar *parent) { this->parent_ = parent; }; - void set_on_command(std::string command) { this->on_command_ = std::move(command); }; - void set_off_command(std::string command) { this->off_command_ = std::move(command); }; + void set_on_command(const std::string &command) { this->on_command_ = command; }; + void set_off_command(const std::string &command) { this->off_command_ = command; }; void dump_config() override; protected: diff --git a/esphome/components/rf_bridge/rf_bridge.h b/esphome/components/rf_bridge/rf_bridge.h index 2fa4eb05c5..9156d995bc 100644 --- a/esphome/components/rf_bridge/rf_bridge.h +++ b/esphome/components/rf_bridge/rf_bridge.h @@ -85,8 +85,7 @@ class RFBridgeReceivedCodeTrigger : public Trigger { class RFBridgeReceivedAdvancedCodeTrigger : public Trigger { public: explicit RFBridgeReceivedAdvancedCodeTrigger(RFBridgeComponent *parent) { - parent->add_on_advanced_code_received_callback( - [this](RFBridgeAdvancedData data) { this->trigger(std::move(data)); }); + parent->add_on_advanced_code_received_callback([this](const RFBridgeAdvancedData &data) { this->trigger(data); }); } }; diff --git a/esphome/components/select/automation.h b/esphome/components/select/automation.h index 59525f879e..1e0bfed63d 100644 --- a/esphome/components/select/automation.h +++ b/esphome/components/select/automation.h @@ -10,7 +10,7 @@ namespace select { class SelectStateTrigger : public Trigger { public: explicit SelectStateTrigger(Select *parent) { - parent->add_on_state_callback([this](std::string value) { this->trigger(std::move(value)); }); + parent->add_on_state_callback([this](const std::string &value) { this->trigger(value); }); } }; diff --git a/esphome/components/sim800l/sim800l.h b/esphome/components/sim800l/sim800l.h index fa9c392bfc..21e9ac4a50 100644 --- a/esphome/components/sim800l/sim800l.h +++ b/esphome/components/sim800l/sim800l.h @@ -74,7 +74,7 @@ class Sim800LReceivedMessageTrigger : public Trigger { public: explicit Sim800LReceivedMessageTrigger(Sim800LComponent *parent) { parent->add_on_sms_received_callback( - [this](std::string message, std::string sender) { this->trigger(std::move(message), std::move(sender)); }); + [this](const std::string &message, const std::string &sender) { this->trigger(message, sender); }); } }; diff --git a/esphome/components/template/select/template_select.h b/esphome/components/template/select/template_select.h index e24eb6e880..2f00765c3d 100644 --- a/esphome/components/template/select/template_select.h +++ b/esphome/components/template/select/template_select.h @@ -19,7 +19,7 @@ class TemplateSelect : public select::Select, public PollingComponent { Trigger *get_set_trigger() const { return this->set_trigger_; } void set_optimistic(bool optimistic) { this->optimistic_ = optimistic; } - void set_initial_option(std::string initial_option) { this->initial_option_ = std::move(initial_option); } + void set_initial_option(const std::string &initial_option) { this->initial_option_ = initial_option; } void set_restore_value(bool restore_value) { this->restore_value_ = restore_value; } protected: diff --git a/esphome/components/text_sensor/automation.h b/esphome/components/text_sensor/automation.h index dd02aeaf3b..d7286845e0 100644 --- a/esphome/components/text_sensor/automation.h +++ b/esphome/components/text_sensor/automation.h @@ -12,14 +12,14 @@ namespace text_sensor { class TextSensorStateTrigger : public Trigger { public: explicit TextSensorStateTrigger(TextSensor *parent) { - parent->add_on_state_callback([this](std::string value) { this->trigger(std::move(value)); }); + parent->add_on_state_callback([this](const std::string &value) { this->trigger(value); }); } }; class TextSensorStateRawTrigger : public Trigger { public: explicit TextSensorStateRawTrigger(TextSensor *parent) { - parent->add_on_raw_state_callback([this](std::string value) { this->trigger(std::move(value)); }); + parent->add_on_raw_state_callback([this](const std::string &value) { this->trigger(value); }); } }; From 1031ea431348893eb30e1866b327c86bcfabcaf2 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Thu, 30 Sep 2021 18:07:28 +0200 Subject: [PATCH 149/207] Fix line endings normalization (#2407) * Strip CRLF line endings from modbus controller files * Normalize all line endings to LF --- .gitattributes | 3 +- .../modbus_controller/number/modbus_number.h | 96 +++++++++---------- .../modbus_controller/output/modbus_output.h | 90 ++++++++--------- 3 files changed, 95 insertions(+), 94 deletions(-) diff --git a/.gitattributes b/.gitattributes index 94f480de94..dad0966222 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ -* text=auto eol=lf \ No newline at end of file +# Normalize line endings to LF in the repository +* text eol=lf diff --git a/esphome/components/modbus_controller/number/modbus_number.h b/esphome/components/modbus_controller/number/modbus_number.h index 0fd4e314bc..271bbfac50 100644 --- a/esphome/components/modbus_controller/number/modbus_number.h +++ b/esphome/components/modbus_controller/number/modbus_number.h @@ -1,48 +1,48 @@ -#pragma once - -#include "esphome/components/number/number.h" -#include "esphome/components/modbus_controller/modbus_controller.h" -#include "esphome/core/component.h" - -namespace esphome { -namespace modbus_controller { - -using value_to_data_t = std::function(float); - -class ModbusNumber : public number::Number, public Component, public SensorItem { - public: - ModbusNumber(uint16_t start_address, uint8_t offset, uint32_t bitmask, SensorValueType value_type, int register_count, - uint8_t skip_updates, bool force_new_range) - : number::Number(), Component(), SensorItem() { - this->register_type = ModbusRegisterType::HOLDING; - this->start_address = start_address; - this->offset = offset; - this->bitmask = bitmask; - this->sensor_value_type = value_type; - this->register_count = register_count; - this->skip_updates = skip_updates; - this->force_new_range = force_new_range; - }; - - void dump_config() override; - void parse_and_publish(const std::vector &data) override; - float get_setup_priority() const override { return setup_priority::HARDWARE; } - void set_update_interval(int) {} - void set_parent(ModbusController *parent) { this->parent_ = parent; } - void set_write_multiply(float factor) { multiply_by_ = factor; } - - using transform_func_t = std::function(ModbusNumber *, float, const std::vector &)>; - using write_transform_func_t = std::function(ModbusNumber *, float, std::vector &)>; - void set_template(transform_func_t &&f) { this->transform_func_ = f; } - void set_write_template(write_transform_func_t &&f) { this->write_transform_func_ = f; } - - protected: - void control(float value) override; - optional transform_func_; - optional write_transform_func_; - ModbusController *parent_; - float multiply_by_{1.0}; -}; - -} // namespace modbus_controller -} // namespace esphome +#pragma once + +#include "esphome/components/number/number.h" +#include "esphome/components/modbus_controller/modbus_controller.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace modbus_controller { + +using value_to_data_t = std::function(float); + +class ModbusNumber : public number::Number, public Component, public SensorItem { + public: + ModbusNumber(uint16_t start_address, uint8_t offset, uint32_t bitmask, SensorValueType value_type, int register_count, + uint8_t skip_updates, bool force_new_range) + : number::Number(), Component(), SensorItem() { + this->register_type = ModbusRegisterType::HOLDING; + this->start_address = start_address; + this->offset = offset; + this->bitmask = bitmask; + this->sensor_value_type = value_type; + this->register_count = register_count; + this->skip_updates = skip_updates; + this->force_new_range = force_new_range; + }; + + void dump_config() override; + void parse_and_publish(const std::vector &data) override; + float get_setup_priority() const override { return setup_priority::HARDWARE; } + void set_update_interval(int) {} + void set_parent(ModbusController *parent) { this->parent_ = parent; } + void set_write_multiply(float factor) { multiply_by_ = factor; } + + using transform_func_t = std::function(ModbusNumber *, float, const std::vector &)>; + using write_transform_func_t = std::function(ModbusNumber *, float, std::vector &)>; + void set_template(transform_func_t &&f) { this->transform_func_ = f; } + void set_write_template(write_transform_func_t &&f) { this->write_transform_func_ = f; } + + protected: + void control(float value) override; + optional transform_func_; + optional write_transform_func_; + ModbusController *parent_; + float multiply_by_{1.0}; +}; + +} // namespace modbus_controller +} // namespace esphome diff --git a/esphome/components/modbus_controller/output/modbus_output.h b/esphome/components/modbus_controller/output/modbus_output.h index f46aef4683..053186a321 100644 --- a/esphome/components/modbus_controller/output/modbus_output.h +++ b/esphome/components/modbus_controller/output/modbus_output.h @@ -1,45 +1,45 @@ -#pragma once - -#include "esphome/components/output/float_output.h" -#include "esphome/components/modbus_controller/modbus_controller.h" -#include "esphome/core/component.h" - -namespace esphome { -namespace modbus_controller { - -using value_to_data_t = std::function(float); - -class ModbusOutput : public output::FloatOutput, public Component, public SensorItem { - public: - ModbusOutput(uint16_t start_address, uint8_t offset, SensorValueType value_type) - : output::FloatOutput(), Component() { - this->register_type = ModbusRegisterType::HOLDING; - this->start_address = start_address; - this->offset = offset; - this->bitmask = bitmask; - this->sensor_value_type = value_type; - this->skip_updates = 0; - this->start_address += offset; - this->offset = 0; - } - void setup() override; - void dump_config() override; - - void set_parent(ModbusController *parent) { this->parent_ = parent; } - void set_write_multiply(float factor) { multiply_by_ = factor; } - // Do nothing - void parse_and_publish(const std::vector &data) override{}; - - using write_transform_func_t = std::function(ModbusOutput *, float, std::vector &)>; - void set_write_template(write_transform_func_t &&f) { this->write_transform_func_ = f; } - - protected: - void write_state(float value) override; - optional write_transform_func_{nullopt}; - - ModbusController *parent_; - float multiply_by_{1.0}; -}; - -} // namespace modbus_controller -} // namespace esphome +#pragma once + +#include "esphome/components/output/float_output.h" +#include "esphome/components/modbus_controller/modbus_controller.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace modbus_controller { + +using value_to_data_t = std::function(float); + +class ModbusOutput : public output::FloatOutput, public Component, public SensorItem { + public: + ModbusOutput(uint16_t start_address, uint8_t offset, SensorValueType value_type) + : output::FloatOutput(), Component() { + this->register_type = ModbusRegisterType::HOLDING; + this->start_address = start_address; + this->offset = offset; + this->bitmask = bitmask; + this->sensor_value_type = value_type; + this->skip_updates = 0; + this->start_address += offset; + this->offset = 0; + } + void setup() override; + void dump_config() override; + + void set_parent(ModbusController *parent) { this->parent_ = parent; } + void set_write_multiply(float factor) { multiply_by_ = factor; } + // Do nothing + void parse_and_publish(const std::vector &data) override{}; + + using write_transform_func_t = std::function(ModbusOutput *, float, std::vector &)>; + void set_write_template(write_transform_func_t &&f) { this->write_transform_func_ = f; } + + protected: + void write_state(float value) override; + optional write_transform_func_{nullopt}; + + ModbusController *parent_; + float multiply_by_{1.0}; +}; + +} // namespace modbus_controller +} // namespace esphome From c89018a4319ada1a5fe96e6f368e194721cb956c Mon Sep 17 00:00:00 2001 From: Maurice Makaay Date: Thu, 30 Sep 2021 18:08:15 +0200 Subject: [PATCH 150/207] Option to ignore CRC for EFuse MAC address (#2399) * Accept changes as proposed by black. * Added test and implemented optional correctly. * Disable PHY RF full calibration (because it calls the breaking MAC retrieval function). * Disable CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE instead of enable, dummy! * Rename CONF_IGNORE_EFUSE_MAC_CRC to CONF_ESP32_IGNORE_EFUSE_MAC_CRC. * Removed unused import. * Fix ordering of constants. * Moved all MAC address logic to core helpers. * Use pretty MAC address for the log. * Use standard MAC formatter function for debug component. * Fix clang-formatting. * Fix clang-formatting. * Brought wording of comments in line with other function-describing comments. * Processed code review by @OttoWinter * Add USE_ESP32_IGNORE_EFUSE_MAC_CRC to defines.h Co-authored-by: Maurice Makaay --- esphome/components/debug/debug_component.cpp | 5 +-- esphome/components/esp32/__init__.py | 13 +++++++ .../wifi/wifi_component_esp_idf.cpp | 6 ++++ esphome/const.py | 2 ++ esphome/core/defines.h | 1 + esphome/core/helpers.cpp | 34 ++++++++++++++----- esphome/core/helpers.h | 12 ++++++- tests/test5.yaml | 2 ++ 8 files changed, 61 insertions(+), 14 deletions(-) diff --git a/esphome/components/debug/debug_component.cpp b/esphome/components/debug/debug_component.cpp index 7fd8956148..b856733121 100644 --- a/esphome/components/debug/debug_component.cpp +++ b/esphome/components/debug/debug_component.cpp @@ -104,10 +104,7 @@ void DebugComponent::dump_config() { ESP_LOGD(TAG, "ESP-IDF Version: %s", esp_get_idf_version()); - uint64_t chip_mac = 0LL; - esp_efuse_mac_get_default((uint8_t *) (&chip_mac)); - std::string mac = uint64_to_string(chip_mac); - ESP_LOGD(TAG, "EFuse MAC: %s", mac.c_str()); + ESP_LOGD(TAG, "EFuse MAC: %s", get_mac_address_pretty().c_str()); const char *reset_reason; switch (rtc_get_reset_reason(0)) { diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 719b7b5f31..1316c7ccbe 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -10,6 +10,8 @@ from esphome.const import ( CONF_TYPE, CONF_VARIANT, CONF_VERSION, + CONF_ADVANCED, + CONF_IGNORE_EFUSE_MAC_CRC, KEY_CORE, KEY_FRAMEWORK_VERSION, KEY_TARGET_FRAMEWORK, @@ -230,6 +232,11 @@ ESP_IDF_FRAMEWORK_SCHEMA = cv.All( cv.string_strict: cv.string_strict }, cv.Optional(CONF_PLATFORM_VERSION): cv.string_strict, + cv.Optional(CONF_ADVANCED, default={}): cv.Schema( + { + cv.Optional(CONF_IGNORE_EFUSE_MAC_CRC, default=False): cv.boolean, + } + ), } ), _esp_idf_check_versions, @@ -295,6 +302,12 @@ async def to_code(config): for name, value in conf[CONF_SDKCONFIG_OPTIONS].items(): add_idf_sdkconfig_option(name, RawSdkconfigValue(value)) + if conf[CONF_ADVANCED][CONF_IGNORE_EFUSE_MAC_CRC]: + cg.add_define("USE_ESP32_IGNORE_EFUSE_MAC_CRC") + add_idf_sdkconfig_option( + "CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE", False + ) + elif conf[CONF_TYPE] == FRAMEWORK_ARDUINO: cg.add_platformio_option( "platform", f"espressif32 @ {conf[CONF_PLATFORM_VERSION]}" diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index 9ec3f80014..7f71b7078c 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -110,6 +110,12 @@ void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, voi } void WiFiComponent::wifi_pre_setup_() { +#ifdef USE_ESP32_IGNORE_EFUSE_MAC_CRC + uint8_t mac[6]; + get_mac_address_raw(mac); + set_mac_address(mac); + ESP_LOGV(TAG, "Use EFuse MAC without checking CRC: %s", get_mac_address_pretty().c_str()); +#endif esp_err_t err = esp_netif_init(); if (err != ERR_OK) { ESP_LOGE(TAG, "esp_netif_init failed: %s", esp_err_to_name(err)); diff --git a/esphome/const.py b/esphome/const.py index 054f032da4..265576bfbe 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -42,6 +42,7 @@ CONF_ACTION_ID = "action_id" CONF_ACTIVE_POWER = "active_power" CONF_ADDRESS = "address" CONF_ADDRESSABLE_LIGHT_ID = "addressable_light_id" +CONF_ADVANCED = "advanced" CONF_ALPHA = "alpha" CONF_ALTITUDE = "altitude" CONF_AND = "and" @@ -281,6 +282,7 @@ CONF_IDLE_ACTION = "idle_action" CONF_IDLE_LEVEL = "idle_level" CONF_IDLE_TIME = "idle_time" CONF_IF = "if" +CONF_IGNORE_EFUSE_MAC_CRC = "ignore_efuse_mac_crc" CONF_IIR_FILTER = "iir_filter" CONF_ILLUMINANCE = "illuminance" CONF_IMPEDANCE = "impedance" diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 1c3b17d071..7c2261920a 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -50,6 +50,7 @@ #ifdef USE_ESP32 #define USE_ESP32_BLE_SERVER #define USE_ESP32_CAMERA +#define USE_ESP32_IGNORE_EFUSE_MAC_CRC #define USE_IMPROV #define USE_SOCKET_IMPL_BSD_SOCKETS diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 731ee6c8f5..780df3ca6d 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -1,4 +1,5 @@ #include "esphome/core/helpers.h" +#include "esphome/core/defines.h" #include #include #include @@ -14,6 +15,10 @@ #include #include #endif +#ifdef USE_ESP32_IGNORE_EFUSE_MAC_CRC +#include "esp_efuse.h" +#include "esp_efuse_table.h" +#endif #include "esphome/core/log.h" #include "esphome/core/hal.h" @@ -22,15 +27,27 @@ namespace esphome { static const char *const TAG = "helpers"; -std::string get_mac_address() { - char tmp[20]; - uint8_t mac[6]; +void get_mac_address_raw(uint8_t *mac) { #ifdef USE_ESP32 +#ifdef USE_ESP32_IGNORE_EFUSE_MAC_CRC + // On some devices, the MAC address that is burnt into EFuse does not + // match the CRC that goes along with it. For those devices, this + // work-around reads and uses the MAC address as-is from EFuse, + // without doing the CRC check. + esp_efuse_read_field_blob(ESP_EFUSE_MAC_FACTORY, mac, 48); +#else esp_efuse_mac_get_default(mac); #endif +#endif #ifdef USE_ESP8266 WiFi.macAddress(mac); #endif +} + +std::string get_mac_address() { + char tmp[20]; + uint8_t mac[6]; + get_mac_address_raw(mac); sprintf(tmp, "%02x%02x%02x%02x%02x%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); return std::string(tmp); } @@ -38,16 +55,15 @@ std::string get_mac_address() { std::string get_mac_address_pretty() { char tmp[20]; uint8_t mac[6]; -#ifdef USE_ESP32 - esp_efuse_mac_get_default(mac); -#endif -#ifdef USE_ESP8266 - WiFi.macAddress(mac); -#endif + get_mac_address_raw(mac); sprintf(tmp, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); return std::string(tmp); } +#ifdef USE_ESP32 +void set_mac_address(uint8_t *mac) { esp_base_mac_addr_set(mac); } +#endif + std::string generate_hostname(const std::string &base) { return base + std::string("-") + get_mac_address(); } uint32_t random_uint32() { diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 24b55eade0..61cc9a9e4a 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -26,11 +26,21 @@ namespace esphome { /// The characters that are allowed in a hostname. extern const char *const HOSTNAME_CHARACTER_ALLOWLIST; -/// Gets the MAC address as a string, this can be used as way to identify this ESP. +/// Read the raw MAC address into the provided byte array (6 bytes). +void get_mac_address_raw(uint8_t *mac); + +/// Get the MAC address as a string, using lower case hex notation. +/// This can be used as way to identify this ESP. std::string get_mac_address(); +/// Get the MAC address as a string, using colon-separated upper case hex notation. std::string get_mac_address_pretty(); +#ifdef USE_ESP32 +/// Set the MAC address to use from the provided byte array (6 bytes). +void set_mac_address(uint8_t *mac); +#endif + std::string to_string(const std::string &val); std::string to_string(int val); std::string to_string(long val); // NOLINT diff --git a/tests/test5.yaml b/tests/test5.yaml index b22b19550e..aca8434fbf 100644 --- a/tests/test5.yaml +++ b/tests/test5.yaml @@ -9,6 +9,8 @@ esp32: board: nodemcu-32s framework: type: esp-idf + advanced: + ignore_efuse_mac_crc: true wifi: networks: From 5a2984d03a17cb1843d505b88205b8042f0a05ab Mon Sep 17 00:00:00 2001 From: Maurice Makaay Date: Fri, 1 Oct 2021 10:11:07 +0200 Subject: [PATCH 151/207] Fix attach_interrupt(...) for esp-idf framework (#2416) Co-authored-by: Maurice Makaay --- esphome/components/esp32/gpio_idf.cpp | 71 +++++++++++++++++++++++++++ esphome/components/esp32/gpio_idf.h | 68 +++---------------------- 2 files changed, 77 insertions(+), 62 deletions(-) diff --git a/esphome/components/esp32/gpio_idf.cpp b/esphome/components/esp32/gpio_idf.cpp index 478b28a89a..d1853e1f8b 100644 --- a/esphome/components/esp32/gpio_idf.cpp +++ b/esphome/components/esp32/gpio_idf.cpp @@ -22,6 +22,77 @@ ISRInternalGPIOPin IDFInternalGPIOPin::to_isr() const { return ISRInternalGPIOPin((void *) arg); } +void IDFInternalGPIOPin::setup() { + pin_mode(flags_); + gpio_set_drive_capability(pin_, drive_strength_); +} + +void IDFInternalGPIOPin::pin_mode(gpio::Flags flags) { + gpio_config_t conf{}; + conf.pin_bit_mask = 1ULL << static_cast(pin_); + conf.mode = flags_to_mode(flags); + conf.pull_up_en = flags & gpio::FLAG_PULLUP ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE; + conf.pull_down_en = flags & gpio::FLAG_PULLDOWN ? GPIO_PULLDOWN_ENABLE : GPIO_PULLDOWN_DISABLE; + conf.intr_type = GPIO_INTR_DISABLE; + gpio_config(&conf); +} + +bool IDFInternalGPIOPin::digital_read() { return bool(gpio_get_level(pin_)) != inverted_; } + +void IDFInternalGPIOPin::digital_write(bool value) { gpio_set_level(pin_, value != inverted_ ? 1 : 0); } + +gpio_mode_t IDFInternalGPIOPin::flags_to_mode(gpio::Flags flags) { + flags = (gpio::Flags)(flags & ~(gpio::FLAG_PULLUP | gpio::FLAG_PULLDOWN)); + if (flags == gpio::FLAG_NONE) { + return GPIO_MODE_DISABLE; + } else if (flags == gpio::FLAG_INPUT) { + return GPIO_MODE_INPUT; + } else if (flags == gpio::FLAG_OUTPUT) { + return GPIO_MODE_OUTPUT; + } else if (flags == (gpio::FLAG_OUTPUT | gpio::FLAG_OPEN_DRAIN)) { + return GPIO_MODE_OUTPUT_OD; + } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_OUTPUT | gpio::FLAG_OPEN_DRAIN)) { + return GPIO_MODE_INPUT_OUTPUT_OD; + } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_OUTPUT)) { + return GPIO_MODE_INPUT_OUTPUT; + } else { + // unsupported + return GPIO_MODE_DISABLE; + } +} + +void IDFInternalGPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const { + gpio_int_type_t idf_type = GPIO_INTR_ANYEDGE; + switch (type) { + case gpio::INTERRUPT_RISING_EDGE: + idf_type = inverted_ ? GPIO_INTR_NEGEDGE : GPIO_INTR_POSEDGE; + break; + case gpio::INTERRUPT_FALLING_EDGE: + idf_type = inverted_ ? GPIO_INTR_POSEDGE : GPIO_INTR_NEGEDGE; + break; + case gpio::INTERRUPT_ANY_EDGE: + idf_type = GPIO_INTR_ANYEDGE; + break; + case gpio::INTERRUPT_LOW_LEVEL: + idf_type = inverted_ ? GPIO_INTR_HIGH_LEVEL : GPIO_INTR_LOW_LEVEL; + break; + case gpio::INTERRUPT_HIGH_LEVEL: + idf_type = inverted_ ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL; + break; + } + gpio_set_intr_type(pin_, idf_type); + gpio_intr_enable(pin_); + if (!isr_service_installed) { + auto res = gpio_install_isr_service(ESP_INTR_FLAG_LEVEL3); + if (res != ESP_OK) { + ESP_LOGE(TAG, "attach_interrupt(): call to gpio_install_isr_service() failed, error code: %d", res); + return; + } + isr_service_installed = true; + } + gpio_isr_handler_add(pin_, func, arg); +} + std::string IDFInternalGPIOPin::dump_summary() const { char buffer[32]; snprintf(buffer, sizeof(buffer), "GPIO%u", static_cast(pin_)); diff --git a/esphome/components/esp32/gpio_idf.h b/esphome/components/esp32/gpio_idf.h index 448151cd0f..a99571cc46 100644 --- a/esphome/components/esp32/gpio_idf.h +++ b/esphome/components/esp32/gpio_idf.h @@ -13,22 +13,10 @@ class IDFInternalGPIOPin : public InternalGPIOPin { void set_inverted(bool inverted) { inverted_ = inverted; } void set_drive_strength(gpio_drive_cap_t drive_strength) { drive_strength_ = drive_strength; } void set_flags(gpio::Flags flags) { flags_ = flags; } - - void setup() override { - pin_mode(flags_); - gpio_set_drive_capability(pin_, drive_strength_); - } - void pin_mode(gpio::Flags flags) override { - gpio_config_t conf{}; - conf.pin_bit_mask = 1ULL << static_cast(pin_); - conf.mode = flags_to_mode(flags); - conf.pull_up_en = flags & gpio::FLAG_PULLUP ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE; - conf.pull_down_en = flags & gpio::FLAG_PULLDOWN ? GPIO_PULLDOWN_ENABLE : GPIO_PULLDOWN_DISABLE; - conf.intr_type = GPIO_INTR_DISABLE; - gpio_config(&conf); - } - bool digital_read() override { return bool(gpio_get_level(pin_)) != inverted_; } - void digital_write(bool value) override { gpio_set_level(pin_, value != inverted_ ? 1 : 0); } + void setup() override; + void pin_mode(gpio::Flags flags) override; + bool digital_read() override; + void digital_write(bool value) override; std::string dump_summary() const override; void detach_interrupt() const override { gpio_intr_disable(pin_); } ISRInternalGPIOPin to_isr() const override; @@ -36,52 +24,8 @@ class IDFInternalGPIOPin : public InternalGPIOPin { bool is_inverted() const override { return inverted_; } protected: - static gpio_mode_t flags_to_mode(gpio::Flags flags) { - flags = (gpio::Flags)(flags & ~(gpio::FLAG_PULLUP | gpio::FLAG_PULLDOWN)); - if (flags == gpio::FLAG_NONE) { - return GPIO_MODE_DISABLE; - } else if (flags == gpio::FLAG_INPUT) { - return GPIO_MODE_INPUT; - } else if (flags == gpio::FLAG_OUTPUT) { - return GPIO_MODE_OUTPUT; - } else if (flags == (gpio::FLAG_OUTPUT | gpio::FLAG_OPEN_DRAIN)) { - return GPIO_MODE_OUTPUT_OD; - } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_OUTPUT | gpio::FLAG_OPEN_DRAIN)) { - return GPIO_MODE_INPUT_OUTPUT_OD; - } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_OUTPUT)) { - return GPIO_MODE_INPUT_OUTPUT; - } else { - // unsupported - return GPIO_MODE_DISABLE; - } - } - void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override { - gpio_int_type_t idf_type = GPIO_INTR_ANYEDGE; - switch (type) { - case gpio::INTERRUPT_RISING_EDGE: - idf_type = inverted_ ? GPIO_INTR_NEGEDGE : GPIO_INTR_POSEDGE; - break; - case gpio::INTERRUPT_FALLING_EDGE: - idf_type = inverted_ ? GPIO_INTR_POSEDGE : GPIO_INTR_NEGEDGE; - break; - case gpio::INTERRUPT_ANY_EDGE: - idf_type = GPIO_INTR_ANYEDGE; - break; - case gpio::INTERRUPT_LOW_LEVEL: - idf_type = inverted_ ? GPIO_INTR_HIGH_LEVEL : GPIO_INTR_LOW_LEVEL; - break; - case gpio::INTERRUPT_HIGH_LEVEL: - idf_type = inverted_ ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL; - break; - } - gpio_set_intr_type(pin_, idf_type); - gpio_intr_enable(pin_); - if (!isr_service_installed) { - gpio_install_isr_service(ESP_INTR_FLAG_LEVEL5); - isr_service_installed = true; - } - gpio_isr_handler_add(pin_, func, arg); - } + static gpio_mode_t flags_to_mode(gpio::Flags flags); + void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override; gpio_num_t pin_; bool inverted_; From d0dfc94a61cd6d3758e4284156d5ce707a7803b2 Mon Sep 17 00:00:00 2001 From: Maurice Makaay Date: Fri, 1 Oct 2021 12:53:37 +0200 Subject: [PATCH 152/207] Fix I2C recovery on Arduino (#2412) Co-authored-by: Maurice Makaay --- esphome/components/i2c/i2c_bus_arduino.cpp | 130 +++++++++++++++++---- esphome/components/i2c/i2c_bus_arduino.h | 7 ++ 2 files changed, 117 insertions(+), 20 deletions(-) diff --git a/esphome/components/i2c/i2c_bus_arduino.cpp b/esphome/components/i2c/i2c_bus_arduino.cpp index 87dbcb66d8..539091ed9c 100644 --- a/esphome/components/i2c/i2c_bus_arduino.cpp +++ b/esphome/components/i2c/i2c_bus_arduino.cpp @@ -32,6 +32,17 @@ void ArduinoI2CBus::dump_config() { ESP_LOGCONFIG(TAG, " SDA Pin: GPIO%u", this->sda_pin_); ESP_LOGCONFIG(TAG, " SCL Pin: GPIO%u", this->scl_pin_); ESP_LOGCONFIG(TAG, " Frequency: %u Hz", this->frequency_); + switch (this->recovery_result_) { + case RECOVERY_COMPLETED: + ESP_LOGCONFIG(TAG, " Recovery: bus successfully recovered"); + break; + case RECOVERY_FAILED_SCL_LOW: + ESP_LOGCONFIG(TAG, " Recovery: failed, SCL is held low on the bus"); + break; + case RECOVERY_FAILED_SDA_LOW: + ESP_LOGCONFIG(TAG, " Recovery: failed, SDA is held low on the bus"); + break; + } if (this->scan_) { ESP_LOGI(TAG, "Scanning i2c bus for active devices..."); uint8_t found = 0; @@ -92,31 +103,110 @@ ErrorCode ArduinoI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cn return ERROR_UNKNOWN; } +/// Perform I2C bus recovery, see: +/// https://www.nxp.com/docs/en/user-guide/UM10204.pdf +/// https://www.analog.com/media/en/technical-documentation/application-notes/54305147357414AN686_0.pdf void ArduinoI2CBus::recover_() { - // Perform I2C bus recovery, see - // https://www.analog.com/media/en/technical-documentation/application-notes/54305147357414AN686_0.pdf - // or see the linux kernel implementation, e.g. - // https://elixir.bootlin.com/linux/v5.14.6/source/drivers/i2c/i2c-core-base.c#L200 + ESP_LOGI(TAG, "Performing I2C bus recovery"); - // try to get about 100kHz toggle frequency - const auto half_period_usec = 1000000 / 100000 / 2; - const auto recover_scl_periods = 9; - - // configure scl as output - pinMode(scl_pin_, OUTPUT); // NOLINT - - // set scl high - digitalWrite(scl_pin_, 1); // NOLINT - - // in total generate 9 falling-rising edges - for (auto i = 0; i < recover_scl_periods; i++) { - delayMicroseconds(half_period_usec); - digitalWrite(scl_pin_, 0); // NOLINT - delayMicroseconds(half_period_usec); - digitalWrite(scl_pin_, 1); // NOLINT + // Activate the pull up resistor on the SCL pin. This should make the + // signal on the line HIGH. If SCL is pulled low on the I2C bus however, + // then some device is interfering with the SCL line. In that case, + // the I2C bus cannot be recovered. + pinMode(scl_pin_, INPUT_PULLUP); // NOLINT + if (digitalRead(scl_pin_) == LOW) { // NOLINT + ESP_LOGE(TAG, "Recovery failed: SCL is held LOW on the I2C bus"); + recovery_result_ = RECOVERY_FAILED_SCL_LOW; + return; } + // From the specification: + // "If the data line (SDA) is stuck LOW, send nine clock pulses. The + // device that held the bus LOW should release it sometime within + // those nine clocks." + // We don't really have to detect if SDA is stuck low. We'll simply send + // nine clock pulses here, just in case SDA is stuck. + + // Use a 100kHz toggle frequency (i.e. the maximum frequency for I2C + // running in standard-mode). The resulting frequency will be lower, + // because of the additional function calls that are done, but that + // is no problem. + const auto half_period_usec = 1000000 / 100000 / 2; + + // Make sure that switching to mode OUTPUT will make SCL low, just in + // case other code has setup the pin to output a HIGH signal. + digitalWrite(scl_pin_, LOW); // NOLINT + + // Activate the pull up resistor for SDA, so after the clock pulse cycle + // we can verify if SDA is pulled high. Also make sure that switching to + // mode OUTPUT will make SDA low. + pinMode(sda_pin_, INPUT_PULLUP); // NOLINT + digitalWrite(sda_pin_, LOW); // NOLINT + + ESP_LOGI(TAG, "Sending 9 clock pulses to drain any stuck device output"); delayMicroseconds(half_period_usec); + for (auto i = 0; i < 9; i++) { + // Release pull up resistor and switch to output to make the signal LOW. + pinMode(scl_pin_, INPUT); // NOLINT + pinMode(scl_pin_, OUTPUT); // NOLINT + delayMicroseconds(half_period_usec); + + // Release output and activate pull up resistor to make the signal HIGH. + pinMode(scl_pin_, INPUT); // NOLINT + pinMode(scl_pin_, INPUT_PULLUP); // NOLINT + delayMicroseconds(half_period_usec); + + // When SCL is kept LOW at this point, we might be looking at a device + // that applies clock stretching. Wait for the release of the SCL line, + // but not forever. There is no specification for the maximum allowed + // time. We'll stick to 500ms here. + auto wait = 20; + while (wait-- && digitalRead(scl_pin_) == LOW) { // NOLINT + delay(25); + } + if (digitalRead(scl_pin_) == LOW) { // NOLINT + ESP_LOGE(TAG, "Recovery failed: SCL is held LOW during clock pulse cycle"); + recovery_result_ = RECOVERY_FAILED_SCL_LOW; + return; + } + } + + // By now, any stuck device ought to have sent all remaining bits of its + // transation, meaning that it should have freed up the SDA line, resulting + // in SDA being pulled up. + if (digitalRead(sda_pin_) == LOW) { // NOLINT + ESP_LOGE(TAG, "Recovery failed: SDA is held LOW after clock pulse cycle"); + recovery_result_ = RECOVERY_FAILED_SDA_LOW; + return; + } + + // From the specification: + // "I2C-bus compatible devices must reset their bus logic on receipt of + // a START or repeated START condition such that they all anticipate + // the sending of a target address, even if these START conditions are + // not positioned according to the proper format." + // While the 9 clock pulses from above might have drained all bits of a + // single byte within a transaction, a device might have more bytes to + // transmit. So here we'll generate a START condition to snap the device + // out of this state. + // SCL and SDA are already high at this point, so we can generate a START + // condition by making the SDA signal LOW. + ESP_LOGI(TAG, "Generate START condition to reset bus logic of I2C devices"); + pinMode(sda_pin_, INPUT); // NOLINT + pinMode(sda_pin_, OUTPUT); // NOLINT + delayMicroseconds(half_period_usec); + + // From the specification: + // "A START condition immediately followed by a STOP condition (void + // message) is an illegal format. Many devices however are designed to + // operate properly under this condition." + // Finally, we'll bring the I2C bus into a starting state by generating + // a STOP condition. + ESP_LOGI(TAG, "Generate STOP condition to finalize recovery"); + pinMode(sda_pin_, INPUT); // NOLINT + pinMode(sda_pin_, INPUT_PULLUP); // NOLINT + + recovery_result_ = RECOVERY_COMPLETED; } } // namespace i2c } // namespace esphome diff --git a/esphome/components/i2c/i2c_bus_arduino.h b/esphome/components/i2c/i2c_bus_arduino.h index 42589dcfb7..82f043ef7d 100644 --- a/esphome/components/i2c/i2c_bus_arduino.h +++ b/esphome/components/i2c/i2c_bus_arduino.h @@ -9,6 +9,12 @@ namespace esphome { namespace i2c { +enum RecoveryCode { + RECOVERY_FAILED_SCL_LOW, + RECOVERY_FAILED_SDA_LOW, + RECOVERY_COMPLETED, +}; + class ArduinoI2CBus : public I2CBus, public Component { public: void setup() override; @@ -24,6 +30,7 @@ class ArduinoI2CBus : public I2CBus, public Component { private: void recover_(); + RecoveryCode recovery_result_; protected: TwoWire *wire_; From 15ab8918af0fbc0f7f6eb2cf09456f2b8ce9c640 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 2 Oct 2021 15:20:12 +0200 Subject: [PATCH 153/207] Bump aioesphomeapi from 9.1.1 to 9.1.2 (#2426) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 7ee22f0415..5ff9f7e282 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ platformio==5.2.0 esptool==3.1 click==8.0.1 esphome-dashboard==20210927.0 -aioesphomeapi==9.1.1 +aioesphomeapi==9.1.2 # esp-idf requires this, but doesn't bundle it by default # https://github.com/espressif/esp-idf/blob/220590d599e134d7a5e7f1e683cc4550349ffbf8/requirements.txt#L24 From 932e0469f7a63c3a20326f1e911620744a9c7c11 Mon Sep 17 00:00:00 2001 From: Maurice Makaay Date: Sat, 2 Oct 2021 16:02:01 +0200 Subject: [PATCH 154/207] Fix ESP32 esp-idf OTA updates (#2424) * WIP on separating out the OTA backends. * Split off the individual OTA backends. * Cleanup the three backends, split into .h and .cpp. * After successfull flashing, activate the new boot partition. * Fix linting issues. * Minor cleanup Co-authored-by: Maurice Makaay Co-authored-by: Otto winter --- esphome/components/ota/ota_backend.h | 18 +++ .../ota/ota_backend_arduino_esp32.cpp | 46 ++++++ .../ota/ota_backend_arduino_esp32.h | 22 +++ .../ota/ota_backend_arduino_esp8266.cpp | 58 ++++++++ .../ota/ota_backend_arduino_esp8266.h | 25 ++++ .../components/ota/ota_backend_esp_idf.cpp | 72 +++++++++ esphome/components/ota/ota_backend_esp_idf.h | 27 ++++ esphome/components/ota/ota_component.cpp | 139 ++---------------- 8 files changed, 279 insertions(+), 128 deletions(-) create mode 100644 esphome/components/ota/ota_backend.h create mode 100644 esphome/components/ota/ota_backend_arduino_esp32.cpp create mode 100644 esphome/components/ota/ota_backend_arduino_esp32.h create mode 100644 esphome/components/ota/ota_backend_arduino_esp8266.cpp create mode 100644 esphome/components/ota/ota_backend_arduino_esp8266.h create mode 100644 esphome/components/ota/ota_backend_esp_idf.cpp create mode 100644 esphome/components/ota/ota_backend_esp_idf.h diff --git a/esphome/components/ota/ota_backend.h b/esphome/components/ota/ota_backend.h new file mode 100644 index 0000000000..c253e009c6 --- /dev/null +++ b/esphome/components/ota/ota_backend.h @@ -0,0 +1,18 @@ +#pragma once +#include "ota_component.h" + +namespace esphome { +namespace ota { + +class OTABackend { + public: + virtual ~OTABackend() = default; + virtual OTAResponseTypes begin(size_t image_size) = 0; + virtual void set_update_md5(const char *md5) = 0; + virtual OTAResponseTypes write(uint8_t *data, size_t len) = 0; + virtual OTAResponseTypes end() = 0; + virtual void abort() = 0; +}; + +} // namespace ota +} // namespace esphome diff --git a/esphome/components/ota/ota_backend_arduino_esp32.cpp b/esphome/components/ota/ota_backend_arduino_esp32.cpp new file mode 100644 index 0000000000..4759737dbd --- /dev/null +++ b/esphome/components/ota/ota_backend_arduino_esp32.cpp @@ -0,0 +1,46 @@ +#include "esphome/core/defines.h" +#ifdef USE_ESP32_FRAMEWORK_ARDUINO + +#include "ota_backend_arduino_esp32.h" +#include "ota_component.h" +#include "ota_backend.h" + +#include + +namespace esphome { +namespace ota { + +OTAResponseTypes ArduinoESP32OTABackend::begin(size_t image_size) { + bool ret = Update.begin(image_size, U_FLASH); + if (ret) { + return OTA_RESPONSE_OK; + } + + uint8_t error = Update.getError(); + if (error == UPDATE_ERROR_SIZE) + return OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE; + return OTA_RESPONSE_ERROR_UNKNOWN; +} + +void ArduinoESP32OTABackend::set_update_md5(const char *md5) { Update.setMD5(md5); } + +OTAResponseTypes ArduinoESP32OTABackend::write(uint8_t *data, size_t len) { + size_t written = Update.write(data, len); + if (written != len) { + return OTA_RESPONSE_ERROR_WRITING_FLASH; + } + return OTA_RESPONSE_OK; +} + +OTAResponseTypes ArduinoESP32OTABackend::end() { + if (!Update.end()) + return OTA_RESPONSE_ERROR_UPDATE_END; + return OTA_RESPONSE_OK; +} + +void ArduinoESP32OTABackend::abort() { Update.abort(); } + +} // namespace ota +} // namespace esphome + +#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/ota/ota_backend_arduino_esp32.h b/esphome/components/ota/ota_backend_arduino_esp32.h new file mode 100644 index 0000000000..8343bdf94f --- /dev/null +++ b/esphome/components/ota/ota_backend_arduino_esp32.h @@ -0,0 +1,22 @@ +#pragma once +#include "esphome/core/defines.h" +#ifdef USE_ESP32_FRAMEWORK_ARDUINO + +#include "ota_component.h" +#include "ota_backend.h" + +namespace esphome { +namespace ota { + +class ArduinoESP32OTABackend : public OTABackend { + OTAResponseTypes begin(size_t image_size) override; + void set_update_md5(const char *md5) override; + OTAResponseTypes write(uint8_t *data, size_t len) override; + OTAResponseTypes end() override; + void abort() override; +}; + +} // namespace ota +} // namespace esphome + +#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/ota/ota_backend_arduino_esp8266.cpp b/esphome/components/ota/ota_backend_arduino_esp8266.cpp new file mode 100644 index 0000000000..8e8a4f36ba --- /dev/null +++ b/esphome/components/ota/ota_backend_arduino_esp8266.cpp @@ -0,0 +1,58 @@ +#include "esphome/core/defines.h" +#ifdef USE_ARDUINO +#ifdef USE_ESP8266 + +#include "ota_backend_arduino_esp8266.h" +#include "ota_component.h" +#include "ota_backend.h" +#include "esphome/components/esp8266/preferences.h" + +#include + +namespace esphome { +namespace ota { + +OTAResponseTypes ArduinoESP8266OTABackend::begin(size_t image_size) { + bool ret = Update.begin(image_size, U_FLASH); + if (ret) { + esp8266::preferences_prevent_write(true); + } + + uint8_t error = Update.getError(); + if (error == UPDATE_ERROR_BOOTSTRAP) + return OTA_RESPONSE_ERROR_INVALID_BOOTSTRAPPING; + if (error == UPDATE_ERROR_NEW_FLASH_CONFIG) + return OTA_RESPONSE_ERROR_WRONG_NEW_FLASH_CONFIG; + if (error == UPDATE_ERROR_FLASH_CONFIG) + return OTA_RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG; + if (error == UPDATE_ERROR_SPACE) + return OTA_RESPONSE_ERROR_ESP8266_NOT_ENOUGH_SPACE; + return OTA_RESPONSE_ERROR_UNKNOWN; +} + +void ArduinoESP8266OTABackend::set_update_md5(const char *md5) { Update.setMD5(md5); } + +OTAResponseTypes ArduinoESP8266OTABackend::write(uint8_t *data, size_t len) { + size_t written = Update.write(data, len); + if (written != len) { + return OTA_RESPONSE_ERROR_WRITING_FLASH; + } + return OTA_RESPONSE_OK; +} + +OTAResponseTypes ArduinoESP8266OTABackend::end() { + if (!Update.end()) + return OTA_RESPONSE_ERROR_UPDATE_END; + return OTA_RESPONSE_OK; +} + +void ArduinoESP8266OTABackend::abort() { + Update.end(); + esp8266::preferences_prevent_write(false); +} + +} // namespace ota +} // namespace esphome + +#endif +#endif diff --git a/esphome/components/ota/ota_backend_arduino_esp8266.h b/esphome/components/ota/ota_backend_arduino_esp8266.h new file mode 100644 index 0000000000..d1195af911 --- /dev/null +++ b/esphome/components/ota/ota_backend_arduino_esp8266.h @@ -0,0 +1,25 @@ +#pragma once +#include "esphome/core/defines.h" +#ifdef USE_ARDUINO +#ifdef USE_ESP8266 + +#include "ota_component.h" +#include "ota_backend.h" + +namespace esphome { +namespace ota { + +class ArduinoESP8266OTABackend : public OTABackend { + public: + OTAResponseTypes begin(size_t image_size) override; + void set_update_md5(const char *md5) override; + OTAResponseTypes write(uint8_t *data, size_t len) override; + OTAResponseTypes end() override; + void abort() override; +}; + +} // namespace ota +} // namespace esphome + +#endif +#endif diff --git a/esphome/components/ota/ota_backend_esp_idf.cpp b/esphome/components/ota/ota_backend_esp_idf.cpp new file mode 100644 index 0000000000..4eb17d82f1 --- /dev/null +++ b/esphome/components/ota/ota_backend_esp_idf.cpp @@ -0,0 +1,72 @@ +#include "esphome/core/defines.h" +#ifdef USE_ESP_IDF + +#include "ota_backend_esp_idf.h" +#include "ota_component.h" +#include + +namespace esphome { +namespace ota { + +OTAResponseTypes IDFOTABackend::begin(size_t image_size) { + this->partition_ = esp_ota_get_next_update_partition(nullptr); + if (this->partition_ == nullptr) { + return OTA_RESPONSE_ERROR_NO_UPDATE_PARTITION; + } + esp_err_t err = esp_ota_begin(this->partition_, image_size, &this->update_handle_); + if (err != ESP_OK) { + esp_ota_abort(this->update_handle_); + this->update_handle_ = 0; + if (err == ESP_ERR_INVALID_SIZE) { + return OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE; + } else if (err == ESP_ERR_FLASH_OP_TIMEOUT || err == ESP_ERR_FLASH_OP_FAIL) { + return OTA_RESPONSE_ERROR_WRITING_FLASH; + } + return OTA_RESPONSE_ERROR_UNKNOWN; + } + return OTA_RESPONSE_OK; +} + +void IDFOTABackend::set_update_md5(const char *md5) { + // pass +} + +OTAResponseTypes IDFOTABackend::write(uint8_t *data, size_t len) { + esp_err_t err = esp_ota_write(this->update_handle_, data, len); + if (err != ESP_OK) { + if (err == ESP_ERR_OTA_VALIDATE_FAILED) { + return OTA_RESPONSE_ERROR_MAGIC; + } else if (err == ESP_ERR_FLASH_OP_TIMEOUT || err == ESP_ERR_FLASH_OP_FAIL) { + return OTA_RESPONSE_ERROR_WRITING_FLASH; + } + return OTA_RESPONSE_ERROR_UNKNOWN; + } + return OTA_RESPONSE_OK; +} + +OTAResponseTypes IDFOTABackend::end() { + esp_err_t err = esp_ota_end(this->update_handle_); + this->update_handle_ = 0; + if (err == ESP_OK) { + err = esp_ota_set_boot_partition(this->partition_); + if (err == ESP_OK) { + return OTA_RESPONSE_OK; + } + } + if (err == ESP_ERR_OTA_VALIDATE_FAILED) { + return OTA_RESPONSE_ERROR_UPDATE_END; + } + if (err == ESP_ERR_FLASH_OP_TIMEOUT || err == ESP_ERR_FLASH_OP_FAIL) { + return OTA_RESPONSE_ERROR_WRITING_FLASH; + } + return OTA_RESPONSE_ERROR_UNKNOWN; +} + +void IDFOTABackend::abort() { + esp_ota_abort(this->update_handle_); + this->update_handle_ = 0; +} + +} // namespace ota +} // namespace esphome +#endif diff --git a/esphome/components/ota/ota_backend_esp_idf.h b/esphome/components/ota/ota_backend_esp_idf.h new file mode 100644 index 0000000000..d6e2e2742a --- /dev/null +++ b/esphome/components/ota/ota_backend_esp_idf.h @@ -0,0 +1,27 @@ +#pragma once +#include "esphome/core/defines.h" +#ifdef USE_ESP_IDF + +#include "ota_component.h" +#include "ota_backend.h" +#include + +namespace esphome { +namespace ota { + +class IDFOTABackend : public OTABackend { + public: + OTAResponseTypes begin(size_t image_size) override; + void set_update_md5(const char *md5) override; + OTAResponseTypes write(uint8_t *data, size_t len) override; + OTAResponseTypes end() override; + void abort() override; + + private: + esp_ota_handle_t update_handle_{0}; + const esp_partition_t *partition_; +}; + +} // namespace ota +} // namespace esphome +#endif diff --git a/esphome/components/ota/ota_component.cpp b/esphome/components/ota/ota_component.cpp index 0c13efa135..e1188a4d31 100644 --- a/esphome/components/ota/ota_component.cpp +++ b/esphome/components/ota/ota_component.cpp @@ -1,4 +1,8 @@ #include "ota_component.h" +#include "ota_backend.h" +#include "ota_backend_arduino_esp32.h" +#include "ota_backend_arduino_esp8266.h" +#include "ota_backend_esp_idf.h" #include "esphome/core/log.h" #include "esphome/core/application.h" @@ -9,23 +13,8 @@ #include #include -#ifdef USE_ARDUINO #ifdef USE_OTA_PASSWORD #include -#endif // USE_OTA_PASSWORD - -#ifdef USE_ESP32 -#include -#endif // USE_ESP32 -#endif // USE_ARDUINO - -#ifdef USE_ESP8266 -#include -#include "esphome/components/esp8266/preferences.h" -#endif // USE_ESP8266 - -#ifdef USE_ESP_IDF -#include #endif namespace esphome { @@ -35,125 +24,19 @@ static const char *const TAG = "ota"; static const uint8_t OTA_VERSION_1_0 = 1; -class OTABackend { - public: - virtual ~OTABackend() = default; - virtual OTAResponseTypes begin(size_t image_size) = 0; - virtual void set_update_md5(const char *md5) = 0; - virtual OTAResponseTypes write(uint8_t *data, size_t len) = 0; - virtual OTAResponseTypes end() = 0; - virtual void abort() = 0; -}; - +std::unique_ptr make_ota_backend() { #ifdef USE_ARDUINO -class ArduinoOTABackend : public OTABackend { - public: - OTAResponseTypes begin(size_t image_size) override { - bool ret = Update.begin(image_size, U_FLASH); - if (ret) { #ifdef USE_ESP8266 - esp8266::preferences_prevent_write(true); -#endif - return OTA_RESPONSE_OK; - } - - uint8_t error = Update.getError(); -#ifdef USE_ESP8266 - if (error == UPDATE_ERROR_BOOTSTRAP) - return OTA_RESPONSE_ERROR_INVALID_BOOTSTRAPPING; - if (error == UPDATE_ERROR_NEW_FLASH_CONFIG) - return OTA_RESPONSE_ERROR_WRONG_NEW_FLASH_CONFIG; - if (error == UPDATE_ERROR_FLASH_CONFIG) - return OTA_RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG; - if (error == UPDATE_ERROR_SPACE) - return OTA_RESPONSE_ERROR_ESP8266_NOT_ENOUGH_SPACE; -#endif + return make_unique(); +#endif // USE_ESP8266 #ifdef USE_ESP32 - if (error == UPDATE_ERROR_SIZE) - return OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE; -#endif - return OTA_RESPONSE_ERROR_UNKNOWN; - } - void set_update_md5(const char *md5) override { Update.setMD5(md5); } - OTAResponseTypes write(uint8_t *data, size_t len) override { - size_t written = Update.write(data, len); - if (written != len) { - return OTA_RESPONSE_ERROR_WRITING_FLASH; - } - return OTA_RESPONSE_OK; - } - OTAResponseTypes end() override { - if (!Update.end()) - return OTA_RESPONSE_ERROR_UPDATE_END; - return OTA_RESPONSE_OK; - } - void abort() override { -#ifdef USE_ESP32 - Update.abort(); -#endif - -#ifdef USE_ESP8266 - Update.end(); - esp8266::preferences_prevent_write(false); -#endif - } -}; -std::unique_ptr make_ota_backend() { return make_unique(); } + return make_unique(); +#endif // USE_ESP32 #endif // USE_ARDUINO - #ifdef USE_ESP_IDF -class IDFOTABackend : public OTABackend { - public: - esp_ota_handle_t update_handle = 0; - - OTAResponseTypes begin(size_t image_size) override { - const esp_partition_t *update_partition = esp_ota_get_next_update_partition(nullptr); - if (update_partition == nullptr) { - return OTA_RESPONSE_ERROR_NO_UPDATE_PARTITION; - } - esp_err_t err = esp_ota_begin(update_partition, image_size, &update_handle); - if (err != ESP_OK) { - esp_ota_abort(update_handle); - update_handle = 0; - if (err == ESP_ERR_INVALID_SIZE) { - return OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE; - } else if (err == ESP_ERR_FLASH_OP_TIMEOUT || err == ESP_ERR_FLASH_OP_FAIL) { - return OTA_RESPONSE_ERROR_WRITING_FLASH; - } - return OTA_RESPONSE_ERROR_UNKNOWN; - } - return OTA_RESPONSE_OK; - } - void set_update_md5(const char *md5) override { - // pass - } - OTAResponseTypes write(uint8_t *data, size_t len) override { - esp_err_t err = esp_ota_write(update_handle, data, len); - if (err != ESP_OK) { - if (err == ESP_ERR_OTA_VALIDATE_FAILED) { - return OTA_RESPONSE_ERROR_MAGIC; - } else if (err == ESP_ERR_FLASH_OP_TIMEOUT || err == ESP_ERR_FLASH_OP_FAIL) { - return OTA_RESPONSE_ERROR_WRITING_FLASH; - } - return OTA_RESPONSE_ERROR_UNKNOWN; - } - return OTA_RESPONSE_OK; - } - OTAResponseTypes end() override { - esp_err_t err = esp_ota_end(update_handle); - update_handle = 0; - if (err != ESP_OK) { - if (err == ESP_ERR_OTA_VALIDATE_FAILED) { - return OTA_RESPONSE_ERROR_UPDATE_END; - } - return OTA_RESPONSE_ERROR_UNKNOWN; - } - return OTA_RESPONSE_OK; - } - void abort() override { esp_ota_abort(update_handle); } -}; -std::unique_ptr make_ota_backend() { return make_unique(); } + return make_unique(); #endif // USE_ESP_IDF +} void OTAComponent::setup() { server_ = socket::socket(AF_INET, SOCK_STREAM, 0); From a7687c3e17245ca383ce5bcf2951a0ab54afdba1 Mon Sep 17 00:00:00 2001 From: cvwillegen Date: Sun, 3 Oct 2021 13:27:59 +0200 Subject: [PATCH 155/207] Add local MAC address to WiFi info (#2428) --- esphome/components/wifi/wifi_component.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 47cd0ef9ad..703afa99bc 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -349,6 +349,7 @@ const LogString *get_signal_bars(int8_t rssi) { void WiFiComponent::print_connect_params_() { bssid_t bssid = wifi_bssid(); + ESP_LOGCONFIG(TAG, " Local MAC: %s", get_mac_address_pretty().c_str()); ESP_LOGCONFIG(TAG, " SSID: " LOG_SECRET("'%s'"), wifi_ssid().c_str()); ESP_LOGCONFIG(TAG, " IP Address: %s", wifi_sta_ip().str().c_str()); ESP_LOGCONFIG(TAG, " BSSID: " LOG_SECRET("%02X:%02X:%02X:%02X:%02X:%02X"), bssid[0], bssid[1], bssid[2], bssid[3], From eaa5200a35ea1b5acec49a5b61356f81c60a1d2f Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sun, 3 Oct 2021 07:10:43 -0500 Subject: [PATCH 156/207] Thermostat publish state fix (#2427) --- .../thermostat/thermostat_climate.cpp | 34 ++++++++++++------- .../thermostat/thermostat_climate.h | 8 ++--- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/esphome/components/thermostat/thermostat_climate.cpp b/esphome/components/thermostat/thermostat_climate.cpp index 6193185321..ce15c53bbe 100644 --- a/esphome/components/thermostat/thermostat_climate.cpp +++ b/esphome/components/thermostat/thermostat_climate.cpp @@ -18,8 +18,8 @@ void ThermostatClimate::setup() { // add a callback so that whenever the sensor state changes we can take action this->sensor_->add_on_state_callback([this](float state) { this->current_temperature = state; - // required action may have changed, recompute, refresh - this->switch_to_action_(this->compute_action_()); + // required action may have changed, recompute, refresh, we'll publish_state() later + this->switch_to_action_(this->compute_action_(), false); this->switch_to_supplemental_action_(this->compute_supplemental_action_()); // current temperature and possibly action changed, so publish the new state this->publish_state(); @@ -34,8 +34,8 @@ void ThermostatClimate::setup() { this->mode = this->default_mode_; this->change_away_(false); } - // refresh the climate action based on the restored settings - this->switch_to_action_(this->compute_action_()); + // refresh the climate action based on the restored settings, we'll publish_state() later + this->switch_to_action_(this->compute_action_(), false); this->switch_to_supplemental_action_(this->compute_supplemental_action_()); this->setup_complete_ = true; this->publish_state(); @@ -47,11 +47,11 @@ float ThermostatClimate::heat_deadband() { return this->heating_deadband_; } float ThermostatClimate::heat_overrun() { return this->heating_overrun_; } void ThermostatClimate::refresh() { - this->switch_to_mode_(this->mode); - this->switch_to_action_(this->compute_action_()); + this->switch_to_mode_(this->mode, false); + this->switch_to_action_(this->compute_action_(), false); this->switch_to_supplemental_action_(this->compute_supplemental_action_()); - this->switch_to_fan_mode_(this->fan_mode.value()); - this->switch_to_swing_mode_(this->swing_mode); + this->switch_to_fan_mode_(this->fan_mode.value(), false); + this->switch_to_swing_mode_(this->swing_mode, false); this->check_temperature_change_trigger_(); this->publish_state(); } @@ -346,7 +346,7 @@ climate::ClimateAction ThermostatClimate::compute_supplemental_action_() { return target_action; } -void ThermostatClimate::switch_to_action_(climate::ClimateAction action) { +void ThermostatClimate::switch_to_action_(climate::ClimateAction action, bool publish_state) { // setup_complete_ helps us ensure an action is called immediately after boot if ((action == this->action) && this->setup_complete_) // already in target mode @@ -358,6 +358,8 @@ void ThermostatClimate::switch_to_action_(climate::ClimateAction action) { // switching from OFF to IDLE or vice-versa -- this is only a visual difference. // OFF means user manually disabled, IDLE means the temperature is in target range. this->action = action; + if (publish_state) + this->publish_state(); return; } @@ -452,6 +454,8 @@ void ThermostatClimate::switch_to_action_(climate::ClimateAction action) { ESP_LOGVV(TAG, "Calling FAN_ONLY action with HEATING/COOLING action"); trig_fan->trigger(); } + if (publish_state) + this->publish_state(); } } @@ -509,13 +513,15 @@ void ThermostatClimate::trigger_supplemental_action_() { } } -void ThermostatClimate::switch_to_fan_mode_(climate::ClimateFanMode fan_mode) { +void ThermostatClimate::switch_to_fan_mode_(climate::ClimateFanMode fan_mode, bool publish_state) { // setup_complete_ helps us ensure an action is called immediately after boot if ((fan_mode == this->prev_fan_mode_) && this->setup_complete_) // already in target mode return; this->fan_mode = fan_mode; + if (publish_state) + this->publish_state(); if (this->fan_mode_ready_()) { Trigger<> *trig = this->fan_mode_auto_trigger_; @@ -574,7 +580,7 @@ void ThermostatClimate::switch_to_fan_mode_(climate::ClimateFanMode fan_mode) { } } -void ThermostatClimate::switch_to_mode_(climate::ClimateMode mode) { +void ThermostatClimate::switch_to_mode_(climate::ClimateMode mode, bool publish_state) { // setup_complete_ helps us ensure an action is called immediately after boot if ((mode == this->prev_mode_) && this->setup_complete_) // already in target mode @@ -615,9 +621,11 @@ void ThermostatClimate::switch_to_mode_(climate::ClimateMode mode) { this->mode = mode; this->prev_mode_ = mode; this->prev_mode_trigger_ = trig; + if (publish_state) + this->publish_state(); } -void ThermostatClimate::switch_to_swing_mode_(climate::ClimateSwingMode swing_mode) { +void ThermostatClimate::switch_to_swing_mode_(climate::ClimateSwingMode swing_mode, bool publish_state) { // setup_complete_ helps us ensure an action is called immediately after boot if ((swing_mode == this->prev_swing_mode_) && this->setup_complete_) // already in target mode @@ -652,6 +660,8 @@ void ThermostatClimate::switch_to_swing_mode_(climate::ClimateSwingMode swing_mo this->swing_mode = swing_mode; this->prev_swing_mode_ = swing_mode; this->prev_swing_mode_trigger_ = trig; + if (publish_state) + this->publish_state(); } bool ThermostatClimate::idle_action_ready_() { diff --git a/esphome/components/thermostat/thermostat_climate.h b/esphome/components/thermostat/thermostat_climate.h index 60777e7c81..8d3e926752 100644 --- a/esphome/components/thermostat/thermostat_climate.h +++ b/esphome/components/thermostat/thermostat_climate.h @@ -160,18 +160,18 @@ class ThermostatClimate : public climate::Climate, public Component { climate::ClimateAction compute_supplemental_action_(); /// Switch the climate device to the given climate action. - void switch_to_action_(climate::ClimateAction action); + void switch_to_action_(climate::ClimateAction action, bool publish_state = true); void switch_to_supplemental_action_(climate::ClimateAction action); void trigger_supplemental_action_(); /// Switch the climate device to the given climate fan mode. - void switch_to_fan_mode_(climate::ClimateFanMode fan_mode); + void switch_to_fan_mode_(climate::ClimateFanMode fan_mode, bool publish_state = true); /// Switch the climate device to the given climate mode. - void switch_to_mode_(climate::ClimateMode mode); + void switch_to_mode_(climate::ClimateMode mode, bool publish_state = true); /// Switch the climate device to the given climate swing mode. - void switch_to_swing_mode_(climate::ClimateSwingMode swing_mode); + void switch_to_swing_mode_(climate::ClimateSwingMode swing_mode, bool publish_state = true); /// Check if the temperature change trigger should be called. void check_temperature_change_trigger_(); From 912793eddf70f2ec59e086f22b076c82e183073f Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Sun, 3 Oct 2021 14:10:53 +0200 Subject: [PATCH 157/207] Convert time to use tzdata (#2425) --- esphome/components/time/__init__.py | 160 ++++++++-------------------- requirements.txt | 4 +- 2 files changed, 49 insertions(+), 115 deletions(-) diff --git a/esphome/components/time/__init__.py b/esphome/components/time/__init__.py index d548575d72..5c2155d764 100644 --- a/esphome/components/time/__init__.py +++ b/esphome/components/time/__init__.py @@ -1,10 +1,8 @@ -import bisect -import datetime import logging -import math -import string +from importlib import resources +from typing import Optional +from datetime import timezone -import pytz import tzlocal import esphome.codegen as cg @@ -44,111 +42,47 @@ ESPTime = time_ns.struct("ESPTime") TimeHasTimeCondition = time_ns.class_("TimeHasTimeCondition", Condition) -def _tz_timedelta(td): - offset_hour = int(td.total_seconds() / (60 * 60)) - offset_minute = int(abs(td.total_seconds() / 60)) % 60 - offset_second = int(abs(td.total_seconds())) % 60 - if offset_hour == 0 and offset_minute == 0 and offset_second == 0: - return "0" - if offset_minute == 0 and offset_second == 0: - return f"{offset_hour}" - if offset_second == 0: - return f"{offset_hour}:{offset_minute}" - return f"{offset_hour}:{offset_minute}:{offset_second}" - - -# https://stackoverflow.com/a/16804556/8924614 -def _week_of_month(dt): - first_day = dt.replace(day=1) - dom = dt.day - adjusted_dom = dom + first_day.weekday() - return int(math.ceil(adjusted_dom / 7.0)) - - -def _tz_dst_str(dt): - td = datetime.timedelta(hours=dt.hour, minutes=dt.minute, seconds=dt.second) - return f"M{dt.month}.{_week_of_month(dt)}.{dt.isoweekday() % 7}/{_tz_timedelta(td)}" - - -def _safe_tzname(tz, dt): - tzname = tz.tzname(dt) - # pytz does not always return valid tznames - # For example: 'Europe/Saratov' returns '+04' - # Work around it by using a generic name for the timezone - if not all(c in string.ascii_letters for c in tzname): - return "TZ" - return tzname - - -def _non_dst_tz(tz, dt): - tzname = _safe_tzname(tz, dt) - utcoffset = tz.utcoffset(dt) - _LOGGER.info( - "Detected timezone '%s' with UTC offset %s", tzname, _tz_timedelta(utcoffset) - ) - tzbase = f"{tzname}{_tz_timedelta(-1 * utcoffset)}" - return tzbase - - -def convert_tz(pytz_obj): - tz = pytz_obj - - now = datetime.datetime.now() - first_january = datetime.datetime(year=now.year, month=1, day=1) - - if not isinstance(tz, pytz.tzinfo.DstTzInfo): - return _non_dst_tz(tz, first_january) - - # pylint: disable=protected-access - transition_times = tz._utc_transition_times - transition_info = tz._transition_info - idx = max(0, bisect.bisect_right(transition_times, now)) - if idx >= len(transition_times): - return _non_dst_tz(tz, now) - - idx1, idx2 = idx, idx + 1 - dstoffset1 = transition_info[idx1][1] - if dstoffset1 == datetime.timedelta(seconds=0): - # Normalize to 1 being DST on - idx1, idx2 = idx + 1, idx + 2 - - if idx2 >= len(transition_times): - return _non_dst_tz(tz, now) - - if transition_times[idx2].year > now.year + 1: - # Next transition is scheduled after this year - # Probably a scheduler timezone change. - return _non_dst_tz(tz, now) - - utcoffset_on, _, tzname_on = transition_info[idx1] - utcoffset_off, _, tzname_off = transition_info[idx2] - dst_begins_utc = transition_times[idx1] - dst_begins_local = dst_begins_utc + utcoffset_off - dst_ends_utc = transition_times[idx2] - dst_ends_local = dst_ends_utc + utcoffset_on - - tzbase = f"{tzname_off}{_tz_timedelta(-1 * utcoffset_off)}" - - tzext = f"{tzname_on}{_tz_timedelta(-1 * utcoffset_on)},{_tz_dst_str(dst_begins_local)},{_tz_dst_str(dst_ends_local)}" - _LOGGER.info( - "Detected timezone '%s' with UTC offset %s and daylight saving time from " - "%s to %s", - tzname_off, - _tz_timedelta(utcoffset_off), - dst_begins_local.strftime("%d %B %X"), - dst_ends_local.strftime("%d %B %X"), - ) - return tzbase + tzext - - -def detect_tz(): +def _load_tzdata(iana_key: str) -> Optional[bytes]: + # From https://tzdata.readthedocs.io/en/latest/#examples try: - tz = tzlocal.get_localzone() - except pytz.exceptions.UnknownTimeZoneError: - _LOGGER.warning("Could not auto-detect timezone. Using UTC...") - return "UTC" + package_loc, resource = iana_key.rsplit("/", 1) + except ValueError: + return None + package = "tzdata.zoneinfo." + package_loc.replace("/", ".") - return convert_tz(tz) + try: + return resources.read_binary(package, resource) + except (FileNotFoundError, ModuleNotFoundError): + return None + + +def _extract_tz_string(tzfile: bytes) -> str: + try: + return tzfile.split(b"\n")[-2].decode() + except (IndexError, UnicodeDecodeError): + _LOGGER.error("Could not determine TZ string. Please report this issue.") + _LOGGER.error("tzfile contents: %s", tzfile, exc_info=True) + raise + + +def detect_tz() -> str: + localzone = tzlocal.get_localzone() + if localzone is timezone.utc: + return "UTC0" + if not hasattr(localzone, "key"): + raise cv.Invalid( + "Could not automatically determine timezone, please set timezone manually." + ) + iana_key = localzone.key + _LOGGER.info("Detected timezone '%s'", iana_key) + tzfile = _load_tzdata(iana_key) + if tzfile is None: + raise cv.Invalid( + "Could not automatically determine timezone, please set timezone manually." + ) + ret = _extract_tz_string(tzfile) + _LOGGER.debug(" -> TZ string %s", ret) + return ret def _parse_cron_int(value, special_mapping, message): @@ -327,15 +261,15 @@ def validate_cron_keys(value): return cv.has_at_least_one_key(*CRON_KEYS)(value) -def validate_tz(value): +def validate_tz(value: str) -> str: value = cv.string_strict(value) - try: - pytz_obj = pytz.timezone(value) - except pytz.UnknownTimeZoneError: # pylint: disable=broad-except + tzfile = _load_tzdata(value) + if tzfile is None: + # Not a IANA key, probably a TZ string return value - return convert_tz(pytz_obj) + return _extract_tz_string(tzfile) TIME_SCHEMA = cv.Schema( diff --git a/requirements.txt b/requirements.txt index 5ff9f7e282..ba2c43e5b7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,8 +3,8 @@ PyYAML==5.4.1 paho-mqtt==1.5.1 colorama==0.4.4 tornado==6.1 -tzlocal==3.0 -pytz==2021.1 +tzlocal==3.0 # from time +tzdata>=2021.1 # from time pyserial==3.5 platformio==5.2.0 esptool==3.1 From cee08debffa2f9f33a76e8948d8a6029c1854403 Mon Sep 17 00:00:00 2001 From: Maurice Makaay Date: Sun, 3 Oct 2021 16:15:01 +0200 Subject: [PATCH 158/207] Hotfix for ESP8266 OTA issue: ERROR Error binary size (#2432) Co-authored-by: Maurice Makaay --- esphome/components/ota/ota_backend_arduino_esp8266.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/ota/ota_backend_arduino_esp8266.cpp b/esphome/components/ota/ota_backend_arduino_esp8266.cpp index 8e8a4f36ba..23dc0d4e21 100644 --- a/esphome/components/ota/ota_backend_arduino_esp8266.cpp +++ b/esphome/components/ota/ota_backend_arduino_esp8266.cpp @@ -16,6 +16,7 @@ OTAResponseTypes ArduinoESP8266OTABackend::begin(size_t image_size) { bool ret = Update.begin(image_size, U_FLASH); if (ret) { esp8266::preferences_prevent_write(true); + return OTA_RESPONSE_OK; } uint8_t error = Update.getError(); From 1627dff166768ec79928fc3e65a3b18f1d7267d4 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Sun, 3 Oct 2021 21:53:40 +0200 Subject: [PATCH 159/207] Disable dependency finder on ESP32 (#2435) --- esphome/components/airthings_wave_plus/sensor.py | 4 ++++ esphome/components/captive_portal/__init__.py | 6 +++++- esphome/components/esp32/__init__.py | 2 ++ esphome/components/ethernet/__init__.py | 3 +++ esphome/components/http_request/__init__.py | 5 +++++ esphome/components/mcp3008/__init__.py | 4 ++++ esphome/components/mdns/__init__.py | 9 +++++---- esphome/components/ota/__init__.py | 1 + esphome/components/spi/__init__.py | 5 ++++- esphome/components/web_server_base/__init__.py | 2 ++ 10 files changed, 35 insertions(+), 6 deletions(-) diff --git a/esphome/components/airthings_wave_plus/sensor.py b/esphome/components/airthings_wave_plus/sensor.py index 8b902ea81c..2100341536 100644 --- a/esphome/components/airthings_wave_plus/sensor.py +++ b/esphome/components/airthings_wave_plus/sensor.py @@ -1,6 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, ble_client +from esphome.core import CORE from esphome.const import ( DEVICE_CLASS_CARBON_DIOXIDE, @@ -116,3 +117,6 @@ async def to_code(config): if CONF_TVOC in config: sens = await sensor.new_sensor(config[CONF_TVOC]) cg.add(var.set_tvoc(sens)) + + if CORE.is_esp32: + cg.add_library("ESP32 BLE Arduino", None) diff --git a/esphome/components/captive_portal/__init__.py b/esphome/components/captive_portal/__init__.py index a1cc5734c1..384a3f23a0 100644 --- a/esphome/components/captive_portal/__init__.py +++ b/esphome/components/captive_portal/__init__.py @@ -3,7 +3,7 @@ import esphome.config_validation as cv from esphome.components import web_server_base from esphome.components.web_server_base import CONF_WEB_SERVER_BASE_ID from esphome.const import CONF_ID -from esphome.core import coroutine_with_priority +from esphome.core import coroutine_with_priority, CORE AUTO_LOAD = ["web_server_base"] DEPENDENCIES = ["wifi"] @@ -32,3 +32,7 @@ async def to_code(config): var = cg.new_Pvariable(config[CONF_ID], paren) await cg.register_component(var, config) cg.add_define("USE_CAPTIVE_PORTAL") + + if CORE.is_esp32: + cg.add_library("DNSServer", None) + cg.add_library("WiFi", None) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 1316c7ccbe..704f9bb3e8 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -276,6 +276,8 @@ async def to_code(config): cg.add_define("ESPHOME_BOARD", config[CONF_BOARD]) cg.add_build_flag(f"-DUSE_ESP32_VARIANT_{config[CONF_VARIANT]}") + cg.add_platformio_option("lib_ldf_mode", "off") + conf = config[CONF_FRAMEWORK] if conf[CONF_TYPE] == FRAMEWORK_ESP_IDF: cg.add_platformio_option( diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index 384fb3dbfb..bbf64a3cd1 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -123,3 +123,6 @@ async def to_code(config): cg.add(var.set_manual_ip(manual_ip(config[CONF_MANUAL_IP]))) cg.add_define("USE_ETHERNET") + + if CORE.is_esp32: + cg.add_library("WiFi", None) diff --git a/esphome/components/http_request/__init__.py b/esphome/components/http_request/__init__.py index a48b3c0acb..6e249c4247 100644 --- a/esphome/components/http_request/__init__.py +++ b/esphome/components/http_request/__init__.py @@ -92,6 +92,11 @@ async def to_code(config): cg.add(var.set_useragent(config[CONF_USERAGENT])) if CORE.is_esp8266 and not config[CONF_ESP8266_DISABLE_SSL_SUPPORT]: cg.add_define("USE_HTTP_REQUEST_ESP8266_HTTPS") + + if CORE.is_esp32: + cg.add_library("WiFiClientSecure", None) + cg.add_library("HTTPClient", None) + await cg.register_component(var, config) diff --git a/esphome/components/mcp3008/__init__.py b/esphome/components/mcp3008/__init__.py index 24a48664c1..431963acfd 100644 --- a/esphome/components/mcp3008/__init__.py +++ b/esphome/components/mcp3008/__init__.py @@ -2,6 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import spi from esphome.const import CONF_ID +from esphome.core import CORE DEPENDENCIES = ["spi"] AUTO_LOAD = ["sensor"] @@ -23,3 +24,6 @@ async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) await spi.register_spi_device(var, config) + + if CORE.is_esp32: + cg.add_library("SPI", None) diff --git a/esphome/components/mdns/__init__.py b/esphome/components/mdns/__init__.py index c0c0865643..b95469d9da 100644 --- a/esphome/components/mdns/__init__.py +++ b/esphome/components/mdns/__init__.py @@ -30,15 +30,16 @@ CONFIG_SCHEMA = cv.All( async def to_code(config): - if config[CONF_DISABLED]: - return - - cg.add_define("USE_MDNS") if CORE.using_arduino: if CORE.is_esp32: cg.add_library("ESPmDNS", None) elif CORE.is_esp8266: cg.add_library("ESP8266mDNS", None) + if config[CONF_DISABLED]: + return + + cg.add_define("USE_MDNS") + var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) diff --git a/esphome/components/ota/__init__.py b/esphome/components/ota/__init__.py index 59ab22056b..7856d35580 100644 --- a/esphome/components/ota/__init__.py +++ b/esphome/components/ota/__init__.py @@ -101,6 +101,7 @@ async def to_code(config): if CORE.is_esp8266: cg.add_library("Update", None) elif CORE.is_esp32 and CORE.using_arduino: + cg.add_library("Update", None) cg.add_library("Hash", None) use_state_callback = False diff --git a/esphome/components/spi/__init__.py b/esphome/components/spi/__init__.py index 803a45814c..3a96cce99b 100644 --- a/esphome/components/spi/__init__.py +++ b/esphome/components/spi/__init__.py @@ -10,7 +10,7 @@ from esphome.const import ( CONF_SPI_ID, CONF_CS_PIN, ) -from esphome.core import coroutine_with_priority +from esphome.core import coroutine_with_priority, CORE CODEOWNERS = ["@esphome/core"] spi_ns = cg.esphome_ns.namespace("spi") @@ -46,6 +46,9 @@ async def to_code(config): mosi = await cg.gpio_pin_expression(config[CONF_MOSI_PIN]) cg.add(var.set_mosi(mosi)) + if CORE.is_esp32: + cg.add_library("SPI", None) + def spi_device_schema(cs_pin_required=True): """Create a schema for an SPI device. diff --git a/esphome/components/web_server_base/__init__.py b/esphome/components/web_server_base/__init__.py index 8f64c473f3..95d59a863e 100644 --- a/esphome/components/web_server_base/__init__.py +++ b/esphome/components/web_server_base/__init__.py @@ -24,6 +24,8 @@ async def to_code(config): await cg.register_component(var, config) if CORE.is_esp32: + cg.add_library("WiFi", None) cg.add_library("FS", None) + cg.add_library("Update", None) # https://github.com/esphome/ESPAsyncWebServer/blob/master/library.json cg.add_library("esphome/ESPAsyncWebServer-esphome", "1.3.0") From 49f46a7cddec08d169ab23a16d6f97c0f8a8f564 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Sun, 3 Oct 2021 21:55:19 +0200 Subject: [PATCH 160/207] Use size_t to fix comparision using RISC-V toolchain (#2436) --- esphome/components/ota/ota_component.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/ota/ota_component.cpp b/esphome/components/ota/ota_component.cpp index e1188a4d31..e897d952ef 100644 --- a/esphome/components/ota/ota_component.cpp +++ b/esphome/components/ota/ota_component.cpp @@ -109,11 +109,11 @@ void OTAComponent::loop() { void OTAComponent::handle_() { OTAResponseTypes error_code = OTA_RESPONSE_ERROR_UNKNOWN; bool update_started = false; - uint32_t total = 0; + size_t total = 0; uint32_t last_progress = 0; uint8_t buf[1024]; char *sbuf = reinterpret_cast(buf); - uint32_t ota_size; + size_t ota_size; uint8_t ota_features; std::unique_ptr backend; (void) ota_features; From 5c06cd8eb32ccbde3d3c9605ba70d302a7ae18b1 Mon Sep 17 00:00:00 2001 From: Maurice Makaay Date: Mon, 4 Oct 2021 12:33:25 +0200 Subject: [PATCH 161/207] Fix I2C recovery ESP32 esp-idf (#2438) Co-authored-by: Maurice Makaay --- esphome/components/i2c/i2c_bus_arduino.cpp | 49 ++++---- esphome/components/i2c/i2c_bus_esp_idf.cpp | 125 +++++++++++++++++---- esphome/components/i2c/i2c_bus_esp_idf.h | 7 ++ 3 files changed, 137 insertions(+), 44 deletions(-) diff --git a/esphome/components/i2c/i2c_bus_arduino.cpp b/esphome/components/i2c/i2c_bus_arduino.cpp index 539091ed9c..4b519e4873 100644 --- a/esphome/components/i2c/i2c_bus_arduino.cpp +++ b/esphome/components/i2c/i2c_bus_arduino.cpp @@ -12,6 +12,7 @@ static const char *const TAG = "i2c.arduino"; void ArduinoI2CBus::setup() { recover_(); + #ifdef USE_ESP32 static uint8_t next_bus_num = 0; if (next_bus_num == 0) @@ -109,11 +110,19 @@ ErrorCode ArduinoI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cn void ArduinoI2CBus::recover_() { ESP_LOGI(TAG, "Performing I2C bus recovery"); - // Activate the pull up resistor on the SCL pin. This should make the - // signal on the line HIGH. If SCL is pulled low on the I2C bus however, - // then some device is interfering with the SCL line. In that case, - // the I2C bus cannot be recovered. - pinMode(scl_pin_, INPUT_PULLUP); // NOLINT + // For the upcoming operations, target for a 100kHz toggle frequency. + // This is the maximum frequency for I2C running in standard-mode. + // The actual frequency will be lower, because of the additional + // function calls that are done, but that is no problem. + const auto half_period_usec = 1000000 / 100000 / 2; + + // Activate input and pull up resistor for the SCL pin. + pinMode(scl_pin_, INPUT_PULLUP); // NOLINT + + // This should make the signal on the line HIGH. If SCL is pulled low + // on the I2C bus however, then some device is interfering with the SCL + // line. In that case, the I2C bus cannot be recovered. + delayMicroseconds(half_period_usec); if (digitalRead(scl_pin_) == LOW) { // NOLINT ESP_LOGE(TAG, "Recovery failed: SCL is held LOW on the I2C bus"); recovery_result_ = RECOVERY_FAILED_SCL_LOW; @@ -125,25 +134,13 @@ void ArduinoI2CBus::recover_() { // device that held the bus LOW should release it sometime within // those nine clocks." // We don't really have to detect if SDA is stuck low. We'll simply send - // nine clock pulses here, just in case SDA is stuck. + // nine clock pulses here, just in case SDA is stuck. Actual checks on + // the SDA line status will be done after the clock pulses. - // Use a 100kHz toggle frequency (i.e. the maximum frequency for I2C - // running in standard-mode). The resulting frequency will be lower, - // because of the additional function calls that are done, but that - // is no problem. - const auto half_period_usec = 1000000 / 100000 / 2; - - // Make sure that switching to mode OUTPUT will make SCL low, just in - // case other code has setup the pin to output a HIGH signal. + // Make sure that switching to output mode will make SCL low, just in + // case other code has setup the pin for a HIGH signal. digitalWrite(scl_pin_, LOW); // NOLINT - // Activate the pull up resistor for SDA, so after the clock pulse cycle - // we can verify if SDA is pulled high. Also make sure that switching to - // mode OUTPUT will make SDA low. - pinMode(sda_pin_, INPUT_PULLUP); // NOLINT - digitalWrite(sda_pin_, LOW); // NOLINT - - ESP_LOGI(TAG, "Sending 9 clock pulses to drain any stuck device output"); delayMicroseconds(half_period_usec); for (auto i = 0; i < 9; i++) { // Release pull up resistor and switch to output to make the signal LOW. @@ -171,6 +168,11 @@ void ArduinoI2CBus::recover_() { } } + // Activate input and pull resistor for the SDA pin, so we can verify + // that SDA is pulled HIGH in the following step. + pinMode(sda_pin_, INPUT_PULLUP); // NOLINT + digitalWrite(sda_pin_, LOW); // NOLINT + // By now, any stuck device ought to have sent all remaining bits of its // transation, meaning that it should have freed up the SDA line, resulting // in SDA being pulled up. @@ -191,10 +193,9 @@ void ArduinoI2CBus::recover_() { // out of this state. // SCL and SDA are already high at this point, so we can generate a START // condition by making the SDA signal LOW. - ESP_LOGI(TAG, "Generate START condition to reset bus logic of I2C devices"); + delayMicroseconds(half_period_usec); pinMode(sda_pin_, INPUT); // NOLINT pinMode(sda_pin_, OUTPUT); // NOLINT - delayMicroseconds(half_period_usec); // From the specification: // "A START condition immediately followed by a STOP condition (void @@ -202,7 +203,7 @@ void ArduinoI2CBus::recover_() { // operate properly under this condition." // Finally, we'll bring the I2C bus into a starting state by generating // a STOP condition. - ESP_LOGI(TAG, "Generate STOP condition to finalize recovery"); + delayMicroseconds(half_period_usec); pinMode(sda_pin_, INPUT); // NOLINT pinMode(sda_pin_, INPUT_PULLUP); // NOLINT diff --git a/esphome/components/i2c/i2c_bus_esp_idf.cpp b/esphome/components/i2c/i2c_bus_esp_idf.cpp index 28e71ab2a0..91fd1499e9 100644 --- a/esphome/components/i2c/i2c_bus_esp_idf.cpp +++ b/esphome/components/i2c/i2c_bus_esp_idf.cpp @@ -43,6 +43,17 @@ void IDFI2CBus::dump_config() { ESP_LOGCONFIG(TAG, " SDA Pin: GPIO%u", this->sda_pin_); ESP_LOGCONFIG(TAG, " SCL Pin: GPIO%u", this->scl_pin_); ESP_LOGCONFIG(TAG, " Frequency: %u Hz", this->frequency_); + switch (this->recovery_result_) { + case RECOVERY_COMPLETED: + ESP_LOGCONFIG(TAG, " Recovery: bus successfully recovered"); + break; + case RECOVERY_FAILED_SCL_LOW: + ESP_LOGCONFIG(TAG, " Recovery: failed, SCL is held low on the bus"); + break; + case RECOVERY_FAILED_SDA_LOW: + ESP_LOGCONFIG(TAG, " Recovery: failed, SDA is held low on the bus"); + break; + } if (this->scan_) { ESP_LOGI(TAG, "Scanning i2c bus for active devices..."); uint8_t found = 0; @@ -144,39 +155,113 @@ ErrorCode IDFI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cnt) { return ERROR_OK; } +/// Perform I2C bus recovery, see: +/// https://www.nxp.com/docs/en/user-guide/UM10204.pdf +/// https://www.analog.com/media/en/technical-documentation/application-notes/54305147357414AN686_0.pdf void IDFI2CBus::recover_() { - // Perform I2C bus recovery, see - // https://www.analog.com/media/en/technical-documentation/application-notes/54305147357414AN686_0.pdf - // or see the linux kernel implementation, e.g. - // https://elixir.bootlin.com/linux/v5.14.6/source/drivers/i2c/i2c-core-base.c#L200 + ESP_LOGI(TAG, "Performing I2C bus recovery"); - // try to get about 100kHz toggle frequency - const auto half_period_usec = 1000000 / 100000 / 2; - const auto recover_scl_periods = 9; const gpio_num_t scl_pin = static_cast(scl_pin_); + const gpio_num_t sda_pin = static_cast(sda_pin_); - // configure scl as output - gpio_config_t conf{}; - conf.pin_bit_mask = 1ULL << static_cast(scl_pin_); - conf.mode = GPIO_MODE_OUTPUT; - conf.pull_up_en = GPIO_PULLUP_DISABLE; - conf.pull_down_en = GPIO_PULLDOWN_DISABLE; - conf.intr_type = GPIO_INTR_DISABLE; + // For the upcoming operations, target for a 60kHz toggle frequency. + // 1000kHz is the maximum frequency for I2C running in standard-mode, + // but lower frequencies are not a problem. + // Note: the timing that is used here is chosen manually, to get + // results that are close to the timing that can be archieved by the + // implementation for the Arduino framework. + const auto half_period_usec = 7; - gpio_config(&conf); - - // set scl high + // Configure SCL pin for open drain input/output, with a pull up resistor. gpio_set_level(scl_pin, 1); + gpio_config_t scl_config{}; + scl_config.pin_bit_mask = 1ULL << scl_pin_; + scl_config.mode = GPIO_MODE_INPUT_OUTPUT_OD; + scl_config.pull_up_en = GPIO_PULLUP_ENABLE; + scl_config.pull_down_en = GPIO_PULLDOWN_DISABLE; + scl_config.intr_type = GPIO_INTR_DISABLE; + gpio_config(&scl_config); - // in total generate 9 falling-rising edges - for (auto i = 0; i < recover_scl_periods; i++) { - delayMicroseconds(half_period_usec); + // Configure SDA pin for open drain input/output, with a pull up resistor. + gpio_set_level(sda_pin, 1); + gpio_config_t sda_conf{}; + sda_conf.pin_bit_mask = 1ULL << sda_pin_; + sda_conf.mode = GPIO_MODE_INPUT_OUTPUT_OD; + sda_conf.pull_up_en = GPIO_PULLUP_ENABLE; + sda_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + sda_conf.intr_type = GPIO_INTR_DISABLE; + gpio_config(&sda_conf); + + // If SCL is pulled low on the I2C bus, then some device is interfering + // with the SCL line. In that case, the I2C bus cannot be recovered. + delayMicroseconds(half_period_usec); + if (gpio_get_level(scl_pin) == 0) { + ESP_LOGE(TAG, "Recovery failed: SCL is held LOW on the I2C bus"); + recovery_result_ = RECOVERY_FAILED_SCL_LOW; + return; + } + + // From the specification: + // "If the data line (SDA) is stuck LOW, send nine clock pulses. The + // device that held the bus LOW should release it sometime within + // those nine clocks." + // We don't really have to detect if SDA is stuck low. We'll simply send + // nine clock pulses here, just in case SDA is stuck. Actual checks on + // the SDA line status will be done after the clock pulses. + for (auto i = 0; i < 9; i++) { gpio_set_level(scl_pin, 0); delayMicroseconds(half_period_usec); gpio_set_level(scl_pin, 1); + delayMicroseconds(half_period_usec); + + // When SCL is kept LOW at this point, we might be looking at a device + // that applies clock stretching. Wait for the release of the SCL line, + // but not forever. There is no specification for the maximum allowed + // time. We'll stick to 500ms here. + auto wait = 20; + while (wait-- && gpio_get_level(scl_pin) == 0) { + delay(25); + } + if (gpio_get_level(scl_pin) == 0) { + ESP_LOGE(TAG, "Recovery failed: SCL is held LOW during clock pulse cycle"); + recovery_result_ = RECOVERY_FAILED_SCL_LOW; + return; + } } + // By now, any stuck device ought to have sent all remaining bits of its + // transation, meaning that it should have freed up the SDA line, resulting + // in SDA being pulled up. + if (gpio_get_level(sda_pin) == 0) { + ESP_LOGE(TAG, "Recovery failed: SDA is held LOW after clock pulse cycle"); + recovery_result_ = RECOVERY_FAILED_SDA_LOW; + return; + } + + // From the specification: + // "I2C-bus compatible devices must reset their bus logic on receipt of + // a START or repeated START condition such that they all anticipate + // the sending of a target address, even if these START conditions are + // not positioned according to the proper format." + // While the 9 clock pulses from above might have drained all bits of a + // single byte within a transaction, a device might have more bytes to + // transmit. So here we'll generate a START condition to snap the device + // out of this state. + // SCL and SDA are already high at this point, so we can generate a START + // condition by making the SDA signal LOW. delayMicroseconds(half_period_usec); + gpio_set_level(sda_pin, 0); + + // From the specification: + // "A START condition immediately followed by a STOP condition (void + // message) is an illegal format. Many devices however are designed to + // operate properly under this condition." + // Finally, we'll bring the I2C bus into a starting state by generating + // a STOP condition. + delayMicroseconds(half_period_usec); + gpio_set_level(sda_pin, 1); + + recovery_result_ = RECOVERY_COMPLETED; } } // namespace i2c diff --git a/esphome/components/i2c/i2c_bus_esp_idf.h b/esphome/components/i2c/i2c_bus_esp_idf.h index ba5fbf25c5..13d996dbd8 100644 --- a/esphome/components/i2c/i2c_bus_esp_idf.h +++ b/esphome/components/i2c/i2c_bus_esp_idf.h @@ -9,6 +9,12 @@ namespace esphome { namespace i2c { +enum RecoveryCode { + RECOVERY_FAILED_SCL_LOW, + RECOVERY_FAILED_SDA_LOW, + RECOVERY_COMPLETED, +}; + class IDFI2CBus : public I2CBus, public Component { public: void setup() override; @@ -26,6 +32,7 @@ class IDFI2CBus : public I2CBus, public Component { private: void recover_(); + RecoveryCode recovery_result_; protected: i2c_port_t port_; From 87358e884392a717a3bd65a5b3ad39d3c2d3524a Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 4 Oct 2021 16:14:51 +0200 Subject: [PATCH 162/207] Fix esp32 no longer has Hash internal lib (#2441) --- esphome/components/ota/__init__.py | 1 - esphome/components/wifi/__init__.py | 2 ++ platformio.ini | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/ota/__init__.py b/esphome/components/ota/__init__.py index 7856d35580..bcfb28979d 100644 --- a/esphome/components/ota/__init__.py +++ b/esphome/components/ota/__init__.py @@ -102,7 +102,6 @@ async def to_code(config): cg.add_library("Update", None) elif CORE.is_esp32 and CORE.using_arduino: cg.add_library("Update", None) - cg.add_library("Hash", None) use_state_callback = False for conf in config.get(CONF_ON_STATE_CHANGE, []): diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index 7d029bbea1..19e4046711 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -342,6 +342,8 @@ async def to_code(config): if CORE.is_esp8266: cg.add_library("ESP8266WiFi", None) + elif CORE.is_esp32 and CORE.using_arduino: + cg.add_library("WiFi", None) cg.add_define("USE_WIFI") diff --git a/platformio.ini b/platformio.ini index 6e605768fc..f38bbc78a9 100644 --- a/platformio.ini +++ b/platformio.ini @@ -89,7 +89,6 @@ framework = arduino board = nodemcu-32s lib_deps = ${common:arduino.lib_deps} - Hash ; ota (Arduino built-in) esphome/AsyncTCP-esphome@1.2.2 ; async_tcp build_flags = ${common:arduino.build_flags} From 871d3b66fb5f3d8484456f4da414f56411989fad Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 4 Oct 2021 16:15:25 +0200 Subject: [PATCH 163/207] Fix restoring globals (#2442) --- esphome/cpp_generator.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/esphome/cpp_generator.py b/esphome/cpp_generator.py index cf357f2814..937b6cceb4 100644 --- a/esphome/cpp_generator.py +++ b/esphome/cpp_generator.py @@ -638,7 +638,7 @@ async def process_lambda( :param return_type: The return type of the lambda. :return: The generated lambda expression. """ - from esphome.components.globals import GlobalsComponent + from esphome.components.globals import GlobalsComponent, RestoringGlobalsComponent if value is None: return @@ -648,7 +648,10 @@ async def process_lambda( if ( full_id is not None and isinstance(full_id.type, MockObjClass) - and full_id.type.inherits_from(GlobalsComponent) + and ( + full_id.type.inherits_from(GlobalsComponent) + or full_id.type.inherits_from(RestoringGlobalsComponent) + ) ): parts[i * 3 + 1] = var.value() continue From 8be40862243c7123b668ccb774f9ce3f910f4835 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 4 Oct 2021 16:59:15 +0200 Subject: [PATCH 164/207] Always upload using esptool (#2433) --- esphome/__main__.py | 35 +++++++++---- esphome/core/__init__.py | 3 ++ esphome/platformio_api.py | 102 ++++++++++++++++++++++++-------------- esphome/storage_json.py | 2 +- 4 files changed, 94 insertions(+), 48 deletions(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index 6bc7e065ce..feb95e93c7 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -184,12 +184,30 @@ def compile_program(args, config): def upload_using_esptool(config, port): - path = CORE.firmware_bin + from esphome import platformio_api + first_baudrate = config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get( "upload_speed", 460800 ) def run_esptool(baud_rate): + idedata = platformio_api.get_idedata(config) + + firmware_offset = "0x10000" if CORE.is_esp32 else "0x0" + flash_images = [ + platformio_api.FlashImage( + path=idedata.firmware_bin_path, + offset=firmware_offset, + ), + *idedata.extra_flash_images, + ] + + mcu = "esp8266" + if CORE.is_esp32: + from esphome.components.esp32 import get_esp32_variant + + mcu = get_esp32_variant().lower() + cmd = [ "esptool.py", "--before", @@ -198,14 +216,15 @@ def upload_using_esptool(config, port): "hard_reset", "--baud", str(baud_rate), - "--chip", - "esp8266", "--port", port, + "--chip", + mcu, "write_flash", - "0x0", - path, + "-z", ] + for img in flash_images: + cmd += [img.offset, img.path] if os.environ.get("ESPHOME_USE_SUBPROCESS") is None: import esptool @@ -229,11 +248,7 @@ def upload_using_esptool(config, port): def upload_program(config, args, host): # if upload is to a serial port use platformio, otherwise assume ota if get_port_type(host) == "SERIAL": - from esphome import platformio_api - - if CORE.is_esp8266: - return upload_using_esptool(config, host) - return platformio_api.run_upload(config, CORE.verbose, host) + return upload_using_esptool(config, host) from esphome import espota2 diff --git a/esphome/core/__init__.py b/esphome/core/__init__.py index 8021fc2f53..8bdef3a4ea 100644 --- a/esphome/core/__init__.py +++ b/esphome/core/__init__.py @@ -542,6 +542,9 @@ class EsphomeCore: path_ = os.path.expanduser(os.path.join(*path)) return os.path.join(self.config_dir, path_) + def relative_internal_path(self, *path: str) -> str: + return self.relative_config_path(".esphome", *path) + def relative_build_path(self, *path): # pylint: disable=no-value-for-parameter path_ = os.path.expanduser(os.path.join(*path)) diff --git a/esphome/platformio_api.py b/esphome/platformio_api.py index 100def3310..054c0cb1b0 100644 --- a/esphome/platformio_api.py +++ b/esphome/platformio_api.py @@ -1,12 +1,15 @@ +from dataclasses import dataclass import json -from typing import Union +from typing import List, Union +from pathlib import Path import logging import os import re import subprocess -from esphome.core import CORE +from esphome.const import KEY_CORE +from esphome.core import CORE, EsphomeError from esphome.util import run_external_command, run_external_process _LOGGER = logging.getLogger(__name__) @@ -96,36 +99,56 @@ def run_compile(config, verbose): return run_platformio_cli_run(config, verbose) -def run_upload(config, verbose, port): - return run_platformio_cli_run( - config, verbose, "-t", "upload", "--upload-port", port - ) - - -def run_idedata(config): +def _run_idedata(config): args = ["-t", "idedata"] stdout = run_platformio_cli_run(config, False, *args, capture_stdout=True) match = re.search(r'{\s*".*}', stdout) if match is None: - _LOGGER.debug("Could not match IDEData for %s", stdout) - return IDEData(None) + _LOGGER.error("Could not match idedata, please report this error") + _LOGGER.error("Stdout: %s", stdout) + raise EsphomeError + try: - return IDEData(json.loads(match.group())) + return json.loads(match.group()) except ValueError: - _LOGGER.debug("Could not load IDEData for %s", stdout, exc_info=1) - return IDEData(None) + _LOGGER.error("Could not parse idedata", exc_info=True) + _LOGGER.error("Stdout: %s", stdout) + raise -IDE_DATA = None +def _load_idedata(config): + platformio_ini = Path(CORE.relative_build_path("platformio.ini")) + temp_idedata = Path(CORE.relative_internal_path(CORE.name, "idedata.json")) + + changed = False + if not platformio_ini.is_file() or not temp_idedata.is_file(): + changed = True + elif platformio_ini.stat().st_mtime >= temp_idedata.stat().st_mtime: + changed = True + + if not changed: + try: + return json.loads(temp_idedata.read_text(encoding="utf-8")) + except ValueError: + pass + + temp_idedata.parent.mkdir(exist_ok=True, parents=True) + + data = _run_idedata(config) + + temp_idedata.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8") + return data -def get_idedata(config): - global IDE_DATA +KEY_IDEDATA = "idedata" - if IDE_DATA is None: - _LOGGER.info("Need to fetch platformio IDE-data, please stand by") - IDE_DATA = run_idedata(config) - return IDE_DATA + +def get_idedata(config) -> "IDEData": + if KEY_IDEDATA in CORE.data[KEY_CORE]: + return CORE.data[KEY_CORE][KEY_IDEDATA] + idedata = IDEData(_load_idedata(config)) + CORE.data[KEY_CORE][KEY_IDEDATA] = idedata + return idedata # ESP logs stack trace decoder, based on https://github.com/me-no-dev/EspExceptionDecoder @@ -261,37 +284,42 @@ def process_stacktrace(config, line, backtrace_state): return backtrace_state +@dataclass +class FlashImage: + path: str + offset: str + + class IDEData: def __init__(self, raw): - if not isinstance(raw, dict): - self.raw = {} - else: - self.raw = raw + self.raw = raw @property def firmware_elf_path(self): - return self.raw.get("prog_path") + return self.raw["prog_path"] @property - def flash_extra_images(self): + def firmware_bin_path(self) -> str: + return str(Path(self.firmware_elf_path).with_suffix(".bin")) + + @property + def extra_flash_images(self) -> List[FlashImage]: return [ - (x["path"], x["offset"]) for x in self.raw.get("flash_extra_images", []) + FlashImage(path=entry["path"], offset=entry["offset"]) + for entry in self.raw["extra"]["flash_images"] ] @property - def cc_path(self): + def cc_path(self) -> str: # For example /Users//.platformio/packages/toolchain-xtensa32/bin/xtensa-esp32-elf-gcc - return self.raw.get("cc_path") + return self.raw["cc_path"] @property - def addr2line_path(self): - cc_path = self.cc_path - if cc_path is None: - return None + def addr2line_path(self) -> str: # replace gcc at end with addr2line # Windows - if cc_path.endswith(".exe"): - return f"{cc_path[:-7]}addr2line.exe" + if self.cc_path.endswith(".exe"): + return f"{self.cc_path[:-7]}addr2line.exe" - return f"{cc_path[:-3]}addr2line" + return f"{self.cc_path[:-3]}addr2line" diff --git a/esphome/storage_json.py b/esphome/storage_json.py index e70d3d6c93..3262559116 100644 --- a/esphome/storage_json.py +++ b/esphome/storage_json.py @@ -16,7 +16,7 @@ _LOGGER = logging.getLogger(__name__) def storage_path(): # type: () -> str - return CORE.relative_config_path(".esphome", f"{CORE.config_filename}.json") + return CORE.relative_internal_path(f"{CORE.config_filename}.json") def ext_storage_path(base_path, config_filename): # type: (str, str) -> str From 877367677b14b397fa023430504791736d47a781 Mon Sep 17 00:00:00 2001 From: NMC Date: Mon, 4 Oct 2021 18:56:34 -0400 Subject: [PATCH 165/207] Add support for Airthing Wave Mini (#2440) --- CODEOWNERS | 1 + .../airthings_wave_mini/__init__.py | 1 + .../airthings_wave_mini.cpp | 120 ++++++++++++++++++ .../airthings_wave_mini/airthings_wave_mini.h | 65 ++++++++++ .../components/airthings_wave_mini/sensor.py | 88 +++++++++++++ tests/test2.yaml | 14 ++ 6 files changed, 289 insertions(+) create mode 100644 esphome/components/airthings_wave_mini/__init__.py create mode 100644 esphome/components/airthings_wave_mini/airthings_wave_mini.cpp create mode 100644 esphome/components/airthings_wave_mini/airthings_wave_mini.h create mode 100644 esphome/components/airthings_wave_mini/sensor.py diff --git a/CODEOWNERS b/CODEOWNERS index 2972b30b33..6576c78f0e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -15,6 +15,7 @@ esphome/components/ac_dimmer/* @glmnet esphome/components/adc/* @esphome/core esphome/components/addressable_light/* @justfalter esphome/components/airthings_ble/* @jeromelaban +esphome/components/airthings_wave_mini/* @ncareau esphome/components/airthings_wave_plus/* @jeromelaban esphome/components/am43/* @buxtronix esphome/components/am43/cover/* @buxtronix diff --git a/esphome/components/airthings_wave_mini/__init__.py b/esphome/components/airthings_wave_mini/__init__.py new file mode 100644 index 0000000000..022f35b4cf --- /dev/null +++ b/esphome/components/airthings_wave_mini/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@ncareau"] diff --git a/esphome/components/airthings_wave_mini/airthings_wave_mini.cpp b/esphome/components/airthings_wave_mini/airthings_wave_mini.cpp new file mode 100644 index 0000000000..0ab0e65148 --- /dev/null +++ b/esphome/components/airthings_wave_mini/airthings_wave_mini.cpp @@ -0,0 +1,120 @@ +#include "airthings_wave_mini.h" + +#ifdef USE_ESP32_FRAMEWORK_ARDUINO + +namespace esphome { +namespace airthings_wave_mini { + +static const char *const TAG = "airthings_wave_mini"; + +void AirthingsWaveMini::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t *param) { + switch (event) { + case ESP_GATTC_OPEN_EVT: { + if (param->open.status == ESP_GATT_OK) { + ESP_LOGI(TAG, "Connected successfully!"); + } + break; + } + + case ESP_GATTC_DISCONNECT_EVT: { + ESP_LOGW(TAG, "Disconnected!"); + break; + } + + case ESP_GATTC_SEARCH_CMPL_EVT: { + this->handle_ = 0; + auto chr = this->parent()->get_characteristic(service_uuid_, sensors_data_characteristic_uuid_); + if (chr == nullptr) { + ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", service_uuid_.to_string().c_str(), + sensors_data_characteristic_uuid_.to_string().c_str()); + break; + } + this->handle_ = chr->handle; + this->node_state = esp32_ble_tracker::ClientState::ESTABLISHED; + + request_read_values_(); + break; + } + + case ESP_GATTC_READ_CHAR_EVT: { + if (param->read.conn_id != this->parent()->conn_id) + break; + if (param->read.status != ESP_GATT_OK) { + ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status); + break; + } + if (param->read.handle == this->handle_) { + read_sensors_(param->read.value, param->read.value_len); + } + break; + } + + default: + break; + } +} + +void AirthingsWaveMini::read_sensors_(uint8_t *raw_value, uint16_t value_len) { + auto value = (WaveMiniReadings *) raw_value; + + if (sizeof(WaveMiniReadings) <= value_len) { + this->humidity_sensor_->publish_state(value->humidity / 100.0f); + this->pressure_sensor_->publish_state(value->pressure / 50.0f); + this->temperature_sensor_->publish_state(value->temperature / 100.0f - 273.15f); + if (is_valid_voc_value_(value->voc)) { + this->tvoc_sensor_->publish_state(value->voc); + } + + // This instance must not stay connected + // so other clients can connect to it (e.g. the + // mobile app). + parent()->set_enabled(false); + } +} + +bool AirthingsWaveMini::is_valid_voc_value_(uint16_t voc) { return 0 <= voc && voc <= 16383; } + +void AirthingsWaveMini::loop() {} + +void AirthingsWaveMini::update() { + if (this->node_state != esp32_ble_tracker::ClientState::ESTABLISHED) { + if (!parent()->enabled) { + ESP_LOGW(TAG, "Reconnecting to device"); + parent()->set_enabled(true); + parent()->connect(); + } else { + ESP_LOGW(TAG, "Connection in progress"); + } + } +} + +void AirthingsWaveMini::request_read_values_() { + auto status = + esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle_, ESP_GATT_AUTH_REQ_NONE); + if (status) { + ESP_LOGW(TAG, "Error sending read request for sensor, status=%d", status); + } +} + +void AirthingsWaveMini::dump_config() { + LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); + LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); + LOG_SENSOR(" ", "Pressure", this->pressure_sensor_); + LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_); +} + +AirthingsWaveMini::AirthingsWaveMini() : PollingComponent(10000) { + auto service_bt = *BLEUUID::fromString(std::string("b42e3882-ade7-11e4-89d3-123b93f75cba")).getNative(); + auto characteristic_bt = *BLEUUID::fromString(std::string("b42e3b98-ade7-11e4-89d3-123b93f75cba")).getNative(); + + service_uuid_ = esp32_ble_tracker::ESPBTUUID::from_uuid(service_bt); + sensors_data_characteristic_uuid_ = esp32_ble_tracker::ESPBTUUID::from_uuid(characteristic_bt); +} + +void AirthingsWaveMini::setup() {} + +} // namespace airthings_wave_mini +} // namespace esphome + +#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/airthings_wave_mini/airthings_wave_mini.h b/esphome/components/airthings_wave_mini/airthings_wave_mini.h new file mode 100644 index 0000000000..5d1964d559 --- /dev/null +++ b/esphome/components/airthings_wave_mini/airthings_wave_mini.h @@ -0,0 +1,65 @@ +#pragma once + +#ifdef USE_ESP32_FRAMEWORK_ARDUINO + +#include +#include +#include +#include +#include "esphome/core/component.h" +#include "esphome/core/log.h" +#include "esphome/components/ble_client/ble_client.h" +#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" +#include "esphome/components/sensor/sensor.h" + +namespace esphome { +namespace airthings_wave_mini { + +class AirthingsWaveMini : public PollingComponent, public ble_client::BLEClientNode { + public: + AirthingsWaveMini(); + + void setup() override; + void dump_config() override; + void update() override; + void loop() override; + + void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t *param) override; + + void set_temperature(sensor::Sensor *temperature) { temperature_sensor_ = temperature; } + void set_humidity(sensor::Sensor *humidity) { humidity_sensor_ = humidity; } + void set_pressure(sensor::Sensor *pressure) { pressure_sensor_ = pressure; } + void set_tvoc(sensor::Sensor *tvoc) { tvoc_sensor_ = tvoc; } + + protected: + bool is_valid_voc_value_(uint16_t voc); + + void read_sensors_(uint8_t *value, uint16_t value_len); + void request_read_values_(); + + sensor::Sensor *temperature_sensor_{nullptr}; + sensor::Sensor *humidity_sensor_{nullptr}; + sensor::Sensor *pressure_sensor_{nullptr}; + sensor::Sensor *tvoc_sensor_{nullptr}; + + uint16_t handle_; + esp32_ble_tracker::ESPBTUUID service_uuid_; + esp32_ble_tracker::ESPBTUUID sensors_data_characteristic_uuid_; + + struct WaveMiniReadings { + uint16_t unused01; + uint16_t temperature; + uint16_t pressure; + uint16_t humidity; + uint16_t voc; + uint16_t unused02; + uint32_t unused03; + uint32_t unused04; + }; +}; + +} // namespace airthings_wave_mini +} // namespace esphome + +#endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/airthings_wave_mini/sensor.py b/esphome/components/airthings_wave_mini/sensor.py new file mode 100644 index 0000000000..6a32cd8771 --- /dev/null +++ b/esphome/components/airthings_wave_mini/sensor.py @@ -0,0 +1,88 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import sensor, ble_client +from esphome.core import CORE + +from esphome.const import ( + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_PRESSURE, + STATE_CLASS_MEASUREMENT, + UNIT_PERCENT, + UNIT_CELSIUS, + UNIT_HECTOPASCAL, + CONF_ID, + CONF_HUMIDITY, + CONF_TVOC, + CONF_PRESSURE, + CONF_TEMPERATURE, + UNIT_PARTS_PER_BILLION, + ICON_RADIATOR, +) + +DEPENDENCIES = ["ble_client"] + +airthings_wave_mini_ns = cg.esphome_ns.namespace("airthings_wave_mini") +AirthingsWaveMini = airthings_wave_mini_ns.class_( + "AirthingsWaveMini", cg.PollingComponent, ble_client.BLEClientNode +) + + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(AirthingsWaveMini), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( + unit_of_measurement=UNIT_PERCENT, + device_class=DEVICE_CLASS_HUMIDITY, + state_class=STATE_CLASS_MEASUREMENT, + accuracy_decimals=2, + ), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=2, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_PRESSURE): sensor.sensor_schema( + unit_of_measurement=UNIT_HECTOPASCAL, + accuracy_decimals=2, + device_class=DEVICE_CLASS_PRESSURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_TVOC): sensor.sensor_schema( + unit_of_measurement=UNIT_PARTS_PER_BILLION, + icon=ICON_RADIATOR, + accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, + ), + } + ) + .extend(cv.polling_component_schema("5mins")) + .extend(ble_client.BLE_CLIENT_SCHEMA), + # Until BLEUUID reference removed + cv.only_with_arduino, +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + + await ble_client.register_ble_node(var, config) + + if CONF_HUMIDITY in config: + sens = await sensor.new_sensor(config[CONF_HUMIDITY]) + cg.add(var.set_humidity(sens)) + if CONF_TEMPERATURE in config: + sens = await sensor.new_sensor(config[CONF_TEMPERATURE]) + cg.add(var.set_temperature(sens)) + if CONF_PRESSURE in config: + sens = await sensor.new_sensor(config[CONF_PRESSURE]) + cg.add(var.set_pressure(sens)) + if CONF_TVOC in config: + sens = await sensor.new_sensor(config[CONF_TVOC]) + cg.add(var.set_tvoc(sens)) + + if CORE.is_esp32: + cg.add_library("ESP32 BLE Arduino", None) diff --git a/tests/test2.yaml b/tests/test2.yaml index 4541fba616..364bcec28f 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -281,6 +281,17 @@ sensor: name: "Wave Plus CO2" tvoc: name: "Wave Plus VOC" + - platform: airthings_wave_mini + ble_client_id: airthingsmini01 + update_interval: 5min + temperature: + name: "Wave Mini Temperature" + humidity: + name: "Wave Mini Humidity" + pressure: + name: "Wave Mini Pressure" + tvoc: + name: "Wave Mini VOC" time: - platform: homeassistant @@ -378,6 +389,9 @@ esp32_ble_tracker: ble_client: - mac_address: 01:02:03:04:05:06 id: airthings01 + - mac_address: 01:02:03:04:05:06 + id: airthingsmini01 + airthings_ble: From 6ec546a6a481ffb449bbd086a1d937cf53e5c1b0 Mon Sep 17 00:00:00 2001 From: Paul Monigatti Date: Tue, 5 Oct 2021 12:00:23 +1300 Subject: [PATCH 166/207] Improved validation for Addressable Light Partition Segments (#2439) Co-authored-by: Otto Winter --- esphome/components/partition/light.py | 30 +++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/esphome/components/partition/light.py b/esphome/components/partition/light.py index ada83a123e..822b7ac306 100644 --- a/esphome/components/partition/light.py +++ b/esphome/components/partition/light.py @@ -1,11 +1,13 @@ import esphome.codegen as cg import esphome.config_validation as cv +import esphome.final_validate as fv from esphome.components import light from esphome.const import ( CONF_ADDRESSABLE_LIGHT_ID, CONF_FROM, CONF_ID, CONF_LIGHT_ID, + CONF_NUM_LEDS, CONF_SEGMENTS, CONF_SINGLE_LIGHT_ID, CONF_TO, @@ -31,6 +33,27 @@ def validate_from_to(value): return value +def validate_segment(config): + fconf = fv.full_config.get() + + if CONF_ID in config: # only validate addressable segments + path = fconf.get_path_for_id(config[CONF_ID])[:-1] + segment_light_config = fconf.get_config_for_path(path) + + if CONF_NUM_LEDS in segment_light_config: + segment_len = segment_light_config[CONF_NUM_LEDS] + if config[CONF_FROM] >= segment_len: + raise cv.Invalid( + f"FROM ({config[CONF_FROM]}) must be less than the number of LEDs in light '{config[CONF_ID]}' ({segment_len})", + [CONF_FROM], + ) + if config[CONF_TO] >= segment_len: + raise cv.Invalid( + f"TO ({config[CONF_TO]}) must be less than the number of LEDs in light '{config[CONF_ID]}' ({segment_len})", + [CONF_TO], + ) + + ADDRESSABLE_SEGMENT_SCHEMA = cv.Schema( { cv.Required(CONF_ID): cv.use_id(light.AddressableLightState), @@ -63,6 +86,13 @@ CONFIG_SCHEMA = light.ADDRESSABLE_LIGHT_SCHEMA.extend( } ) +FINAL_VALIDATE_SCHEMA = cv.Schema( + { + cv.Required(CONF_SEGMENTS): [validate_segment], + }, + extra=cv.ALLOW_EXTRA, +) + async def to_code(config): segments = [] From e09ee8f23d7a862f8026ccaf06797878a501a42b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Oct 2021 14:49:55 +0200 Subject: [PATCH 167/207] Bump aioesphomeapi from 9.1.2 to 9.1.4 (#2443) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index ba2c43e5b7..176c623691 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ platformio==5.2.0 esptool==3.1 click==8.0.1 esphome-dashboard==20210927.0 -aioesphomeapi==9.1.2 +aioesphomeapi==9.1.4 # esp-idf requires this, but doesn't bundle it by default # https://github.com/espressif/esp-idf/blob/220590d599e134d7a5e7f1e683cc4550349ffbf8/requirements.txt#L24 From e22f1fc044d30cd92628d53c0eaa8e4cd438ded0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Oct 2021 14:50:18 +0200 Subject: [PATCH 168/207] Bump pytest-cov from 2.12.1 to 3.0.0 (#2444) --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index 1d09bb6388..85456643bc 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -6,7 +6,7 @@ pre-commit # Unit tests pytest==6.2.5 -pytest-cov==2.12.1 +pytest-cov==3.0.0 pytest-mock==3.6.1 pytest-asyncio==0.15.1 asyncmock==0.4.2 From a57580b5ab972e97e1f24f077afd5691091a374f Mon Sep 17 00:00:00 2001 From: Martin <25747549+martgras@users.noreply.github.com> Date: Tue, 5 Oct 2021 17:56:32 +0200 Subject: [PATCH 169/207] Fix compilation error (#2447) --- esphome/components/shutdown/shutdown_switch.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/shutdown/shutdown_switch.cpp b/esphome/components/shutdown/shutdown_switch.cpp index 0e5853cc46..a5f9a92982 100644 --- a/esphome/components/shutdown/shutdown_switch.cpp +++ b/esphome/components/shutdown/shutdown_switch.cpp @@ -1,4 +1,5 @@ #include "shutdown_switch.h" +#include "esphome/core/hal.h" #include "esphome/core/log.h" #include "esphome/core/application.h" From e083d7f4d0c233e27b1c0cc9cfdb98b1dd416e1f Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 6 Oct 2021 11:26:18 +1300 Subject: [PATCH 170/207] Add log line to show if API encryption is being used (#2450) --- esphome/components/api/api_server.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index 8728597537..4e2899d94f 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -133,6 +133,11 @@ void APIServer::loop() { void APIServer::dump_config() { ESP_LOGCONFIG(TAG, "API Server:"); ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->port_); +#ifdef USE_API_NOISE + ESP_LOGCONFIG(TAG, " Using noise encryption: YES"); +#else + ESP_LOGCONFIG(TAG, " Using noise encryption: NO"); +#endif } bool APIServer::uses_password() const { return !this->password_.empty(); } bool APIServer::check_password(const std::string &password) const { From b8b30599ee150c021c0d2350093e00f3c8ab4f86 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Oct 2021 11:29:30 +1300 Subject: [PATCH 171/207] Bump aioesphomeapi from 9.1.4 to 9.1.5 (#2449) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 176c623691..202e38a21b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ platformio==5.2.0 esptool==3.1 click==8.0.1 esphome-dashboard==20210927.0 -aioesphomeapi==9.1.4 +aioesphomeapi==9.1.5 # esp-idf requires this, but doesn't bundle it by default # https://github.com/espressif/esp-idf/blob/220590d599e134d7a5e7f1e683cc4550349ffbf8/requirements.txt#L24 From 7bbb5213f3cbfd5265f19718b484bb5e32be8fdf Mon Sep 17 00:00:00 2001 From: Alex Iribarren Date: Wed, 6 Oct 2021 00:44:48 +0200 Subject: [PATCH 172/207] Only ping once every two seconds (#2448) --- esphome/dashboard/dashboard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/dashboard/dashboard.py b/esphome/dashboard/dashboard.py index 492b86384d..eb698a7de1 100644 --- a/esphome/dashboard/dashboard.py +++ b/esphome/dashboard/dashboard.py @@ -582,7 +582,7 @@ class MDNSStatusThread(threading.Thread): class PingStatusThread(threading.Thread): def run(self): with multiprocessing.Pool(processes=8) as pool: - while not STOP_EVENT.is_set(): + while not STOP_EVENT.wait(2): # Only do pings if somebody has the dashboard open def callback(ret): From 9ff824080274fdb4eff49311926a6ac13b244ed6 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 6 Oct 2021 11:45:01 +1300 Subject: [PATCH 173/207] Bump esphome-dashboard to 20211006.0 (#2451) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 202e38a21b..880faeddbe 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ pyserial==3.5 platformio==5.2.0 esptool==3.1 click==8.0.1 -esphome-dashboard==20210927.0 +esphome-dashboard==20211006.0 aioesphomeapi==9.1.5 # esp-idf requires this, but doesn't bundle it by default From 54a173dbf1aecae345e94a7eecdc664613f251e6 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Wed, 6 Oct 2021 00:57:23 +0200 Subject: [PATCH 174/207] I2C re-introduce very verbose logging (#2446) --- esphome/components/i2c/i2c_bus_arduino.cpp | 49 ++++++++++++++++++- esphome/components/i2c/i2c_bus_esp_idf.cpp | 56 +++++++++++++++++++++- 2 files changed, 101 insertions(+), 4 deletions(-) diff --git a/esphome/components/i2c/i2c_bus_arduino.cpp b/esphome/components/i2c/i2c_bus_arduino.cpp index 4b519e4873..4afabbfa53 100644 --- a/esphome/components/i2c/i2c_bus_arduino.cpp +++ b/esphome/components/i2c/i2c_bus_arduino.cpp @@ -62,33 +62,75 @@ void ArduinoI2CBus::dump_config() { } } ErrorCode ArduinoI2CBus::readv(uint8_t address, ReadBuffer *buffers, size_t cnt) { - if (!initialized_) + // logging is only enabled with vv level, if warnings are shown the caller + // should log them + if (!initialized_) { + ESP_LOGVV(TAG, "i2c bus not initialized!"); return ERROR_NOT_INITIALIZED; + } size_t to_request = 0; for (size_t i = 0; i < cnt; i++) to_request += buffers[i].len; size_t ret = wire_->requestFrom((int) address, (int) to_request, 1); if (ret != to_request) { + ESP_LOGVV(TAG, "RX %u from %02X failed with error %u", to_request, address, ret); return ERROR_TIMEOUT; } + for (size_t i = 0; i < cnt; i++) { const auto &buf = buffers[i]; for (size_t j = 0; j < buf.len; j++) buf.data[j] = wire_->read(); } + +#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE + char debug_buf[4]; + std::string debug_hex; + + for (size_t i = 0; i < cnt; i++) { + const auto &buf = buffers[i]; + for (size_t j = 0; j < buf.len; j++) { + snprintf(debug_buf, sizeof(debug_buf), "%02X", buf.data[j]); + debug_hex += debug_buf; + } + } + ESP_LOGVV(TAG, "0x%02X RX %s", address, debug_hex.c_str()); +#endif + return ERROR_OK; } ErrorCode ArduinoI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cnt) { - if (!initialized_) + // logging is only enabled with vv level, if warnings are shown the caller + // should log them + if (!initialized_) { + ESP_LOGVV(TAG, "i2c bus not initialized!"); return ERROR_NOT_INITIALIZED; + } + +#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE + char debug_buf[4]; + std::string debug_hex; + + for (size_t i = 0; i < cnt; i++) { + const auto &buf = buffers[i]; + for (size_t j = 0; j < buf.len; j++) { + snprintf(debug_buf, sizeof(debug_buf), "%02X", buf.data[j]); + debug_hex += debug_buf; + } + } + ESP_LOGVV(TAG, "0x%02X TX %s", address, debug_hex.c_str()); +#endif wire_->beginTransmission(address); + size_t written = 0; for (size_t i = 0; i < cnt; i++) { const auto &buf = buffers[i]; if (buf.len == 0) continue; size_t ret = wire_->write(buf.data, buf.len); + written += ret; if (ret != buf.len) { + ESP_LOGVV(TAG, "TX failed at %u", written); return ERROR_UNKNOWN; } } @@ -97,10 +139,13 @@ ErrorCode ArduinoI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cn return ERROR_OK; } else if (status == 1) { // transmit buffer not large enough + ESP_LOGVV(TAG, "TX failed: buffer not large enough"); return ERROR_UNKNOWN; } else if (status == 2 || status == 3) { + ESP_LOGVV(TAG, "TX failed: not acknowledged"); return ERROR_NOT_ACKNOWLEDGED; } + ESP_LOGVV(TAG, "TX failed: unknown error %u", status); return ERROR_UNKNOWN; } diff --git a/esphome/components/i2c/i2c_bus_esp_idf.cpp b/esphome/components/i2c/i2c_bus_esp_idf.cpp index 91fd1499e9..f7ecfe5f7c 100644 --- a/esphome/components/i2c/i2c_bus_esp_idf.cpp +++ b/esphome/components/i2c/i2c_bus_esp_idf.cpp @@ -73,16 +73,22 @@ void IDFI2CBus::dump_config() { } } ErrorCode IDFI2CBus::readv(uint8_t address, ReadBuffer *buffers, size_t cnt) { - if (!initialized_) + // logging is only enabled with vv level, if warnings are shown the caller + // should log them + if (!initialized_) { + ESP_LOGVV(TAG, "i2c bus not initialized!"); return ERROR_NOT_INITIALIZED; + } i2c_cmd_handle_t cmd = i2c_cmd_link_create(); esp_err_t err = i2c_master_start(cmd); if (err != ESP_OK) { + ESP_LOGVV(TAG, "RX from %02X master start failed: %s", address, esp_err_to_name(err)); i2c_cmd_link_delete(cmd); return ERROR_UNKNOWN; } err = i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_READ, true); if (err != ESP_OK) { + ESP_LOGVV(TAG, "RX from %02X address write failed: %s", address, esp_err_to_name(err)); i2c_cmd_link_delete(cmd); return ERROR_UNKNOWN; } @@ -92,12 +98,14 @@ ErrorCode IDFI2CBus::readv(uint8_t address, ReadBuffer *buffers, size_t cnt) { continue; err = i2c_master_read(cmd, buf.data, buf.len, i == cnt - 1 ? I2C_MASTER_LAST_NACK : I2C_MASTER_ACK); if (err != ESP_OK) { + ESP_LOGVV(TAG, "RX from %02X data read failed: %s", address, esp_err_to_name(err)); i2c_cmd_link_delete(cmd); return ERROR_UNKNOWN; } } err = i2c_master_stop(cmd); if (err != ESP_OK) { + ESP_LOGVV(TAG, "RX from %02X stop failed: %s", address, esp_err_to_name(err)); i2c_cmd_link_delete(cmd); return ERROR_UNKNOWN; } @@ -105,25 +113,64 @@ ErrorCode IDFI2CBus::readv(uint8_t address, ReadBuffer *buffers, size_t cnt) { i2c_cmd_link_delete(cmd); if (err == ESP_FAIL) { // transfer not acked + ESP_LOGVV(TAG, "RX from %02X failed: not acked", address); return ERROR_NOT_ACKNOWLEDGED; } else if (err == ESP_ERR_TIMEOUT) { + ESP_LOGVV(TAG, "RX from %02X failed: timeout", address); return ERROR_TIMEOUT; } else if (err != ESP_OK) { + ESP_LOGVV(TAG, "RX from %02X failed: %s", address, esp_err_to_name(err)); return ERROR_UNKNOWN; } + +#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE + char debug_buf[4]; + std::string debug_hex; + + for (size_t i = 0; i < cnt; i++) { + const auto &buf = buffers[i]; + for (size_t j = 0; j < buf.len; j++) { + snprintf(debug_buf, sizeof(debug_buf), "%02X", buf.data[j]); + debug_hex += debug_buf; + } + } + ESP_LOGVV(TAG, "0x%02X RX %s", address, debug_hex.c_str()); +#endif + return ERROR_OK; } ErrorCode IDFI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cnt) { - if (!initialized_) + // logging is only enabled with vv level, if warnings are shown the caller + // should log them + if (!initialized_) { + ESP_LOGVV(TAG, "i2c bus not initialized!"); return ERROR_NOT_INITIALIZED; + } + +#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE + char debug_buf[4]; + std::string debug_hex; + + for (size_t i = 0; i < cnt; i++) { + const auto &buf = buffers[i]; + for (size_t j = 0; j < buf.len; j++) { + snprintf(debug_buf, sizeof(debug_buf), "%02X", buf.data[j]); + debug_hex += debug_buf; + } + } + ESP_LOGVV(TAG, "0x%02X TX %s", address, debug_hex.c_str()); +#endif + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); esp_err_t err = i2c_master_start(cmd); if (err != ESP_OK) { + ESP_LOGVV(TAG, "TX to %02X master start failed: %s", address, esp_err_to_name(err)); i2c_cmd_link_delete(cmd); return ERROR_UNKNOWN; } err = i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_WRITE, true); if (err != ESP_OK) { + ESP_LOGVV(TAG, "TX to %02X address write failed: %s", address, esp_err_to_name(err)); i2c_cmd_link_delete(cmd); return ERROR_UNKNOWN; } @@ -133,12 +180,14 @@ ErrorCode IDFI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cnt) { continue; err = i2c_master_write(cmd, buf.data, buf.len, true); if (err != ESP_OK) { + ESP_LOGVV(TAG, "TX to %02X data write failed: %s", address, esp_err_to_name(err)); i2c_cmd_link_delete(cmd); return ERROR_UNKNOWN; } } err = i2c_master_stop(cmd); if (err != ESP_OK) { + ESP_LOGVV(TAG, "TX to %02X master stop failed: %s", address, esp_err_to_name(err)); i2c_cmd_link_delete(cmd); return ERROR_UNKNOWN; } @@ -146,10 +195,13 @@ ErrorCode IDFI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cnt) { i2c_cmd_link_delete(cmd); if (err == ESP_FAIL) { // transfer not acked + ESP_LOGVV(TAG, "TX to %02X failed: not acked", address); return ERROR_NOT_ACKNOWLEDGED; } else if (err == ESP_ERR_TIMEOUT) { + ESP_LOGVV(TAG, "TX to %02X failed: timeout", address); return ERROR_TIMEOUT; } else if (err != ESP_OK) { + ESP_LOGVV(TAG, "TX to %02X failed: %s", address, esp_err_to_name(err)); return ERROR_UNKNOWN; } return ERROR_OK; From 955c96731ef35dcca9c8d0fec769f166df99bc66 Mon Sep 17 00:00:00 2001 From: Paul Monigatti Date: Wed, 6 Oct 2021 20:44:48 +1300 Subject: [PATCH 175/207] Add Safe Mode Restart Switch (#2437) --- CODEOWNERS | 1 + esphome/components/ota/ota_component.cpp | 38 ++++++++++++++++--- esphome/components/ota/ota_component.h | 7 ++++ esphome/components/safe_mode/__init__.py | 5 +++ .../components/safe_mode/switch/__init__.py | 36 ++++++++++++++++++ .../safe_mode/switch/safe_mode_switch.cpp | 29 ++++++++++++++ .../safe_mode/switch/safe_mode_switch.h | 21 ++++++++++ esphome/const.py | 1 + tests/test1.yaml | 2 + 9 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 esphome/components/safe_mode/__init__.py create mode 100644 esphome/components/safe_mode/switch/__init__.py create mode 100644 esphome/components/safe_mode/switch/safe_mode_switch.cpp create mode 100644 esphome/components/safe_mode/switch/safe_mode_switch.h diff --git a/CODEOWNERS b/CODEOWNERS index 6576c78f0e..49cb60c177 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -126,6 +126,7 @@ esphome/components/restart/* @esphome/core esphome/components/rf_bridge/* @jesserockz esphome/components/rgbct/* @jesserockz esphome/components/rtttl/* @glmnet +esphome/components/safe_mode/* @paulmonigatti esphome/components/scd4x/* @sjtrny esphome/components/script/* @esphome/core esphome/components/sdm_meter/* @jesserockz @polyfaces diff --git a/esphome/components/ota/ota_component.cpp b/esphome/components/ota/ota_component.cpp index e897d952ef..9ad3814f5c 100644 --- a/esphome/components/ota/ota_component.cpp +++ b/esphome/components/ota/ota_component.cpp @@ -89,7 +89,8 @@ void OTAComponent::dump_config() { ESP_LOGCONFIG(TAG, " Using Password."); } #endif - if (this->has_safe_mode_ && this->safe_mode_rtc_value_ > 1) { + if (this->has_safe_mode_ && this->safe_mode_rtc_value_ > 1 && + this->safe_mode_rtc_value_ != esphome::ota::OTAComponent::ENTER_SAFE_MODE_MAGIC) { ESP_LOGW(TAG, "Last Boot was an unhandled reset, will proceed to safe mode in %d restarts", this->safe_mode_num_attempts_ - this->safe_mode_rtc_value_); } @@ -401,6 +402,27 @@ bool OTAComponent::writeall_(const uint8_t *buf, size_t len) { float OTAComponent::get_setup_priority() const { return setup_priority::AFTER_WIFI; } uint16_t OTAComponent::get_port() const { return this->port_; } void OTAComponent::set_port(uint16_t port) { this->port_ = port; } + +void OTAComponent::set_safe_mode_pending(const bool &pending) { + if (!this->has_safe_mode_) + return; + + uint32_t current_rtc = this->read_rtc_(); + + if (pending && current_rtc != esphome::ota::OTAComponent::ENTER_SAFE_MODE_MAGIC) { + ESP_LOGI(TAG, "Device will enter safe mode on next boot."); + this->write_rtc_(esphome::ota::OTAComponent::ENTER_SAFE_MODE_MAGIC); + } + + if (!pending && current_rtc == esphome::ota::OTAComponent::ENTER_SAFE_MODE_MAGIC) { + ESP_LOGI(TAG, "Safe mode pending has been cleared"); + this->clean_rtc(); + } +} +bool OTAComponent::get_safe_mode_pending() { + return this->has_safe_mode_ && this->read_rtc_() == esphome::ota::OTAComponent::ENTER_SAFE_MODE_MAGIC; +} + bool OTAComponent::should_enter_safe_mode(uint8_t num_attempts, uint32_t enable_time) { this->has_safe_mode_ = true; this->safe_mode_start_time_ = millis(); @@ -409,12 +431,18 @@ bool OTAComponent::should_enter_safe_mode(uint8_t num_attempts, uint32_t enable_ this->rtc_ = global_preferences->make_preference(233825507UL, false); this->safe_mode_rtc_value_ = this->read_rtc_(); - ESP_LOGCONFIG(TAG, "There have been %u suspected unsuccessful boot attempts.", this->safe_mode_rtc_value_); + bool is_manual_safe_mode = this->safe_mode_rtc_value_ == esphome::ota::OTAComponent::ENTER_SAFE_MODE_MAGIC; - if (this->safe_mode_rtc_value_ >= num_attempts) { + if (is_manual_safe_mode) + ESP_LOGI(TAG, "Safe mode has been entered manually"); + else + ESP_LOGCONFIG(TAG, "There have been %u suspected unsuccessful boot attempts.", this->safe_mode_rtc_value_); + + if (this->safe_mode_rtc_value_ >= num_attempts || is_manual_safe_mode) { this->clean_rtc(); - ESP_LOGE(TAG, "Boot loop detected. Proceeding to safe mode."); + if (!is_manual_safe_mode) + ESP_LOGE(TAG, "Boot loop detected. Proceeding to safe mode."); this->status_set_error(); this->set_timeout(enable_time, []() { @@ -445,7 +473,7 @@ uint32_t OTAComponent::read_rtc_() { } void OTAComponent::clean_rtc() { this->write_rtc_(0); } void OTAComponent::on_safe_shutdown() { - if (this->has_safe_mode_) + if (this->has_safe_mode_ && this->read_rtc_() != esphome::ota::OTAComponent::ENTER_SAFE_MODE_MAGIC) this->clean_rtc(); } diff --git a/esphome/components/ota/ota_component.h b/esphome/components/ota/ota_component.h index f76295735e..e08e187df6 100644 --- a/esphome/components/ota/ota_component.h +++ b/esphome/components/ota/ota_component.h @@ -48,6 +48,10 @@ class OTAComponent : public Component { bool should_enter_safe_mode(uint8_t num_attempts, uint32_t enable_time); + /// Set to true if the next startup will enter safe mode + void set_safe_mode_pending(const bool &pending); + bool get_safe_mode_pending(); + #ifdef USE_OTA_STATE_CALLBACK void add_on_state_callback(std::function &&callback); #endif @@ -89,6 +93,9 @@ class OTAComponent : public Component { uint8_t safe_mode_num_attempts_; ESPPreferenceObject rtc_; + static const uint32_t ENTER_SAFE_MODE_MAGIC = + 0x5afe5afe; ///< a magic number to indicate that safe mode should be entered on next boot + #ifdef USE_OTA_STATE_CALLBACK CallbackManager state_callback_{}; #endif diff --git a/esphome/components/safe_mode/__init__.py b/esphome/components/safe_mode/__init__.py new file mode 100644 index 0000000000..f150d6e086 --- /dev/null +++ b/esphome/components/safe_mode/__init__.py @@ -0,0 +1,5 @@ +import esphome.codegen as cg + +CODEOWNERS = ["@paulmonigatti"] + +safe_mode_ns = cg.esphome_ns.namespace("safe_mode") diff --git a/esphome/components/safe_mode/switch/__init__.py b/esphome/components/safe_mode/switch/__init__.py new file mode 100644 index 0000000000..0ad814ff4f --- /dev/null +++ b/esphome/components/safe_mode/switch/__init__.py @@ -0,0 +1,36 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import switch +from esphome.components.ota import OTAComponent +from esphome.const import ( + CONF_ID, + CONF_INVERTED, + CONF_ICON, + CONF_OTA, + ICON_RESTART_ALERT, +) +from .. import safe_mode_ns + +DEPENDENCIES = ["ota"] + +SafeModeSwitch = safe_mode_ns.class_("SafeModeSwitch", switch.Switch, cg.Component) + +CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(SafeModeSwitch), + cv.GenerateID(CONF_OTA): cv.use_id(OTAComponent), + cv.Optional(CONF_INVERTED): cv.invalid( + "Safe Mode Restart switches do not support inverted mode!" + ), + cv.Optional(CONF_ICON, default=ICON_RESTART_ALERT): switch.icon, + } +).extend(cv.COMPONENT_SCHEMA) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await switch.register_switch(var, config) + + ota = await cg.get_variable(config[CONF_OTA]) + cg.add(var.set_ota(ota)) diff --git a/esphome/components/safe_mode/switch/safe_mode_switch.cpp b/esphome/components/safe_mode/switch/safe_mode_switch.cpp new file mode 100644 index 0000000000..a3979eec06 --- /dev/null +++ b/esphome/components/safe_mode/switch/safe_mode_switch.cpp @@ -0,0 +1,29 @@ +#include "safe_mode_switch.h" +#include "esphome/core/hal.h" +#include "esphome/core/log.h" +#include "esphome/core/application.h" + +namespace esphome { +namespace safe_mode { + +static const char *const TAG = "safe_mode_switch"; + +void SafeModeSwitch::set_ota(ota::OTAComponent *ota) { this->ota_ = ota; } + +void SafeModeSwitch::write_state(bool state) { + // Acknowledge + this->publish_state(false); + + if (state) { + ESP_LOGI(TAG, "Restarting device in safe mode..."); + this->ota_->set_safe_mode_pending(true); + + // Let MQTT settle a bit + delay(100); // NOLINT + App.safe_reboot(); + } +} +void SafeModeSwitch::dump_config() { LOG_SWITCH("", "Safe Mode Switch", this); } + +} // namespace safe_mode +} // namespace esphome diff --git a/esphome/components/safe_mode/switch/safe_mode_switch.h b/esphome/components/safe_mode/switch/safe_mode_switch.h new file mode 100644 index 0000000000..2772db3d84 --- /dev/null +++ b/esphome/components/safe_mode/switch/safe_mode_switch.h @@ -0,0 +1,21 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/ota/ota_component.h" +#include "esphome/components/switch/switch.h" + +namespace esphome { +namespace safe_mode { + +class SafeModeSwitch : public switch_::Switch, public Component { + public: + void dump_config() override; + void set_ota(ota::OTAComponent *ota); + + protected: + ota::OTAComponent *ota_; + void write_state(bool state) override; +}; + +} // namespace safe_mode +} // namespace esphome diff --git a/esphome/const.py b/esphome/const.py index 265576bfbe..a65285a4b5 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -761,6 +761,7 @@ ICON_PULSE = "mdi:pulse" ICON_RADIATOR = "mdi:radiator" ICON_RADIOACTIVE = "mdi:radioactive" ICON_RESTART = "mdi:restart" +ICON_RESTART_ALERT = "mdi:restart-alert" ICON_ROTATE_RIGHT = "mdi:rotate-right" ICON_RULER = "mdi:ruler" ICON_SCALE = "mdi:scale" diff --git a/tests/test1.yaml b/tests/test1.yaml index 77f7da2b08..400cdb3b6b 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1905,6 +1905,8 @@ switch: state: yes - platform: restart name: 'Living Room Restart' + - platform: safe_mode + name: 'Living Room Restart (Safe Mode)' - platform: shutdown name: 'Living Room Shutdown' - platform: output From 22e3bc7cfe75501004148826c666f80fa3123016 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 6 Oct 2021 22:35:11 +1300 Subject: [PATCH 176/207] Add id() for restoring global (#2454) --- esphome/components/globals/globals_component.h | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/globals/globals_component.h b/esphome/components/globals/globals_component.h index b39c5f404b..3286e43575 100644 --- a/esphome/components/globals/globals_component.h +++ b/esphome/components/globals/globals_component.h @@ -76,6 +76,7 @@ template class GlobalVarSetAction : public Action T &id(GlobalsComponent *value) { return value->value(); } +template T &id(RestoringGlobalsComponent *value) { return value->value(); } } // namespace globals } // namespace esphome From d34a1c3ed6efa005341d3b43d03894b814ec8328 Mon Sep 17 00:00:00 2001 From: Alex Iribarren Date: Wed, 6 Oct 2021 21:56:07 +0200 Subject: [PATCH 177/207] Add timestamp to ESPHome dashboard/cli logs (#2455) --- esphome/log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/log.py b/esphome/log.py index fa79efa833..abefcf6308 100644 --- a/esphome/log.py +++ b/esphome/log.py @@ -50,7 +50,7 @@ def color(col: str, msg: str, reset: bool = True) -> bool: class ESPHomeLogFormatter(logging.Formatter): def __init__(self): - super().__init__(fmt="%(levelname)s %(message)s", datefmt="%H:%M:%S", style="%") + super().__init__(fmt="%(asctime)s %(levelname)s %(message)s", style="%") def format(self, record): formatted = super().format(record) From 1c58b172358a898370de5188872ece374ecd0410 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Wed, 6 Oct 2021 22:36:12 +0200 Subject: [PATCH 178/207] API encryption switch to libsodium backend (#2456) --- esphome/components/api/__init__.py | 2 +- platformio.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/api/__init__.py b/esphome/components/api/__init__.py index 3705f0d7ca..b0608a69dd 100644 --- a/esphome/components/api/__init__.py +++ b/esphome/components/api/__init__.py @@ -121,7 +121,7 @@ async def to_code(config): decoded = base64.b64decode(conf[CONF_KEY]) cg.add(var.set_noise_psk(list(decoded))) cg.add_define("USE_API_NOISE") - cg.add_library("esphome/noise-c", "0.1.1") + cg.add_library("esphome/noise-c", "0.1.3") else: cg.add_define("USE_API_PLAINTEXT") diff --git a/platformio.ini b/platformio.ini index f38bbc78a9..e038224f69 100644 --- a/platformio.ini +++ b/platformio.ini @@ -26,7 +26,7 @@ build_flags = [common] lib_deps = - esphome/noise-c@0.1.1 ; api + esphome/noise-c@0.1.3 ; api makuna/NeoPixelBus@2.6.7 ; neopixelbus build_flags = -DESPHOME_LOG_LEVEL=ESPHOME_LOG_LEVEL_VERY_VERBOSE From 5461f87ff016cbb31a1ffe6876062dfee3f0ee63 Mon Sep 17 00:00:00 2001 From: Martin <25747549+martgras@users.noreply.github.com> Date: Thu, 7 Oct 2021 21:18:00 +0200 Subject: [PATCH 179/207] I2c fix (#2460) --- esphome/components/i2c/i2c.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/i2c/i2c.cpp b/esphome/components/i2c/i2c.cpp index 035c483344..82ab7bd09a 100644 --- a/esphome/components/i2c/i2c.cpp +++ b/esphome/components/i2c/i2c.cpp @@ -12,7 +12,7 @@ bool I2CDevice::write_bytes_16(uint8_t a_register, const uint16_t *data, uint8_t std::unique_ptr temp{new uint16_t[len]}; for (size_t i = 0; i < len; i++) temp[i] = htoi2cs(data[i]); - return write_register(a_register, reinterpret_cast(data), len * 2) == ERROR_OK; + return write_register(a_register, reinterpret_cast(temp.get()), len * 2) == ERROR_OK; } I2CRegister &I2CRegister::operator=(uint8_t value) { From 3f2d9abfe6434cfbfc32a8b7de7c100acb95b6ae Mon Sep 17 00:00:00 2001 From: Maurice Makaay Date: Sat, 9 Oct 2021 01:30:21 -0700 Subject: [PATCH 180/207] Correct I2C read() return val check in bh1750 component. (#2465) Co-authored-by: Maurice Makaay --- esphome/components/bh1750/bh1750.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/bh1750/bh1750.cpp b/esphome/components/bh1750/bh1750.cpp index 3645a45bf9..951fe3670c 100644 --- a/esphome/components/bh1750/bh1750.cpp +++ b/esphome/components/bh1750/bh1750.cpp @@ -71,7 +71,7 @@ void BH1750Sensor::update() { float BH1750Sensor::get_setup_priority() const { return setup_priority::DATA; } void BH1750Sensor::read_data_() { uint16_t raw_value; - if (!this->read(reinterpret_cast(&raw_value), 2)) { + if (this->read(reinterpret_cast(&raw_value), 2) != i2c::ERROR_OK) { this->status_set_warning(); return; } From a1b28cb36e60bbc20f68cacb017881be87eaaed6 Mon Sep 17 00:00:00 2001 From: davidmonro Date: Sun, 10 Oct 2021 02:44:16 +1100 Subject: [PATCH 181/207] atm90e32: make the total_increasing class sensors actually be increasing totals. (#2459) Co-authored-by: David Monro --- esphome/components/atm90e32/atm90e32.cpp | 42 ++++++++++++++++++++---- esphome/components/atm90e32/atm90e32.h | 2 ++ 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/esphome/components/atm90e32/atm90e32.cpp b/esphome/components/atm90e32/atm90e32.cpp index ebceaa817c..e4b8448da6 100644 --- a/esphome/components/atm90e32/atm90e32.cpp +++ b/esphome/components/atm90e32/atm90e32.cpp @@ -265,27 +265,57 @@ float ATM90E32Component::get_power_factor_c_() { } float ATM90E32Component::get_forward_active_energy_a_() { uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGYA); - return (float) val * 10 / 3200; // convert register value to WattHours + if ((UINT32_MAX - this->phase_[0].cumulative_forward_active_energy_) > val) { + this->phase_[0].cumulative_forward_active_energy_ += val; + } else { + this->phase_[0].cumulative_forward_active_energy_ = val; + } + return ((float) this->phase_[0].cumulative_forward_active_energy_ * 10 / 3200); } float ATM90E32Component::get_forward_active_energy_b_() { uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGYB); - return (float) val * 10 / 3200; + if (UINT32_MAX - this->phase_[1].cumulative_forward_active_energy_ > val) { + this->phase_[1].cumulative_forward_active_energy_ += val; + } else { + this->phase_[1].cumulative_forward_active_energy_ = val; + } + return ((float) this->phase_[1].cumulative_forward_active_energy_ * 10 / 3200); } float ATM90E32Component::get_forward_active_energy_c_() { uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGYC); - return (float) val * 10 / 3200; + if (UINT32_MAX - this->phase_[2].cumulative_forward_active_energy_ > val) { + this->phase_[2].cumulative_forward_active_energy_ += val; + } else { + this->phase_[2].cumulative_forward_active_energy_ = val; + } + return ((float) this->phase_[2].cumulative_forward_active_energy_ * 10 / 3200); } float ATM90E32Component::get_reverse_active_energy_a_() { uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGYA); - return (float) val * 10 / 3200; + if (UINT32_MAX - this->phase_[0].cumulative_reverse_active_energy_ > val) { + this->phase_[0].cumulative_reverse_active_energy_ += val; + } else { + this->phase_[0].cumulative_reverse_active_energy_ = val; + } + return ((float) this->phase_[0].cumulative_reverse_active_energy_ * 10 / 3200); } float ATM90E32Component::get_reverse_active_energy_b_() { uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGYB); - return (float) val * 10 / 3200; + if (UINT32_MAX - this->phase_[1].cumulative_reverse_active_energy_ > val) { + this->phase_[1].cumulative_reverse_active_energy_ += val; + } else { + this->phase_[1].cumulative_reverse_active_energy_ = val; + } + return ((float) this->phase_[1].cumulative_reverse_active_energy_ * 10 / 3200); } float ATM90E32Component::get_reverse_active_energy_c_() { uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGYC); - return (float) val * 10 / 3200; + if (UINT32_MAX - this->phase_[2].cumulative_reverse_active_energy_ > val) { + this->phase_[2].cumulative_reverse_active_energy_ += val; + } else { + this->phase_[2].cumulative_reverse_active_energy_ = val; + } + return ((float) this->phase_[2].cumulative_reverse_active_energy_ * 10 / 3200); } float ATM90E32Component::get_frequency_() { uint16_t freq = this->read16_(ATM90E32_REGISTER_FREQ); diff --git a/esphome/components/atm90e32/atm90e32.h b/esphome/components/atm90e32/atm90e32.h index 89d62adaf6..c9662df26e 100644 --- a/esphome/components/atm90e32/atm90e32.h +++ b/esphome/components/atm90e32/atm90e32.h @@ -77,6 +77,8 @@ class ATM90E32Component : public PollingComponent, sensor::Sensor *power_factor_sensor_{nullptr}; sensor::Sensor *forward_active_energy_sensor_{nullptr}; sensor::Sensor *reverse_active_energy_sensor_{nullptr}; + uint32_t cumulative_forward_active_energy_{0}; + uint32_t cumulative_reverse_active_energy_{0}; } phase_[3]; sensor::Sensor *freq_sensor_{nullptr}; sensor::Sensor *chip_temperature_sensor_{nullptr}; From e514a1fcd446f926b1f813971d22c450b75493f0 Mon Sep 17 00:00:00 2001 From: Ryan Mounce Date: Sun, 10 Oct 2021 18:58:37 +1030 Subject: [PATCH 182/207] Use enum for Tuya fan direction datapoint (#2471) Fix regression from PR2059. Tested with Arlec DCF5242HA. --- esphome/components/tuya/fan/tuya_fan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/tuya/fan/tuya_fan.cpp b/esphome/components/tuya/fan/tuya_fan.cpp index f060b18eba..d0c8809564 100644 --- a/esphome/components/tuya/fan/tuya_fan.cpp +++ b/esphome/components/tuya/fan/tuya_fan.cpp @@ -76,7 +76,7 @@ void TuyaFan::write_state() { if (this->direction_id_.has_value()) { bool enable = this->fan_->direction == fan::FAN_DIRECTION_REVERSE; ESP_LOGV(TAG, "Setting reverse direction: %s", ONOFF(enable)); - this->parent_->set_boolean_datapoint_value(*this->direction_id_, enable); + this->parent_->set_enum_datapoint_value(*this->direction_id_, enable); } if (this->speed_id_.has_value()) { ESP_LOGV(TAG, "Setting speed: %d", this->fan_->speed); From c092d92d45488dadadd5857fdb223bd0a8e92c5a Mon Sep 17 00:00:00 2001 From: definitio <37266727+definitio@users.noreply.github.com> Date: Sun, 10 Oct 2021 11:31:15 +0300 Subject: [PATCH 183/207] Fix cover state (#2468) --- esphome/components/mqtt/mqtt_cover.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/mqtt/mqtt_cover.cpp b/esphome/components/mqtt/mqtt_cover.cpp index 6a0d2d1bc5..61c8fa2d94 100644 --- a/esphome/components/mqtt/mqtt_cover.cpp +++ b/esphome/components/mqtt/mqtt_cover.cpp @@ -70,6 +70,7 @@ void MQTTCoverComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryCon root["optimistic"] = true; } if (traits.get_supports_position()) { + config.state_topic = false; root["position_topic"] = this->get_position_state_topic(); root["set_position_topic"] = this->get_position_command_topic(); } From 92b85f98e861638b70d6021b838f52f1998342cc Mon Sep 17 00:00:00 2001 From: Nate Lust Date: Sun, 10 Oct 2021 04:33:04 -0400 Subject: [PATCH 184/207] Sgp40 fix (#2462) * Sample from SGP40 sensor at the appropriate interval The spg40 sensor must be sampled at 1Hz for the VOC index algorithm to work correctly. This commit introduces a on device timer to sample correctly seperately from updating the public state of the component. * Add missing configuration values for SGP40 The SGP40 component was not printing all of it's configuration in dump_config, add in the missing store_baseline value. * Address review comments * Format according to clang-tidy * Attempt 2 at clang tidy --- esphome/components/sgp40/sgp40.cpp | 35 ++++++++++++++++++++++++------ esphome/components/sgp40/sgp40.h | 2 ++ 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/esphome/components/sgp40/sgp40.cpp b/esphome/components/sgp40/sgp40.cpp index fddd0255b8..a3d2c74eb7 100644 --- a/esphome/components/sgp40/sgp40.cpp +++ b/esphome/components/sgp40/sgp40.cpp @@ -77,6 +77,20 @@ void SGP40Component::setup() { } this->self_test_(); + + /* The official spec for this sensor at https://docs.rs-online.com/1956/A700000007055193.pdf + indicates this sensor should be driven at 1Hz. Comments from the developers at: + https://github.com/Sensirion/embedded-sgp/issues/136 indicate the algorithm should be a bit + resilient to slight timing variations so the software timer should be accurate enough for + this. + + This block starts sampling from the sensor at 1Hz, and is done seperately from the call + to the update method. This seperation is to support getting accurate measurements but + limit the amount of communication done over wifi for power consumption or to keep the + number of records reported from being overwhelming. + */ + ESP_LOGD(TAG, "Component requires sampling of 1Hz, setting up background sampler"); + this->set_interval(1000, [this]() { this->update_voc_index(); }); } void SGP40Component::self_test_() { @@ -224,21 +238,26 @@ uint8_t SGP40Component::generate_crc_(const uint8_t *data, uint8_t datalen) { return crc; } -void SGP40Component::update() { - this->seconds_since_last_store_ += this->update_interval_ / 1000; - - uint32_t voc_index = this->measure_voc_index_(); +void SGP40Component::update_voc_index() { + this->seconds_since_last_store_ += 1; + this->voc_index_ = this->measure_voc_index_(); if (this->samples_read_ < this->samples_to_stabalize_) { this->samples_read_++; ESP_LOGD(TAG, "Sensor has not collected enough samples yet. (%d/%d) VOC index is: %u", this->samples_read_, - this->samples_to_stabalize_, voc_index); + this->samples_to_stabalize_, this->voc_index_); + return; + } +} + +void SGP40Component::update() { + if (this->samples_read_ < this->samples_to_stabalize_) { return; } - if (voc_index != UINT16_MAX) { + if (this->voc_index_ != UINT16_MAX) { this->status_clear_warning(); - this->publish_state(voc_index); + this->publish_state(this->voc_index_); } else { this->status_set_warning(); } @@ -247,6 +266,8 @@ void SGP40Component::update() { void SGP40Component::dump_config() { ESP_LOGCONFIG(TAG, "SGP40:"); LOG_I2C_DEVICE(this); + ESP_LOGCONFIG(TAG, " store_baseline: %d", this->store_baseline_); + if (this->is_failed()) { switch (this->error_code_) { case COMMUNICATION_FAILED: diff --git a/esphome/components/sgp40/sgp40.h b/esphome/components/sgp40/sgp40.h index 62936102e7..bb68a1ffcf 100644 --- a/esphome/components/sgp40/sgp40.h +++ b/esphome/components/sgp40/sgp40.h @@ -46,6 +46,7 @@ class SGP40Component : public PollingComponent, public sensor::Sensor, public i2 void setup() override; void update() override; + void update_voc_index(); void dump_config() override; float get_setup_priority() const override { return setup_priority::DATA; } void set_store_baseline(bool store_baseline) { store_baseline_ = store_baseline; } @@ -72,6 +73,7 @@ class SGP40Component : public PollingComponent, public sensor::Sensor, public i2 bool store_baseline_; int32_t state0_; int32_t state1_; + int32_t voc_index_ = 0; uint8_t samples_read_ = 0; uint8_t samples_to_stabalize_ = static_cast(VOC_ALGORITHM_INITIAL_BLACKOUT) * 2; From 471b82f727e2f698b8879fb51c4161a9cf49d34b Mon Sep 17 00:00:00 2001 From: Paul Monigatti Date: Sun, 10 Oct 2021 21:37:05 +1300 Subject: [PATCH 185/207] EntityBase Refactor (#2418) * Renamed Nameable to EntityBase (cpp) * Renamed NAMEABLE_SCHEMA to ENTITY_BASE_SCHEMA (Python) * Renamed cg.Nameable to cg.EntityBase (Python) * Remove redundant use of CONF_NAME from esp32_touch * Remove redundant use of CONF_NAME from mcp3008 * Updated test * Moved EntityBase from Component.h and Component.cpp * Added icon property to EntityBase * Added CONF_ICON to ENTITY_BASE_SCHEMA and added setup_entity function to cpp_helpers * Added MQTT component getters for icon and disabled_by_default * Lint * Removed icon field from MQTT components * Code generation now uses setup_entity to setENTITY_BASE_SCHEMA fields * Removed unused import * Added cstdint include * Optimisation: don't set icon if it is empty * Remove icon from NumberTraits and SelectTraits * Removed unused import * Integration and Total Daily Energy sensors now inherit icons from their parents during code generation * Minor comment correction * Removed redundant icon-handling code from sensor, switch, and text_sensor * Update esphome/components/tsl2591/tsl2591.h Co-authored-by: Oxan van Leeuwen * Added icon property to binary sensor, climate, cover, and fan component tests * Added icons for Binary Sensor, Climate, Cover, Fan, and Light to API * Consolidated EntityBase fields in MQTT components Co-authored-by: Oxan van Leeuwen --- esphome/codegen.py | 2 +- esphome/components/api/api.proto | 5 ++ esphome/components/api/api_connection.cpp | 14 ++++-- esphome/components/api/api_pb2.cpp | 46 ++++++++++++++++- esphome/components/api/api_pb2.h | 5 ++ esphome/components/binary_sensor/__init__.py | 13 ++--- .../binary_sensor/binary_sensor.cpp | 2 +- .../components/binary_sensor/binary_sensor.h | 3 +- esphome/components/climate/__init__.py | 14 ++---- esphome/components/climate/climate.cpp | 2 +- esphome/components/climate/climate.h | 3 +- esphome/components/cover/__init__.py | 14 ++---- esphome/components/cover/cover.cpp | 2 +- esphome/components/cover/cover.h | 3 +- esphome/components/esp32_camera/__init__.py | 2 +- .../components/esp32_camera/esp32_camera.cpp | 2 +- .../components/esp32_camera/esp32_camera.h | 3 +- .../components/esp32_touch/binary_sensor.py | 2 - .../components/esp32_touch/esp32_touch.cpp | 5 +- esphome/components/esp32_touch/esp32_touch.h | 2 +- esphome/components/fan/__init__.py | 13 ++--- esphome/components/fan/fan_state.cpp | 2 +- esphome/components/fan/fan_state.h | 3 +- .../integration/integration_sensor.h | 1 - esphome/components/integration/sensor.py | 17 ++++++- esphome/components/light/__init__.py | 14 +++--- esphome/components/light/light_state.cpp | 3 +- esphome/components/light/light_state.h | 5 +- esphome/components/light/types.py | 2 +- esphome/components/mcp3008/mcp3008.cpp | 6 +-- esphome/components/mcp3008/mcp3008.h | 2 +- esphome/components/mcp3008/sensor.py | 3 +- .../components/mqtt/mqtt_binary_sensor.cpp | 3 +- esphome/components/mqtt/mqtt_binary_sensor.h | 3 +- esphome/components/mqtt/mqtt_climate.cpp | 4 +- esphome/components/mqtt/mqtt_climate.h | 3 +- esphome/components/mqtt/mqtt_component.cpp | 15 +++++- esphome/components/mqtt/mqtt_component.h | 18 +++++-- esphome/components/mqtt/mqtt_cover.cpp | 4 +- esphome/components/mqtt/mqtt_cover.h | 3 +- esphome/components/mqtt/mqtt_fan.cpp | 4 +- esphome/components/mqtt/mqtt_fan.h | 4 +- esphome/components/mqtt/mqtt_light.cpp | 4 +- esphome/components/mqtt/mqtt_light.h | 4 +- esphome/components/mqtt/mqtt_number.cpp | 5 +- esphome/components/mqtt/mqtt_number.h | 4 +- esphome/components/mqtt/mqtt_select.cpp | 5 +- esphome/components/mqtt/mqtt_select.h | 4 +- esphome/components/mqtt/mqtt_sensor.cpp | 7 +-- esphome/components/mqtt/mqtt_sensor.h | 5 +- esphome/components/mqtt/mqtt_switch.cpp | 6 +-- esphome/components/mqtt/mqtt_switch.h | 4 +- esphome/components/mqtt/mqtt_text_sensor.cpp | 6 +-- esphome/components/mqtt/mqtt_text_sensor.h | 6 +-- esphome/components/number/__init__.py | 17 ++----- esphome/components/number/number.h | 10 ++-- .../remote_receiver/binary_sensor.py | 3 -- esphome/components/select/__init__.py | 17 ++----- esphome/components/select/select.h | 10 ++-- esphome/components/sensor/__init__.py | 17 ++----- esphome/components/sensor/sensor.cpp | 10 +--- esphome/components/sensor/sensor.h | 12 +---- esphome/components/switch/__init__.py | 18 ++----- esphome/components/switch/switch.cpp | 10 +--- esphome/components/switch/switch.h | 19 +------ esphome/components/text_sensor/__init__.py | 17 ++----- .../components/text_sensor/text_sensor.cpp | 10 +--- esphome/components/text_sensor/text_sensor.h | 10 +--- .../components/total_daily_energy/sensor.py | 14 ++++++ .../total_daily_energy/total_daily_energy.h | 1 - esphome/components/tsl2591/tsl2591.h | 2 +- esphome/components/web_server/web_server.cpp | 7 +-- esphome/config_validation.py | 8 +-- esphome/core/component.cpp | 20 -------- esphome/core/component.h | 34 ------------- esphome/core/entity_base.cpp | 40 +++++++++++++++ esphome/core/entity_base.h | 50 +++++++++++++++++++ esphome/core/entity_helpers.py | 32 ++++++++++++ esphome/cpp_helpers.py | 14 ++++++ esphome/cpp_types.py | 2 +- tests/test1.yaml | 3 ++ tests/test5.yaml | 1 + tests/unit_tests/test_codegen.py | 2 +- 83 files changed, 395 insertions(+), 351 deletions(-) create mode 100644 esphome/core/entity_base.cpp create mode 100644 esphome/core/entity_base.h create mode 100644 esphome/core/entity_helpers.py diff --git a/esphome/codegen.py b/esphome/codegen.py index 1b38fd9ed2..4f9f67245d 100644 --- a/esphome/codegen.py +++ b/esphome/codegen.py @@ -67,7 +67,7 @@ from esphome.cpp_types import ( # noqa NAN, esphome_ns, App, - Nameable, + EntityBase, Component, ComponentPtr, PollingComponent, diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 7648ffeaa2..5a6eba004c 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -215,6 +215,7 @@ message ListEntitiesBinarySensorResponse { string device_class = 5; bool is_status_binary_sensor = 6; bool disabled_by_default = 7; + string icon = 8; } message BinarySensorStateResponse { option (id) = 21; @@ -245,6 +246,7 @@ message ListEntitiesCoverResponse { bool supports_tilt = 7; string device_class = 8; bool disabled_by_default = 9; + string icon = 10; } enum LegacyCoverState { @@ -313,6 +315,7 @@ message ListEntitiesFanResponse { bool supports_direction = 7; int32 supported_speed_count = 8; bool disabled_by_default = 9; + string icon = 10; } enum FanSpeed { FAN_SPEED_LOW = 0; @@ -388,6 +391,7 @@ message ListEntitiesLightResponse { float max_mireds = 10; repeated string effects = 11; bool disabled_by_default = 13; + string icon = 14; } message LightStateResponse { option (id) = 24; @@ -790,6 +794,7 @@ message ListEntitiesClimateResponse { repeated ClimatePreset supported_presets = 16; repeated string supported_custom_presets = 17; bool disabled_by_default = 18; + string icon = 19; } message ClimateStateResponse { option (id) = 47; diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 450375f7cd..47171ba50f 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1,4 +1,5 @@ #include "api_connection.h" +#include "esphome/core/entity_base.h" #include "esphome/core/log.h" #include "esphome/components/network/util.h" #include "esphome/core/version.h" @@ -143,8 +144,8 @@ void APIConnection::loop() { } } -std::string get_default_unique_id(const std::string &component_type, Nameable *nameable) { - return App.get_name() + component_type + nameable->get_object_id(); +std::string get_default_unique_id(const std::string &component_type, EntityBase *entity) { + return App.get_name() + component_type + entity->get_object_id(); } DisconnectResponse APIConnection::disconnect(const DisconnectRequest &msg) { @@ -180,6 +181,7 @@ bool APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_ msg.device_class = binary_sensor->get_device_class(); msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor(); msg.disabled_by_default = binary_sensor->is_disabled_by_default(); + msg.icon = binary_sensor->get_icon(); return this->send_list_entities_binary_sensor_response(msg); } #endif @@ -212,6 +214,7 @@ bool APIConnection::send_cover_info(cover::Cover *cover) { msg.supports_tilt = traits.get_supports_tilt(); msg.device_class = cover->get_device_class(); msg.disabled_by_default = cover->is_disabled_by_default(); + msg.icon = cover->get_icon(); return this->send_list_entities_cover_response(msg); } void APIConnection::cover_command(const CoverCommandRequest &msg) { @@ -277,6 +280,7 @@ bool APIConnection::send_fan_info(fan::FanState *fan) { msg.supports_direction = traits.supports_direction(); msg.supported_speed_count = traits.supported_speed_count(); msg.disabled_by_default = fan->is_disabled_by_default(); + msg.icon = fan->get_icon(); return this->send_list_entities_fan_response(msg); } void APIConnection::fan_command(const FanCommandRequest &msg) { @@ -339,6 +343,7 @@ bool APIConnection::send_light_info(light::LightState *light) { msg.unique_id = get_default_unique_id("light", light); msg.disabled_by_default = light->is_disabled_by_default(); + msg.icon = light->get_icon(); for (auto mode : traits.get_supported_color_modes()) msg.supported_color_modes.push_back(static_cast(mode)); @@ -529,6 +534,7 @@ bool APIConnection::send_climate_info(climate::Climate *climate) { msg.unique_id = get_default_unique_id("climate", climate); msg.disabled_by_default = climate->is_disabled_by_default(); + msg.icon = climate->get_icon(); msg.supports_current_temperature = traits.get_supports_current_temperature(); msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_temperature(); @@ -601,7 +607,7 @@ bool APIConnection::send_number_info(number::Number *number) { msg.object_id = number->get_object_id(); msg.name = number->get_name(); msg.unique_id = get_default_unique_id("number", number); - msg.icon = number->traits.get_icon(); + msg.icon = number->get_icon(); msg.disabled_by_default = number->is_disabled_by_default(); msg.min_value = number->traits.get_min_value(); @@ -638,7 +644,7 @@ bool APIConnection::send_select_info(select::Select *select) { msg.object_id = select->get_object_id(); msg.name = select->get_name(); msg.unique_id = get_default_unique_id("select", select); - msg.icon = select->traits.get_icon(); + msg.icon = select->get_icon(); msg.disabled_by_default = select->is_disabled_by_default(); for (const auto &option : select->traits.get_options()) diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 580e147245..6a87238186 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -2,7 +2,6 @@ // See scripts/api_protobuf/api_protobuf.py #include "api_pb2.h" #include "esphome/core/log.h" -#include namespace esphome { namespace api { @@ -532,6 +531,10 @@ bool ListEntitiesBinarySensorResponse::decode_length(uint32_t field_id, ProtoLen this->device_class = value.as_string(); return true; } + case 8: { + this->icon = value.as_string(); + return true; + } default: return false; } @@ -554,6 +557,7 @@ void ListEntitiesBinarySensorResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(5, this->device_class); buffer.encode_bool(6, this->is_status_binary_sensor); buffer.encode_bool(7, this->disabled_by_default); + buffer.encode_string(8, this->icon); } #ifdef HAS_PROTO_MESSAGE_DUMP void ListEntitiesBinarySensorResponse::dump_to(std::string &out) const { @@ -587,6 +591,10 @@ void ListEntitiesBinarySensorResponse::dump_to(std::string &out) const { out.append(" disabled_by_default: "); out.append(YESNO(this->disabled_by_default)); out.append("\n"); + + out.append(" icon: "); + out.append("'").append(this->icon).append("'"); + out.append("\n"); out.append("}"); } #endif @@ -678,6 +686,10 @@ bool ListEntitiesCoverResponse::decode_length(uint32_t field_id, ProtoLengthDeli this->device_class = value.as_string(); return true; } + case 10: { + this->icon = value.as_string(); + return true; + } default: return false; } @@ -702,6 +714,7 @@ void ListEntitiesCoverResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(7, this->supports_tilt); buffer.encode_string(8, this->device_class); buffer.encode_bool(9, this->disabled_by_default); + buffer.encode_string(10, this->icon); } #ifdef HAS_PROTO_MESSAGE_DUMP void ListEntitiesCoverResponse::dump_to(std::string &out) const { @@ -743,6 +756,10 @@ void ListEntitiesCoverResponse::dump_to(std::string &out) const { out.append(" disabled_by_default: "); out.append(YESNO(this->disabled_by_default)); out.append("\n"); + + out.append(" icon: "); + out.append("'").append(this->icon).append("'"); + out.append("\n"); out.append("}"); } #endif @@ -949,6 +966,10 @@ bool ListEntitiesFanResponse::decode_length(uint32_t field_id, ProtoLengthDelimi this->unique_id = value.as_string(); return true; } + case 10: { + this->icon = value.as_string(); + return true; + } default: return false; } @@ -973,6 +994,7 @@ void ListEntitiesFanResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(7, this->supports_direction); buffer.encode_int32(8, this->supported_speed_count); buffer.encode_bool(9, this->disabled_by_default); + buffer.encode_string(10, this->icon); } #ifdef HAS_PROTO_MESSAGE_DUMP void ListEntitiesFanResponse::dump_to(std::string &out) const { @@ -1015,6 +1037,10 @@ void ListEntitiesFanResponse::dump_to(std::string &out) const { out.append(" disabled_by_default: "); out.append(YESNO(this->disabled_by_default)); out.append("\n"); + + out.append(" icon: "); + out.append("'").append(this->icon).append("'"); + out.append("\n"); out.append("}"); } #endif @@ -1263,6 +1289,10 @@ bool ListEntitiesLightResponse::decode_length(uint32_t field_id, ProtoLengthDeli this->effects.push_back(value.as_string()); return true; } + case 14: { + this->icon = value.as_string(); + return true; + } default: return false; } @@ -1303,6 +1333,7 @@ void ListEntitiesLightResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(11, it, true); } buffer.encode_bool(13, this->disabled_by_default); + buffer.encode_string(14, this->icon); } #ifdef HAS_PROTO_MESSAGE_DUMP void ListEntitiesLightResponse::dump_to(std::string &out) const { @@ -1366,6 +1397,10 @@ void ListEntitiesLightResponse::dump_to(std::string &out) const { out.append(" disabled_by_default: "); out.append(YESNO(this->disabled_by_default)); out.append("\n"); + + out.append(" icon: "); + out.append("'").append(this->icon).append("'"); + out.append("\n"); out.append("}"); } #endif @@ -3073,6 +3108,10 @@ bool ListEntitiesClimateResponse::decode_length(uint32_t field_id, ProtoLengthDe this->supported_custom_presets.push_back(value.as_string()); return true; } + case 19: { + this->icon = value.as_string(); + return true; + } default: return false; } @@ -3130,6 +3169,7 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(17, it, true); } buffer.encode_bool(18, this->disabled_by_default); + buffer.encode_string(19, this->icon); } #ifdef HAS_PROTO_MESSAGE_DUMP void ListEntitiesClimateResponse::dump_to(std::string &out) const { @@ -3222,6 +3262,10 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const { out.append(" disabled_by_default: "); out.append(YESNO(this->disabled_by_default)); out.append("\n"); + + out.append(" icon: "); + out.append("'").append(this->icon).append("'"); + out.append("\n"); out.append("}"); } #endif diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 1371ab5248..13a21c4772 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -269,6 +269,7 @@ class ListEntitiesBinarySensorResponse : public ProtoMessage { std::string device_class{}; bool is_status_binary_sensor{false}; bool disabled_by_default{false}; + std::string icon{}; void encode(ProtoWriteBuffer buffer) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; @@ -304,6 +305,7 @@ class ListEntitiesCoverResponse : public ProtoMessage { bool supports_tilt{false}; std::string device_class{}; bool disabled_by_default{false}; + std::string icon{}; void encode(ProtoWriteBuffer buffer) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; @@ -360,6 +362,7 @@ class ListEntitiesFanResponse : public ProtoMessage { bool supports_direction{false}; int32_t supported_speed_count{0}; bool disabled_by_default{false}; + std::string icon{}; void encode(ProtoWriteBuffer buffer) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; @@ -424,6 +427,7 @@ class ListEntitiesLightResponse : public ProtoMessage { float max_mireds{0.0f}; std::vector effects{}; bool disabled_by_default{false}; + std::string icon{}; void encode(ProtoWriteBuffer buffer) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; @@ -856,6 +860,7 @@ class ListEntitiesClimateResponse : public ProtoMessage { std::vector supported_presets{}; std::vector supported_custom_presets{}; bool disabled_by_default{false}; + std::string icon{}; void encode(ProtoWriteBuffer buffer) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; diff --git a/esphome/components/binary_sensor/__init__.py b/esphome/components/binary_sensor/__init__.py index 60ac4303a7..ec199cc5fa 100644 --- a/esphome/components/binary_sensor/__init__.py +++ b/esphome/components/binary_sensor/__init__.py @@ -1,15 +1,14 @@ import esphome.codegen as cg import esphome.config_validation as cv +from esphome.cpp_helpers import setup_entity from esphome import automation, core from esphome.automation import Condition, maybe_simple_id from esphome.components import mqtt from esphome.const import ( CONF_DELAY, CONF_DEVICE_CLASS, - CONF_DISABLED_BY_DEFAULT, CONF_FILTERS, CONF_ID, - CONF_INTERNAL, CONF_INVALID_COOLDOWN, CONF_INVERTED, CONF_MAX_LENGTH, @@ -88,7 +87,7 @@ DEVICE_CLASSES = [ IS_PLATFORM_COMPONENT = True binary_sensor_ns = cg.esphome_ns.namespace("binary_sensor") -BinarySensor = binary_sensor_ns.class_("BinarySensor", cg.Nameable) +BinarySensor = binary_sensor_ns.class_("BinarySensor", cg.EntityBase) BinarySensorInitiallyOff = binary_sensor_ns.class_( "BinarySensorInitiallyOff", BinarySensor ) @@ -314,7 +313,7 @@ def validate_multi_click_timing(value): device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_") -BINARY_SENSOR_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend( +BINARY_SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend( { cv.GenerateID(): cv.declare_id(BinarySensor), cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id( @@ -375,10 +374,8 @@ BINARY_SENSOR_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).exten async def setup_binary_sensor_core_(var, config): - cg.add(var.set_name(config[CONF_NAME])) - cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT])) - if CONF_INTERNAL in config: - cg.add(var.set_internal(config[CONF_INTERNAL])) + await setup_entity(var, config) + if CONF_DEVICE_CLASS in config: cg.add(var.set_device_class(config[CONF_DEVICE_CLASS])) if CONF_INVERTED in config: diff --git a/esphome/components/binary_sensor/binary_sensor.cpp b/esphome/components/binary_sensor/binary_sensor.cpp index 2e1f228be6..41da83aa3e 100644 --- a/esphome/components/binary_sensor/binary_sensor.cpp +++ b/esphome/components/binary_sensor/binary_sensor.cpp @@ -42,7 +42,7 @@ void BinarySensor::send_state_internal(bool state, bool is_initial) { } } std::string BinarySensor::device_class() { return ""; } -BinarySensor::BinarySensor(const std::string &name) : Nameable(name), state(false) {} +BinarySensor::BinarySensor(const std::string &name) : EntityBase(name), state(false) {} BinarySensor::BinarySensor() : BinarySensor("") {} void BinarySensor::set_device_class(const std::string &device_class) { this->device_class_ = device_class; } std::string BinarySensor::get_device_class() { diff --git a/esphome/components/binary_sensor/binary_sensor.h b/esphome/components/binary_sensor/binary_sensor.h index 195badd798..9c0d43fa98 100644 --- a/esphome/components/binary_sensor/binary_sensor.h +++ b/esphome/components/binary_sensor/binary_sensor.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/entity_base.h" #include "esphome/core/helpers.h" #include "esphome/components/binary_sensor/filter.h" @@ -22,7 +23,7 @@ namespace binary_sensor { * The sub classes should notify the front-end of new states via the publish_state() method which * handles inverted inputs for you. */ -class BinarySensor : public Nameable { +class BinarySensor : public EntityBase { public: explicit BinarySensor(); /** Construct a binary sensor with the specified name diff --git a/esphome/components/climate/__init__.py b/esphome/components/climate/__init__.py index c2f07ce423..ca1ea6a756 100644 --- a/esphome/components/climate/__init__.py +++ b/esphome/components/climate/__init__.py @@ -1,14 +1,13 @@ import esphome.codegen as cg import esphome.config_validation as cv +from esphome.cpp_helpers import setup_entity from esphome import automation from esphome.components import mqtt from esphome.const import ( CONF_AWAY, CONF_CUSTOM_FAN_MODE, CONF_CUSTOM_PRESET, - CONF_DISABLED_BY_DEFAULT, CONF_ID, - CONF_INTERNAL, CONF_MAX_TEMPERATURE, CONF_MIN_TEMPERATURE, CONF_MODE, @@ -19,7 +18,6 @@ from esphome.const import ( CONF_TEMPERATURE_STEP, CONF_VISUAL, CONF_MQTT_ID, - CONF_NAME, CONF_FAN_MODE, CONF_SWING_MODE, ) @@ -30,7 +28,7 @@ IS_PLATFORM_COMPONENT = True CODEOWNERS = ["@esphome/core"] climate_ns = cg.esphome_ns.namespace("climate") -Climate = climate_ns.class_("Climate", cg.Nameable) +Climate = climate_ns.class_("Climate", cg.EntityBase) ClimateCall = climate_ns.class_("ClimateCall") ClimateTraits = climate_ns.class_("ClimateTraits") @@ -88,7 +86,7 @@ validate_climate_swing_mode = cv.enum(CLIMATE_SWING_MODES, upper=True) # Actions ControlAction = climate_ns.class_("ControlAction", automation.Action) -CLIMATE_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend( +CLIMATE_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend( { cv.GenerateID(): cv.declare_id(Climate), cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTClimateComponent), @@ -105,10 +103,8 @@ CLIMATE_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).ext async def setup_climate_core_(var, config): - cg.add(var.set_name(config[CONF_NAME])) - cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT])) - if CONF_INTERNAL in config: - cg.add(var.set_internal(config[CONF_INTERNAL])) + await setup_entity(var, config) + visual = config[CONF_VISUAL] if CONF_MIN_TEMPERATURE in visual: cg.add(var.set_visual_min_temperature_override(visual[CONF_MIN_TEMPERATURE])) diff --git a/esphome/components/climate/climate.cpp b/esphome/components/climate/climate.cpp index a365b933bb..34e6328d8a 100644 --- a/esphome/components/climate/climate.cpp +++ b/esphome/components/climate/climate.cpp @@ -440,7 +440,7 @@ void Climate::set_visual_max_temperature_override(float visual_max_temperature_o void Climate::set_visual_temperature_step_override(float visual_temperature_step_override) { this->visual_temperature_step_override_ = visual_temperature_step_override; } -Climate::Climate(const std::string &name) : Nameable(name) {} +Climate::Climate(const std::string &name) : EntityBase(name) {} Climate::Climate() : Climate("") {} ClimateCall Climate::make_call() { return ClimateCall(this); } diff --git a/esphome/components/climate/climate.h b/esphome/components/climate/climate.h index 418db02485..852b76686c 100644 --- a/esphome/components/climate/climate.h +++ b/esphome/components/climate/climate.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/entity_base.h" #include "esphome/core/helpers.h" #include "esphome/core/preferences.h" #include "esphome/core/log.h" @@ -163,7 +164,7 @@ struct ClimateDeviceRestoreState { * mode etc). These are read-only for the user and rw for integrations. The reason these are public * is for simple access to them from lambdas `if (id(my_climate).mode == climate::CLIMATE_MODE_HEAT_COOL) ...` */ -class Climate : public Nameable { +class Climate : public EntityBase { public: /// Construct a climate device with empty name (will be set later). Climate(); diff --git a/esphome/components/cover/__init__.py b/esphome/components/cover/__init__.py index 46b8906adb..eb57637283 100644 --- a/esphome/components/cover/__init__.py +++ b/esphome/components/cover/__init__.py @@ -4,18 +4,16 @@ from esphome import automation from esphome.automation import maybe_simple_id, Condition from esphome.components import mqtt from esphome.const import ( - CONF_DISABLED_BY_DEFAULT, CONF_ID, - CONF_INTERNAL, CONF_DEVICE_CLASS, CONF_STATE, CONF_POSITION, CONF_TILT, CONF_STOP, CONF_MQTT_ID, - CONF_NAME, ) from esphome.core import CORE, coroutine_with_priority +from esphome.cpp_helpers import setup_entity IS_PLATFORM_COMPONENT = True @@ -36,7 +34,7 @@ DEVICE_CLASSES = [ cover_ns = cg.esphome_ns.namespace("cover") -Cover = cover_ns.class_("Cover", cg.Nameable) +Cover = cover_ns.class_("Cover", cg.EntityBase) COVER_OPEN = cover_ns.COVER_OPEN COVER_CLOSED = cover_ns.COVER_CLOSED @@ -65,7 +63,7 @@ CoverPublishAction = cover_ns.class_("CoverPublishAction", automation.Action) CoverIsOpenCondition = cover_ns.class_("CoverIsOpenCondition", Condition) CoverIsClosedCondition = cover_ns.class_("CoverIsClosedCondition", Condition) -COVER_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend( +COVER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend( { cv.GenerateID(): cv.declare_id(Cover), cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTCoverComponent), @@ -76,10 +74,8 @@ COVER_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).exten async def setup_cover_core_(var, config): - cg.add(var.set_name(config[CONF_NAME])) - cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT])) - if CONF_INTERNAL in config: - cg.add(var.set_internal(config[CONF_INTERNAL])) + await setup_entity(var, config) + if CONF_DEVICE_CLASS in config: cg.add(var.set_device_class(config[CONF_DEVICE_CLASS])) diff --git a/esphome/components/cover/cover.cpp b/esphome/components/cover/cover.cpp index 863adb1d81..a8d3d691a4 100644 --- a/esphome/components/cover/cover.cpp +++ b/esphome/components/cover/cover.cpp @@ -31,7 +31,7 @@ const char *cover_operation_to_str(CoverOperation op) { } } -Cover::Cover(const std::string &name) : Nameable(name), position{COVER_OPEN} {} +Cover::Cover(const std::string &name) : EntityBase(name), position{COVER_OPEN} {} uint32_t Cover::hash_base() { return 1727367479UL; } diff --git a/esphome/components/cover/cover.h b/esphome/components/cover/cover.h index 8f98a88a42..a67f8d2393 100644 --- a/esphome/components/cover/cover.h +++ b/esphome/components/cover/cover.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/entity_base.h" #include "esphome/core/helpers.h" #include "esphome/core/preferences.h" #include "cover_traits.h" @@ -107,7 +108,7 @@ const char *cover_operation_to_str(CoverOperation op); * to control all values of the cover. Also implement get_traits() to return what operations * the cover supports. */ -class Cover : public Nameable { +class Cover : public EntityBase { public: explicit Cover(); explicit Cover(const std::string &name); diff --git a/esphome/components/esp32_camera/__init__.py b/esphome/components/esp32_camera/__init__.py index de61ab43cf..7f3aebe238 100644 --- a/esphome/components/esp32_camera/__init__.py +++ b/esphome/components/esp32_camera/__init__.py @@ -21,7 +21,7 @@ from esphome.components.esp32 import add_idf_sdkconfig_option DEPENDENCIES = ["esp32", "api"] esp32_camera_ns = cg.esphome_ns.namespace("esp32_camera") -ESP32Camera = esp32_camera_ns.class_("ESP32Camera", cg.PollingComponent, cg.Nameable) +ESP32Camera = esp32_camera_ns.class_("ESP32Camera", cg.PollingComponent, cg.EntityBase) ESP32CameraFrameSize = esp32_camera_ns.enum("ESP32CameraFrameSize") FRAME_SIZES = { "160X120": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_160X120, diff --git a/esphome/components/esp32_camera/esp32_camera.cpp b/esphome/components/esp32_camera/esp32_camera.cpp index 05445f024b..babfda4113 100644 --- a/esphome/components/esp32_camera/esp32_camera.cpp +++ b/esphome/components/esp32_camera/esp32_camera.cpp @@ -172,7 +172,7 @@ void ESP32Camera::framebuffer_task(void *pv) { esp_camera_fb_return(framebuffer); } } -ESP32Camera::ESP32Camera(const std::string &name) : Nameable(name) { +ESP32Camera::ESP32Camera(const std::string &name) : EntityBase(name) { this->config_.pin_pwdn = -1; this->config_.pin_reset = -1; this->config_.pin_xclk = -1; diff --git a/esphome/components/esp32_camera/esp32_camera.h b/esphome/components/esp32_camera/esp32_camera.h index 6246dc2f12..d0445607a4 100644 --- a/esphome/components/esp32_camera/esp32_camera.h +++ b/esphome/components/esp32_camera/esp32_camera.h @@ -3,6 +3,7 @@ #ifdef USE_ESP32 #include "esphome/core/component.h" +#include "esphome/core/entity_base.h" #include "esphome/core/helpers.h" #include #include @@ -50,7 +51,7 @@ enum ESP32CameraFrameSize { ESP32_CAMERA_SIZE_1600X1200, // UXGA }; -class ESP32Camera : public Component, public Nameable { +class ESP32Camera : public Component, public EntityBase { public: ESP32Camera(const std::string &name); void set_data_pins(std::array pins); diff --git a/esphome/components/esp32_touch/binary_sensor.py b/esphome/components/esp32_touch/binary_sensor.py index 93640334cd..bd3e06545d 100644 --- a/esphome/components/esp32_touch/binary_sensor.py +++ b/esphome/components/esp32_touch/binary_sensor.py @@ -2,7 +2,6 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import binary_sensor from esphome.const import ( - CONF_NAME, CONF_PIN, CONF_THRESHOLD, CONF_ID, @@ -55,7 +54,6 @@ async def to_code(config): hub = await cg.get_variable(config[CONF_ESP32_TOUCH_ID]) var = cg.new_Pvariable( config[CONF_ID], - config[CONF_NAME], TOUCH_PADS[config[CONF_PIN]], config[CONF_THRESHOLD], config[CONF_WAKEUP_THRESHOLD], diff --git a/esphome/components/esp32_touch/esp32_touch.cpp b/esphome/components/esp32_touch/esp32_touch.cpp index 801106ab6c..cb72820900 100644 --- a/esphome/components/esp32_touch/esp32_touch.cpp +++ b/esphome/components/esp32_touch/esp32_touch.cpp @@ -159,9 +159,8 @@ void ESP32TouchComponent::on_shutdown() { } } -ESP32TouchBinarySensor::ESP32TouchBinarySensor(const std::string &name, touch_pad_t touch_pad, uint16_t threshold, - uint16_t wakeup_threshold) - : BinarySensor(name), touch_pad_(touch_pad), threshold_(threshold), wakeup_threshold_(wakeup_threshold) {} +ESP32TouchBinarySensor::ESP32TouchBinarySensor(touch_pad_t touch_pad, uint16_t threshold, uint16_t wakeup_threshold) + : BinarySensor(), touch_pad_(touch_pad), threshold_(threshold), wakeup_threshold_(wakeup_threshold) {} } // namespace esp32_touch } // namespace esphome diff --git a/esphome/components/esp32_touch/esp32_touch.h b/esphome/components/esp32_touch/esp32_touch.h index c584a6d9bc..d49e4703a7 100644 --- a/esphome/components/esp32_touch/esp32_touch.h +++ b/esphome/components/esp32_touch/esp32_touch.h @@ -64,7 +64,7 @@ class ESP32TouchComponent : public Component { /// Simple helper class to expose a touch pad value as a binary sensor. class ESP32TouchBinarySensor : public binary_sensor::BinarySensor { public: - ESP32TouchBinarySensor(const std::string &name, touch_pad_t touch_pad, uint16_t threshold, uint16_t wakeup_threshold); + ESP32TouchBinarySensor(touch_pad_t touch_pad, uint16_t threshold, uint16_t wakeup_threshold); touch_pad_t get_touch_pad() const { return touch_pad_; } uint16_t get_threshold() const { return threshold_; } diff --git a/esphome/components/fan/__init__.py b/esphome/components/fan/__init__.py index 15895976d1..52bec3b5b6 100644 --- a/esphome/components/fan/__init__.py +++ b/esphome/components/fan/__init__.py @@ -4,9 +4,7 @@ from esphome import automation from esphome.automation import maybe_simple_id from esphome.components import mqtt from esphome.const import ( - CONF_DISABLED_BY_DEFAULT, CONF_ID, - CONF_INTERNAL, CONF_MQTT_ID, CONF_OSCILLATING, CONF_OSCILLATION_COMMAND_TOPIC, @@ -16,7 +14,6 @@ from esphome.const import ( CONF_SPEED_LEVEL_STATE_TOPIC, CONF_SPEED_COMMAND_TOPIC, CONF_SPEED_STATE_TOPIC, - CONF_NAME, CONF_ON_SPEED_SET, CONF_ON_TURN_OFF, CONF_ON_TURN_ON, @@ -24,11 +21,12 @@ from esphome.const import ( CONF_DIRECTION, ) from esphome.core import CORE, coroutine_with_priority +from esphome.cpp_helpers import setup_entity IS_PLATFORM_COMPONENT = True fan_ns = cg.esphome_ns.namespace("fan") -FanState = fan_ns.class_("FanState", cg.Nameable, cg.Component) +FanState = fan_ns.class_("FanState", cg.EntityBase, cg.Component) MakeFan = cg.Application.struct("MakeFan") FanDirection = fan_ns.enum("FanDirection") @@ -50,7 +48,7 @@ FanSpeedSetTrigger = fan_ns.class_("FanSpeedSetTrigger", automation.Trigger.temp FanIsOnCondition = fan_ns.class_("FanIsOnCondition", automation.Condition.template()) FanIsOffCondition = fan_ns.class_("FanIsOffCondition", automation.Condition.template()) -FAN_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend( +FAN_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend( { cv.GenerateID(): cv.declare_id(FanState), cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTFanComponent), @@ -92,10 +90,7 @@ FAN_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend( async def setup_fan_core_(var, config): - cg.add(var.set_name(config[CONF_NAME])) - cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT])) - if CONF_INTERNAL in config: - cg.add(var.set_internal(config[CONF_INTERNAL])) + await setup_entity(var, config) if CONF_MQTT_ID in config: mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var) diff --git a/esphome/components/fan/fan_state.cpp b/esphome/components/fan/fan_state.cpp index 921b8d57e7..6ff4d3a833 100644 --- a/esphome/components/fan/fan_state.cpp +++ b/esphome/components/fan/fan_state.cpp @@ -12,7 +12,7 @@ void FanState::set_traits(const FanTraits &traits) { this->traits_ = traits; } void FanState::add_on_state_callback(std::function &&callback) { this->state_callback_.add(std::move(callback)); } -FanState::FanState(const std::string &name) : Nameable(name) {} +FanState::FanState(const std::string &name) : EntityBase(name) {} FanStateCall FanState::turn_on() { return this->make_call().set_state(true); } FanStateCall FanState::turn_off() { return this->make_call().set_state(false); } diff --git a/esphome/components/fan/fan_state.h b/esphome/components/fan/fan_state.h index af00275df0..c5a6f59ac4 100644 --- a/esphome/components/fan/fan_state.h +++ b/esphome/components/fan/fan_state.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/entity_base.h" #include "esphome/core/helpers.h" #include "esphome/core/preferences.h" #include "esphome/core/log.h" @@ -66,7 +67,7 @@ class FanStateCall { optional direction_{}; }; -class FanState : public Nameable, public Component { +class FanState : public EntityBase, public Component { public: FanState() = default; /// Construct the fan state with name. diff --git a/esphome/components/integration/integration_sensor.h b/esphome/components/integration/integration_sensor.h index a3bdfbbb2b..437649c1dd 100644 --- a/esphome/components/integration/integration_sensor.h +++ b/esphome/components/integration/integration_sensor.h @@ -64,7 +64,6 @@ class IntegrationSensor : public sensor::Sensor, public Component { this->rtc_.save(&result_f); } std::string unit_of_measurement() override; - std::string icon() override { return this->sensor_->get_icon(); } int8_t accuracy_decimals() override { return this->sensor_->get_accuracy_decimals() + 2; } sensor::Sensor *sensor_; diff --git a/esphome/components/integration/sensor.py b/esphome/components/integration/sensor.py index 460dd46619..26c7c2871a 100644 --- a/esphome/components/integration/sensor.py +++ b/esphome/components/integration/sensor.py @@ -2,7 +2,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation from esphome.components import sensor -from esphome.const import CONF_ID, CONF_SENSOR, CONF_RESTORE +from esphome.const import CONF_ICON, CONF_ID, CONF_SENSOR, CONF_RESTORE +from esphome.core.entity_helpers import inherit_property_from integration_ns = cg.esphome_ns.namespace("integration") IntegrationSensor = integration_ns.class_( @@ -29,7 +30,6 @@ CONF_TIME_UNIT = "time_unit" CONF_INTEGRATION_METHOD = "integration_method" CONF_MIN_SAVE_INTERVAL = "min_save_interval" - CONFIG_SCHEMA = sensor.SENSOR_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(IntegrationSensor), @@ -46,6 +46,19 @@ CONFIG_SCHEMA = sensor.SENSOR_SCHEMA.extend( ).extend(cv.COMPONENT_SCHEMA) +FINAL_VALIDATE_SCHEMA = cv.All( + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(IntegrationSensor), + cv.Optional(CONF_ICON): cv.icon, + cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor), + }, + extra=cv.ALLOW_EXTRA, + ), + inherit_property_from(CONF_ICON, CONF_SENSOR), +) + + async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) diff --git a/esphome/components/light/__init__.py b/esphome/components/light/__init__.py index 69cb87e539..03224d4c10 100644 --- a/esphome/components/light/__init__.py +++ b/esphome/components/light/__init__.py @@ -5,13 +5,10 @@ from esphome.components import mqtt, power_supply from esphome.const import ( CONF_COLOR_CORRECT, CONF_DEFAULT_TRANSITION_LENGTH, - CONF_DISABLED_BY_DEFAULT, CONF_EFFECTS, CONF_FLASH_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, CONF_ID, - CONF_INTERNAL, - CONF_NAME, CONF_MQTT_ID, CONF_POWER_SUPPLY, CONF_RESTORE_MODE, @@ -22,6 +19,7 @@ from esphome.const import ( CONF_WARM_WHITE_COLOR_TEMPERATURE, ) from esphome.core import coroutine_with_priority +from esphome.cpp_helpers import setup_entity from .automation import light_control_to_code # noqa from .effects import ( validate_effects, @@ -54,7 +52,7 @@ RESTORE_MODES = { "RESTORE_INVERTED_DEFAULT_ON": LightRestoreMode.LIGHT_RESTORE_INVERTED_DEFAULT_ON, } -LIGHT_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend( +LIGHT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend( { cv.GenerateID(): cv.declare_id(LightState), cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTJSONLightComponent), @@ -126,10 +124,10 @@ def validate_color_temperature_channels(value): async def setup_light_core_(light_var, output_var, config): - cg.add(light_var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT])) + await setup_entity(light_var, config) + cg.add(light_var.set_restore_mode(config[CONF_RESTORE_MODE])) - if CONF_INTERNAL in config: - cg.add(light_var.set_internal(config[CONF_INTERNAL])) + if CONF_DEFAULT_TRANSITION_LENGTH in config: cg.add( light_var.set_default_transition_length( @@ -167,7 +165,7 @@ async def setup_light_core_(light_var, output_var, config): async def register_light(output_var, config): - light_var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], output_var) + light_var = cg.new_Pvariable(config[CONF_ID], output_var) cg.add(cg.App.register_light(light_var)) await cg.register_component(light_var, config) await setup_light_core_(light_var, output_var, config) diff --git a/esphome/components/light/light_state.cpp b/esphome/components/light/light_state.cpp index f888006b17..5f16585c36 100644 --- a/esphome/components/light/light_state.cpp +++ b/esphome/components/light/light_state.cpp @@ -8,7 +8,8 @@ namespace light { static const char *const TAG = "light"; -LightState::LightState(const std::string &name, LightOutput *output) : Nameable(name), output_(output) {} +LightState::LightState(const std::string &name, LightOutput *output) : EntityBase(name), output_(output) {} +LightState::LightState(LightOutput *output) : output_(output) {} LightTraits LightState::get_traits() { return this->output_->get_traits(); } LightCall LightState::turn_on() { return this->make_call().set_state(true); } diff --git a/esphome/components/light/light_state.h b/esphome/components/light/light_state.h index f73a4c3b17..ae3711234d 100644 --- a/esphome/components/light/light_state.h +++ b/esphome/components/light/light_state.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/entity_base.h" #include "esphome/core/optional.h" #include "esphome/core/preferences.h" #include "light_call.h" @@ -26,11 +27,13 @@ enum LightRestoreMode { /** This class represents the communication layer between the front-end MQTT layer and the * hardware output layer. */ -class LightState : public Nameable, public Component { +class LightState : public EntityBase, public Component { public: /// Construct this LightState using the provided traits and name. LightState(const std::string &name, LightOutput *output); + LightState(LightOutput *output); + LightTraits get_traits(); /// Make a light state call diff --git a/esphome/components/light/types.py b/esphome/components/light/types.py index 66329f7cf9..cf544e5435 100644 --- a/esphome/components/light/types.py +++ b/esphome/components/light/types.py @@ -3,7 +3,7 @@ from esphome import automation # Base light_ns = cg.esphome_ns.namespace("light") -LightState = light_ns.class_("LightState", cg.Nameable, cg.Component) +LightState = light_ns.class_("LightState", cg.EntityBase, cg.Component) # Fake class for addressable lights AddressableLightState = light_ns.class_("LightState", LightState) LightOutput = light_ns.class_("LightOutput") diff --git a/esphome/components/mcp3008/mcp3008.cpp b/esphome/components/mcp3008/mcp3008.cpp index bea24b5c6a..81abc4f012 100644 --- a/esphome/components/mcp3008/mcp3008.cpp +++ b/esphome/components/mcp3008/mcp3008.cpp @@ -37,10 +37,8 @@ float MCP3008::read_data(uint8_t pin) { return data / 1023.0f; } -MCP3008Sensor::MCP3008Sensor(MCP3008 *parent, const std::string &name, uint8_t pin, float reference_voltage) - : PollingComponent(1000), parent_(parent), pin_(pin), reference_voltage_(reference_voltage) { - this->set_name(name); -} +MCP3008Sensor::MCP3008Sensor(MCP3008 *parent, uint8_t pin, float reference_voltage) + : PollingComponent(1000), parent_(parent), pin_(pin), reference_voltage_(reference_voltage) {} float MCP3008Sensor::get_setup_priority() const { return setup_priority::DATA; } diff --git a/esphome/components/mcp3008/mcp3008.h b/esphome/components/mcp3008/mcp3008.h index 6f3dc576ea..5d8b823111 100644 --- a/esphome/components/mcp3008/mcp3008.h +++ b/esphome/components/mcp3008/mcp3008.h @@ -26,7 +26,7 @@ class MCP3008 : public Component, class MCP3008Sensor : public PollingComponent, public sensor::Sensor, public voltage_sampler::VoltageSampler { public: - MCP3008Sensor(MCP3008 *parent, const std::string &name, uint8_t pin, float reference_voltage); + MCP3008Sensor(MCP3008 *parent, uint8_t pin, float reference_voltage); void set_reference_voltage(float reference_voltage) { reference_voltage_ = reference_voltage; } void setup() override; diff --git a/esphome/components/mcp3008/sensor.py b/esphome/components/mcp3008/sensor.py index 4fc9b83afb..d4b9e979ce 100644 --- a/esphome/components/mcp3008/sensor.py +++ b/esphome/components/mcp3008/sensor.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, voltage_sampler -from esphome.const import CONF_ID, CONF_NUMBER, CONF_NAME +from esphome.const import CONF_ID, CONF_NUMBER from . import mcp3008_ns, MCP3008 AUTO_LOAD = ["voltage_sampler"] @@ -29,7 +29,6 @@ async def to_code(config): var = cg.new_Pvariable( config[CONF_ID], parent, - config[CONF_NAME], config[CONF_NUMBER], config[CONF_REFERENCE_VOLTAGE], ) diff --git a/esphome/components/mqtt/mqtt_binary_sensor.cpp b/esphome/components/mqtt/mqtt_binary_sensor.cpp index d7322298bb..188df0f7b9 100644 --- a/esphome/components/mqtt/mqtt_binary_sensor.cpp +++ b/esphome/components/mqtt/mqtt_binary_sensor.cpp @@ -10,6 +10,7 @@ namespace mqtt { static const char *const TAG = "mqtt.binary_sensor"; std::string MQTTBinarySensorComponent::component_type() const { return "binary_sensor"; } +const EntityBase *MQTTBinarySensorComponent::get_entity() const { return this->binary_sensor_; } void MQTTBinarySensorComponent::setup() { this->binary_sensor_->add_on_state_callback([this](bool state) { this->publish_state(state); }); @@ -25,7 +26,6 @@ MQTTBinarySensorComponent::MQTTBinarySensorComponent(binary_sensor::BinarySensor this->set_custom_state_topic(mqtt::global_mqtt_client->get_availability().topic); } } -std::string MQTTBinarySensorComponent::friendly_name() const { return this->binary_sensor_->get_name(); } void MQTTBinarySensorComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) { if (!this->binary_sensor_->get_device_class().empty()) @@ -43,7 +43,6 @@ bool MQTTBinarySensorComponent::send_initial_state() { return true; } } -bool MQTTBinarySensorComponent::is_internal() { return this->binary_sensor_->is_internal(); } bool MQTTBinarySensorComponent::publish_state(bool state) { if (this->binary_sensor_->is_status_binary_sensor()) return true; diff --git a/esphome/components/mqtt/mqtt_binary_sensor.h b/esphome/components/mqtt/mqtt_binary_sensor.h index c459bea1f7..0efb490367 100644 --- a/esphome/components/mqtt/mqtt_binary_sensor.h +++ b/esphome/components/mqtt/mqtt_binary_sensor.h @@ -28,11 +28,10 @@ class MQTTBinarySensorComponent : public mqtt::MQTTComponent { bool send_initial_state() override; bool publish_state(bool state); - bool is_internal() override; protected: - std::string friendly_name() const override; std::string component_type() const override; + const EntityBase *get_entity() const override; binary_sensor::BinarySensor *binary_sensor_; }; diff --git a/esphome/components/mqtt/mqtt_climate.cpp b/esphome/components/mqtt/mqtt_climate.cpp index 8519e296b0..47b6684dec 100644 --- a/esphome/components/mqtt/mqtt_climate.cpp +++ b/esphome/components/mqtt/mqtt_climate.cpp @@ -212,9 +212,9 @@ void MQTTClimateComponent::setup() { } MQTTClimateComponent::MQTTClimateComponent(Climate *device) : device_(device) {} bool MQTTClimateComponent::send_initial_state() { return this->publish_state_(); } -bool MQTTClimateComponent::is_internal() { return this->device_->is_internal(); } std::string MQTTClimateComponent::component_type() const { return "climate"; } -std::string MQTTClimateComponent::friendly_name() const { return this->device_->get_name(); } +const EntityBase *MQTTClimateComponent::get_entity() const { return this->device_; } + bool MQTTClimateComponent::publish_state_() { auto traits = this->device_->get_traits(); // mode diff --git a/esphome/components/mqtt/mqtt_climate.h b/esphome/components/mqtt/mqtt_climate.h index 8b8a8e866e..40ac4c18c1 100644 --- a/esphome/components/mqtt/mqtt_climate.h +++ b/esphome/components/mqtt/mqtt_climate.h @@ -16,7 +16,6 @@ class MQTTClimateComponent : public mqtt::MQTTComponent { MQTTClimateComponent(climate::Climate *device); void send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) override; bool send_initial_state() override; - bool is_internal() override; std::string component_type() const override; void setup() override; @@ -38,7 +37,7 @@ class MQTTClimateComponent : public mqtt::MQTTComponent { MQTT_COMPONENT_CUSTOM_TOPIC(swing_mode, command) protected: - std::string friendly_name() const override; + const EntityBase *get_entity() const override; bool publish_state_(); diff --git a/esphome/components/mqtt/mqtt_component.cpp b/esphome/components/mqtt/mqtt_component.cpp index 96cda57914..0ece4b3501 100644 --- a/esphome/components/mqtt/mqtt_component.cpp +++ b/esphome/components/mqtt/mqtt_component.cpp @@ -68,8 +68,13 @@ bool MQTTComponent::send_discovery_() { this->send_discovery(root, config); - std::string name = this->friendly_name(); - root["name"] = name; + // Fields from EntityBase + root["name"] = this->friendly_name(); + if (this->is_disabled_by_default()) + root["enabled_by_default"] = false; + if (!this->get_icon().empty()) + root["icon"] = this->get_icon(); + if (config.state_topic) root["state_topic"] = this->get_state_topic_(); if (config.command_topic) @@ -199,6 +204,12 @@ void MQTTComponent::schedule_resend_state() { this->resend_state_ = true; } std::string MQTTComponent::unique_id() { return ""; } bool MQTTComponent::is_connected_() const { return global_mqtt_client->is_connected(); } +// Pull these properties from EntityBase if not overridden +std::string MQTTComponent::friendly_name() const { return this->get_entity()->get_name(); } +std::string MQTTComponent::get_icon() const { return this->get_entity()->get_icon(); } +bool MQTTComponent::is_disabled_by_default() const { return this->get_entity()->is_disabled_by_default(); } +bool MQTTComponent::is_internal() { return this->get_entity()->is_internal(); } + } // namespace mqtt } // namespace esphome diff --git a/esphome/components/mqtt/mqtt_component.h b/esphome/components/mqtt/mqtt_component.h index f07e752e02..657ab7b608 100644 --- a/esphome/components/mqtt/mqtt_component.h +++ b/esphome/components/mqtt/mqtt_component.h @@ -7,6 +7,7 @@ #include #include "esphome/core/component.h" +#include "esphome/core/entity_base.h" #include "mqtt_client.h" namespace esphome { @@ -73,7 +74,7 @@ class MQTTComponent : public Component { virtual bool send_initial_state() = 0; - virtual bool is_internal() = 0; + virtual bool is_internal(); /// Set whether state message should be retained. void set_retain(bool retain); @@ -148,8 +149,10 @@ class MQTTComponent : public Component { */ std::string get_default_topic_for_(const std::string &suffix) const; - /// Get the friendly name of this MQTT component. - virtual std::string friendly_name() const = 0; + /** + * Gets the Entity served by this MQTT component. + */ + virtual const EntityBase *get_entity() const = 0; /** A unique ID for this MQTT component, empty for no unique id. See unique ID requirements: * https://developers.home-assistant.io/docs/en/entity_registry_index.html#unique-id-requirements @@ -158,6 +161,15 @@ class MQTTComponent : public Component { */ virtual std::string unique_id(); + /// Get the friendly name of this MQTT component. + virtual std::string friendly_name() const; + + /// Get the icon field of this component + virtual std::string get_icon() const; + + /// Get whether the underlying Entity is disabled by default + virtual bool is_disabled_by_default() const; + /// Get the MQTT topic that new states will be shared to. const std::string get_state_topic_() const; diff --git a/esphome/components/mqtt/mqtt_cover.cpp b/esphome/components/mqtt/mqtt_cover.cpp index 61c8fa2d94..e8bc7f0e30 100644 --- a/esphome/components/mqtt/mqtt_cover.cpp +++ b/esphome/components/mqtt/mqtt_cover.cpp @@ -84,9 +84,9 @@ void MQTTCoverComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryCon } std::string MQTTCoverComponent::component_type() const { return "cover"; } -std::string MQTTCoverComponent::friendly_name() const { return this->cover_->get_name(); } +const EntityBase *MQTTCoverComponent::get_entity() const { return this->cover_; } + bool MQTTCoverComponent::send_initial_state() { return this->publish_state(); } -bool MQTTCoverComponent::is_internal() { return this->cover_->is_internal(); } bool MQTTCoverComponent::publish_state() { auto traits = this->cover_->get_traits(); bool success = true; diff --git a/esphome/components/mqtt/mqtt_cover.h b/esphome/components/mqtt/mqtt_cover.h index b8c9f2617c..149d46ac85 100644 --- a/esphome/components/mqtt/mqtt_cover.h +++ b/esphome/components/mqtt/mqtt_cover.h @@ -24,7 +24,6 @@ class MQTTCoverComponent : public mqtt::MQTTComponent { MQTT_COMPONENT_CUSTOM_TOPIC(tilt, state) bool send_initial_state() override; - bool is_internal() override; bool publish_state(); @@ -32,7 +31,7 @@ class MQTTCoverComponent : public mqtt::MQTTComponent { protected: std::string component_type() const override; - std::string friendly_name() const override; + const EntityBase *get_entity() const override; cover::Cover *cover_; }; diff --git a/esphome/components/mqtt/mqtt_fan.cpp b/esphome/components/mqtt/mqtt_fan.cpp index ed1ab605aa..898183cc58 100644 --- a/esphome/components/mqtt/mqtt_fan.cpp +++ b/esphome/components/mqtt/mqtt_fan.cpp @@ -16,6 +16,7 @@ MQTTFanComponent::MQTTFanComponent(FanState *state) : MQTTComponent(), state_(st FanState *MQTTFanComponent::get_state() const { return this->state_; } std::string MQTTFanComponent::component_type() const { return "fan"; } +const EntityBase *MQTTFanComponent::get_entity() const { return this->state_; } void MQTTFanComponent::setup() { this->subscribe(this->get_command_topic_(), [this](const std::string &topic, const std::string &payload) { @@ -113,7 +114,7 @@ void MQTTFanComponent::dump_config() { } bool MQTTFanComponent::send_initial_state() { return this->publish_state(); } -std::string MQTTFanComponent::friendly_name() const { return this->state_->get_name(); } + void MQTTFanComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) { if (this->state_->get_traits().supports_oscillation()) { root["oscillation_command_topic"] = this->get_oscillation_command_topic(); @@ -126,7 +127,6 @@ void MQTTFanComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryConfi root["speed_state_topic"] = this->get_speed_state_topic(); } } -bool MQTTFanComponent::is_internal() { return this->state_->is_internal(); } bool MQTTFanComponent::publish_state() { const char *state_s = this->state_->state ? "ON" : "OFF"; ESP_LOGD(TAG, "'%s' Sending state %s.", this->state_->get_name().c_str(), state_s); diff --git a/esphome/components/mqtt/mqtt_fan.h b/esphome/components/mqtt/mqtt_fan.h index 00263e13eb..a160d5366b 100644 --- a/esphome/components/mqtt/mqtt_fan.h +++ b/esphome/components/mqtt/mqtt_fan.h @@ -39,10 +39,8 @@ class MQTTFanComponent : public mqtt::MQTTComponent { fan::FanState *get_state() const; - bool is_internal() override; - protected: - std::string friendly_name() const override; + const EntityBase *get_entity() const override; fan::FanState *state_; }; diff --git a/esphome/components/mqtt/mqtt_light.cpp b/esphome/components/mqtt/mqtt_light.cpp index d028b1b037..a88358a6b2 100644 --- a/esphome/components/mqtt/mqtt_light.cpp +++ b/esphome/components/mqtt/mqtt_light.cpp @@ -13,6 +13,7 @@ static const char *const TAG = "mqtt.light"; using namespace esphome::light; std::string MQTTJSONLightComponent::component_type() const { return "light"; } +const EntityBase *MQTTJSONLightComponent::get_entity() const { return this->state_; } void MQTTJSONLightComponent::setup() { this->subscribe_json(this->get_command_topic_(), [this](const std::string &topic, JsonObject &root) { @@ -32,7 +33,7 @@ bool MQTTJSONLightComponent::publish_state_() { [this](JsonObject &root) { LightJSONSchema::dump_json(*this->state_, root); }); } LightState *MQTTJSONLightComponent::get_state() const { return this->state_; } -std::string MQTTJSONLightComponent::friendly_name() const { return this->state_->get_name(); } + void MQTTJSONLightComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) { root["schema"] = "json"; auto traits = this->state_->get_traits(); @@ -70,7 +71,6 @@ void MQTTJSONLightComponent::send_discovery(JsonObject &root, mqtt::SendDiscover } } bool MQTTJSONLightComponent::send_initial_state() { return this->publish_state_(); } -bool MQTTJSONLightComponent::is_internal() { return this->state_->is_internal(); } void MQTTJSONLightComponent::dump_config() { ESP_LOGCONFIG(TAG, "MQTT Light '%s':", this->state_->get_name().c_str()); LOG_MQTT_COMPONENT(true, true) diff --git a/esphome/components/mqtt/mqtt_light.h b/esphome/components/mqtt/mqtt_light.h index b0f3145900..192cba39b6 100644 --- a/esphome/components/mqtt/mqtt_light.h +++ b/esphome/components/mqtt/mqtt_light.h @@ -25,11 +25,9 @@ class MQTTJSONLightComponent : public mqtt::MQTTComponent { bool send_initial_state() override; - bool is_internal() override; - protected: - std::string friendly_name() const override; std::string component_type() const override; + const EntityBase *get_entity() const override; bool publish_state_(); diff --git a/esphome/components/mqtt/mqtt_number.cpp b/esphome/components/mqtt/mqtt_number.cpp index faa38056a9..674fd77bdf 100644 --- a/esphome/components/mqtt/mqtt_number.cpp +++ b/esphome/components/mqtt/mqtt_number.cpp @@ -33,13 +33,11 @@ void MQTTNumberComponent::dump_config() { } std::string MQTTNumberComponent::component_type() const { return "number"; } +const EntityBase *MQTTNumberComponent::get_entity() const { return this->number_; } -std::string MQTTNumberComponent::friendly_name() const { return this->number_->get_name(); } void MQTTNumberComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) { const auto &traits = number_->traits; // https://www.home-assistant.io/integrations/number.mqtt/ - if (!traits.get_icon().empty()) - root["icon"] = traits.get_icon(); root["min"] = traits.get_min_value(); root["max"] = traits.get_max_value(); root["step"] = traits.get_step(); @@ -53,7 +51,6 @@ bool MQTTNumberComponent::send_initial_state() { return true; } } -bool MQTTNumberComponent::is_internal() { return this->number_->is_internal(); } bool MQTTNumberComponent::publish_state(float value) { char buffer[64]; snprintf(buffer, sizeof(buffer), "%f", value); diff --git a/esphome/components/mqtt/mqtt_number.h b/esphome/components/mqtt/mqtt_number.h index 46dc221e9e..66622d7c29 100644 --- a/esphome/components/mqtt/mqtt_number.h +++ b/esphome/components/mqtt/mqtt_number.h @@ -28,15 +28,13 @@ class MQTTNumberComponent : public mqtt::MQTTComponent { void send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) override; bool send_initial_state() override; - bool is_internal() override; bool publish_state(float value); protected: /// Override for MQTTComponent, returns "number". std::string component_type() const override; - - std::string friendly_name() const override; + const EntityBase *get_entity() const override; number::Number *number_; }; diff --git a/esphome/components/mqtt/mqtt_select.cpp b/esphome/components/mqtt/mqtt_select.cpp index 467a0cd84c..b499636006 100644 --- a/esphome/components/mqtt/mqtt_select.cpp +++ b/esphome/components/mqtt/mqtt_select.cpp @@ -28,13 +28,11 @@ void MQTTSelectComponent::dump_config() { } std::string MQTTSelectComponent::component_type() const { return "select"; } +const EntityBase *MQTTSelectComponent::get_entity() const { return this->select_; } -std::string MQTTSelectComponent::friendly_name() const { return this->select_->get_name(); } void MQTTSelectComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) { const auto &traits = select_->traits; // https://www.home-assistant.io/integrations/select.mqtt/ - if (!traits.get_icon().empty()) - root["icon"] = traits.get_icon(); JsonArray &options = root.createNestedArray("options"); for (const auto &option : traits.get_options()) options.add(option); @@ -48,7 +46,6 @@ bool MQTTSelectComponent::send_initial_state() { return true; } } -bool MQTTSelectComponent::is_internal() { return this->select_->is_internal(); } bool MQTTSelectComponent::publish_state(const std::string &value) { return this->publish(this->get_state_topic_(), value); } diff --git a/esphome/components/mqtt/mqtt_select.h b/esphome/components/mqtt/mqtt_select.h index 0115c0a41e..d77d0cf513 100644 --- a/esphome/components/mqtt/mqtt_select.h +++ b/esphome/components/mqtt/mqtt_select.h @@ -28,15 +28,13 @@ class MQTTSelectComponent : public mqtt::MQTTComponent { void send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) override; bool send_initial_state() override; - bool is_internal() override; bool publish_state(const std::string &value); protected: /// Override for MQTTComponent, returns "select". std::string component_type() const override; - - std::string friendly_name() const override; + const EntityBase *get_entity() const override; select::Select *select_; }; diff --git a/esphome/components/mqtt/mqtt_sensor.cpp b/esphome/components/mqtt/mqtt_sensor.cpp index 72ec7c54ee..78710ff403 100644 --- a/esphome/components/mqtt/mqtt_sensor.cpp +++ b/esphome/components/mqtt/mqtt_sensor.cpp @@ -30,6 +30,7 @@ void MQTTSensorComponent::dump_config() { } std::string MQTTSensorComponent::component_type() const { return "sensor"; } +const EntityBase *MQTTSensorComponent::get_entity() const { return this->sensor_; } uint32_t MQTTSensorComponent::get_expire_after() const { if (this->expire_after_.has_value()) @@ -38,7 +39,7 @@ uint32_t MQTTSensorComponent::get_expire_after() const { } void MQTTSensorComponent::set_expire_after(uint32_t expire_after) { this->expire_after_ = expire_after; } void MQTTSensorComponent::disable_expire_after() { this->expire_after_ = 0; } -std::string MQTTSensorComponent::friendly_name() const { return this->sensor_->get_name(); } + void MQTTSensorComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) { if (!this->sensor_->get_device_class().empty()) root["device_class"] = this->sensor_->get_device_class(); @@ -49,9 +50,6 @@ void MQTTSensorComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryCo if (this->get_expire_after() > 0) root["expire_after"] = this->get_expire_after() / 1000; - if (!this->sensor_->get_icon().empty()) - root["icon"] = this->sensor_->get_icon(); - if (this->sensor_->get_force_update()) root["force_update"] = true; @@ -67,7 +65,6 @@ bool MQTTSensorComponent::send_initial_state() { return true; } } -bool MQTTSensorComponent::is_internal() { return this->sensor_->is_internal(); } bool MQTTSensorComponent::publish_state(float value) { int8_t accuracy = this->sensor_->get_accuracy_decimals(); return this->publish(this->get_state_topic_(), value_accuracy_to_string(value, accuracy)); diff --git a/esphome/components/mqtt/mqtt_sensor.h b/esphome/components/mqtt/mqtt_sensor.h index 2385529f4f..22609fdfef 100644 --- a/esphome/components/mqtt/mqtt_sensor.h +++ b/esphome/components/mqtt/mqtt_sensor.h @@ -41,14 +41,11 @@ class MQTTSensorComponent : public mqtt::MQTTComponent { bool publish_state(float value); bool send_initial_state() override; - bool is_internal() override; protected: /// Override for MQTTComponent, returns "sensor". std::string component_type() const override; - - std::string friendly_name() const override; - + const EntityBase *get_entity() const override; std::string unique_id() override; sensor::Sensor *sensor_; diff --git a/esphome/components/mqtt/mqtt_switch.cpp b/esphome/components/mqtt/mqtt_switch.cpp index b13ddd5d9d..16cf102f7e 100644 --- a/esphome/components/mqtt/mqtt_switch.cpp +++ b/esphome/components/mqtt/mqtt_switch.cpp @@ -41,15 +41,13 @@ void MQTTSwitchComponent::dump_config() { } std::string MQTTSwitchComponent::component_type() const { return "switch"; } +const EntityBase *MQTTSwitchComponent::get_entity() const { return this->switch_; } void MQTTSwitchComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) { - if (!this->switch_->get_icon().empty()) - root["icon"] = this->switch_->get_icon(); if (this->switch_->assumed_state()) root["optimistic"] = true; } bool MQTTSwitchComponent::send_initial_state() { return this->publish_state(this->switch_->state); } -bool MQTTSwitchComponent::is_internal() { return this->switch_->is_internal(); } -std::string MQTTSwitchComponent::friendly_name() const { return this->switch_->get_name(); } + bool MQTTSwitchComponent::publish_state(bool state) { const char *state_s = state ? "ON" : "OFF"; return this->publish(this->get_state_topic_(), state_s); diff --git a/esphome/components/mqtt/mqtt_switch.h b/esphome/components/mqtt/mqtt_switch.h index 9959d21872..a0a7a23220 100644 --- a/esphome/components/mqtt/mqtt_switch.h +++ b/esphome/components/mqtt/mqtt_switch.h @@ -23,15 +23,13 @@ class MQTTSwitchComponent : public mqtt::MQTTComponent { void send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) override; bool send_initial_state() override; - bool is_internal() override; bool publish_state(bool state); protected: - std::string friendly_name() const override; - /// "switch" component type. std::string component_type() const override; + const EntityBase *get_entity() const override; switch_::Switch *switch_; }; diff --git a/esphome/components/mqtt/mqtt_text_sensor.cpp b/esphome/components/mqtt/mqtt_text_sensor.cpp index fd96cd0902..7b89915649 100644 --- a/esphome/components/mqtt/mqtt_text_sensor.cpp +++ b/esphome/components/mqtt/mqtt_text_sensor.cpp @@ -13,9 +13,6 @@ using namespace esphome::text_sensor; MQTTTextSensor::MQTTTextSensor(TextSensor *sensor) : MQTTComponent(), sensor_(sensor) {} void MQTTTextSensor::send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) { - if (!this->sensor_->get_icon().empty()) - root["icon"] = this->sensor_->get_icon(); - config.command_topic = false; } void MQTTTextSensor::setup() { @@ -35,9 +32,8 @@ bool MQTTTextSensor::send_initial_state() { return true; } } -bool MQTTTextSensor::is_internal() { return this->sensor_->is_internal(); } std::string MQTTTextSensor::component_type() const { return "sensor"; } -std::string MQTTTextSensor::friendly_name() const { return this->sensor_->get_name(); } +const EntityBase *MQTTTextSensor::get_entity() const { return this->sensor_; } std::string MQTTTextSensor::unique_id() { return this->sensor_->unique_id(); } } // namespace mqtt diff --git a/esphome/components/mqtt/mqtt_text_sensor.h b/esphome/components/mqtt/mqtt_text_sensor.h index 1d3f95b894..83743245cc 100644 --- a/esphome/components/mqtt/mqtt_text_sensor.h +++ b/esphome/components/mqtt/mqtt_text_sensor.h @@ -25,13 +25,9 @@ class MQTTTextSensor : public mqtt::MQTTComponent { bool send_initial_state() override; - bool is_internal() override; - protected: std::string component_type() const override; - - std::string friendly_name() const override; - + const EntityBase *get_entity() const override; std::string unique_id() override; text_sensor::TextSensor *sensor_; diff --git a/esphome/components/number/__init__.py b/esphome/components/number/__init__.py index 88153492d3..2856a25ee7 100644 --- a/esphome/components/number/__init__.py +++ b/esphome/components/number/__init__.py @@ -6,25 +6,21 @@ from esphome.components import mqtt from esphome.const import ( CONF_ABOVE, CONF_BELOW, - CONF_DISABLED_BY_DEFAULT, - CONF_ICON, CONF_ID, - CONF_INTERNAL, CONF_ON_VALUE, CONF_ON_VALUE_RANGE, CONF_TRIGGER_ID, - CONF_NAME, CONF_MQTT_ID, CONF_VALUE, - ICON_EMPTY, ) from esphome.core import CORE, coroutine_with_priority +from esphome.cpp_helpers import setup_entity CODEOWNERS = ["@esphome/core"] IS_PLATFORM_COMPONENT = True number_ns = cg.esphome_ns.namespace("number") -Number = number_ns.class_("Number", cg.Nameable) +Number = number_ns.class_("Number", cg.EntityBase) NumberPtr = Number.operator("ptr") # Triggers @@ -46,11 +42,10 @@ NumberInRangeCondition = number_ns.class_( icon = cv.icon -NUMBER_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend( +NUMBER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend( { cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTNumberComponent), cv.GenerateID(): cv.declare_id(Number), - cv.Optional(CONF_ICON, default=ICON_EMPTY): icon, cv.Optional(CONF_ON_VALUE): automation.validate_automation( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(NumberStateTrigger), @@ -71,12 +66,8 @@ NUMBER_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend( async def setup_number_core_( var, config, *, min_value: float, max_value: float, step: Optional[float] ): - cg.add(var.set_name(config[CONF_NAME])) - cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT])) - if CONF_INTERNAL in config: - cg.add(var.set_internal(config[CONF_INTERNAL])) + await setup_entity(var, config) - cg.add(var.traits.set_icon(config[CONF_ICON])) cg.add(var.traits.set_min_value(min_value)) cg.add(var.traits.set_max_value(max_value)) if step is not None: diff --git a/esphome/components/number/number.h b/esphome/components/number/number.h index 945f174510..ed104fb477 100644 --- a/esphome/components/number/number.h +++ b/esphome/components/number/number.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/entity_base.h" #include "esphome/core/helpers.h" namespace esphome { @@ -9,8 +10,8 @@ namespace number { #define LOG_NUMBER(prefix, type, obj) \ if ((obj) != nullptr) { \ ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \ - if (!(obj)->traits.get_icon().empty()) { \ - ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->traits.get_icon().c_str()); \ + if (!(obj)->get_icon().empty()) { \ + ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon().c_str()); \ } \ } @@ -40,21 +41,18 @@ class NumberTraits { float get_max_value() const { return max_value_; } void set_step(float step) { step_ = step; } float get_step() const { return step_; } - void set_icon(std::string icon) { icon_ = std::move(icon); } - const std::string &get_icon() const { return icon_; } protected: float min_value_ = NAN; float max_value_ = NAN; float step_ = NAN; - std::string icon_; }; /** Base-class for all numbers. * * A number can use publish_state to send out a new value. */ -class Number : public Nameable { +class Number : public EntityBase { public: float state; diff --git a/esphome/components/remote_receiver/binary_sensor.py b/esphome/components/remote_receiver/binary_sensor.py index 62c1d9cb27..218b40d6cc 100644 --- a/esphome/components/remote_receiver/binary_sensor.py +++ b/esphome/components/remote_receiver/binary_sensor.py @@ -1,6 +1,4 @@ -import esphome.codegen as cg from esphome.components import binary_sensor, remote_base -from esphome.const import CONF_NAME DEPENDENCIES = ["remote_receiver"] @@ -9,5 +7,4 @@ CONFIG_SCHEMA = remote_base.validate_binary_sensor async def to_code(config): var = await remote_base.build_binary_sensor(config) - cg.add(var.set_name(config[CONF_NAME])) await binary_sensor.register_binary_sensor(var, config) diff --git a/esphome/components/select/__init__.py b/esphome/components/select/__init__.py index d3ab344926..c156a63a86 100644 --- a/esphome/components/select/__init__.py +++ b/esphome/components/select/__init__.py @@ -4,24 +4,20 @@ import esphome.config_validation as cv from esphome import automation from esphome.components import mqtt from esphome.const import ( - CONF_DISABLED_BY_DEFAULT, - CONF_ICON, CONF_ID, - CONF_INTERNAL, CONF_ON_VALUE, CONF_OPTION, CONF_TRIGGER_ID, - CONF_NAME, CONF_MQTT_ID, - ICON_EMPTY, ) from esphome.core import CORE, coroutine_with_priority +from esphome.cpp_helpers import setup_entity CODEOWNERS = ["@esphome/core"] IS_PLATFORM_COMPONENT = True select_ns = cg.esphome_ns.namespace("select") -Select = select_ns.class_("Select", cg.Nameable) +Select = select_ns.class_("Select", cg.EntityBase) SelectPtr = Select.operator("ptr") # Triggers @@ -35,11 +31,10 @@ SelectSetAction = select_ns.class_("SelectSetAction", automation.Action) icon = cv.icon -SELECT_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend( +SELECT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend( { cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTSelectComponent), cv.GenerateID(): cv.declare_id(Select), - cv.Optional(CONF_ICON, default=ICON_EMPTY): icon, cv.Optional(CONF_ON_VALUE): automation.validate_automation( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SelectStateTrigger), @@ -50,12 +45,8 @@ SELECT_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend( async def setup_select_core_(var, config, *, options: List[str]): - cg.add(var.set_name(config[CONF_NAME])) - cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT])) - if CONF_INTERNAL in config: - cg.add(var.set_internal(config[CONF_INTERNAL])) + await setup_entity(var, config) - cg.add(var.traits.set_icon(config[CONF_ICON])) cg.add(var.traits.set_options(options)) for conf in config.get(CONF_ON_VALUE, []): diff --git a/esphome/components/select/select.h b/esphome/components/select/select.h index 0dec74b627..6113cca1fd 100644 --- a/esphome/components/select/select.h +++ b/esphome/components/select/select.h @@ -3,6 +3,7 @@ #include #include #include "esphome/core/component.h" +#include "esphome/core/entity_base.h" #include "esphome/core/helpers.h" namespace esphome { @@ -11,8 +12,8 @@ namespace select { #define LOG_SELECT(prefix, type, obj) \ if ((obj) != nullptr) { \ ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \ - if (!(obj)->traits.get_icon().empty()) { \ - ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->traits.get_icon().c_str()); \ + if (!(obj)->get_icon().empty()) { \ + ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon().c_str()); \ } \ } @@ -38,19 +39,16 @@ class SelectTraits { public: void set_options(std::vector options) { this->options_ = std::move(options); } const std::vector get_options() const { return this->options_; } - void set_icon(std::string icon) { icon_ = std::move(icon); } - const std::string &get_icon() const { return icon_; } protected: std::vector options_; - std::string icon_; }; /** Base-class for all selects. * * A select can use publish_state to send out a new value. */ -class Select : public Nameable { +class Select : public EntityBase { public: std::string state; diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index ce4aafb3b0..cb74a41119 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -10,13 +10,11 @@ from esphome.const import ( CONF_ACCURACY_DECIMALS, CONF_ALPHA, CONF_BELOW, - CONF_DISABLED_BY_DEFAULT, CONF_EXPIRE_AFTER, CONF_FILTERS, CONF_FROM, CONF_ICON, CONF_ID, - CONF_INTERNAL, CONF_ON_RAW_VALUE, CONF_ON_VALUE, CONF_ON_VALUE_RANGE, @@ -27,7 +25,6 @@ from esphome.const import ( CONF_TRIGGER_ID, CONF_UNIT_OF_MEASUREMENT, CONF_WINDOW_SIZE, - CONF_NAME, CONF_MQTT_ID, CONF_FORCE_UPDATE, DEVICE_CLASS_EMPTY, @@ -59,6 +56,7 @@ from esphome.const import ( DEVICE_CLASS_VOLTAGE, ) from esphome.core import CORE, coroutine_with_priority +from esphome.cpp_helpers import setup_entity from esphome.util import Registry CODEOWNERS = ["@esphome/core"] @@ -136,7 +134,7 @@ def validate_datapoint(value): # Base sensor_ns = cg.esphome_ns.namespace("sensor") -Sensor = sensor_ns.class_("Sensor", cg.Nameable) +Sensor = sensor_ns.class_("Sensor", cg.EntityBase) SensorPtr = Sensor.operator("ptr") # Triggers @@ -180,12 +178,11 @@ validate_accuracy_decimals = cv.int_ validate_icon = cv.icon validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_") -SENSOR_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend( +SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend( { cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTSensorComponent), cv.GenerateID(): cv.declare_id(Sensor), cv.Optional(CONF_UNIT_OF_MEASUREMENT): validate_unit_of_measurement, - cv.Optional(CONF_ICON): validate_icon, cv.Optional(CONF_ACCURACY_DECIMALS): validate_accuracy_decimals, cv.Optional(CONF_DEVICE_CLASS): validate_device_class, cv.Optional(CONF_STATE_CLASS): validate_state_class, @@ -495,18 +492,14 @@ async def build_filters(config): async def setup_sensor_core_(var, config): - cg.add(var.set_name(config[CONF_NAME])) - cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT])) - if CONF_INTERNAL in config: - cg.add(var.set_internal(config[CONF_INTERNAL])) + await setup_entity(var, config) + if CONF_DEVICE_CLASS in config: cg.add(var.set_device_class(config[CONF_DEVICE_CLASS])) if CONF_STATE_CLASS in config: cg.add(var.set_state_class(config[CONF_STATE_CLASS])) if CONF_UNIT_OF_MEASUREMENT in config: cg.add(var.set_unit_of_measurement(config[CONF_UNIT_OF_MEASUREMENT])) - if CONF_ICON in config: - cg.add(var.set_icon(config[CONF_ICON])) if CONF_ACCURACY_DECIMALS in config: cg.add(var.set_accuracy_decimals(config[CONF_ACCURACY_DECIMALS])) cg.add(var.set_force_update(config[CONF_FORCE_UPDATE])) diff --git a/esphome/components/sensor/sensor.cpp b/esphome/components/sensor/sensor.cpp index 0dc0275715..793ae170c3 100644 --- a/esphome/components/sensor/sensor.cpp +++ b/esphome/components/sensor/sensor.cpp @@ -18,7 +18,7 @@ std::string state_class_to_string(StateClass state_class) { } } -Sensor::Sensor(const std::string &name) : Nameable(name), state(NAN), raw_state(NAN) {} +Sensor::Sensor(const std::string &name) : EntityBase(name), state(NAN), raw_state(NAN) {} Sensor::Sensor() : Sensor("") {} std::string Sensor::get_unit_of_measurement() { @@ -31,14 +31,6 @@ void Sensor::set_unit_of_measurement(const std::string &unit_of_measurement) { } std::string Sensor::unit_of_measurement() { return ""; } -std::string Sensor::get_icon() { - if (this->icon_.has_value()) - return *this->icon_; - return this->icon(); -} -void Sensor::set_icon(const std::string &icon) { this->icon_ = icon; } -std::string Sensor::icon() { return ""; } - int8_t Sensor::get_accuracy_decimals() { if (this->accuracy_decimals_.has_value()) return *this->accuracy_decimals_; diff --git a/esphome/components/sensor/sensor.h b/esphome/components/sensor/sensor.h index d284f931b1..6cab46f7f9 100644 --- a/esphome/components/sensor/sensor.h +++ b/esphome/components/sensor/sensor.h @@ -2,6 +2,7 @@ #include "esphome/core/log.h" #include "esphome/core/component.h" +#include "esphome/core/entity_base.h" #include "esphome/core/helpers.h" #include "esphome/components/sensor/filter.h" @@ -43,7 +44,7 @@ std::string state_class_to_string(StateClass state_class); * * A sensor has unit of measurement and can use publish_state to send out a new value with the specified accuracy. */ -class Sensor : public Nameable { +class Sensor : public EntityBase { public: explicit Sensor(); explicit Sensor(const std::string &name); @@ -53,11 +54,6 @@ class Sensor : public Nameable { /// Manually set the unit of measurement. void set_unit_of_measurement(const std::string &unit_of_measurement); - /// Get the icon. Uses the manual override if specified or the default value instead. - std::string get_icon(); - /// Manually set the icon, for example "mdi:flash". - void set_icon(const std::string &icon); - /// Get the accuracy in decimals, using the manual override if set. int8_t get_accuracy_decimals(); /// Manually set the accuracy in decimals. @@ -157,9 +153,6 @@ class Sensor : public Nameable { /// Override this to set the default unit of measurement. virtual std::string unit_of_measurement(); // NOLINT - /// Override this to set the default icon. - virtual std::string icon(); // NOLINT - /// Override this to set the default accuracy in decimals. virtual int8_t accuracy_decimals(); // NOLINT @@ -178,7 +171,6 @@ class Sensor : public Nameable { Filter *filter_list_{nullptr}; ///< Store all active filters. optional unit_of_measurement_; ///< Unit of measurement override - optional icon_; ///< Icon override optional accuracy_decimals_; ///< Accuracy in decimals override optional device_class_; ///< Device class override optional state_class_{STATE_CLASS_NONE}; ///< State class override diff --git a/esphome/components/switch/__init__.py b/esphome/components/switch/__init__.py index 8aa213a9f6..88341e0add 100644 --- a/esphome/components/switch/__init__.py +++ b/esphome/components/switch/__init__.py @@ -4,24 +4,21 @@ from esphome import automation from esphome.automation import Condition, maybe_simple_id from esphome.components import mqtt from esphome.const import ( - CONF_DISABLED_BY_DEFAULT, - CONF_ICON, CONF_ID, - CONF_INTERNAL, CONF_INVERTED, CONF_ON_TURN_OFF, CONF_ON_TURN_ON, CONF_TRIGGER_ID, CONF_MQTT_ID, - CONF_NAME, ) from esphome.core import CORE, coroutine_with_priority +from esphome.cpp_helpers import setup_entity CODEOWNERS = ["@esphome/core"] IS_PLATFORM_COMPONENT = True switch_ns = cg.esphome_ns.namespace("switch_") -Switch = switch_ns.class_("Switch", cg.Nameable) +Switch = switch_ns.class_("Switch", cg.EntityBase) SwitchPtr = Switch.operator("ptr") ToggleAction = switch_ns.class_("ToggleAction", automation.Action) @@ -39,10 +36,9 @@ SwitchTurnOffTrigger = switch_ns.class_( icon = cv.icon -SWITCH_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend( +SWITCH_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend( { cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTSwitchComponent), - cv.Optional(CONF_ICON): icon, cv.Optional(CONF_INVERTED): cv.boolean, cv.Optional(CONF_ON_TURN_ON): automation.validate_automation( { @@ -59,12 +55,8 @@ SWITCH_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).exte async def setup_switch_core_(var, config): - cg.add(var.set_name(config[CONF_NAME])) - cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT])) - if CONF_INTERNAL in config: - cg.add(var.set_internal(config[CONF_INTERNAL])) - if CONF_ICON in config: - cg.add(var.set_icon(config[CONF_ICON])) + await setup_entity(var, config) + if CONF_INVERTED in config: cg.add(var.set_inverted(config[CONF_INVERTED])) for conf in config.get(CONF_ON_TURN_ON, []): diff --git a/esphome/components/switch/switch.cpp b/esphome/components/switch/switch.cpp index 0e12f5af0f..e4d20719e1 100644 --- a/esphome/components/switch/switch.cpp +++ b/esphome/components/switch/switch.cpp @@ -6,17 +6,9 @@ namespace switch_ { static const char *const TAG = "switch"; -std::string Switch::icon() { return ""; } -Switch::Switch(const std::string &name) : Nameable(name), state(false) {} +Switch::Switch(const std::string &name) : EntityBase(name), state(false) {} Switch::Switch() : Switch("") {} -std::string Switch::get_icon() { - if (this->icon_.has_value()) - return *this->icon_; - return this->icon(); -} - -void Switch::set_icon(const std::string &icon) { this->icon_ = icon; } void Switch::turn_on() { ESP_LOGD(TAG, "'%s' Turning ON.", this->get_name().c_str()); this->write_state(!this->inverted_); diff --git a/esphome/components/switch/switch.h b/esphome/components/switch/switch.h index 8cfae3b6f8..071393003a 100644 --- a/esphome/components/switch/switch.h +++ b/esphome/components/switch/switch.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/entity_base.h" #include "esphome/core/preferences.h" #include "esphome/core/helpers.h" @@ -26,7 +27,7 @@ namespace switch_ { * A switch is basically just a combination of a binary sensor (for reporting switch values) * and a write_state method that writes a state to the hardware. */ -class Switch : public Nameable { +class Switch : public EntityBase { public: explicit Switch(); explicit Switch(const std::string &name); @@ -70,12 +71,6 @@ class Switch : public Nameable { */ void set_inverted(bool inverted); - /// Set the icon for this switch. "" for no icon. - void set_icon(const std::string &icon); - - /// Get the icon for this switch. Using icon() if not manually set - std::string get_icon(); - /** Set callback for state changes. * * @param callback The void(bool) callback. @@ -104,18 +99,8 @@ class Switch : public Nameable { */ virtual void write_state(bool state) = 0; - /** Override this to set the Home Assistant icon for this switch. - * - * Return "" to disable this feature. - * - * @return The icon of this switch, for example "mdi:fan". - */ - virtual std::string icon(); // NOLINT - uint32_t hash_base() override; - optional icon_{}; ///< The icon shown here. Not set means use default from switch. Empty means no icon. - CallbackManager state_callback_{}; bool inverted_{false}; Deduplicator publish_dedup_; diff --git a/esphome/components/text_sensor/__init__.py b/esphome/components/text_sensor/__init__.py index cdb4b85e9a..5c739e1d0a 100644 --- a/esphome/components/text_sensor/__init__.py +++ b/esphome/components/text_sensor/__init__.py @@ -3,21 +3,18 @@ import esphome.config_validation as cv from esphome import automation from esphome.components import mqtt from esphome.const import ( - CONF_DISABLED_BY_DEFAULT, CONF_FILTERS, - CONF_ICON, CONF_ID, - CONF_INTERNAL, CONF_ON_VALUE, CONF_ON_RAW_VALUE, CONF_TRIGGER_ID, CONF_MQTT_ID, - CONF_NAME, CONF_STATE, CONF_FROM, CONF_TO, ) from esphome.core import CORE, coroutine_with_priority +from esphome.cpp_helpers import setup_entity from esphome.util import Registry @@ -25,7 +22,7 @@ IS_PLATFORM_COMPONENT = True # pylint: disable=invalid-name text_sensor_ns = cg.esphome_ns.namespace("text_sensor") -TextSensor = text_sensor_ns.class_("TextSensor", cg.Nameable) +TextSensor = text_sensor_ns.class_("TextSensor", cg.EntityBase) TextSensorPtr = TextSensor.operator("ptr") TextSensorStateTrigger = text_sensor_ns.class_( @@ -111,10 +108,9 @@ async def substitute_filter_to_code(config, filter_id): icon = cv.icon -TEXT_SENSOR_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend( +TEXT_SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend( { cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTTextSensor), - cv.Optional(CONF_ICON): icon, cv.Optional(CONF_FILTERS): validate_filters, cv.Optional(CONF_ON_VALUE): automation.validate_automation( { @@ -137,12 +133,7 @@ async def build_filters(config): async def setup_text_sensor_core_(var, config): - cg.add(var.set_name(config[CONF_NAME])) - cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT])) - if CONF_INTERNAL in config: - cg.add(var.set_internal(config[CONF_INTERNAL])) - if CONF_ICON in config: - cg.add(var.set_icon(config[CONF_ICON])) + await setup_entity(var, config) if config.get(CONF_FILTERS): # must exist and not be empty filters = await build_filters(config[CONF_FILTERS]) diff --git a/esphome/components/text_sensor/text_sensor.cpp b/esphome/components/text_sensor/text_sensor.cpp index 774f3a8cb6..0bcab90843 100644 --- a/esphome/components/text_sensor/text_sensor.cpp +++ b/esphome/components/text_sensor/text_sensor.cpp @@ -7,7 +7,7 @@ namespace text_sensor { static const char *const TAG = "text_sensor"; TextSensor::TextSensor() : TextSensor("") {} -TextSensor::TextSensor(const std::string &name) : Nameable(name) {} +TextSensor::TextSensor(const std::string &name) : EntityBase(name) {} void TextSensor::publish_state(const std::string &state) { this->raw_state = state; @@ -68,14 +68,6 @@ void TextSensor::internal_send_state_to_frontend(const std::string &state) { this->callback_.call(state); } -void TextSensor::set_icon(const std::string &icon) { this->icon_ = icon; } -std::string TextSensor::get_icon() { - if (this->icon_.has_value()) - return *this->icon_; - return this->icon(); -} -std::string TextSensor::icon() { return ""; } - std::string TextSensor::unique_id() { return ""; } bool TextSensor::has_state() { return this->has_state_; } uint32_t TextSensor::hash_base() { return 334300109UL; } diff --git a/esphome/components/text_sensor/text_sensor.h b/esphome/components/text_sensor/text_sensor.h index 7804deedb6..4bd77131d7 100644 --- a/esphome/components/text_sensor/text_sensor.h +++ b/esphome/components/text_sensor/text_sensor.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/entity_base.h" #include "esphome/core/helpers.h" #include "esphome/components/text_sensor/filter.h" @@ -18,7 +19,7 @@ namespace text_sensor { } \ } -class TextSensor : public Nameable { +class TextSensor : public EntityBase { public: explicit TextSensor(); explicit TextSensor(const std::string &name); @@ -30,8 +31,6 @@ class TextSensor : public Nameable { void publish_state(const std::string &state); - void set_icon(const std::string &icon); - /// Add a filter to the filter chain. Will be appended to the back. void add_filter(Filter *filter); @@ -53,10 +52,6 @@ class TextSensor : public Nameable { // ========== INTERNAL METHODS ========== // (In most use cases you won't need these) - std::string get_icon(); - - virtual std::string icon(); - virtual std::string unique_id(); bool has_state(); @@ -71,7 +66,6 @@ class TextSensor : public Nameable { Filter *filter_list_{nullptr}; ///< Store all active filters. - optional icon_; bool has_state_{false}; }; diff --git a/esphome/components/total_daily_energy/sensor.py b/esphome/components/total_daily_energy/sensor.py index 46eaac98eb..6a8a416b81 100644 --- a/esphome/components/total_daily_energy/sensor.py +++ b/esphome/components/total_daily_energy/sensor.py @@ -2,12 +2,14 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, time from esphome.const import ( + CONF_ICON, CONF_ID, CONF_TIME_ID, DEVICE_CLASS_ENERGY, CONF_METHOD, STATE_CLASS_TOTAL_INCREASING, ) +from esphome.core.entity_helpers import inherit_property_from DEPENDENCIES = ["time"] @@ -45,6 +47,18 @@ CONFIG_SCHEMA = ( .extend(cv.COMPONENT_SCHEMA) ) +FINAL_VALIDATE_SCHEMA = cv.All( + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(TotalDailyEnergy), + cv.Optional(CONF_ICON): cv.icon, + cv.Required(CONF_POWER_ID): cv.use_id(sensor.Sensor), + }, + extra=cv.ALLOW_EXTRA, + ), + inherit_property_from(CONF_ICON, CONF_POWER_ID), +) + async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) diff --git a/esphome/components/total_daily_energy/total_daily_energy.h b/esphome/components/total_daily_energy/total_daily_energy.h index 9d2396d6e3..fedceafbd3 100644 --- a/esphome/components/total_daily_energy/total_daily_energy.h +++ b/esphome/components/total_daily_energy/total_daily_energy.h @@ -25,7 +25,6 @@ class TotalDailyEnergy : public sensor::Sensor, public Component { void dump_config() override; float get_setup_priority() const override { return setup_priority::DATA; } std::string unit_of_measurement() override { return this->parent_->get_unit_of_measurement() + "h"; } - std::string icon() override { return this->parent_->get_icon(); } int8_t accuracy_decimals() override { return this->parent_->get_accuracy_decimals() + 2; } void loop() override; diff --git a/esphome/components/tsl2591/tsl2591.h b/esphome/components/tsl2591/tsl2591.h index d377d082a8..19352a15c5 100644 --- a/esphome/components/tsl2591/tsl2591.h +++ b/esphome/components/tsl2591/tsl2591.h @@ -224,7 +224,7 @@ class TSL2591Component : public PollingComponent, public i2c::I2CDevice { float get_setup_priority() const override; protected: - const char *name_; // TODO: extend esphome::Nameable + const char *name_; sensor::Sensor *full_spectrum_sensor_; sensor::Sensor *infrared_sensor_; sensor::Sensor *visible_sensor_; diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index d72262b4d3..e99431be36 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -3,6 +3,7 @@ #include "web_server.h" #include "esphome/core/log.h" #include "esphome/core/application.h" +#include "esphome/core/entity_base.h" #include "esphome/core/util.h" #include "esphome/components/json/json_util.h" #include "esphome/components/network/util.h" @@ -28,8 +29,8 @@ namespace web_server { static const char *const TAG = "web_server"; -void write_row(AsyncResponseStream *stream, Nameable *obj, const std::string &klass, const std::string &action, - const std::function &action_func = nullptr) { +void write_row(AsyncResponseStream *stream, EntityBase *obj, const std::string &klass, const std::string &action, + const std::function &action_func = nullptr) { if (obj->is_internal()) return; stream->print(""); stream.print(""); diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 0d8f4f3b64..fcec74b245 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -18,6 +18,7 @@ from esphome.const import ( CONF_COMMAND_TOPIC, CONF_DISABLED_BY_DEFAULT, CONF_DISCOVERY, + CONF_ICON, CONF_ID, CONF_INTERNAL, CONF_NAME, @@ -1476,7 +1477,7 @@ class OnlyWith(Optional): pass -def _nameable_validator(config): +def _entity_base_validator(config): if CONF_NAME not in config and CONF_ID not in config: raise Invalid("At least one of 'id:' or 'name:' is required!") if CONF_NAME not in config: @@ -1587,15 +1588,16 @@ MQTT_COMMAND_COMPONENT_SCHEMA = MQTT_COMPONENT_SCHEMA.extend( } ) -NAMEABLE_SCHEMA = Schema( +ENTITY_BASE_SCHEMA = Schema( { Optional(CONF_NAME): string, Optional(CONF_INTERNAL): boolean, Optional(CONF_DISABLED_BY_DEFAULT, default=False): boolean, + Optional(CONF_ICON): icon, } ) -NAMEABLE_SCHEMA.add_extra(_nameable_validator) +ENTITY_BASE_SCHEMA.add_extra(_entity_base_validator) COMPONENT_SCHEMA = Schema({Optional(CONF_SETUP_PRIORITY): float_}) diff --git a/esphome/core/component.cpp b/esphome/core/component.cpp index c85b445b08..5692194a91 100644 --- a/esphome/core/component.cpp +++ b/esphome/core/component.cpp @@ -177,26 +177,6 @@ void PollingComponent::call_setup() { uint32_t PollingComponent::get_update_interval() const { return this->update_interval_; } void PollingComponent::set_update_interval(uint32_t update_interval) { this->update_interval_ = update_interval; } -const std::string &Nameable::get_name() const { return this->name_; } -void Nameable::set_name(const std::string &name) { - this->name_ = name; - this->calc_object_id_(); -} -Nameable::Nameable(std::string name) : name_(std::move(name)) { this->calc_object_id_(); } - -const std::string &Nameable::get_object_id() { return this->object_id_; } -bool Nameable::is_internal() const { return this->internal_; } -void Nameable::set_internal(bool internal) { this->internal_ = internal; } -void Nameable::calc_object_id_() { - this->object_id_ = sanitize_string_allowlist(to_lowercase_underscore(this->name_), HOSTNAME_CHARACTER_ALLOWLIST); - // FNV-1 hash - this->object_id_hash_ = fnv1_hash(this->object_id_); -} -uint32_t Nameable::get_object_id_hash() { return this->object_id_hash_; } - -bool Nameable::is_disabled_by_default() const { return this->disabled_by_default_; } -void Nameable::set_disabled_by_default(bool disabled_by_default) { this->disabled_by_default_ = disabled_by_default; } - WarnIfComponentBlockingGuard::WarnIfComponentBlockingGuard(Component *component) : started_(millis()), component_(component) {} WarnIfComponentBlockingGuard::~WarnIfComponentBlockingGuard() { diff --git a/esphome/core/component.h b/esphome/core/component.h index 85256c0f0f..a1afc17c2c 100644 --- a/esphome/core/component.h +++ b/esphome/core/component.h @@ -264,40 +264,6 @@ class PollingComponent : public Component { uint32_t update_interval_; }; -/// Helper class that enables naming of objects so that it doesn't have to be re-implement every time. -class Nameable { - public: - Nameable() : Nameable("") {} - explicit Nameable(std::string name); - const std::string &get_name() const; - void set_name(const std::string &name); - /// Get the sanitized name of this nameable as an ID. Caching it internally. - const std::string &get_object_id(); - uint32_t get_object_id_hash(); - - bool is_internal() const; - void set_internal(bool internal); - - /** Check if this object is declared to be disabled by default. - * - * That means that when the device gets added to Home Assistant (or other clients) it should - * not be added to the default view by default, and a user action is necessary to manually add it. - */ - bool is_disabled_by_default() const; - void set_disabled_by_default(bool disabled_by_default); - - protected: - virtual uint32_t hash_base() = 0; - - void calc_object_id_(); - - std::string name_; - std::string object_id_; - uint32_t object_id_hash_; - bool internal_{false}; - bool disabled_by_default_{false}; -}; - class WarnIfComponentBlockingGuard { public: WarnIfComponentBlockingGuard(Component *component); diff --git a/esphome/core/entity_base.cpp b/esphome/core/entity_base.cpp new file mode 100644 index 0000000000..bc94da85fe --- /dev/null +++ b/esphome/core/entity_base.cpp @@ -0,0 +1,40 @@ +#include "esphome/core/entity_base.h" +#include "esphome/core/helpers.h" + +namespace esphome { + +static const char *const TAG = "entity_base"; + +EntityBase::EntityBase(std::string name) : name_(std::move(name)) { this->calc_object_id_(); } + +// Entity Name +const std::string &EntityBase::get_name() const { return this->name_; } +void EntityBase::set_name(const std::string &name) { + this->name_ = name; + this->calc_object_id_(); +} + +// Entity Internal +bool EntityBase::is_internal() const { return this->internal_; } +void EntityBase::set_internal(bool internal) { this->internal_ = internal; } + +// Entity Disabled by Default +bool EntityBase::is_disabled_by_default() const { return this->disabled_by_default_; } +void EntityBase::set_disabled_by_default(bool disabled_by_default) { this->disabled_by_default_ = disabled_by_default; } + +// Entity Icon +const std::string &EntityBase::get_icon() const { return this->icon_; } +void EntityBase::set_icon(const std::string &name) { this->icon_ = name; } + +// Entity Object ID +const std::string &EntityBase::get_object_id() { return this->object_id_; } + +// Calculate Object ID Hash from Entity Name +void EntityBase::calc_object_id_() { + this->object_id_ = sanitize_string_allowlist(to_lowercase_underscore(this->name_), HOSTNAME_CHARACTER_ALLOWLIST); + // FNV-1 hash + this->object_id_hash_ = fnv1_hash(this->object_id_); +} +uint32_t EntityBase::get_object_id_hash() { return this->object_id_hash_; } + +} // namespace esphome diff --git a/esphome/core/entity_base.h b/esphome/core/entity_base.h new file mode 100644 index 0000000000..263747b721 --- /dev/null +++ b/esphome/core/entity_base.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include + +namespace esphome { + +// The generic Entity base class that provides an interface common to all Entities. +class EntityBase { + public: + EntityBase() : EntityBase("") {} + explicit EntityBase(std::string name); + + // Get/set the name of this Entity + const std::string &get_name() const; + void set_name(const std::string &name); + + // Get the sanitized name of this Entity as an ID. Caching it internally. + const std::string &get_object_id(); + + // Get the unique Object ID of this Entity + uint32_t get_object_id_hash(); + + // Get/set whether this Entity should be hidden from outside of ESPHome + bool is_internal() const; + void set_internal(bool internal); + + // Check if this object is declared to be disabled by default. + // That means that when the device gets added to Home Assistant (or other clients) it should + // not be added to the default view by default, and a user action is necessary to manually add it. + bool is_disabled_by_default() const; + void set_disabled_by_default(bool disabled_by_default); + + // Get/set this entity's icon + const std::string &get_icon() const; + void set_icon(const std::string &name); + + protected: + virtual uint32_t hash_base() = 0; + void calc_object_id_(); + + std::string name_; + std::string object_id_; + std::string icon_; + uint32_t object_id_hash_; + bool internal_{false}; + bool disabled_by_default_{false}; +}; + +} // namespace esphome diff --git a/esphome/core/entity_helpers.py b/esphome/core/entity_helpers.py new file mode 100644 index 0000000000..b2dbe2116e --- /dev/null +++ b/esphome/core/entity_helpers.py @@ -0,0 +1,32 @@ +import esphome.final_validate as fv + +from esphome.const import CONF_ID + + +def inherit_property_from(property_to_inherit, parent_id_property): + """Validator that inherits a configuration property from another entity, for use with FINAL_VALIDATE_SCHEMA. + + If a property is already set, it will not be inherited. + + Keyword arguments: + property_to_inherit -- the name of the property to inherit, e.g. CONF_ICON + parent_id_property -- the name of the property that holds the ID of the parent, e.g. CONF_POWER_ID + """ + + def inherit_property(config): + if property_to_inherit not in config: + fconf = fv.full_config.get() + + # Get config for the parent entity + path = fconf.get_path_for_id(config[parent_id_property])[:-1] + parent_config = fconf.get_config_for_path(path) + + # If parent sensor has the property set, inherit it + if property_to_inherit in parent_config: + path = fconf.get_path_for_id(config[CONF_ID])[:-1] + this_config = fconf.get_config_for_path(path) + this_config[property_to_inherit] = parent_config[property_to_inherit] + + return config + + return inherit_property diff --git a/esphome/cpp_helpers.py b/esphome/cpp_helpers.py index a2eafaa0e8..5b081698ad 100644 --- a/esphome/cpp_helpers.py +++ b/esphome/cpp_helpers.py @@ -1,6 +1,10 @@ import logging from esphome.const import ( + CONF_DISABLED_BY_DEFAULT, + CONF_ICON, + CONF_INTERNAL, + CONF_NAME, CONF_SETUP_PRIORITY, CONF_UPDATE_INTERVAL, CONF_TYPE_ID, @@ -90,6 +94,16 @@ async def register_parented(var, value): add(var.set_parent(paren)) +async def setup_entity(var, config): + """Set up generic properties of an Entity""" + add(var.set_name(config[CONF_NAME])) + add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT])) + if CONF_INTERNAL in config: + add(var.set_internal(config[CONF_INTERNAL])) + if CONF_ICON in config: + add(var.set_icon(config[CONF_ICON])) + + def extract_registry_entry_config(registry, full_config): # type: (Registry, ConfigType) -> RegistryEntry key, config = next((k, v) for k, v in full_config.items() if k in registry) diff --git a/esphome/cpp_types.py b/esphome/cpp_types.py index 7a8eb8e04c..888c319024 100644 --- a/esphome/cpp_types.py +++ b/esphome/cpp_types.py @@ -19,7 +19,7 @@ const_char_ptr = global_ns.namespace("const char *") NAN = global_ns.namespace("NAN") esphome_ns = global_ns # using namespace esphome; App = esphome_ns.App -Nameable = esphome_ns.class_("Nameable") +EntityBase = esphome_ns.class_("EntityBase") Component = esphome_ns.class_("Component") ComponentPtr = Component.operator("ptr") PollingComponent = esphome_ns.class_("PollingComponent", Component) diff --git a/tests/test1.yaml b/tests/test1.yaml index 400cdb3b6b..c0bfbb8f0c 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1708,6 +1708,7 @@ climate: name: Anova cooker ble_client_id: ble_blah unit_of_measurement: c + icon: mdi:stove script: - id: climate_custom @@ -1986,6 +1987,7 @@ fan: direction_output: gpio_26 - platform: speed id: fan_speed + icon: mdi:weather-windy output: pca_6 speed_count: 10 name: 'Living Room Fan 2' @@ -2287,6 +2289,7 @@ cover: name: 'Test AM43' id: am43_test ble_client_id: ble_foo + icon: mdi:blinds debug: diff --git a/tests/test5.yaml b/tests/test5.yaml index aca8434fbf..72df3ed212 100644 --- a/tests/test5.yaml +++ b/tests/test5.yaml @@ -51,6 +51,7 @@ binary_sensor: - platform: gpio pin: GPIO0 id: io0_button + icon: mdi:gesture-tap-button tlc5947: data_pin: GPIO12 diff --git a/tests/unit_tests/test_codegen.py b/tests/unit_tests/test_codegen.py index 9f402465fa..32d82b3062 100644 --- a/tests/unit_tests/test_codegen.py +++ b/tests/unit_tests/test_codegen.py @@ -59,7 +59,7 @@ from esphome import codegen as cg "NAN", "esphome_ns", "App", - "Nameable", + "EntityBase", "Component", "ComponentPtr", # from cpp_types From c3b8c84131bc4ae4d62bc44f102abb0e3e6caccc Mon Sep 17 00:00:00 2001 From: Chris Nussbaum Date: Sun, 10 Oct 2021 03:53:58 -0500 Subject: [PATCH 186/207] Fix below freezing temperature for Inkbird sensors (#2466) --- esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp b/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp index c01fc274f4..76013e28ff 100644 --- a/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp +++ b/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp @@ -72,7 +72,7 @@ bool InkbirdIbstH1Mini::parse_device(const esp32_ble_tracker::ESPBTDevice &devic auto external_temperature = NAN; // Read bluetooth data into variable - auto measured_temperature = mnf_data.uuid.get_uuid().uuid.uuid16 / 100.0f; + auto measured_temperature = ((int16_t) mnf_data.uuid.get_uuid().uuid.uuid16) / 100.0f; // Set temperature or external_temperature based on which sensor is in use if (mnf_data.data[2] == 0) { From a1f9b0d7f2f746633e51881ba694522ab00bd568 Mon Sep 17 00:00:00 2001 From: definitio <37266727+definitio@users.noreply.github.com> Date: Sun, 10 Oct 2021 18:54:07 +0300 Subject: [PATCH 187/207] Add configuration for cover topics (#2472) --- esphome/components/cover/__init__.py | 32 +++++++++++++++++++++++++++- esphome/const.py | 4 ++++ tests/test1.yaml | 6 ++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/esphome/components/cover/__init__.py b/esphome/components/cover/__init__.py index eb57637283..137aaac872 100644 --- a/esphome/components/cover/__init__.py +++ b/esphome/components/cover/__init__.py @@ -8,7 +8,11 @@ from esphome.const import ( CONF_DEVICE_CLASS, CONF_STATE, CONF_POSITION, + CONF_POSITION_COMMAND_TOPIC, + CONF_POSITION_STATE_TOPIC, CONF_TILT, + CONF_TILT_COMMAND_TOPIC, + CONF_TILT_STATE_TOPIC, CONF_STOP, CONF_MQTT_ID, ) @@ -68,7 +72,18 @@ COVER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).ex cv.GenerateID(): cv.declare_id(Cover), cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTCoverComponent), cv.Optional(CONF_DEVICE_CLASS): cv.one_of(*DEVICE_CLASSES, lower=True), - # TODO: MQTT topic options + cv.Optional(CONF_POSITION_COMMAND_TOPIC): cv.All( + cv.requires_component("mqtt"), cv.subscribe_topic + ), + cv.Optional(CONF_POSITION_STATE_TOPIC): cv.All( + cv.requires_component("mqtt"), cv.subscribe_topic + ), + cv.Optional(CONF_TILT_COMMAND_TOPIC): cv.All( + cv.requires_component("mqtt"), cv.subscribe_topic + ), + cv.Optional(CONF_TILT_STATE_TOPIC): cv.All( + cv.requires_component("mqtt"), cv.subscribe_topic + ), } ) @@ -83,6 +98,21 @@ async def setup_cover_core_(var, config): mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var) await mqtt.register_mqtt_component(mqtt_, config) + if CONF_POSITION_STATE_TOPIC in config: + cg.add( + mqtt_.set_custom_position_state_topic(config[CONF_POSITION_STATE_TOPIC]) + ) + if CONF_POSITION_COMMAND_TOPIC in config: + cg.add( + mqtt_.set_custom_position_command_topic( + config[CONF_POSITION_COMMAND_TOPIC] + ) + ) + if CONF_TILT_STATE_TOPIC in config: + cg.add(mqtt_.set_custom_tilt_state_topic(config[CONF_TILT_STATE_TOPIC])) + if CONF_TILT_COMMAND_TOPIC in config: + cg.add(mqtt_.set_custom_tilt_command_topic(config[CONF_TILT_COMMAND_TOPIC])) + async def register_cover(var, config): if not CORE.has_id(config[CONF_ID]): diff --git a/esphome/const.py b/esphome/const.py index a65285a4b5..ac9e759ffd 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -497,6 +497,8 @@ CONF_PMC_4_0 = "pmc_4_0" CONF_PORT = "port" CONF_POSITION = "position" CONF_POSITION_ACTION = "position_action" +CONF_POSITION_COMMAND_TOPIC = "position_command_topic" +CONF_POSITION_STATE_TOPIC = "position_state_topic" CONF_POWER = "power" CONF_POWER_FACTOR = "power_factor" CONF_POWER_ON_VALUE = "power_on_value" @@ -654,7 +656,9 @@ CONF_THRESHOLD = "threshold" CONF_THROTTLE = "throttle" CONF_TILT = "tilt" CONF_TILT_ACTION = "tilt_action" +CONF_TILT_COMMAND_TOPIC = "tilt_command_topic" CONF_TILT_LAMBDA = "tilt_lambda" +CONF_TILT_STATE_TOPIC = "tilt_state_topic" CONF_TIME = "time" CONF_TIME_ID = "time_id" CONF_TIMEOUT = "timeout" diff --git a/tests/test1.yaml b/tests/test1.yaml index c0bfbb8f0c..537ef6b1c4 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -2285,6 +2285,12 @@ cover: id: template_cover state: CLOSED assumed_state: no + has_position: yes + position_state_topic: position/state/topic + position_command_topic: position/command/topic + tilt_lambda: !lambda 'return 0.5;' + tilt_state_topic: tilt/state/topic + tilt_command_topic: tilt/command/topic - platform: am43 name: 'Test AM43' id: am43_test From 42739f0b22d02b97b6545d77e8731303447457ab Mon Sep 17 00:00:00 2001 From: definitio <37266727+definitio@users.noreply.github.com> Date: Sun, 10 Oct 2021 18:55:22 +0300 Subject: [PATCH 188/207] Add configuration for climate topics (#2473) --- esphome/components/climate/__init__.py | 145 ++++++++++++++++++++++++- esphome/const.py | 16 +++ tests/test1.yaml | 16 +++ 3 files changed, 174 insertions(+), 3 deletions(-) diff --git a/esphome/components/climate/__init__.py b/esphome/components/climate/__init__.py index ca1ea6a756..7ff769e5cb 100644 --- a/esphome/components/climate/__init__.py +++ b/esphome/components/climate/__init__.py @@ -4,22 +4,38 @@ from esphome.cpp_helpers import setup_entity from esphome import automation from esphome.components import mqtt from esphome.const import ( + CONF_ACTION_STATE_TOPIC, CONF_AWAY, + CONF_AWAY_COMMAND_TOPIC, + CONF_AWAY_STATE_TOPIC, + CONF_CURRENT_TEMPERATURE_STATE_TOPIC, CONF_CUSTOM_FAN_MODE, CONF_CUSTOM_PRESET, + CONF_FAN_MODE, + CONF_FAN_MODE_COMMAND_TOPIC, + CONF_FAN_MODE_STATE_TOPIC, CONF_ID, CONF_MAX_TEMPERATURE, CONF_MIN_TEMPERATURE, CONF_MODE, + CONF_MODE_COMMAND_TOPIC, + CONF_MODE_STATE_TOPIC, CONF_PRESET, + CONF_SWING_MODE, + CONF_SWING_MODE_COMMAND_TOPIC, + CONF_SWING_MODE_STATE_TOPIC, CONF_TARGET_TEMPERATURE, + CONF_TARGET_TEMPERATURE_COMMAND_TOPIC, + CONF_TARGET_TEMPERATURE_STATE_TOPIC, CONF_TARGET_TEMPERATURE_HIGH, + CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC, + CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC, CONF_TARGET_TEMPERATURE_LOW, + CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC, + CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC, CONF_TEMPERATURE_STEP, CONF_VISUAL, CONF_MQTT_ID, - CONF_FAN_MODE, - CONF_SWING_MODE, ) from esphome.core import CORE, coroutine_with_priority @@ -97,7 +113,54 @@ CLIMATE_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA). cv.Optional(CONF_TEMPERATURE_STEP): cv.temperature, } ), - # TODO: MQTT topic options + cv.Optional(CONF_ACTION_STATE_TOPIC): cv.All( + cv.requires_component("mqtt"), cv.publish_topic + ), + cv.Optional(CONF_AWAY_COMMAND_TOPIC): cv.All( + cv.requires_component("mqtt"), cv.publish_topic + ), + cv.Optional(CONF_AWAY_STATE_TOPIC): cv.All( + cv.requires_component("mqtt"), cv.publish_topic + ), + cv.Optional(CONF_CURRENT_TEMPERATURE_STATE_TOPIC): cv.All( + cv.requires_component("mqtt"), cv.publish_topic + ), + cv.Optional(CONF_FAN_MODE_COMMAND_TOPIC): cv.All( + cv.requires_component("mqtt"), cv.publish_topic + ), + cv.Optional(CONF_FAN_MODE_STATE_TOPIC): cv.All( + cv.requires_component("mqtt"), cv.publish_topic + ), + cv.Optional(CONF_MODE_COMMAND_TOPIC): cv.All( + cv.requires_component("mqtt"), cv.publish_topic + ), + cv.Optional(CONF_MODE_STATE_TOPIC): cv.All( + cv.requires_component("mqtt"), cv.publish_topic + ), + cv.Optional(CONF_SWING_MODE_COMMAND_TOPIC): cv.All( + cv.requires_component("mqtt"), cv.publish_topic + ), + cv.Optional(CONF_SWING_MODE_STATE_TOPIC): cv.All( + cv.requires_component("mqtt"), cv.publish_topic + ), + cv.Optional(CONF_TARGET_TEMPERATURE_COMMAND_TOPIC): cv.All( + cv.requires_component("mqtt"), cv.publish_topic + ), + cv.Optional(CONF_TARGET_TEMPERATURE_STATE_TOPIC): cv.All( + cv.requires_component("mqtt"), cv.publish_topic + ), + cv.Optional(CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC): cv.All( + cv.requires_component("mqtt"), cv.publish_topic + ), + cv.Optional(CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC): cv.All( + cv.requires_component("mqtt"), cv.publish_topic + ), + cv.Optional(CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC): cv.All( + cv.requires_component("mqtt"), cv.publish_topic + ), + cv.Optional(CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC): cv.All( + cv.requires_component("mqtt"), cv.publish_topic + ), } ) @@ -117,6 +180,82 @@ async def setup_climate_core_(var, config): mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var) await mqtt.register_mqtt_component(mqtt_, config) + if CONF_ACTION_STATE_TOPIC in config: + cg.add(mqtt_.set_custom_action_state_topic(config[CONF_ACTION_STATE_TOPIC])) + if CONF_AWAY_COMMAND_TOPIC in config: + cg.add(mqtt_.set_custom_away_command_topic(config[CONF_AWAY_COMMAND_TOPIC])) + if CONF_AWAY_STATE_TOPIC in config: + cg.add(mqtt_.set_custom_away_state_topic(config[CONF_AWAY_STATE_TOPIC])) + if CONF_CURRENT_TEMPERATURE_STATE_TOPIC in config: + cg.add( + mqtt_.set_custom_current_temperature_state_topic( + config[CONF_CURRENT_TEMPERATURE_STATE_TOPIC] + ) + ) + if CONF_FAN_MODE_COMMAND_TOPIC in config: + cg.add( + mqtt_.set_custom_fan_mode_command_topic( + config[CONF_FAN_MODE_COMMAND_TOPIC] + ) + ) + if CONF_FAN_MODE_STATE_TOPIC in config: + cg.add( + mqtt_.set_custom_fan_mode_state_topic(config[CONF_FAN_MODE_STATE_TOPIC]) + ) + if CONF_MODE_COMMAND_TOPIC in config: + cg.add(mqtt_.set_custom_mode_command_topic(config[CONF_MODE_COMMAND_TOPIC])) + if CONF_MODE_STATE_TOPIC in config: + cg.add(mqtt_.set_custom_state_topic(config[CONF_MODE_STATE_TOPIC])) + + if CONF_SWING_MODE_COMMAND_TOPIC in config: + cg.add( + mqtt_.set_custom_swing_mode_command_topic( + config[CONF_SWING_MODE_COMMAND_TOPIC] + ) + ) + if CONF_SWING_MODE_STATE_TOPIC in config: + cg.add( + mqtt_.set_custom_swing_mode_state_topic( + config[CONF_SWING_MODE_STATE_TOPIC] + ) + ) + if CONF_TARGET_TEMPERATURE_COMMAND_TOPIC in config: + cg.add( + mqtt_.set_custom_target_temperature_command_topic( + config[CONF_TARGET_TEMPERATURE_COMMAND_TOPIC] + ) + ) + if CONF_TARGET_TEMPERATURE_STATE_TOPIC in config: + cg.add( + mqtt_.set_custom_target_temperature_state_topic( + config[CONF_TARGET_TEMPERATURE_STATE_TOPIC] + ) + ) + if CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC in config: + cg.add( + mqtt_.set_custom_target_temperature_high_command_topic( + config[CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC] + ) + ) + if CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC in config: + cg.add( + mqtt_.set_custom_target_temperature_high_state_topic( + config[CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC] + ) + ) + if CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC in config: + cg.add( + mqtt_.set_custom_target_temperature_low_command_topic( + config[CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC] + ) + ) + if CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC in config: + cg.add( + mqtt_.set_custom_target_temperature_state_topic( + config[CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC] + ) + ) + async def register_climate(var, config): if not CORE.has_id(config[CONF_ID]): diff --git a/esphome/const.py b/esphome/const.py index ac9e759ffd..780f4f6e22 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -39,6 +39,7 @@ CONF_ACCELERATION_Z = "acceleration_z" CONF_ACCURACY = "accuracy" CONF_ACCURACY_DECIMALS = "accuracy_decimals" CONF_ACTION_ID = "action_id" +CONF_ACTION_STATE_TOPIC = "action_state_topic" CONF_ACTIVE_POWER = "active_power" CONF_ADDRESS = "address" CONF_ADDRESSABLE_LIGHT_ID = "addressable_light_id" @@ -60,7 +61,9 @@ CONF_AUTOCONF = "autoconf" CONF_AUTOMATION_ID = "automation_id" CONF_AVAILABILITY = "availability" CONF_AWAY = "away" +CONF_AWAY_COMMAND_TOPIC = "away_command_topic" CONF_AWAY_CONFIG = "away_config" +CONF_AWAY_STATE_TOPIC = "away_state_topic" CONF_BACKLIGHT_PIN = "backlight_pin" CONF_BASELINE = "baseline" CONF_BATTERY_LEVEL = "battery_level" @@ -141,6 +144,7 @@ CONF_CSS_URL = "css_url" CONF_CURRENT = "current" CONF_CURRENT_OPERATION = "current_operation" CONF_CURRENT_RESISTOR = "current_resistor" +CONF_CURRENT_TEMPERATURE_STATE_TOPIC = "current_temperature_state_topic" CONF_CUSTOM_FAN_MODE = "custom_fan_mode" CONF_CUSTOM_FAN_MODES = "custom_fan_modes" CONF_CUSTOM_PRESET = "custom_preset" @@ -209,6 +213,7 @@ CONF_FALLING_EDGE = "falling_edge" CONF_FAMILY = "family" CONF_FAN_MODE = "fan_mode" CONF_FAN_MODE_AUTO_ACTION = "fan_mode_auto_action" +CONF_FAN_MODE_COMMAND_TOPIC = "fan_mode_command_topic" CONF_FAN_MODE_DIFFUSE_ACTION = "fan_mode_diffuse_action" CONF_FAN_MODE_FOCUS_ACTION = "fan_mode_focus_action" CONF_FAN_MODE_HIGH_ACTION = "fan_mode_high_action" @@ -217,6 +222,7 @@ CONF_FAN_MODE_MEDIUM_ACTION = "fan_mode_medium_action" CONF_FAN_MODE_MIDDLE_ACTION = "fan_mode_middle_action" CONF_FAN_MODE_OFF_ACTION = "fan_mode_off_action" CONF_FAN_MODE_ON_ACTION = "fan_mode_on_action" +CONF_FAN_MODE_STATE_TOPIC = "fan_mode_state_topic" CONF_FAN_ONLY_ACTION = "fan_only_action" CONF_FAN_ONLY_ACTION_USES_FAN_MODE_TIMER = "fan_only_action_uses_fan_mode_timer" CONF_FAN_ONLY_COOLING = "fan_only_cooling" @@ -378,6 +384,8 @@ CONF_MINUTE = "minute" CONF_MINUTES = "minutes" CONF_MISO_PIN = "miso_pin" CONF_MODE = "mode" +CONF_MODE_COMMAND_TOPIC = "mode_command_topic" +CONF_MODE_STATE_TOPIC = "mode_state_topic" CONF_MODEL = "model" CONF_MOISTURE = "moisture" CONF_MONTHS = "months" @@ -636,6 +644,8 @@ CONF_SUPPORTS_HEAT = "supports_heat" CONF_SWING_BOTH_ACTION = "swing_both_action" CONF_SWING_HORIZONTAL_ACTION = "swing_horizontal_action" CONF_SWING_MODE = "swing_mode" +CONF_SWING_MODE_COMMAND_TOPIC = "swing_mode_command_topic" +CONF_SWING_MODE_STATE_TOPIC = "swing_mode_state_topic" CONF_SWING_OFF_ACTION = "swing_off_action" CONF_SWING_VERTICAL_ACTION = "swing_vertical_action" CONF_SWITCH_DATAPOINT = "switch_datapoint" @@ -646,8 +656,14 @@ CONF_TAG = "tag" CONF_TARGET = "target" CONF_TARGET_TEMPERATURE = "target_temperature" CONF_TARGET_TEMPERATURE_CHANGE_ACTION = "target_temperature_change_action" +CONF_TARGET_TEMPERATURE_COMMAND_TOPIC = "target_temperature_command_topic" CONF_TARGET_TEMPERATURE_HIGH = "target_temperature_high" +CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC = "target_temperature_high_command_topic" +CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC = "target_temperature_high_state_topic" CONF_TARGET_TEMPERATURE_LOW = "target_temperature_low" +CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC = "target_temperature_low_command_topic" +CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC = "target_temperature_low_state_topic" +CONF_TARGET_TEMPERATURE_STATE_TOPIC = "target_temperature_state_topic" CONF_TEMPERATURE = "temperature" CONF_TEMPERATURE_STEP = "temperature_step" CONF_TEXT_SENSORS = "text_sensors" diff --git a/tests/test1.yaml b/tests/test1.yaml index 537ef6b1c4..058da35d2c 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1642,6 +1642,22 @@ climate: sensor: ${sensorname}_sensor - platform: tcl112 name: TCL112 Climate + action_state_topic: action/state/topic + away_command_topic: away/command/topic + away_state_topic: away/state/topic + current_temperature_state_topic: current/temperature/state/topic + fan_mode_command_topic: fan_mode/mode/command/topic + fan_mode_state_topic: fan_mode/mode/state/topic + mode_command_topic: mode/command/topic + mode_state_topic: mode/state/topic + swing_mode_command_topic: swing_mode/command/topic + swing_mode_state_topic: swing_mode/state/topic + target_temperature_command_topic: target/temperature/command/topic + target_temperature_high_command_topic: target/temperature/high/command/topic + target_temperature_high_state_topic: target/temperature/high/state/topic + target_temperature_low_command_topic: target/temperature/low/command/topic + target_temperature_low_state_topic: target/temperature/low/state/topic + target_temperature_state_topic: target/temperature/state/topic - platform: coolix name: Coolix Climate With Sensor supports_heat: True From 2c517e3e8ccf727f9e4ae948dcec38bd5c1dc46f Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 11 Oct 2021 10:38:45 +1300 Subject: [PATCH 189/207] Use arduino btStart for arduino framework (#2457) --- esphome/components/esp32_ble/ble.cpp | 7 +++++++ .../components/esp32_ble_beacon/esp32_ble_beacon.cpp | 11 +++++++++++ .../esp32_ble_tracker/esp32_ble_tracker.cpp | 7 +++++++ 3 files changed, 25 insertions(+) diff --git a/esphome/components/esp32_ble/ble.cpp b/esphome/components/esp32_ble/ble.cpp index 143be06e3b..ecd591d169 100644 --- a/esphome/components/esp32_ble/ble.cpp +++ b/esphome/components/esp32_ble/ble.cpp @@ -56,6 +56,12 @@ bool ESP32BLE::ble_setup_() { return false; } +#ifdef USE_ARDUINO + if (!btStart()) { + ESP_LOGE(TAG, "btStart failed: %d", esp_bt_controller_get_status()); + return false; + } +#else if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) { // start bt controller if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) { @@ -80,6 +86,7 @@ bool ESP32BLE::ble_setup_() { return false; } } +#endif esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); diff --git a/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp b/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp index 96afadd19a..f6bab8e6df 100644 --- a/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp +++ b/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp @@ -12,6 +12,10 @@ #include #include "esphome/core/hal.h" +#ifdef USE_ARDUINO +#include +#endif + namespace esphome { namespace esp32_ble_beacon { @@ -70,6 +74,12 @@ void ESP32BLEBeacon::ble_setup() { return; } +#ifdef USE_ARDUINO + if (!btStart()) { + ESP_LOGE(TAG, "btStart failed: %d", esp_bt_controller_get_status()); + return; + } +#else if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) { // start bt controller if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) { @@ -94,6 +104,7 @@ void ESP32BLEBeacon::ble_setup() { return; } } +#endif esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index 9e987a994a..95176bb179 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -132,6 +132,12 @@ bool ESP32BLETracker::ble_setup() { return false; } +#ifdef USE_ARDUINO + if (!btStart()) { + ESP_LOGE(TAG, "btStart failed: %d", esp_bt_controller_get_status()); + return false; + } +#else if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) { // start bt controller if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) { @@ -156,6 +162,7 @@ bool ESP32BLETracker::ble_setup() { return false; } } +#endif esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); From 11d286675510ed01430cd75938dc8c6d34bf4dc1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 16:45:27 +0200 Subject: [PATCH 190/207] Bump click from 8.0.1 to 8.0.3 (#2481) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 880faeddbe..3912f099df 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ tzdata>=2021.1 # from time pyserial==3.5 platformio==5.2.0 esptool==3.1 -click==8.0.1 +click==8.0.3 esphome-dashboard==20211006.0 aioesphomeapi==9.1.5 From 3cb4b4ca039914c53b8b2c8f26c0a17b5eeb2a1c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 16:52:42 +0200 Subject: [PATCH 191/207] Bump flake8 from 3.9.2 to 4.0.1 (#2483) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index 85456643bc..8ebcf24d4d 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,5 +1,5 @@ pylint==2.11.1 -flake8==3.9.2 +flake8==4.0.1 black==21.9b0 pexpect==4.8.0 pre-commit From 55e9560e7400f43015de29cbd74015e5c2619111 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 16:58:51 +0200 Subject: [PATCH 192/207] Bump platformio from 5.2.0 to 5.2.1 (#2482) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 3912f099df..4e32753ba3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ tornado==6.1 tzlocal==3.0 # from time tzdata>=2021.1 # from time pyserial==3.5 -platformio==5.2.0 +platformio==5.2.1 esptool==3.1 click==8.0.3 esphome-dashboard==20211006.0 From ea56a39e11aaaef20a2bdf1e48a8931330651f26 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Oct 2021 17:21:04 +0200 Subject: [PATCH 193/207] Bump esphome-dashboard from 20211006.0 to 20211011.1 (#2484) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4e32753ba3..23a00d3755 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ pyserial==3.5 platformio==5.2.1 esptool==3.1 click==8.0.3 -esphome-dashboard==20211006.0 +esphome-dashboard==20211011.1 aioesphomeapi==9.1.5 # esp-idf requires this, but doesn't bundle it by default From 039fbc677de4d4d7625413f22b06cdc01ffb270a Mon Sep 17 00:00:00 2001 From: Dave T <17680170+davet2001@users.noreply.github.com> Date: Mon, 11 Oct 2021 22:44:05 +0100 Subject: [PATCH 194/207] Replace deprecated COLOR_BLACK constant (#2487) --- esphome/components/ili9341/ili9341_display.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/ili9341/ili9341_display.cpp b/esphome/components/ili9341/ili9341_display.cpp index 88f8bac272..ab5586fa28 100644 --- a/esphome/components/ili9341/ili9341_display.cpp +++ b/esphome/components/ili9341/ili9341_display.cpp @@ -142,7 +142,7 @@ void ILI9341Display::fill(Color color) { } void ILI9341Display::fill_internal_(Color color) { - if (color.raw_32 == COLOR_BLACK.raw_32) { + if (color.raw_32 == Color::BLACK.raw_32) { memset(transfer_buffer_, 0, sizeof(transfer_buffer_)); } else { uint8_t *dst = transfer_buffer_; From 85461a752acb7075ee9a4dc973bb31473f95ad7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerm=C3=A1k?= Date: Mon, 11 Oct 2021 23:56:35 +0200 Subject: [PATCH 195/207] Fix color temperature persistence on CWWW lights (#2486) --- esphome/components/light/light_call.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/light/light_call.cpp b/esphome/components/light/light_call.cpp index dc1e7d39fb..9858590850 100644 --- a/esphome/components/light/light_call.cpp +++ b/esphome/components/light/light_call.cpp @@ -532,7 +532,8 @@ LightCall &LightCall::set_white_if_supported(float white) { return *this; } LightCall &LightCall::set_color_temperature_if_supported(float color_temperature) { - if (this->get_active_color_mode_() & ColorCapability::COLOR_TEMPERATURE) + if (this->get_active_color_mode_() & ColorCapability::COLOR_TEMPERATURE || + this->get_active_color_mode_() & ColorCapability::COLD_WARM_WHITE) this->set_color_temperature(color_temperature); return *this; } From d7ad1558856a9e795117e038d21b5616d9d1d86a Mon Sep 17 00:00:00 2001 From: niklasweber Date: Tue, 12 Oct 2021 00:11:04 +0200 Subject: [PATCH 196/207] Fix reset on http_request without network connection (#2474) * Fix reset problem when http_request is sent without network connection (#2501) * Fix format --- esphome/components/http_request/http_request.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/esphome/components/http_request/http_request.cpp b/esphome/components/http_request/http_request.cpp index f88ee19e5c..309977a915 100644 --- a/esphome/components/http_request/http_request.cpp +++ b/esphome/components/http_request/http_request.cpp @@ -3,6 +3,7 @@ #include "http_request.h" #include "esphome/core/macros.h" #include "esphome/core/log.h" +#include "esphome/components/network/util.h" namespace esphome { namespace http_request { @@ -28,6 +29,13 @@ void HttpRequestComponent::set_url(std::string url) { } void HttpRequestComponent::send(const std::vector &response_triggers) { + if (!network::is_connected()) { + this->client_.end(); + this->status_set_warning(); + ESP_LOGW(TAG, "HTTP Request failed; Not connected to network"); + return; + } + bool begin_status = false; const String url = this->url_.c_str(); #ifdef USE_ESP32 From 04ec1c8b56d49353865f8eb8f616c40d4ee598f0 Mon Sep 17 00:00:00 2001 From: Dave T <17680170+davet2001@users.noreply.github.com> Date: Mon, 11 Oct 2021 23:14:04 +0100 Subject: [PATCH 197/207] Consolidate CONF_RAW_DATA_ID to const.py (#2491) --- esphome/components/animation/__init__.py | 4 +--- esphome/components/font/__init__.py | 3 +-- esphome/components/image/__init__.py | 11 ++++++++--- esphome/const.py | 1 + 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/esphome/components/animation/__init__.py b/esphome/components/animation/__init__.py index 3ae3aa94f9..3f03e5c185 100644 --- a/esphome/components/animation/__init__.py +++ b/esphome/components/animation/__init__.py @@ -5,7 +5,7 @@ from esphome.components import display, font import esphome.components.image as espImage import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_FILE, CONF_ID, CONF_TYPE, CONF_RESIZE +from esphome.const import CONF_FILE, CONF_ID, CONF_RAW_DATA_ID, CONF_RESIZE, CONF_TYPE from esphome.core import CORE, HexInt _LOGGER = logging.getLogger(__name__) @@ -15,8 +15,6 @@ MULTI_CONF = True Animation_ = display.display_ns.class_("Animation") -CONF_RAW_DATA_ID = "raw_data_id" - ANIMATION_SCHEMA = cv.Schema( { cv.Required(CONF_ID): cv.declare_id(Animation_), diff --git a/esphome/components/font/__init__.py b/esphome/components/font/__init__.py index 7225bf5bb9..6af5be45d4 100644 --- a/esphome/components/font/__init__.py +++ b/esphome/components/font/__init__.py @@ -4,7 +4,7 @@ from esphome import core from esphome.components import display import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_FILE, CONF_GLYPHS, CONF_ID, CONF_SIZE +from esphome.const import CONF_FILE, CONF_GLYPHS, CONF_ID, CONF_RAW_DATA_ID, CONF_SIZE from esphome.core import CORE, HexInt DEPENDENCIES = ["display"] @@ -74,7 +74,6 @@ def validate_truetype_file(value): DEFAULT_GLYPHS = ( ' !"%()+=,-.:/0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz°' ) -CONF_RAW_DATA_ID = "raw_data_id" CONF_RAW_GLYPH_ID = "raw_glyph_id" FONT_SCHEMA = cv.Schema( diff --git a/esphome/components/image/__init__.py b/esphome/components/image/__init__.py index b946a86bc4..a721263dff 100644 --- a/esphome/components/image/__init__.py +++ b/esphome/components/image/__init__.py @@ -4,7 +4,14 @@ from esphome import core from esphome.components import display, font import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_FILE, CONF_ID, CONF_TYPE, CONF_RESIZE, CONF_DITHER +from esphome.const import ( + CONF_DITHER, + CONF_FILE, + CONF_ID, + CONF_RAW_DATA_ID, + CONF_RESIZE, + CONF_TYPE, +) from esphome.core import CORE, HexInt _LOGGER = logging.getLogger(__name__) @@ -21,8 +28,6 @@ IMAGE_TYPE = { Image_ = display.display_ns.class_("Image") -CONF_RAW_DATA_ID = "raw_data_id" - IMAGE_SCHEMA = cv.Schema( { cv.Required(CONF_ID): cv.declare_id(Image_), diff --git a/esphome/const.py b/esphome/const.py index 780f4f6e22..a9eec3e249 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -533,6 +533,7 @@ CONF_RANGE_FROM = "range_from" CONF_RANGE_TO = "range_to" CONF_RATE = "rate" CONF_RAW = "raw" +CONF_RAW_DATA_ID = "raw_data_id" CONF_RC_CODE_1 = "rc_code_1" CONF_RC_CODE_2 = "rc_code_2" CONF_REACTIVE_POWER = "reactive_power" From 6a5eb43454dcbbcf8d41338fc2a526f77373b6e2 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 12 Oct 2021 11:56:47 +1300 Subject: [PATCH 198/207] Update Airthings BLE (#2453) --- .../airthings_wave_mini.cpp | 19 ++----- .../airthings_wave_mini/airthings_wave_mini.h | 16 +++--- .../components/airthings_wave_mini/sensor.py | 8 +-- .../airthings_wave_plus.cpp | 19 ++----- .../airthings_wave_plus/airthings_wave_plus.h | 16 +++--- .../components/airthings_wave_plus/sensor.py | 8 +-- .../esp32_ble_tracker/esp32_ble_tracker.cpp | 57 +++++++++++++++++++ .../esp32_ble_tracker/esp32_ble_tracker.h | 2 + 8 files changed, 89 insertions(+), 56 deletions(-) diff --git a/esphome/components/airthings_wave_mini/airthings_wave_mini.cpp b/esphome/components/airthings_wave_mini/airthings_wave_mini.cpp index 0ab0e65148..6b6418f7e6 100644 --- a/esphome/components/airthings_wave_mini/airthings_wave_mini.cpp +++ b/esphome/components/airthings_wave_mini/airthings_wave_mini.cpp @@ -1,6 +1,6 @@ #include "airthings_wave_mini.h" -#ifdef USE_ESP32_FRAMEWORK_ARDUINO +#ifdef USE_ESP32 namespace esphome { namespace airthings_wave_mini { @@ -75,8 +75,6 @@ void AirthingsWaveMini::read_sensors_(uint8_t *raw_value, uint16_t value_len) { bool AirthingsWaveMini::is_valid_voc_value_(uint16_t voc) { return 0 <= voc && voc <= 16383; } -void AirthingsWaveMini::loop() {} - void AirthingsWaveMini::update() { if (this->node_state != esp32_ble_tracker::ClientState::ESTABLISHED) { if (!parent()->enabled) { @@ -104,17 +102,12 @@ void AirthingsWaveMini::dump_config() { LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_); } -AirthingsWaveMini::AirthingsWaveMini() : PollingComponent(10000) { - auto service_bt = *BLEUUID::fromString(std::string("b42e3882-ade7-11e4-89d3-123b93f75cba")).getNative(); - auto characteristic_bt = *BLEUUID::fromString(std::string("b42e3b98-ade7-11e4-89d3-123b93f75cba")).getNative(); - - service_uuid_ = esp32_ble_tracker::ESPBTUUID::from_uuid(service_bt); - sensors_data_characteristic_uuid_ = esp32_ble_tracker::ESPBTUUID::from_uuid(characteristic_bt); -} - -void AirthingsWaveMini::setup() {} +AirthingsWaveMini::AirthingsWaveMini() + : PollingComponent(10000), + service_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(SERVICE_UUID)), + sensors_data_characteristic_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(CHARACTERISTIC_UUID)) {} } // namespace airthings_wave_mini } // namespace esphome -#endif // USE_ESP32_FRAMEWORK_ARDUINO +#endif // USE_ESP32 diff --git a/esphome/components/airthings_wave_mini/airthings_wave_mini.h b/esphome/components/airthings_wave_mini/airthings_wave_mini.h index 5d1964d559..128774f9cb 100644 --- a/esphome/components/airthings_wave_mini/airthings_wave_mini.h +++ b/esphome/components/airthings_wave_mini/airthings_wave_mini.h @@ -1,28 +1,28 @@ #pragma once -#ifdef USE_ESP32_FRAMEWORK_ARDUINO +#ifdef USE_ESP32 +#include #include #include -#include -#include -#include "esphome/core/component.h" -#include "esphome/core/log.h" #include "esphome/components/ble_client/ble_client.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/sensor/sensor.h" +#include "esphome/core/component.h" +#include "esphome/core/log.h" namespace esphome { namespace airthings_wave_mini { +static const char *const SERVICE_UUID = "b42e3882-ade7-11e4-89d3-123b93f75cba"; +static const char *const CHARACTERISTIC_UUID = "b42e3b98-ade7-11e4-89d3-123b93f75cba"; + class AirthingsWaveMini : public PollingComponent, public ble_client::BLEClientNode { public: AirthingsWaveMini(); - void setup() override; void dump_config() override; void update() override; - void loop() override; void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) override; @@ -62,4 +62,4 @@ class AirthingsWaveMini : public PollingComponent, public ble_client::BLEClientN } // namespace airthings_wave_mini } // namespace esphome -#endif // USE_ESP32_FRAMEWORK_ARDUINO +#endif // USE_ESP32 diff --git a/esphome/components/airthings_wave_mini/sensor.py b/esphome/components/airthings_wave_mini/sensor.py index 6a32cd8771..d38354fa84 100644 --- a/esphome/components/airthings_wave_mini/sensor.py +++ b/esphome/components/airthings_wave_mini/sensor.py @@ -1,7 +1,6 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, ble_client -from esphome.core import CORE from esphome.const import ( DEVICE_CLASS_HUMIDITY, @@ -58,10 +57,8 @@ CONFIG_SCHEMA = cv.All( ), } ) - .extend(cv.polling_component_schema("5mins")) + .extend(cv.polling_component_schema("5min")) .extend(ble_client.BLE_CLIENT_SCHEMA), - # Until BLEUUID reference removed - cv.only_with_arduino, ) @@ -83,6 +80,3 @@ async def to_code(config): if CONF_TVOC in config: sens = await sensor.new_sensor(config[CONF_TVOC]) cg.add(var.set_tvoc(sens)) - - if CORE.is_esp32: - cg.add_library("ESP32 BLE Arduino", None) diff --git a/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp b/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp index 0eaffbd889..79f2cb7741 100644 --- a/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp +++ b/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp @@ -1,6 +1,6 @@ #include "airthings_wave_plus.h" -#ifdef USE_ESP32_FRAMEWORK_ARDUINO +#ifdef USE_ESP32 namespace esphome { namespace airthings_wave_plus { @@ -96,8 +96,6 @@ bool AirthingsWavePlus::is_valid_voc_value_(uint16_t voc) { return 0 <= voc && v bool AirthingsWavePlus::is_valid_co2_value_(uint16_t co2) { return 0 <= co2 && co2 <= 16383; } -void AirthingsWavePlus::loop() {} - void AirthingsWavePlus::update() { if (this->node_state != esp32_ble_tracker::ClientState::ESTABLISHED) { if (!parent()->enabled) { @@ -128,17 +126,12 @@ void AirthingsWavePlus::dump_config() { LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_); } -AirthingsWavePlus::AirthingsWavePlus() : PollingComponent(10000) { - auto service_bt = *BLEUUID::fromString(std::string("b42e1c08-ade7-11e4-89d3-123b93f75cba")).getNative(); - auto characteristic_bt = *BLEUUID::fromString(std::string("b42e2a68-ade7-11e4-89d3-123b93f75cba")).getNative(); - - service_uuid_ = esp32_ble_tracker::ESPBTUUID::from_uuid(service_bt); - sensors_data_characteristic_uuid_ = esp32_ble_tracker::ESPBTUUID::from_uuid(characteristic_bt); -} - -void AirthingsWavePlus::setup() {} +AirthingsWavePlus::AirthingsWavePlus() + : PollingComponent(10000), + service_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(SERVICE_UUID)), + sensors_data_characteristic_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(CHARACTERISTIC_UUID)) {} } // namespace airthings_wave_plus } // namespace esphome -#endif // USE_ESP32_FRAMEWORK_ARDUINO +#endif // USE_ESP32 diff --git a/esphome/components/airthings_wave_plus/airthings_wave_plus.h b/esphome/components/airthings_wave_plus/airthings_wave_plus.h index 5677f05a62..9dd6ed92d5 100644 --- a/esphome/components/airthings_wave_plus/airthings_wave_plus.h +++ b/esphome/components/airthings_wave_plus/airthings_wave_plus.h @@ -1,28 +1,28 @@ #pragma once -#ifdef USE_ESP32_FRAMEWORK_ARDUINO +#ifdef USE_ESP32 +#include #include #include -#include -#include -#include "esphome/core/component.h" -#include "esphome/core/log.h" #include "esphome/components/ble_client/ble_client.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/sensor/sensor.h" +#include "esphome/core/component.h" +#include "esphome/core/log.h" namespace esphome { namespace airthings_wave_plus { +static const char *const SERVICE_UUID = "b42e1c08-ade7-11e4-89d3-123b93f75cba"; +static const char *const CHARACTERISTIC_UUID = "b42e2a68-ade7-11e4-89d3-123b93f75cba"; + class AirthingsWavePlus : public PollingComponent, public ble_client::BLEClientNode { public: AirthingsWavePlus(); - void setup() override; void dump_config() override; void update() override; - void loop() override; void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) override; @@ -72,4 +72,4 @@ class AirthingsWavePlus : public PollingComponent, public ble_client::BLEClientN } // namespace airthings_wave_plus } // namespace esphome -#endif // USE_ESP32_FRAMEWORK_ARDUINO +#endif // USE_ESP32 diff --git a/esphome/components/airthings_wave_plus/sensor.py b/esphome/components/airthings_wave_plus/sensor.py index 2100341536..727fbe15fb 100644 --- a/esphome/components/airthings_wave_plus/sensor.py +++ b/esphome/components/airthings_wave_plus/sensor.py @@ -1,7 +1,6 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, ble_client -from esphome.core import CORE from esphome.const import ( DEVICE_CLASS_CARBON_DIOXIDE, @@ -83,10 +82,8 @@ CONFIG_SCHEMA = cv.All( ), } ) - .extend(cv.polling_component_schema("5mins")) + .extend(cv.polling_component_schema("5min")) .extend(ble_client.BLE_CLIENT_SCHEMA), - # Until BLEUUID reference removed - cv.only_with_arduino, ) @@ -117,6 +114,3 @@ async def to_code(config): if CONF_TVOC in config: sens = await sensor.new_sensor(config[CONF_TVOC]) cg.add(var.set_tvoc(sens)) - - if CORE.is_esp32: - cg.add_library("ESP32 BLE Arduino", None) diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index 95176bb179..65749f5124 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -317,6 +317,63 @@ ESPBTUUID ESPBTUUID::from_raw(const uint8_t *data) { ret.uuid_.uuid.uuid128[i] = data[i]; return ret; } +ESPBTUUID ESPBTUUID::from_raw(const std::string &data) { + ESPBTUUID ret; + if (data.length() == 4) { + ret.uuid_.len = ESP_UUID_LEN_16; + ret.uuid_.uuid.uuid16 = 0; + for (int i = 0; i < data.length();) { + uint8_t msb = data.c_str()[i]; + uint8_t lsb = data.c_str()[i + 1]; + + if (msb > '9') + msb -= 7; + if (lsb > '9') + lsb -= 7; + ret.uuid_.uuid.uuid16 += (((msb & 0x0F) << 4) | (lsb & 0x0F)) << (2 - i) * 4; + i += 2; + } + } else if (data.length() == 8) { + ret.uuid_.len = ESP_UUID_LEN_32; + ret.uuid_.uuid.uuid32 = 0; + for (int i = 0; i < data.length();) { + uint8_t msb = data.c_str()[i]; + uint8_t lsb = data.c_str()[i + 1]; + + if (msb > '9') + msb -= 7; + if (lsb > '9') + lsb -= 7; + ret.uuid_.uuid.uuid32 += (((msb & 0x0F) << 4) | (lsb & 0x0F)) << (6 - i) * 4; + i += 2; + } + } else if (data.length() == 16) { // how we can have 16 byte length string reprezenting 128 bit uuid??? needs to be + // investigated (lack of time) + ret.uuid_.len = ESP_UUID_LEN_128; + memcpy(ret.uuid_.uuid.uuid128, (uint8_t *) data.data(), 16); + } else if (data.length() == 36) { + // If the length of the string is 36 bytes then we will assume it is a long hex string in + // UUID format. + ret.uuid_.len = ESP_UUID_LEN_128; + int n = 0; + for (int i = 0; i < data.length();) { + if (data.c_str()[i] == '-') + i++; + uint8_t msb = data.c_str()[i]; + uint8_t lsb = data.c_str()[i + 1]; + + if (msb > '9') + msb -= 7; + if (lsb > '9') + lsb -= 7; + ret.uuid_.uuid.uuid128[15 - n++] = ((msb & 0x0F) << 4) | (lsb & 0x0F); + i += 2; + } + } else { + ESP_LOGE(TAG, "ERROR: UUID value not 2, 4, 16 or 36 bytes - %s", data.c_str()); + } + return ret; +} ESPBTUUID ESPBTUUID::from_uuid(esp_bt_uuid_t uuid) { ESPBTUUID ret; ret.uuid_.len = uuid.len; diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index 71885a564f..1308119df5 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -25,6 +25,8 @@ class ESPBTUUID { static ESPBTUUID from_raw(const uint8_t *data); + static ESPBTUUID from_raw(const std::string &data); + static ESPBTUUID from_uuid(esp_bt_uuid_t uuid); ESPBTUUID as_128bit() const; From b4f57972fb4e7f20e0d83632366f4ee32f348b7b Mon Sep 17 00:00:00 2001 From: Chris Nussbaum Date: Mon, 11 Oct 2021 21:39:21 -0500 Subject: [PATCH 199/207] Add on_open and on_closed triggers to cover (#2488) --- esphome/components/cover/__init__.py | 27 +++++++++++++++++++++++++++ esphome/components/cover/automation.h | 23 +++++++++++++++++++++++ tests/test1.yaml | 6 ++++++ 3 files changed, 56 insertions(+) diff --git a/esphome/components/cover/__init__.py b/esphome/components/cover/__init__.py index 137aaac872..0fd27f3f27 100644 --- a/esphome/components/cover/__init__.py +++ b/esphome/components/cover/__init__.py @@ -15,6 +15,7 @@ from esphome.const import ( CONF_TILT_STATE_TOPIC, CONF_STOP, CONF_MQTT_ID, + CONF_TRIGGER_ID, ) from esphome.core import CORE, coroutine_with_priority from esphome.cpp_helpers import setup_entity @@ -67,6 +68,15 @@ CoverPublishAction = cover_ns.class_("CoverPublishAction", automation.Action) CoverIsOpenCondition = cover_ns.class_("CoverIsOpenCondition", Condition) CoverIsClosedCondition = cover_ns.class_("CoverIsClosedCondition", Condition) +# Triggers +CoverOpenTrigger = cover_ns.class_("CoverOpenTrigger", automation.Trigger.template()) +CoverClosedTrigger = cover_ns.class_( + "CoverClosedTrigger", automation.Trigger.template() +) + +CONF_ON_OPEN = "on_open" +CONF_ON_CLOSED = "on_closed" + COVER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend( { cv.GenerateID(): cv.declare_id(Cover), @@ -84,6 +94,16 @@ COVER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).ex cv.Optional(CONF_TILT_STATE_TOPIC): cv.All( cv.requires_component("mqtt"), cv.subscribe_topic ), + cv.Optional(CONF_ON_OPEN): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CoverOpenTrigger), + } + ), + cv.Optional(CONF_ON_CLOSED): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CoverClosedTrigger), + } + ), } ) @@ -94,6 +114,13 @@ async def setup_cover_core_(var, config): if CONF_DEVICE_CLASS in config: cg.add(var.set_device_class(config[CONF_DEVICE_CLASS])) + for conf in config.get(CONF_ON_OPEN, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) + for conf in config.get(CONF_ON_CLOSED, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) + if CONF_MQTT_ID in config: mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var) await mqtt.register_mqtt_component(mqtt_, config) diff --git a/esphome/components/cover/automation.h b/esphome/components/cover/automation.h index 79bca6826e..6406ba52cb 100644 --- a/esphome/components/cover/automation.h +++ b/esphome/components/cover/automation.h @@ -99,6 +99,7 @@ template class CoverIsOpenCondition : public Condition { protected: Cover *cover_; }; + template class CoverIsClosedCondition : public Condition { public: CoverIsClosedCondition(Cover *cover) : cover_(cover) {} @@ -108,5 +109,27 @@ template class CoverIsClosedCondition : public Condition Cover *cover_; }; +class CoverOpenTrigger : public Trigger<> { + public: + CoverOpenTrigger(Cover *a_cover) { + a_cover->add_on_state_callback([this, a_cover]() { + if (a_cover->is_fully_open()) { + this->trigger(); + } + }); + } +}; + +class CoverClosedTrigger : public Trigger<> { + public: + CoverClosedTrigger(Cover *a_cover) { + a_cover->add_on_state_callback([this, a_cover]() { + if (a_cover->is_fully_closed()) { + this->trigger(); + } + }); + } +}; + } // namespace cover } // namespace esphome diff --git a/tests/test1.yaml b/tests/test1.yaml index 058da35d2c..130022c14d 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -2307,6 +2307,12 @@ cover: tilt_lambda: !lambda 'return 0.5;' tilt_state_topic: tilt/state/topic tilt_command_topic: tilt/command/topic + on_open: + then: + - lambda: 'ESP_LOGD("cover", "open");' + on_closed: + then: + - lambda: 'ESP_LOGD("cover", "closed");' - platform: am43 name: 'Test AM43' id: am43_test From d13134135bfde3cf2ddbc5f8df890737a83516f1 Mon Sep 17 00:00:00 2001 From: Maurice Makaay Date: Tue, 12 Oct 2021 13:51:41 +0200 Subject: [PATCH 200/207] Fix LoadProhibited crash for logger baud_rate 0 (#2498) Co-authored-by: Maurice Makaay --- esphome/components/logger/logger.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/esphome/components/logger/logger.cpp b/esphome/components/logger/logger.cpp index 4352b7e208..2d85969bf3 100644 --- a/esphome/components/logger/logger.cpp +++ b/esphome/components/logger/logger.cpp @@ -111,14 +111,15 @@ void HOT Logger::log_message_(int level, const char *tag, int offset) { this->set_null_terminator_(); const char *msg = this->tx_buffer_ + offset; + if (this->baud_rate_ > 0) { #ifdef USE_ARDUINO - if (this->baud_rate_ > 0) this->hw_serial_->println(msg); #endif // USE_ARDUINO #ifdef USE_ESP_IDF - uart_write_bytes(uart_num_, msg, strlen(msg)); - uart_write_bytes(uart_num_, "\n", 1); + uart_write_bytes(uart_num_, msg, strlen(msg)); + uart_write_bytes(uart_num_, "\n", 1); #endif + } #ifdef USE_ESP32 // Suppress network-logging if memory constrained, but still log to serial From a3eb2a7ee0d16e1e0b7d3e05636372300791ab29 Mon Sep 17 00:00:00 2001 From: Rob Deutsch Date: Wed, 13 Oct 2021 05:38:19 +1100 Subject: [PATCH 201/207] Added heatpumpir support (#1343) Co-authored-by: Otto winter Co-authored-by: Oxan van Leeuwen --- CODEOWNERS | 1 + esphome/components/heatpumpir/__init__.py | 0 esphome/components/heatpumpir/climate.py | 114 +++++++++++ esphome/components/heatpumpir/heatpumpir.cpp | 183 ++++++++++++++++++ esphome/components/heatpumpir/heatpumpir.h | 116 +++++++++++ .../heatpumpir/ir_sender_esphome.cpp | 32 +++ .../components/heatpumpir/ir_sender_esphome.h | 27 +++ platformio.ini | 1 + tests/test1.yaml | 7 + 9 files changed, 481 insertions(+) create mode 100644 esphome/components/heatpumpir/__init__.py create mode 100644 esphome/components/heatpumpir/climate.py create mode 100644 esphome/components/heatpumpir/heatpumpir.cpp create mode 100644 esphome/components/heatpumpir/heatpumpir.h create mode 100644 esphome/components/heatpumpir/ir_sender_esphome.cpp create mode 100644 esphome/components/heatpumpir/ir_sender_esphome.h diff --git a/CODEOWNERS b/CODEOWNERS index 49cb60c177..4c3084d463 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -64,6 +64,7 @@ esphome/components/graph/* @synco esphome/components/havells_solar/* @sourabhjaiswal esphome/components/hbridge/fan/* @WeekendWarrior esphome/components/hbridge/light/* @DotNetDann +esphome/components/heatpumpir/* @rob-deutsch esphome/components/hitachi_ac424/* @sourabhjaiswal esphome/components/homeassistant/* @OttoWinter esphome/components/hrxl_maxsonar_wr/* @netmikey diff --git a/esphome/components/heatpumpir/__init__.py b/esphome/components/heatpumpir/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/heatpumpir/climate.py b/esphome/components/heatpumpir/climate.py new file mode 100644 index 0000000000..36e56aa5da --- /dev/null +++ b/esphome/components/heatpumpir/climate.py @@ -0,0 +1,114 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import climate_ir +from esphome.const import ( + CONF_ID, + CONF_MAX_TEMPERATURE, + CONF_MIN_TEMPERATURE, + CONF_PROTOCOL, + CONF_VISUAL, +) + +CODEOWNERS = ["@rob-deutsch"] + +AUTO_LOAD = ["climate_ir"] + +heatpumpir_ns = cg.esphome_ns.namespace("heatpumpir") +HeatpumpIRClimate = heatpumpir_ns.class_("HeatpumpIRClimate", climate_ir.ClimateIR) + +Protocol = heatpumpir_ns.enum("Protocol") +PROTOCOLS = { + "aux": Protocol.PROTOCOL_AUX, + "ballu": Protocol.PROTOCOL_BALLU, + "carrier_mca": Protocol.PROTOCOL_CARRIER_MCA, + "carrier_nqv": Protocol.PROTOCOL_CARRIER_NQV, + "daikin_arc417": Protocol.PROTOCOL_DAIKIN_ARC417, + "daikin_arc480": Protocol.PROTOCOL_DAIKIN_ARC480, + "daikin": Protocol.PROTOCOL_DAIKIN, + "fuego": Protocol.PROTOCOL_FUEGO, + "fujitsu_awyz": Protocol.PROTOCOL_FUJITSU_AWYZ, + "gree": Protocol.PROTOCOL_GREE, + "greeya": Protocol.PROTOCOL_GREEYAA, + "greeyan": Protocol.PROTOCOL_GREEYAN, + "hisense_aud": Protocol.PROTOCOL_HISENSE_AUD, + "hitachi": Protocol.PROTOCOL_HITACHI, + "hyundai": Protocol.PROTOCOL_HYUNDAI, + "ivt": Protocol.PROTOCOL_IVT, + "midea": Protocol.PROTOCOL_MIDEA, + "mitsubishi_fa": Protocol.PROTOCOL_MITSUBISHI_FA, + "mitsubishi_fd": Protocol.PROTOCOL_MITSUBISHI_FD, + "mitsubishi_fe": Protocol.PROTOCOL_MITSUBISHI_FE, + "mitsubishi_heavy_fdtc": Protocol.PROTOCOL_MITSUBISHI_HEAVY_FDTC, + "mitsubishi_heavy_zj": Protocol.PROTOCOL_MITSUBISHI_HEAVY_ZJ, + "mitsubishi_heavy_zm": Protocol.PROTOCOL_MITSUBISHI_HEAVY_ZM, + "mitsubishi_heavy_zmp": Protocol.PROTOCOL_MITSUBISHI_HEAVY_ZMP, + "mitsubishi_heavy_kj": Protocol.PROTOCOL_MITSUBISHI_KJ, + "mitsubishi_msc": Protocol.PROTOCOL_MITSUBISHI_MSC, + "mitsubishi_msy": Protocol.PROTOCOL_MITSUBISHI_MSY, + "mitsubishi_sez": Protocol.PROTOCOL_MITSUBISHI_SEZ, + "panasonic_ckp": Protocol.PROTOCOL_PANASONIC_CKP, + "panasonic_dke": Protocol.PROTOCOL_PANASONIC_DKE, + "panasonic_jke": Protocol.PROTOCOL_PANASONIC_JKE, + "panasonic_lke": Protocol.PROTOCOL_PANASONIC_LKE, + "panasonic_nke": Protocol.PROTOCOL_PANASONIC_NKE, + "samsung_aqv": Protocol.PROTOCOL_SAMSUNG_AQV, + "samsung_fjm": Protocol.PROTOCOL_SAMSUNG_FJM, + "sharp": Protocol.PROTOCOL_SHARP, + "toshiba_daiseikai": Protocol.PROTOCOL_TOSHIBA_DAISEIKAI, + "toshiba": Protocol.PROTOCOL_TOSHIBA, +} + +CONF_HORIZONTAL_DEFAULT = "horizontal_default" +HorizontalDirections = heatpumpir_ns.enum("HorizontalDirections") +HORIZONTAL_DIRECTIONS = { + "auto": HorizontalDirections.HORIZONTAL_DIRECTION_AUTO, + "middle": HorizontalDirections.HORIZONTAL_DIRECTION_MIDDLE, + "left": HorizontalDirections.HORIZONTAL_DIRECTION_LEFT, + "mleft": HorizontalDirections.HORIZONTAL_DIRECTION_MLEFT, + "mright": HorizontalDirections.HORIZONTAL_DIRECTION_MRIGHT, + "right": HorizontalDirections.HORIZONTAL_DIRECTION_RIGHT, +} + +CONF_VERTICAL_DEFAULT = "vertical_default" +VerticalDirections = heatpumpir_ns.enum("VerticalDirections") +VERTICAL_DIRECTIONS = { + "auto": VerticalDirections.VERTICAL_DIRECTION_AUTO, + "up": VerticalDirections.VERTICAL_DIRECTION_UP, + "mup": VerticalDirections.VERTICAL_DIRECTION_MUP, + "middle": VerticalDirections.VERTICAL_DIRECTION_MIDDLE, + "mdown": VerticalDirections.VERTICAL_DIRECTION_MDOWN, + "down": VerticalDirections.VERTICAL_DIRECTION_DOWN, +} + +CONFIG_SCHEMA = cv.All( + climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(HeatpumpIRClimate), + cv.Required(CONF_PROTOCOL): cv.enum(PROTOCOLS), + cv.Required(CONF_HORIZONTAL_DEFAULT): cv.enum(HORIZONTAL_DIRECTIONS), + cv.Required(CONF_VERTICAL_DEFAULT): cv.enum(VERTICAL_DIRECTIONS), + cv.Required(CONF_MIN_TEMPERATURE): cv.temperature, + cv.Required(CONF_MAX_TEMPERATURE): cv.temperature, + } + ), + cv.only_with_arduino, +) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + if CONF_VISUAL not in config: + config[CONF_VISUAL] = {} + visual = config[CONF_VISUAL] + if CONF_MAX_TEMPERATURE not in visual: + visual[CONF_MAX_TEMPERATURE] = config[CONF_MAX_TEMPERATURE] + if CONF_MIN_TEMPERATURE not in visual: + visual[CONF_MIN_TEMPERATURE] = config[CONF_MIN_TEMPERATURE] + yield climate_ir.register_climate_ir(var, config) + cg.add(var.set_protocol(config[CONF_PROTOCOL])) + cg.add(var.set_horizontal_default(config[CONF_HORIZONTAL_DEFAULT])) + cg.add(var.set_vertical_default(config[CONF_VERTICAL_DEFAULT])) + cg.add(var.set_max_temperature(config[CONF_MIN_TEMPERATURE])) + cg.add(var.set_min_temperature(config[CONF_MAX_TEMPERATURE])) + + cg.add_library("tonia/HeatpumpIR", "1.0.15") diff --git a/esphome/components/heatpumpir/heatpumpir.cpp b/esphome/components/heatpumpir/heatpumpir.cpp new file mode 100644 index 0000000000..8d9fc962c0 --- /dev/null +++ b/esphome/components/heatpumpir/heatpumpir.cpp @@ -0,0 +1,183 @@ +#include "heatpumpir.h" + +#ifdef USE_ARDUINO + +#include +#include "ir_sender_esphome.h" +#include "HeatpumpIRFactory.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace heatpumpir { + +static const char *const TAG = "heatpumpir.climate"; + +const std::map> PROTOCOL_CONSTRUCTOR_MAP = { + {PROTOCOL_AUX, []() { return new AUXHeatpumpIR(); }}, // NOLINT + {PROTOCOL_BALLU, []() { return new BalluHeatpumpIR(); }}, // NOLINT + {PROTOCOL_CARRIER_MCA, []() { return new CarrierMCAHeatpumpIR(); }}, // NOLINT + {PROTOCOL_CARRIER_NQV, []() { return new CarrierNQVHeatpumpIR(); }}, // NOLINT + {PROTOCOL_DAIKIN_ARC417, []() { return new DaikinHeatpumpARC417IR(); }}, // NOLINT + {PROTOCOL_DAIKIN_ARC480, []() { return new DaikinHeatpumpARC480A14IR(); }}, // NOLINT + {PROTOCOL_DAIKIN, []() { return new DaikinHeatpumpIR(); }}, // NOLINT + {PROTOCOL_FUEGO, []() { return new FuegoHeatpumpIR(); }}, // NOLINT + {PROTOCOL_FUJITSU_AWYZ, []() { return new FujitsuHeatpumpIR(); }}, // NOLINT + {PROTOCOL_GREE, []() { return new GreeGenericHeatpumpIR(); }}, // NOLINT + {PROTOCOL_GREEYAA, []() { return new GreeYAAHeatpumpIR(); }}, // NOLINT + {PROTOCOL_GREEYAN, []() { return new GreeYANHeatpumpIR(); }}, // NOLINT + {PROTOCOL_HISENSE_AUD, []() { return new HisenseHeatpumpIR(); }}, // NOLINT + {PROTOCOL_HITACHI, []() { return new HitachiHeatpumpIR(); }}, // NOLINT + {PROTOCOL_HYUNDAI, []() { return new HyundaiHeatpumpIR(); }}, // NOLINT + {PROTOCOL_IVT, []() { return new IVTHeatpumpIR(); }}, // NOLINT + {PROTOCOL_MIDEA, []() { return new MideaHeatpumpIR(); }}, // NOLINT + {PROTOCOL_MITSUBISHI_FA, []() { return new MitsubishiFAHeatpumpIR(); }}, // NOLINT + {PROTOCOL_MITSUBISHI_FD, []() { return new MitsubishiFDHeatpumpIR(); }}, // NOLINT + {PROTOCOL_MITSUBISHI_FE, []() { return new MitsubishiFEHeatpumpIR(); }}, // NOLINT + {PROTOCOL_MITSUBISHI_HEAVY_FDTC, []() { return new MitsubishiHeavyFDTCHeatpumpIR(); }}, // NOLINT + {PROTOCOL_MITSUBISHI_HEAVY_ZJ, []() { return new MitsubishiHeavyZJHeatpumpIR(); }}, // NOLINT + {PROTOCOL_MITSUBISHI_HEAVY_ZM, []() { return new MitsubishiHeavyZMHeatpumpIR(); }}, // NOLINT + {PROTOCOL_MITSUBISHI_HEAVY_ZMP, []() { return new MitsubishiHeavyZMPHeatpumpIR(); }}, // NOLINT + {PROTOCOL_MITSUBISHI_KJ, []() { return new MitsubishiKJHeatpumpIR(); }}, // NOLINT + {PROTOCOL_MITSUBISHI_MSC, []() { return new MitsubishiMSCHeatpumpIR(); }}, // NOLINT + {PROTOCOL_MITSUBISHI_MSY, []() { return new MitsubishiMSYHeatpumpIR(); }}, // NOLINT + {PROTOCOL_MITSUBISHI_SEZ, []() { return new MitsubishiSEZKDXXHeatpumpIR(); }}, // NOLINT + {PROTOCOL_PANASONIC_CKP, []() { return new PanasonicCKPHeatpumpIR(); }}, // NOLINT + {PROTOCOL_PANASONIC_DKE, []() { return new PanasonicDKEHeatpumpIR(); }}, // NOLINT + {PROTOCOL_PANASONIC_JKE, []() { return new PanasonicJKEHeatpumpIR(); }}, // NOLINT + {PROTOCOL_PANASONIC_LKE, []() { return new PanasonicLKEHeatpumpIR(); }}, // NOLINT + {PROTOCOL_PANASONIC_NKE, []() { return new PanasonicNKEHeatpumpIR(); }}, // NOLINT + {PROTOCOL_SAMSUNG_AQV, []() { return new SamsungAQVHeatpumpIR(); }}, // NOLINT + {PROTOCOL_SAMSUNG_FJM, []() { return new SamsungFJMHeatpumpIR(); }}, // NOLINT + {PROTOCOL_SHARP, []() { return new SharpHeatpumpIR(); }}, // NOLINT + {PROTOCOL_TOSHIBA_DAISEIKAI, []() { return new ToshibaDaiseikaiHeatpumpIR(); }}, // NOLINT + {PROTOCOL_TOSHIBA, []() { return new ToshibaHeatpumpIR(); }}, // NOLINT +}; + +void HeatpumpIRClimate::setup() { + auto protocol_constructor = PROTOCOL_CONSTRUCTOR_MAP.find(protocol_); + if (protocol_constructor == PROTOCOL_CONSTRUCTOR_MAP.end()) { + ESP_LOGE(TAG, "Invalid protocol"); + return; + } + this->heatpump_ir_ = protocol_constructor->second(); + climate_ir::ClimateIR::setup(); +} + +void HeatpumpIRClimate::transmit_state() { + uint8_t power_mode_cmd; + uint8_t operating_mode_cmd; + uint8_t temperature_cmd; + uint8_t fan_speed_cmd; + + uint8_t swing_v_cmd; + switch (default_vertical_direction_) { + case VERTICAL_DIRECTION_AUTO: + swing_v_cmd = VDIR_AUTO; + break; + case VERTICAL_DIRECTION_UP: + swing_v_cmd = VDIR_UP; + break; + case VERTICAL_DIRECTION_MUP: + swing_v_cmd = VDIR_MUP; + break; + case VERTICAL_DIRECTION_MIDDLE: + swing_v_cmd = VDIR_MIDDLE; + break; + case VERTICAL_DIRECTION_MDOWN: + swing_v_cmd = VDIR_MDOWN; + break; + case VERTICAL_DIRECTION_DOWN: + swing_v_cmd = VDIR_DOWN; + break; + default: + ESP_LOGE(TAG, "Invalid default vertical direction"); + return; + } + if ((this->swing_mode == climate::CLIMATE_SWING_VERTICAL) || (this->swing_mode == climate::CLIMATE_SWING_BOTH)) { + swing_v_cmd = VDIR_SWING; + } + + uint8_t swing_h_cmd; + switch (default_horizontal_direction_) { + case HORIZONTAL_DIRECTION_AUTO: + swing_h_cmd = HDIR_AUTO; + break; + case HORIZONTAL_DIRECTION_MIDDLE: + swing_h_cmd = HDIR_MIDDLE; + break; + case HORIZONTAL_DIRECTION_LEFT: + swing_h_cmd = HDIR_LEFT; + break; + case HORIZONTAL_DIRECTION_MLEFT: + swing_h_cmd = HDIR_MLEFT; + break; + case HORIZONTAL_DIRECTION_MRIGHT: + swing_h_cmd = HDIR_MRIGHT; + break; + case HORIZONTAL_DIRECTION_RIGHT: + swing_h_cmd = HDIR_RIGHT; + break; + default: + ESP_LOGE(TAG, "Invalid default horizontal direction"); + return; + } + if ((this->swing_mode == climate::CLIMATE_SWING_HORIZONTAL) || (this->swing_mode == climate::CLIMATE_SWING_BOTH)) { + swing_h_cmd = HDIR_SWING; + } + + switch (this->fan_mode.value_or(climate::CLIMATE_FAN_AUTO)) { + case climate::CLIMATE_FAN_LOW: + fan_speed_cmd = FAN_2; + break; + case climate::CLIMATE_FAN_MEDIUM: + fan_speed_cmd = FAN_3; + break; + case climate::CLIMATE_FAN_HIGH: + fan_speed_cmd = FAN_4; + break; + case climate::CLIMATE_FAN_AUTO: + default: + fan_speed_cmd = FAN_AUTO; + break; + } + + switch (this->mode) { + case climate::CLIMATE_MODE_COOL: + power_mode_cmd = POWER_ON; + operating_mode_cmd = MODE_COOL; + break; + case climate::CLIMATE_MODE_HEAT: + power_mode_cmd = POWER_ON; + operating_mode_cmd = MODE_HEAT; + break; + case climate::CLIMATE_MODE_AUTO: + power_mode_cmd = POWER_ON; + operating_mode_cmd = MODE_AUTO; + break; + case climate::CLIMATE_MODE_FAN_ONLY: + power_mode_cmd = POWER_ON; + operating_mode_cmd = MODE_FAN; + break; + case climate::CLIMATE_MODE_DRY: + power_mode_cmd = POWER_ON; + operating_mode_cmd = MODE_DRY; + break; + case climate::CLIMATE_MODE_OFF: + default: + power_mode_cmd = POWER_OFF; + operating_mode_cmd = MODE_AUTO; + break; + } + + temperature_cmd = (uint8_t) clamp(this->target_temperature, this->min_temperature_, this->max_temperature_); + + IRSenderESPHome esp_sender(0, this->transmitter_); + + heatpump_ir_->send(esp_sender, power_mode_cmd, operating_mode_cmd, fan_speed_cmd, temperature_cmd, swing_v_cmd, + swing_h_cmd); +} + +} // namespace heatpumpir +} // namespace esphome + +#endif diff --git a/esphome/components/heatpumpir/heatpumpir.h b/esphome/components/heatpumpir/heatpumpir.h new file mode 100644 index 0000000000..e2d2b45dc4 --- /dev/null +++ b/esphome/components/heatpumpir/heatpumpir.h @@ -0,0 +1,116 @@ +#pragma once + +#ifdef USE_ARDUINO + +#include "esphome/components/climate_ir/climate_ir.h" + +// Forward-declare HeatpumpIR class from library. We cannot include its header here because it has unnamespaced defines +// that conflict with ESPHome. +class HeatpumpIR; + +namespace esphome { +namespace heatpumpir { + +// Simple enum to represent protocols. +enum Protocol { + PROTOCOL_AUX, + PROTOCOL_BALLU, + PROTOCOL_CARRIER_MCA, + PROTOCOL_CARRIER_NQV, + PROTOCOL_DAIKIN_ARC417, + PROTOCOL_DAIKIN_ARC480, + PROTOCOL_DAIKIN, + PROTOCOL_FUEGO, + PROTOCOL_FUJITSU_AWYZ, + PROTOCOL_GREE, + PROTOCOL_GREEYAA, + PROTOCOL_GREEYAN, + PROTOCOL_HISENSE_AUD, + PROTOCOL_HITACHI, + PROTOCOL_HYUNDAI, + PROTOCOL_IVT, + PROTOCOL_MIDEA, + PROTOCOL_MITSUBISHI_FA, + PROTOCOL_MITSUBISHI_FD, + PROTOCOL_MITSUBISHI_FE, + PROTOCOL_MITSUBISHI_HEAVY_FDTC, + PROTOCOL_MITSUBISHI_HEAVY_ZJ, + PROTOCOL_MITSUBISHI_HEAVY_ZM, + PROTOCOL_MITSUBISHI_HEAVY_ZMP, + PROTOCOL_MITSUBISHI_KJ, + PROTOCOL_MITSUBISHI_MSC, + PROTOCOL_MITSUBISHI_MSY, + PROTOCOL_MITSUBISHI_SEZ, + PROTOCOL_PANASONIC_CKP, + PROTOCOL_PANASONIC_DKE, + PROTOCOL_PANASONIC_JKE, + PROTOCOL_PANASONIC_LKE, + PROTOCOL_PANASONIC_NKE, + PROTOCOL_SAMSUNG_AQV, + PROTOCOL_SAMSUNG_FJM, + PROTOCOL_SHARP, + PROTOCOL_TOSHIBA_DAISEIKAI, + PROTOCOL_TOSHIBA, +}; + +// Simple enum to represent horizontal directios +enum HorizontalDirection { + HORIZONTAL_DIRECTION_AUTO = 0, + HORIZONTAL_DIRECTION_MIDDLE = 1, + HORIZONTAL_DIRECTION_LEFT = 2, + HORIZONTAL_DIRECTION_MLEFT = 3, + HORIZONTAL_DIRECTION_MRIGHT = 4, + HORIZONTAL_DIRECTION_RIGHT = 5, +}; + +// Simple enum to represent vertical directions +enum VerticalDirection { + VERTICAL_DIRECTION_AUTO = 0, + VERTICAL_DIRECTION_UP = 1, + VERTICAL_DIRECTION_MUP = 2, + VERTICAL_DIRECTION_MIDDLE = 3, + VERTICAL_DIRECTION_MDOWN = 4, + VERTICAL_DIRECTION_DOWN = 5, +}; + +// Temperature +const float TEMP_MIN = 0; // Celsius +const float TEMP_MAX = 100; // Celsius + +class HeatpumpIRClimate : public climate_ir::ClimateIR { + public: + HeatpumpIRClimate() + : climate_ir::ClimateIR( + TEMP_MIN, TEMP_MAX, 1.0f, true, true, + std::set{climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM, + climate::CLIMATE_FAN_HIGH, climate::CLIMATE_FAN_AUTO}, + std::set{climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_HORIZONTAL, + climate::CLIMATE_SWING_VERTICAL, climate::CLIMATE_SWING_BOTH}) {} + void setup() override; + void set_protocol(Protocol protocol) { this->protocol_ = protocol; } + void set_horizontal_default(HorizontalDirection horizontal_direction) { + this->default_horizontal_direction_ = horizontal_direction; + } + void set_vertical_default(VerticalDirection vertical_direction) { + this->default_vertical_direction_ = vertical_direction; + } + + void set_max_temperature(float temperature) { this->max_temperature_ = temperature; } + void set_min_temperature(float temperature) { this->min_temperature_ = temperature; } + + protected: + HeatpumpIR *heatpump_ir_; + /// Transmit via IR the state of this climate controller. + void transmit_state() override; + Protocol protocol_; + HorizontalDirection default_horizontal_direction_; + VerticalDirection default_vertical_direction_; + + float max_temperature_; + float min_temperature_; +}; + +} // namespace heatpumpir +} // namespace esphome + +#endif diff --git a/esphome/components/heatpumpir/ir_sender_esphome.cpp b/esphome/components/heatpumpir/ir_sender_esphome.cpp new file mode 100644 index 0000000000..24c7933563 --- /dev/null +++ b/esphome/components/heatpumpir/ir_sender_esphome.cpp @@ -0,0 +1,32 @@ +#include "ir_sender_esphome.h" + +#ifdef USE_ARDUINO + +namespace esphome { +namespace heatpumpir { + +void IRSenderESPHome::setFrequency(int frequency) { // NOLINT(readability-identifier-naming) + auto data = transmit_.get_data(); + data->set_carrier_frequency(1000 * frequency); +} + +// Send an IR 'mark' symbol, i.e. transmitter ON +void IRSenderESPHome::mark(int mark_length) { + auto data = transmit_.get_data(); + data->mark(mark_length); +} + +// Send an IR 'space' symbol, i.e. transmitter OFF +void IRSenderESPHome::space(int space_length) { + if (space_length) { + auto data = transmit_.get_data(); + data->space(space_length); + } else { + transmit_.perform(); + } +} + +} // namespace heatpumpir +} // namespace esphome + +#endif diff --git a/esphome/components/heatpumpir/ir_sender_esphome.h b/esphome/components/heatpumpir/ir_sender_esphome.h new file mode 100644 index 0000000000..24e8ba9883 --- /dev/null +++ b/esphome/components/heatpumpir/ir_sender_esphome.h @@ -0,0 +1,27 @@ +#pragma once + +#ifdef USE_ARDUINO + +#include "esphome/components/remote_base/remote_base.h" +#include "esphome/components/remote_transmitter/remote_transmitter.h" +#include // arduino-heatpump library + +namespace esphome { +namespace heatpumpir { + +class IRSenderESPHome : public IRSender { + public: + IRSenderESPHome(uint8_t pin, remote_transmitter::RemoteTransmitterComponent *transmitter) + : IRSender(pin), transmit_(transmitter->transmit()){}; + void setFrequency(int frequency) override; // NOLINT(readability-identifier-naming) + void space(int space_length) override; + void mark(int mark_length) override; + + protected: + remote_transmitter::RemoteTransmitterComponent::TransmitCall transmit_; +}; + +} // namespace heatpumpir +} // namespace esphome + +#endif diff --git a/platformio.ini b/platformio.ini index e038224f69..9cc7477d51 100644 --- a/platformio.ini +++ b/platformio.ini @@ -49,6 +49,7 @@ lib_deps = glmnet/Dsmr@0.5 ; dsmr rweather/Crypto@0.2.0 ; dsmr dudanov/MideaUART@1.1.8 ; midea + tonia/HeatpumpIR@^1.0.15 ; heatpumpir build_flags = ${common.build_flags} -DUSE_ARDUINO diff --git a/tests/test1.yaml b/tests/test1.yaml index 130022c14d..fd142a63fd 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1681,6 +1681,13 @@ climate: name: Toshiba Climate - platform: hitachi_ac344 name: Hitachi Climate + - platform: heatpumpir + protocol: mitsubishi_heavy_zm + horizontal_default: left + vertical_default: up + name: HeatpumpIR Climate + min_temperature: 18 + max_temperature: 30 - platform: midea id: midea_unit uart_id: uart0 From 1184bbc976e27aebbfcdaee27219d2141c22efd2 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Tue, 12 Oct 2021 21:22:38 +0200 Subject: [PATCH 202/207] Reduce IRAM usage in test3 (#2499) --- tests/test2.yaml | 6 ++++++ tests/test3.yaml | 6 ------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test2.yaml b/tests/test2.yaml index 364bcec28f..7e71d1ab4e 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -61,6 +61,12 @@ mcp3008: - id: 'mcp3008_hub' cs_pin: GPIO12 +output: + - platform: ac_dimmer + id: dimmer1 + gate_pin: GPIO5 + zero_cross_pin: GPIO12 + sensor: - platform: homeassistant entity_id: sensor.hello_world diff --git a/tests/test3.yaml b/tests/test3.yaml index b261d6cc8e..9d2839a026 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -261,8 +261,6 @@ logger: level: DEBUG esp8266_store_log_strings_in_flash: true -web_server: - deep_sleep: run_duration: 20s sleep_duration: 50s @@ -1092,10 +1090,6 @@ output: return {s}; outputs: - id: custom_float - - platform: ac_dimmer - id: dimmer1 - gate_pin: GPIO5 - zero_cross_pin: GPIO12 - platform: slow_pwm pin: GPIO5 id: my_slow_pwm From 34db9d9ef2b0748abee992958e783ec88a69398b Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 13 Oct 2021 08:23:24 +1300 Subject: [PATCH 203/207] Add optional timeout for wait_until action (#2282) --- esphome/automation.py | 7 +++++++ esphome/core/base_automation.h | 12 ++++++++++++ tests/test1.yaml | 5 ++++- tests/test3.yaml | 12 +++++++----- 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/esphome/automation.py b/esphome/automation.py index 71c564b906..0768bf8869 100644 --- a/esphome/automation.py +++ b/esphome/automation.py @@ -6,6 +6,7 @@ from esphome.const import ( CONF_ELSE, CONF_ID, CONF_THEN, + CONF_TIMEOUT, CONF_TRIGGER_ID, CONF_TYPE_ID, CONF_TIME, @@ -244,6 +245,9 @@ def validate_wait_until(value): schema = cv.Schema( { cv.Required(CONF_CONDITION): validate_potentially_and_condition, + cv.Optional(CONF_TIMEOUT): cv.templatable( + cv.positive_time_period_milliseconds + ), } ) if isinstance(value, dict) and CONF_CONDITION in value: @@ -255,6 +259,9 @@ def validate_wait_until(value): async def wait_until_action_to_code(config, action_id, template_arg, args): conditions = await build_condition(config[CONF_CONDITION], template_arg, args) var = cg.new_Pvariable(action_id, template_arg, conditions) + if CONF_TIMEOUT in config: + template_ = await cg.templatable(config[CONF_TIMEOUT], args, cg.uint32) + cg.add(var.set_timeout_value(template_)) await cg.register_component(var, {}) return var diff --git a/esphome/core/base_automation.h b/esphome/core/base_automation.h index fa49786d1d..d97d369d33 100644 --- a/esphome/core/base_automation.h +++ b/esphome/core/base_automation.h @@ -228,6 +228,8 @@ template class WaitUntilAction : public Action, public Co public: WaitUntilAction(Condition *condition) : condition_(condition) {} + TEMPLATABLE_VALUE(uint32_t, timeout_value) + void play_complex(Ts... x) override { this->num_running_++; // Check if we can continue immediately. @@ -238,6 +240,12 @@ template class WaitUntilAction : public Action, public Co return; } this->var_ = std::make_tuple(x...); + + if (this->timeout_value_.has_value()) { + auto f = std::bind(&WaitUntilAction::play_next_, this, x...); + this->set_timeout("timeout", this->timeout_value_.value(x...), f); + } + this->loop(); } @@ -249,6 +257,8 @@ template class WaitUntilAction : public Action, public Co return; } + this->cancel_timeout("timeout"); + this->play_next_tuple_(this->var_); } @@ -257,6 +267,8 @@ template class WaitUntilAction : public Action, public Co void play(Ts... x) override { /* ignore - see play_complex */ } + void stop() override { this->cancel_timeout("timeout"); } + protected: Condition *condition_; std::tuple var_{}; diff --git a/tests/test1.yaml b/tests/test1.yaml index fd142a63fd..540a715dde 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -364,8 +364,11 @@ sensor: then: - lambda: >- ESP_LOGD("main", "Got value range %f", x); + - wait_until: wifi.connected - wait_until: - binary_sensor.is_on: binary_sensor1 + condition: + binary_sensor.is_on: binary_sensor1 + timeout: 1s on_raw_value: - lambda: >- ESP_LOGD("main", "Got raw value %f", x); diff --git a/tests/test3.yaml b/tests/test3.yaml index 9d2839a026..73e314c94c 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -5,10 +5,13 @@ esphome: board: d1_mini build_path: build/test3 on_boot: - - wait_until: - - api.connected - - wifi.connected - - time.has_time + - if: + condition: + - api.connected + - wifi.connected + - time.has_time + then: + - logger.log: "Have time" includes: - custom.h @@ -1291,4 +1294,3 @@ dsmr: daly_bms: update_interval: 20s uart_id: uart1 - From c33077bc61a214df23d82753ae9077898c280786 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Trevi=C3=B1o?= Date: Tue, 12 Oct 2021 21:42:51 +0200 Subject: [PATCH 204/207] Improves ct_clamp component accuracy (#2283) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Rafa Treviño --- .../components/ct_clamp/ct_clamp_sensor.cpp | 22 ++++++++++++------- esphome/components/ct_clamp/ct_clamp_sensor.h | 1 + 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/esphome/components/ct_clamp/ct_clamp_sensor.cpp b/esphome/components/ct_clamp/ct_clamp_sensor.cpp index 0052b1426d..51b0f1318c 100644 --- a/esphome/components/ct_clamp/ct_clamp_sensor.cpp +++ b/esphome/components/ct_clamp/ct_clamp_sensor.cpp @@ -31,19 +31,20 @@ void CTClampSensor::update() { return; } - float dc = this->sample_sum_ / this->num_samples_; - float var = (this->sample_squared_sum_ / this->num_samples_) - dc * dc; - float ac = std::sqrt(var); - ESP_LOGD(TAG, "'%s' - Got %d samples", this->name_.c_str(), this->num_samples_); - ESP_LOGD(TAG, "'%s' - Raw AC Value: %.3fA", this->name_.c_str(), ac); - this->publish_state(ac); + const float rms_ac_dc_squared = this->sample_squared_sum_ / this->num_samples_; + const float rms_dc = this->sample_sum_ / this->num_samples_; + const float rms_ac = std::sqrt(rms_ac_dc_squared - rms_dc * rms_dc); + ESP_LOGD(TAG, "'%s' - Raw AC Value: %.3fA after %d different samples (%d SPS)", this->name_.c_str(), rms_ac, + this->num_samples_, 1000 * this->num_samples_ / this->sample_duration_); + this->publish_state(rms_ac); }); // Set sampling values - this->is_sampling_ = true; + this->last_value_ = 0.0; this->num_samples_ = 0; this->sample_sum_ = 0.0f; this->sample_squared_sum_ = 0.0f; + this->is_sampling_ = true; } void CTClampSensor::loop() { @@ -55,9 +56,14 @@ void CTClampSensor::loop() { if (std::isnan(value)) return; + // Assuming a sine wave, avoid requesting values faster than the ADC can provide them + if (this->last_value_ == value) + return; + this->last_value_ = value; + + this->num_samples_++; this->sample_sum_ += value; this->sample_squared_sum_ += value * value; - this->num_samples_++; } } // namespace ct_clamp diff --git a/esphome/components/ct_clamp/ct_clamp_sensor.h b/esphome/components/ct_clamp/ct_clamp_sensor.h index 10601ab852..db4dc1ea57 100644 --- a/esphome/components/ct_clamp/ct_clamp_sensor.h +++ b/esphome/components/ct_clamp/ct_clamp_sensor.h @@ -43,6 +43,7 @@ class CTClampSensor : public sensor::Sensor, public PollingComponent { * https://en.wikipedia.org/wiki/Root_mean_square */ + float last_value_ = 0.0f; float sample_sum_ = 0.0f; float sample_squared_sum_ = 0.0f; uint32_t num_samples_ = 0; From 4406a08fa7b88ef9b7a97e273c1c1d127d0c19aa Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 13 Oct 2021 09:06:52 +1300 Subject: [PATCH 205/207] Allow multiple pn532_spi entries (#2489) --- esphome/components/pn532_spi/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/pn532_spi/__init__.py b/esphome/components/pn532_spi/__init__.py index 2683f34ad5..8a8ab1b175 100644 --- a/esphome/components/pn532_spi/__init__.py +++ b/esphome/components/pn532_spi/__init__.py @@ -6,6 +6,7 @@ from esphome.const import CONF_ID AUTO_LOAD = ["pn532"] CODEOWNERS = ["@OttoWinter", "@jesserockz"] DEPENDENCIES = ["spi"] +MULTI_CONF = True pn532_spi_ns = cg.esphome_ns.namespace("pn532_spi") PN532Spi = pn532_spi_ns.class_("PN532Spi", pn532.PN532, spi.SPIDevice) From 3dee057826cbccbdb90a0fe92b6664eb029c7d2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Mayoral=20Mart=C3=ADnez?= Date: Wed, 13 Oct 2021 00:35:30 +0200 Subject: [PATCH 206/207] Add throttle_average sensor filter (#2485) --- esphome/components/sensor/__init__.py | 10 ++++++++++ esphome/components/sensor/filter.cpp | 25 +++++++++++++++++++++++++ esphome/components/sensor/filter.h | 20 ++++++++++++++++++++ tests/test1.yaml | 1 + 4 files changed, 56 insertions(+) diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index cb74a41119..4b2e9dc019 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -160,6 +160,7 @@ SlidingWindowMovingAverageFilter = sensor_ns.class_( ExponentialMovingAverageFilter = sensor_ns.class_( "ExponentialMovingAverageFilter", Filter ) +ThrottleAverageFilter = sensor_ns.class_("ThrottleAverageFilter", Filter, cg.Component) LambdaFilter = sensor_ns.class_("LambdaFilter", Filter) OffsetFilter = sensor_ns.class_("OffsetFilter", Filter) MultiplyFilter = sensor_ns.class_("MultiplyFilter", Filter) @@ -381,6 +382,15 @@ async def exponential_moving_average_filter_to_code(config, filter_id): return cg.new_Pvariable(filter_id, config[CONF_ALPHA], config[CONF_SEND_EVERY]) +@FILTER_REGISTRY.register( + "throttle_average", ThrottleAverageFilter, cv.positive_time_period_milliseconds +) +async def throttle_average_filter_to_code(config, filter_id): + var = cg.new_Pvariable(filter_id, config) + await cg.register_component(var, {}) + return var + + @FILTER_REGISTRY.register("lambda", LambdaFilter, cv.returning_lambda) async def lambda_filter_to_code(config, filter_id): lambda_ = await cg.process_lambda( diff --git a/esphome/components/sensor/filter.cpp b/esphome/components/sensor/filter.cpp index 63801e7996..321e3a4a4f 100644 --- a/esphome/components/sensor/filter.cpp +++ b/esphome/components/sensor/filter.cpp @@ -187,6 +187,31 @@ optional ExponentialMovingAverageFilter::new_value(float value) { void ExponentialMovingAverageFilter::set_send_every(size_t send_every) { this->send_every_ = send_every; } void ExponentialMovingAverageFilter::set_alpha(float alpha) { this->alpha_ = alpha; } +// ThrottleAverageFilter +ThrottleAverageFilter::ThrottleAverageFilter(uint32_t time_period) : time_period_(time_period) {} + +optional ThrottleAverageFilter::new_value(float value) { + ESP_LOGVV(TAG, "ThrottleAverageFilter(%p)::new_value(value=%f)", this, value); + if (!std::isnan(value)) { + this->sum_ += value; + this->n_++; + } + return {}; +} +void ThrottleAverageFilter::setup() { + this->set_interval("throttle_average", this->time_period_, [this]() { + ESP_LOGVV(TAG, "ThrottleAverageFilter(%p)::interval(sum=%f, n=%i)", this, this->sum_, this->n_); + if (this->n_ == 0) { + this->output(NAN); + } else { + this->output(this->sum_ / this->n_); + this->sum_ = 0.0f; + this->n_ = 0; + } + }); +} +float ThrottleAverageFilter::get_setup_priority() const { return setup_priority::HARDWARE; } + // LambdaFilter LambdaFilter::LambdaFilter(lambda_filter_t lambda_filter) : lambda_filter_(std::move(lambda_filter)) {} const lambda_filter_t &LambdaFilter::get_lambda_filter() const { return this->lambda_filter_; } diff --git a/esphome/components/sensor/filter.h b/esphome/components/sensor/filter.h index 29a6813ea9..d595e419a6 100644 --- a/esphome/components/sensor/filter.h +++ b/esphome/components/sensor/filter.h @@ -178,6 +178,26 @@ class ExponentialMovingAverageFilter : public Filter { float alpha_; }; +/** Simple throttle average filter. + * + * It takes the average of all the values received in a period of time. + */ +class ThrottleAverageFilter : public Filter, public Component { + public: + explicit ThrottleAverageFilter(uint32_t time_period); + + void setup() override; + + optional new_value(float value) override; + + float get_setup_priority() const override; + + protected: + uint32_t time_period_; + float sum_{0.0f}; + unsigned int n_{0}; +}; + using lambda_filter_t = std::function(float)>; /** This class allows for creation of simple template filters. diff --git a/tests/test1.yaml b/tests/test1.yaml index 540a715dde..157ccfc5d1 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -343,6 +343,7 @@ sensor: - exponential_moving_average: alpha: 0.1 send_every: 15 + - throttle_average: 60s - throttle: 1s - heartbeat: 5s - debounce: 0.1s From a779592414092163f5e29299567102f9072f6475 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 13 Oct 2021 16:40:46 +1300 Subject: [PATCH 207/207] Bump version to 2021.10.0b1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index a9eec3e249..ce7d363e92 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2021.10.0-dev" +__version__ = "2021.10.0b1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"