mirror of
https://github.com/esphome/esphome.git
synced 2026-02-21 08:55:36 -07:00
Merge remote-tracking branch 'upstream/dev' into integration
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
#include "micronova_button.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace micronova {
|
||||
namespace esphome::micronova {
|
||||
|
||||
void MicroNovaButton::press_action() {
|
||||
switch (this->get_function()) {
|
||||
@@ -14,5 +13,4 @@ void MicroNovaButton::press_action() {
|
||||
this->micronova_->update();
|
||||
}
|
||||
|
||||
} // namespace micronova
|
||||
} // namespace esphome
|
||||
} // namespace esphome::micronova
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/button/button.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace micronova {
|
||||
namespace esphome::micronova {
|
||||
|
||||
class MicroNovaButton : public Component, public button::Button, public MicroNovaButtonListener {
|
||||
public:
|
||||
@@ -19,5 +18,4 @@ class MicroNovaButton : public Component, public button::Button, public MicroNov
|
||||
void press_action() override;
|
||||
};
|
||||
|
||||
} // namespace micronova
|
||||
} // namespace esphome
|
||||
} // namespace esphome::micronova
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#include "micronova.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace micronova {
|
||||
namespace esphome::micronova {
|
||||
|
||||
void MicroNova::setup() {
|
||||
if (this->enable_rx_pin_ != nullptr) {
|
||||
@@ -144,5 +143,4 @@ void MicroNova::write_address(uint8_t location, uint8_t address, uint8_t data) {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace micronova
|
||||
} // namespace esphome
|
||||
} // namespace esphome::micronova
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace esphome {
|
||||
namespace micronova {
|
||||
namespace esphome::micronova {
|
||||
|
||||
static const char *const TAG = "micronova";
|
||||
static const int STOVE_REPLY_DELAY = 60;
|
||||
@@ -160,5 +159,4 @@ class MicroNova : public PollingComponent, public uart::UARTDevice {
|
||||
MicroNovaSwitchListener *stove_switch_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace micronova
|
||||
} // namespace esphome
|
||||
} // namespace esphome::micronova
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "micronova_number.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace micronova {
|
||||
namespace esphome::micronova {
|
||||
|
||||
void MicroNovaNumber::process_value_from_stove(int value_from_stove) {
|
||||
float new_sensor_value = 0;
|
||||
@@ -41,5 +40,4 @@ void MicroNovaNumber::control(float value) {
|
||||
this->micronova_->update();
|
||||
}
|
||||
|
||||
} // namespace micronova
|
||||
} // namespace esphome
|
||||
} // namespace esphome::micronova
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
#include "esphome/components/micronova/micronova.h"
|
||||
#include "esphome/components/number/number.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace micronova {
|
||||
namespace esphome::micronova {
|
||||
|
||||
class MicroNovaNumber : public number::Number, public MicroNovaSensorListener {
|
||||
public:
|
||||
@@ -24,5 +23,4 @@ class MicroNovaNumber : public number::Number, public MicroNovaSensorListener {
|
||||
uint8_t memory_write_location_ = 0;
|
||||
};
|
||||
|
||||
} // namespace micronova
|
||||
} // namespace esphome
|
||||
} // namespace esphome::micronova
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "micronova_sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace micronova {
|
||||
namespace esphome::micronova {
|
||||
|
||||
void MicroNovaSensor::process_value_from_stove(int value_from_stove) {
|
||||
if (value_from_stove == -1) {
|
||||
@@ -31,5 +30,4 @@ void MicroNovaSensor::process_value_from_stove(int value_from_stove) {
|
||||
this->publish_state(new_sensor_value);
|
||||
}
|
||||
|
||||
} // namespace micronova
|
||||
} // namespace esphome
|
||||
} // namespace esphome::micronova
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
#include "esphome/components/micronova/micronova.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace micronova {
|
||||
namespace esphome::micronova {
|
||||
|
||||
class MicroNovaSensor : public sensor::Sensor, public MicroNovaSensorListener {
|
||||
public:
|
||||
@@ -23,5 +22,4 @@ class MicroNovaSensor : public sensor::Sensor, public MicroNovaSensorListener {
|
||||
int fan_speed_offset_ = 0;
|
||||
};
|
||||
|
||||
} // namespace micronova
|
||||
} // namespace esphome
|
||||
} // namespace esphome::micronova
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "micronova_switch.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace micronova {
|
||||
namespace esphome::micronova {
|
||||
|
||||
void MicroNovaSwitch::write_state(bool state) {
|
||||
switch (this->get_function()) {
|
||||
@@ -31,5 +30,4 @@ void MicroNovaSwitch::write_state(bool state) {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace micronova
|
||||
} // namespace esphome
|
||||
} // namespace esphome::micronova
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/switch/switch.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace micronova {
|
||||
namespace esphome::micronova {
|
||||
|
||||
class MicroNovaSwitch : public Component, public switch_::Switch, public MicroNovaSwitchListener {
|
||||
public:
|
||||
@@ -25,5 +24,4 @@ class MicroNovaSwitch : public Component, public switch_::Switch, public MicroNo
|
||||
void write_state(bool state) override;
|
||||
};
|
||||
|
||||
} // namespace micronova
|
||||
} // namespace esphome
|
||||
} // namespace esphome::micronova
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "micronova_text_sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace micronova {
|
||||
namespace esphome::micronova {
|
||||
|
||||
void MicroNovaTextSensor::process_value_from_stove(int value_from_stove) {
|
||||
if (value_from_stove == -1) {
|
||||
@@ -27,5 +26,4 @@ void MicroNovaTextSensor::process_value_from_stove(int value_from_stove) {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace micronova
|
||||
} // namespace esphome
|
||||
} // namespace esphome::micronova
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
#include "esphome/components/micronova/micronova.h"
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace micronova {
|
||||
namespace esphome::micronova {
|
||||
|
||||
class MicroNovaTextSensor : public text_sensor::TextSensor, public MicroNovaSensorListener {
|
||||
public:
|
||||
@@ -16,5 +15,4 @@ class MicroNovaTextSensor : public text_sensor::TextSensor, public MicroNovaSens
|
||||
void process_value_from_stove(int value_from_stove) override;
|
||||
};
|
||||
|
||||
} // namespace micronova
|
||||
} // namespace esphome
|
||||
} // namespace esphome::micronova
|
||||
|
||||
@@ -114,6 +114,7 @@ CONFIG_SCHEMA = remote_base.validate_triggers(
|
||||
bk72xx="1000b",
|
||||
ln882x="1000b",
|
||||
rtl87xx="1000b",
|
||||
rp2040="1000b",
|
||||
): cv.validate_bytes,
|
||||
cv.Optional(CONF_FILTER, default="50us"): cv.All(
|
||||
cv.positive_time_period_microseconds,
|
||||
@@ -213,6 +214,7 @@ FILTER_SOURCE_FILES = filter_source_files_from_platform(
|
||||
PlatformFramework.BK72XX_ARDUINO,
|
||||
PlatformFramework.RTL87XX_ARDUINO,
|
||||
PlatformFramework.LN882X_ARDUINO,
|
||||
PlatformFramework.RP2040_ARDUINO,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#if defined(USE_LIBRETINY) || defined(USE_ESP8266)
|
||||
#if defined(USE_LIBRETINY) || defined(USE_ESP8266) || defined(USE_RP2040)
|
||||
|
||||
namespace esphome {
|
||||
namespace remote_receiver {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
namespace esphome {
|
||||
namespace remote_receiver {
|
||||
|
||||
#if defined(USE_ESP8266) || defined(USE_LIBRETINY)
|
||||
#if defined(USE_ESP8266) || defined(USE_LIBRETINY) || defined(USE_RP2040)
|
||||
struct RemoteReceiverComponentStore {
|
||||
static void gpio_intr(RemoteReceiverComponentStore *arg);
|
||||
|
||||
@@ -84,7 +84,7 @@ class RemoteReceiverComponent : public remote_base::RemoteReceiverBase,
|
||||
std::string error_string_{""};
|
||||
#endif
|
||||
|
||||
#if defined(USE_ESP8266) || defined(USE_LIBRETINY) || defined(USE_ESP32)
|
||||
#if defined(USE_ESP8266) || defined(USE_LIBRETINY) || defined(USE_ESP32) || defined(USE_RP2040)
|
||||
RemoteReceiverComponentStore store_;
|
||||
HighFrequencyLoopRequester high_freq_;
|
||||
#endif
|
||||
|
||||
@@ -156,6 +156,7 @@ FILTER_SOURCE_FILES = filter_source_files_from_platform(
|
||||
PlatformFramework.BK72XX_ARDUINO,
|
||||
PlatformFramework.RTL87XX_ARDUINO,
|
||||
PlatformFramework.LN882X_ARDUINO,
|
||||
PlatformFramework.RP2040_ARDUINO,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/application.h"
|
||||
|
||||
#if defined(USE_LIBRETINY) || defined(USE_ESP8266)
|
||||
#if defined(USE_LIBRETINY) || defined(USE_ESP8266) || defined(USE_RP2040)
|
||||
|
||||
namespace esphome {
|
||||
namespace remote_transmitter {
|
||||
@@ -40,8 +40,8 @@ void RemoteTransmitterComponent::await_target_time_() {
|
||||
if (this->target_time_ == 0) {
|
||||
this->target_time_ = current_time;
|
||||
} else if ((int32_t) (this->target_time_ - current_time) > 0) {
|
||||
#if defined(USE_LIBRETINY)
|
||||
// busy loop for libretiny is required (see the comment inside micros() in wiring.c)
|
||||
#if defined(USE_LIBRETINY) || defined(USE_RP2040)
|
||||
// busy loop is required for libretiny and rp2040 as interrupts are disabled
|
||||
while ((int32_t) (this->target_time_ - micros()) > 0)
|
||||
;
|
||||
#else
|
||||
|
||||
@@ -62,7 +62,7 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase,
|
||||
|
||||
protected:
|
||||
void send_internal(uint32_t send_times, uint32_t send_wait) override;
|
||||
#if defined(USE_ESP8266) || defined(USE_LIBRETINY)
|
||||
#if defined(USE_ESP8266) || defined(USE_LIBRETINY) || defined(USE_RP2040)
|
||||
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);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from dataclasses import dataclass
|
||||
from logging import getLogger
|
||||
import math
|
||||
import re
|
||||
@@ -32,13 +33,26 @@ from esphome.const import (
|
||||
PLATFORM_HOST,
|
||||
PlatformFramework,
|
||||
)
|
||||
from esphome.core import CORE, ID
|
||||
from esphome.core import CORE, ID, CoroPriority, coroutine_with_priority
|
||||
import esphome.final_validate as fv
|
||||
from esphome.yaml_util import make_data_base
|
||||
|
||||
_LOGGER = getLogger(__name__)
|
||||
|
||||
CODEOWNERS = ["@esphome/core"]
|
||||
DOMAIN = "uart"
|
||||
|
||||
|
||||
def AUTO_LOAD() -> list[str]:
|
||||
"""Ideally, we would only auto-load socket only when wake_loop_on_rx is requested;
|
||||
however, AUTO_LOAD is examined before wake_loop_on_rx is set, so instead, since ESP32
|
||||
always uses socket select support in the main app, we'll just ensure it's loaded here.
|
||||
"""
|
||||
if CORE.is_esp32:
|
||||
return ["socket"]
|
||||
return []
|
||||
|
||||
|
||||
uart_ns = cg.esphome_ns.namespace("uart")
|
||||
UARTComponent = uart_ns.class_("UARTComponent")
|
||||
|
||||
@@ -52,6 +66,7 @@ LibreTinyUARTComponent = uart_ns.class_(
|
||||
)
|
||||
HostUartComponent = uart_ns.class_("HostUartComponent", UARTComponent, cg.Component)
|
||||
|
||||
|
||||
NATIVE_UART_CLASSES = (
|
||||
str(IDFUARTComponent),
|
||||
str(ESP8266UartComponent),
|
||||
@@ -100,6 +115,38 @@ MULTI_CONF = True
|
||||
MULTI_CONF_NO_DEFAULT = True
|
||||
|
||||
|
||||
@dataclass
|
||||
class UARTData:
|
||||
"""State data for UART component configuration generation."""
|
||||
|
||||
wake_loop_on_rx: bool = False
|
||||
|
||||
|
||||
def _get_data() -> UARTData:
|
||||
"""Get UART component data from CORE.data."""
|
||||
if DOMAIN not in CORE.data:
|
||||
CORE.data[DOMAIN] = UARTData()
|
||||
return CORE.data[DOMAIN]
|
||||
|
||||
|
||||
def request_wake_loop_on_rx() -> None:
|
||||
"""Request that the UART wake the main loop when data is received.
|
||||
|
||||
Components that need low-latency notification of incoming UART data
|
||||
should call this function during their code generation.
|
||||
This enables the RX event task which wakes the main loop when data arrives.
|
||||
"""
|
||||
data = _get_data()
|
||||
if not data.wake_loop_on_rx:
|
||||
data.wake_loop_on_rx = True
|
||||
|
||||
# UART RX event task uses wake_loop_threadsafe() to notify the main loop
|
||||
# Automatically enable the socket wake infrastructure when RX wake is requested
|
||||
from esphome.components import socket
|
||||
|
||||
socket.require_wake_loop_threadsafe()
|
||||
|
||||
|
||||
def validate_raw_data(value):
|
||||
if isinstance(value, str):
|
||||
return value.encode("utf-8")
|
||||
@@ -335,6 +382,8 @@ async def to_code(config):
|
||||
if CONF_DEBUG in config:
|
||||
await debug_to_code(config[CONF_DEBUG], var)
|
||||
|
||||
CORE.add_job(final_step)
|
||||
|
||||
|
||||
# A schema to use for all UART devices, all UART integrations must extend this!
|
||||
UART_DEVICE_SCHEMA = cv.Schema(
|
||||
@@ -472,6 +521,13 @@ async def uart_write_to_code(config, action_id, template_arg, args):
|
||||
return var
|
||||
|
||||
|
||||
@coroutine_with_priority(CoroPriority.FINAL)
|
||||
async def final_step():
|
||||
"""Final code generation step to configure optional UART features."""
|
||||
if _get_data().wake_loop_on_rx:
|
||||
cg.add_define("USE_UART_WAKE_LOOP_ON_RX")
|
||||
|
||||
|
||||
FILTER_SOURCE_FILES = filter_source_files_from_platform(
|
||||
{
|
||||
"uart_component_esp_idf.cpp": {
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
namespace esphome::uart {
|
||||
|
||||
template<typename... Ts> class UARTWriteAction : public Action<Ts...>, public Parented<UARTComponent> {
|
||||
public:
|
||||
@@ -41,5 +40,4 @@ template<typename... Ts> class UARTWriteAction : public Action<Ts...>, public Pa
|
||||
} code_;
|
||||
};
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
} // namespace esphome::uart
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#include "uart_button.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
namespace esphome::uart {
|
||||
|
||||
static const char *const TAG = "uart.button";
|
||||
|
||||
@@ -13,5 +12,4 @@ void UARTButton::press_action() {
|
||||
|
||||
void UARTButton::dump_config() { LOG_BUTTON("", "UART Button", this); }
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
} // namespace esphome::uart
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
namespace esphome::uart {
|
||||
|
||||
class UARTButton : public button::Button, public UARTDevice, public Component {
|
||||
public:
|
||||
@@ -21,5 +20,4 @@ class UARTButton : public button::Button, public UARTDevice, public Component {
|
||||
std::vector<uint8_t> data_;
|
||||
};
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
} // namespace esphome::uart
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
#include "esphome/core/application.h"
|
||||
#include "uart_transport.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
namespace esphome::uart {
|
||||
|
||||
static const char *const TAG = "uart_transport";
|
||||
|
||||
@@ -84,5 +83,5 @@ void UARTTransport::send_packet(const std::vector<uint8_t> &buf) const {
|
||||
this->write_byte_(crc >> 8);
|
||||
this->parent_->write_byte(FLAG_BYTE);
|
||||
}
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
|
||||
} // namespace esphome::uart
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
#include <vector>
|
||||
#include "../uart.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
namespace esphome::uart {
|
||||
|
||||
/**
|
||||
* A transport protocol for sending and receiving packets over a UART connection.
|
||||
@@ -37,5 +36,4 @@ class UARTTransport : public packet_transport::PacketTransport, public UARTDevic
|
||||
bool rx_control_{};
|
||||
};
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
} // namespace esphome::uart
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/application.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
namespace esphome::uart {
|
||||
|
||||
static const char *const TAG = "uart.switch";
|
||||
|
||||
@@ -58,5 +57,4 @@ void UARTSwitch::dump_config() {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
} // namespace esphome::uart
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
#include <cinttypes>
|
||||
#include <vector>
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
namespace esphome::uart {
|
||||
|
||||
class UARTSwitch : public switch_::Switch, public UARTDevice, public Component {
|
||||
public:
|
||||
@@ -33,5 +32,4 @@ class UARTSwitch : public switch_::Switch, public UARTDevice, public Component {
|
||||
uint32_t last_transmission_;
|
||||
};
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
} // namespace esphome::uart
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include <cinttypes>
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
namespace esphome::uart {
|
||||
|
||||
static const char *const TAG = "uart";
|
||||
|
||||
@@ -43,5 +42,4 @@ const LogString *parity_to_str(UARTParityOptions parity) {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
} // namespace esphome::uart
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include "uart_component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
namespace esphome::uart {
|
||||
|
||||
class UARTDevice {
|
||||
public:
|
||||
@@ -74,5 +73,4 @@ class UARTDevice {
|
||||
UARTComponent *parent_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
} // namespace esphome::uart
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "uart_component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
namespace esphome::uart {
|
||||
|
||||
static const char *const TAG = "uart";
|
||||
|
||||
@@ -28,5 +27,4 @@ void UARTComponent::set_rx_full_threshold_ms(uint8_t time) {
|
||||
this->set_rx_full_threshold(val);
|
||||
}
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
} // namespace esphome::uart
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
#include "esphome/core/automation.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
namespace esphome::uart {
|
||||
|
||||
enum UARTParityOptions {
|
||||
UART_CONFIG_PARITY_NONE,
|
||||
@@ -199,5 +198,4 @@ class UARTComponent {
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
} // namespace esphome::uart
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
#include "esphome/components/logger/logger.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
namespace esphome::uart {
|
||||
|
||||
static const char *const TAG = "uart.arduino_esp8266";
|
||||
bool ESP8266UartComponent::serial0_in_use = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
@@ -331,6 +330,5 @@ int ESP8266SoftwareSerial::available() {
|
||||
return avail;
|
||||
}
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
} // namespace esphome::uart
|
||||
#endif // USE_ESP8266
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include "uart_component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
namespace esphome::uart {
|
||||
|
||||
class ESP8266SoftwareSerial {
|
||||
public:
|
||||
@@ -88,7 +87,5 @@ class ESP8266UartComponent : public UARTComponent, public Component {
|
||||
static bool serial0_in_use; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
};
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
|
||||
} // namespace esphome::uart
|
||||
#endif // USE_ESP8266
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
#include "esphome/components/logger/logger.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
namespace esphome::uart {
|
||||
|
||||
static const char *const TAG = "uart.idf";
|
||||
|
||||
uart_config_t IDFUARTComponent::get_config_() {
|
||||
@@ -112,6 +112,12 @@ void IDFUARTComponent::load_settings(bool dump_config) {
|
||||
esp_err_t err;
|
||||
|
||||
if (uart_is_driver_installed(this->uart_num_)) {
|
||||
#ifdef USE_UART_WAKE_LOOP_ON_RX
|
||||
if (this->rx_event_task_handle_ != nullptr) {
|
||||
vTaskDelete(this->rx_event_task_handle_);
|
||||
this->rx_event_task_handle_ = nullptr;
|
||||
}
|
||||
#endif
|
||||
err = uart_driver_delete(this->uart_num_);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "uart_driver_delete failed: %s", esp_err_to_name(err));
|
||||
@@ -204,6 +210,11 @@ void IDFUARTComponent::load_settings(bool dump_config) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef USE_UART_WAKE_LOOP_ON_RX
|
||||
// Start the RX event task to enable low-latency data notifications
|
||||
this->start_rx_event_task_();
|
||||
#endif // USE_UART_WAKE_LOOP_ON_RX
|
||||
|
||||
if (dump_config) {
|
||||
ESP_LOGCONFIG(TAG, "Reloaded UART %u", this->uart_num_);
|
||||
this->dump_config();
|
||||
@@ -226,7 +237,11 @@ void IDFUARTComponent::dump_config() {
|
||||
" Baud Rate: %" PRIu32 " baud\n"
|
||||
" Data Bits: %u\n"
|
||||
" Parity: %s\n"
|
||||
" Stop bits: %u",
|
||||
" Stop bits: %u"
|
||||
#ifdef USE_UART_WAKE_LOOP_ON_RX
|
||||
"\n Wake on data RX: ENABLED"
|
||||
#endif
|
||||
,
|
||||
this->baud_rate_, this->data_bits_, LOG_STR_ARG(parity_to_str(this->parity_)), this->stop_bits_);
|
||||
this->check_logger_conflict();
|
||||
}
|
||||
@@ -337,7 +352,58 @@ void IDFUARTComponent::flush() {
|
||||
|
||||
void IDFUARTComponent::check_logger_conflict() {}
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
#ifdef USE_UART_WAKE_LOOP_ON_RX
|
||||
void IDFUARTComponent::start_rx_event_task_() {
|
||||
// Create FreeRTOS task to monitor UART events
|
||||
BaseType_t result = xTaskCreate(rx_event_task_func, // Task function
|
||||
"uart_rx_evt", // Task name (max 16 chars)
|
||||
2240, // Stack size in bytes (~2.2KB); increase if needed for logging
|
||||
this, // Task parameter (this pointer)
|
||||
tskIDLE_PRIORITY + 1, // Priority (low, just above idle)
|
||||
&this->rx_event_task_handle_ // Task handle
|
||||
);
|
||||
|
||||
if (result != pdPASS) {
|
||||
ESP_LOGE(TAG, "Failed to create RX event task");
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, "RX event task started");
|
||||
}
|
||||
|
||||
void IDFUARTComponent::rx_event_task_func(void *param) {
|
||||
auto *self = static_cast<IDFUARTComponent *>(param);
|
||||
uart_event_t event;
|
||||
|
||||
ESP_LOGV(TAG, "RX event task running");
|
||||
|
||||
// Run forever - task lifecycle matches component lifecycle
|
||||
while (true) {
|
||||
// Wait for UART events (blocks efficiently)
|
||||
if (xQueueReceive(self->uart_event_queue_, &event, portMAX_DELAY) == pdTRUE) {
|
||||
switch (event.type) {
|
||||
case UART_DATA:
|
||||
// Data available in UART RX buffer - wake the main loop
|
||||
ESP_LOGVV(TAG, "Data event: %d bytes", event.size);
|
||||
App.wake_loop_threadsafe();
|
||||
break;
|
||||
|
||||
case UART_FIFO_OVF:
|
||||
case UART_BUFFER_FULL:
|
||||
ESP_LOGW(TAG, "FIFO overflow or ring buffer full - clearing");
|
||||
uart_flush_input(self->uart_num_);
|
||||
App.wake_loop_threadsafe();
|
||||
break;
|
||||
|
||||
default:
|
||||
// Ignore other event types
|
||||
ESP_LOGVV(TAG, "Event type: %d", event.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // USE_UART_WAKE_LOOP_ON_RX
|
||||
|
||||
} // namespace esphome::uart
|
||||
#endif // USE_ESP32
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "uart_component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
namespace esphome::uart {
|
||||
|
||||
class IDFUARTComponent : public UARTComponent, public Component {
|
||||
public:
|
||||
@@ -53,9 +52,15 @@ class IDFUARTComponent : public UARTComponent, public Component {
|
||||
|
||||
bool has_peek_{false};
|
||||
uint8_t peek_byte_;
|
||||
|
||||
#ifdef USE_UART_WAKE_LOOP_ON_RX
|
||||
// RX notification support
|
||||
void start_rx_event_task_();
|
||||
static void rx_event_task_func(void *param);
|
||||
|
||||
TaskHandle_t rx_event_task_handle_{nullptr};
|
||||
#endif // USE_UART_WAKE_LOOP_ON_RX
|
||||
};
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
|
||||
} // namespace esphome::uart
|
||||
#endif // USE_ESP32
|
||||
|
||||
@@ -96,8 +96,7 @@ speed_t get_baud(int baud) {
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
namespace esphome::uart {
|
||||
|
||||
static const char *const TAG = "uart.host";
|
||||
|
||||
@@ -296,7 +295,5 @@ void HostUartComponent::update_error_(const std::string &error) {
|
||||
ESP_LOGE(TAG, "Port error: %s", error.c_str());
|
||||
}
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
|
||||
} // namespace esphome::uart
|
||||
#endif // USE_HOST
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include "uart_component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
namespace esphome::uart {
|
||||
|
||||
class HostUartComponent : public UARTComponent, public Component {
|
||||
public:
|
||||
@@ -32,7 +31,5 @@ class HostUartComponent : public UARTComponent, public Component {
|
||||
uint8_t peek_byte_;
|
||||
};
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
|
||||
} // namespace esphome::uart
|
||||
#endif // USE_HOST
|
||||
|
||||
@@ -14,8 +14,7 @@
|
||||
#include <SoftwareSerial.h>
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
namespace esphome::uart {
|
||||
|
||||
static const char *const TAG = "uart.lt";
|
||||
|
||||
@@ -187,7 +186,5 @@ void LibreTinyUARTComponent::check_logger_conflict() {
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
|
||||
} // namespace esphome::uart
|
||||
#endif // USE_LIBRETINY
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include "uart_component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
namespace esphome::uart {
|
||||
|
||||
class LibreTinyUARTComponent : public UARTComponent, public Component {
|
||||
public:
|
||||
@@ -37,7 +36,5 @@ class LibreTinyUARTComponent : public UARTComponent, public Component {
|
||||
int8_t hardware_idx_{-1};
|
||||
};
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
|
||||
} // namespace esphome::uart
|
||||
#endif // USE_LIBRETINY
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
#include "esphome/components/logger/logger.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
namespace esphome::uart {
|
||||
|
||||
static const char *const TAG = "uart.arduino_rp2040";
|
||||
|
||||
@@ -193,7 +192,5 @@ void RP2040UartComponent::flush() {
|
||||
this->serial_->flush();
|
||||
}
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
|
||||
} // namespace esphome::uart
|
||||
#endif // USE_RP2040
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include "uart_component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
namespace esphome::uart {
|
||||
|
||||
class RP2040UartComponent : public UARTComponent, public Component {
|
||||
public:
|
||||
@@ -40,7 +39,5 @@ class RP2040UartComponent : public UARTComponent, public Component {
|
||||
HardwareSerial *serial_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
|
||||
} // namespace esphome::uart
|
||||
#endif // USE_RP2040
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
namespace esphome::uart {
|
||||
|
||||
static const char *const TAG = "uart_debug";
|
||||
|
||||
@@ -197,6 +196,5 @@ void UARTDebug::log_binary(UARTDirection direction, std::vector<uint8_t> bytes,
|
||||
delay(10);
|
||||
}
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
} // namespace esphome::uart
|
||||
#endif
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
#include "uart.h"
|
||||
#include "uart_component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
namespace esphome::uart {
|
||||
|
||||
/// The UARTDebugger class adds debugging support to a UART bus.
|
||||
///
|
||||
@@ -96,6 +95,5 @@ class UARTDebug {
|
||||
static void log_binary(UARTDirection direction, std::vector<uint8_t> bytes, uint8_t separator);
|
||||
};
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
} // namespace esphome::uart
|
||||
#endif
|
||||
|
||||
@@ -6,6 +6,8 @@ namespace esphome::wifi_info {
|
||||
|
||||
static const char *const TAG = "wifi_info";
|
||||
|
||||
#ifdef USE_WIFI_LISTENERS
|
||||
|
||||
static constexpr size_t MAX_STATE_LENGTH = 255;
|
||||
|
||||
/********************
|
||||
@@ -98,6 +100,8 @@ void BSSIDWiFiInfo::on_wifi_connect_state(const std::string &ssid, const wifi::b
|
||||
this->publish_state(buf);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* MacAddressWifiInfo
|
||||
********************/
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
namespace esphome::wifi_info {
|
||||
|
||||
#ifdef USE_WIFI_LISTENERS
|
||||
class IPAddressWiFiInfo final : public Component, public text_sensor::TextSensor, public wifi::WiFiIPStateListener {
|
||||
public:
|
||||
void setup() override;
|
||||
@@ -62,6 +63,7 @@ class BSSIDWiFiInfo final : public Component, public text_sensor::TextSensor, pu
|
||||
// WiFiConnectStateListener interface
|
||||
void on_wifi_connect_state(const std::string &ssid, const wifi::bssid_t &bssid) override;
|
||||
};
|
||||
#endif
|
||||
|
||||
class MacAddressWifiInfo final : public Component, public text_sensor::TextSensor {
|
||||
public:
|
||||
|
||||
@@ -41,3 +41,6 @@ async def to_code(config):
|
||||
await cg.register_component(var, config)
|
||||
await uart.register_uart_device(var, config)
|
||||
cg.add_define("USE_ZWAVE_PROXY")
|
||||
|
||||
# Request UART to wake the main loop when data arrives for low-latency processing
|
||||
uart.request_wake_loop_on_rx()
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/util.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace zwave_proxy {
|
||||
namespace esphome::zwave_proxy {
|
||||
|
||||
static const char *const TAG = "zwave_proxy";
|
||||
|
||||
@@ -144,6 +143,7 @@ void ZWaveProxy::zwave_proxy_request(api::APIConnection *api_connection, api::en
|
||||
this->api_connection_ = api_connection;
|
||||
ESP_LOGV(TAG, "API connection is now subscribed");
|
||||
break;
|
||||
|
||||
case api::enums::ZWAVE_PROXY_REQUEST_TYPE_UNSUBSCRIBE:
|
||||
if (this->api_connection_ != api_connection) {
|
||||
ESP_LOGV(TAG, "API connection is not subscribed");
|
||||
@@ -151,6 +151,7 @@ void ZWaveProxy::zwave_proxy_request(api::APIConnection *api_connection, api::en
|
||||
}
|
||||
this->api_connection_ = nullptr;
|
||||
break;
|
||||
|
||||
default:
|
||||
ESP_LOGW(TAG, "Unknown request type: %d", type);
|
||||
break;
|
||||
@@ -342,5 +343,4 @@ bool ZWaveProxy::response_handler_() {
|
||||
|
||||
ZWaveProxy *global_zwave_proxy = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
} // namespace zwave_proxy
|
||||
} // namespace esphome
|
||||
} // namespace esphome::zwave_proxy
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace esphome {
|
||||
namespace zwave_proxy {
|
||||
namespace esphome::zwave_proxy {
|
||||
|
||||
static constexpr size_t MAX_ZWAVE_FRAME_SIZE = 257; // Maximum Z-Wave frame size
|
||||
|
||||
@@ -89,5 +88,4 @@ class ZWaveProxy : public uart::UARTDevice, public Component {
|
||||
|
||||
extern ZWaveProxy *global_zwave_proxy; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
} // namespace zwave_proxy
|
||||
} // namespace esphome
|
||||
} // namespace esphome::zwave_proxy
|
||||
|
||||
@@ -108,6 +108,7 @@
|
||||
#define USE_TIME
|
||||
#define USE_TOUCHSCREEN
|
||||
#define USE_UART_DEBUGGER
|
||||
#define USE_UART_WAKE_LOOP_ON_RX
|
||||
#define USE_UPDATE
|
||||
#define USE_VALVE
|
||||
#define USE_ZWAVE_PROXY
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
from collections.abc import Callable
|
||||
import importlib
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import shutil
|
||||
import stat
|
||||
from types import TracebackType
|
||||
|
||||
from esphome import loader
|
||||
from esphome.config import iter_component_configs, iter_components
|
||||
@@ -301,9 +305,24 @@ def clean_cmake_cache():
|
||||
pioenvs_cmake_path.unlink()
|
||||
|
||||
|
||||
def clean_build(clear_pio_cache: bool = True):
|
||||
import shutil
|
||||
def _rmtree_error_handler(
|
||||
func: Callable[[str], object],
|
||||
path: str,
|
||||
exc_info: tuple[type[BaseException], BaseException, TracebackType | None],
|
||||
) -> None:
|
||||
"""Error handler for shutil.rmtree to handle read-only files on Windows.
|
||||
|
||||
On Windows, git pack files and other files may be marked read-only,
|
||||
causing shutil.rmtree to fail with "Access is denied". This handler
|
||||
removes the read-only flag and retries the deletion.
|
||||
"""
|
||||
if os.access(path, os.W_OK):
|
||||
raise exc_info[1].with_traceback(exc_info[2])
|
||||
os.chmod(path, stat.S_IWUSR | stat.S_IRUSR)
|
||||
func(path)
|
||||
|
||||
|
||||
def clean_build(clear_pio_cache: bool = True):
|
||||
# Allow skipping cache cleaning for integration tests
|
||||
if os.environ.get("ESPHOME_SKIP_CLEAN_BUILD"):
|
||||
_LOGGER.warning("Skipping build cleaning (ESPHOME_SKIP_CLEAN_BUILD set)")
|
||||
@@ -312,11 +331,11 @@ def clean_build(clear_pio_cache: bool = True):
|
||||
pioenvs = CORE.relative_pioenvs_path()
|
||||
if pioenvs.is_dir():
|
||||
_LOGGER.info("Deleting %s", pioenvs)
|
||||
shutil.rmtree(pioenvs)
|
||||
shutil.rmtree(pioenvs, onerror=_rmtree_error_handler)
|
||||
piolibdeps = CORE.relative_piolibdeps_path()
|
||||
if piolibdeps.is_dir():
|
||||
_LOGGER.info("Deleting %s", piolibdeps)
|
||||
shutil.rmtree(piolibdeps)
|
||||
shutil.rmtree(piolibdeps, onerror=_rmtree_error_handler)
|
||||
dependencies_lock = CORE.relative_build_path("dependencies.lock")
|
||||
if dependencies_lock.is_file():
|
||||
_LOGGER.info("Deleting %s", dependencies_lock)
|
||||
@@ -337,12 +356,10 @@ def clean_build(clear_pio_cache: bool = True):
|
||||
cache_dir = Path(config.get("platformio", "cache_dir"))
|
||||
if cache_dir.is_dir():
|
||||
_LOGGER.info("Deleting PlatformIO cache %s", cache_dir)
|
||||
shutil.rmtree(cache_dir)
|
||||
shutil.rmtree(cache_dir, onerror=_rmtree_error_handler)
|
||||
|
||||
|
||||
def clean_all(configuration: list[str]):
|
||||
import shutil
|
||||
|
||||
data_dirs = []
|
||||
for config in configuration:
|
||||
item = Path(config)
|
||||
@@ -364,7 +381,7 @@ def clean_all(configuration: list[str]):
|
||||
if item.is_file() and not item.name.endswith(".json"):
|
||||
item.unlink()
|
||||
elif item.is_dir() and item.name != "storage":
|
||||
shutil.rmtree(item)
|
||||
shutil.rmtree(item, onerror=_rmtree_error_handler)
|
||||
|
||||
# Clean PlatformIO project files
|
||||
try:
|
||||
@@ -378,7 +395,7 @@ def clean_all(configuration: list[str]):
|
||||
path = Path(config.get("platformio", pio_dir))
|
||||
if path.is_dir():
|
||||
_LOGGER.info("Deleting PlatformIO %s %s", pio_dir, path)
|
||||
shutil.rmtree(path)
|
||||
shutil.rmtree(path, onerror=_rmtree_error_handler)
|
||||
|
||||
|
||||
GITIGNORE_CONTENT = """# Gitignore settings for ESPHome
|
||||
|
||||
12
tests/components/remote_receiver/test.rp2040-ard.yaml
Normal file
12
tests/components/remote_receiver/test.rp2040-ard.yaml
Normal file
@@ -0,0 +1,12 @@
|
||||
remote_receiver:
|
||||
id: rcvr
|
||||
pin: GPIO5
|
||||
dump: all
|
||||
<<: !include common-actions.yaml
|
||||
|
||||
binary_sensor:
|
||||
- platform: remote_receiver
|
||||
name: Panasonic Remote Input
|
||||
panasonic:
|
||||
address: 0x4004
|
||||
command: 0x100BCBD
|
||||
7
tests/components/remote_transmitter/test.rp2040-ard.yaml
Normal file
7
tests/components/remote_transmitter/test.rp2040-ard.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
remote_transmitter:
|
||||
id: xmitr
|
||||
pin: GPIO5
|
||||
carrier_duty_percent: 50%
|
||||
|
||||
packages:
|
||||
buttons: !include common-buttons.yaml
|
||||
8
tests/components/wifi_info/common-mac.yaml
Normal file
8
tests/components/wifi_info/common-mac.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
wifi:
|
||||
ssid: MySSID
|
||||
password: password1
|
||||
|
||||
text_sensor:
|
||||
- platform: wifi_info
|
||||
mac_address:
|
||||
name: MAC Address
|
||||
@@ -13,6 +13,6 @@ text_sensor:
|
||||
bssid:
|
||||
name: BSSID
|
||||
mac_address:
|
||||
name: Mac Address
|
||||
name: MAC Address
|
||||
dns_address:
|
||||
name: DNS ADdress
|
||||
|
||||
4
tests/components/wifi_info/test-mac.esp32-idf.yaml
Normal file
4
tests/components/wifi_info/test-mac.esp32-idf.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml
|
||||
|
||||
<<: !include common-mac.yaml
|
||||
4
tests/components/wifi_info/test-mac.esp8266-ard.yaml
Normal file
4
tests/components/wifi_info/test-mac.esp8266-ard.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml
|
||||
|
||||
<<: !include common-mac.yaml
|
||||
4
tests/components/wifi_info/test-mac.rp2040-ard.yaml
Normal file
4
tests/components/wifi_info/test-mac.rp2040-ard.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml
|
||||
|
||||
<<: !include common-mac.yaml
|
||||
@@ -1,7 +1,9 @@
|
||||
"""Test writer module functionality."""
|
||||
|
||||
from collections.abc import Callable
|
||||
import os
|
||||
from pathlib import Path
|
||||
import stat
|
||||
from typing import Any
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
@@ -15,6 +17,7 @@ from esphome.writer import (
|
||||
CPP_INCLUDE_BEGIN,
|
||||
CPP_INCLUDE_END,
|
||||
GITIGNORE_CONTENT,
|
||||
clean_all,
|
||||
clean_build,
|
||||
clean_cmake_cache,
|
||||
storage_should_clean,
|
||||
@@ -1063,3 +1066,103 @@ def test_clean_all_preserves_json_files(
|
||||
# Verify logging mentions cleaning
|
||||
assert "Cleaning" in caplog.text
|
||||
assert str(build_dir) in caplog.text
|
||||
|
||||
|
||||
@patch("esphome.writer.CORE")
|
||||
def test_clean_build_handles_readonly_files(
|
||||
mock_core: MagicMock,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""Test clean_build handles read-only files (e.g., git pack files on Windows)."""
|
||||
# Create directory structure with read-only files
|
||||
pioenvs_dir = tmp_path / ".pioenvs"
|
||||
pioenvs_dir.mkdir()
|
||||
git_dir = pioenvs_dir / ".git" / "objects" / "pack"
|
||||
git_dir.mkdir(parents=True)
|
||||
|
||||
# Create a read-only file (simulating git pack files on Windows)
|
||||
readonly_file = git_dir / "pack-abc123.pack"
|
||||
readonly_file.write_text("pack data")
|
||||
os.chmod(readonly_file, stat.S_IRUSR) # Read-only
|
||||
|
||||
# Setup mocks
|
||||
mock_core.relative_pioenvs_path.return_value = pioenvs_dir
|
||||
mock_core.relative_piolibdeps_path.return_value = tmp_path / ".piolibdeps"
|
||||
mock_core.relative_build_path.return_value = tmp_path / "dependencies.lock"
|
||||
|
||||
# Verify file is read-only
|
||||
assert not os.access(readonly_file, os.W_OK)
|
||||
|
||||
# Call the function - should not crash
|
||||
clean_build()
|
||||
|
||||
# Verify directory was removed despite read-only files
|
||||
assert not pioenvs_dir.exists()
|
||||
|
||||
|
||||
@patch("esphome.writer.CORE")
|
||||
def test_clean_all_handles_readonly_files(
|
||||
mock_core: MagicMock,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""Test clean_all handles read-only files."""
|
||||
# Create config directory
|
||||
config_dir = tmp_path / "config"
|
||||
config_dir.mkdir()
|
||||
|
||||
build_dir = config_dir / ".esphome"
|
||||
build_dir.mkdir()
|
||||
|
||||
# Create a subdirectory with read-only files
|
||||
subdir = build_dir / "subdir"
|
||||
subdir.mkdir()
|
||||
readonly_file = subdir / "readonly.txt"
|
||||
readonly_file.write_text("content")
|
||||
os.chmod(readonly_file, stat.S_IRUSR) # Read-only
|
||||
|
||||
# Verify file is read-only
|
||||
assert not os.access(readonly_file, os.W_OK)
|
||||
|
||||
# Call the function - should not crash
|
||||
clean_all([str(config_dir)])
|
||||
|
||||
# Verify directory was removed despite read-only files
|
||||
assert not subdir.exists()
|
||||
assert build_dir.exists() # .esphome dir itself is preserved
|
||||
|
||||
|
||||
@patch("esphome.writer.CORE")
|
||||
def test_clean_build_reraises_for_other_errors(
|
||||
mock_core: MagicMock,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""Test clean_build re-raises errors that are not read-only permission issues."""
|
||||
# Create directory structure with a read-only subdirectory
|
||||
# This prevents file deletion and triggers the error handler
|
||||
pioenvs_dir = tmp_path / ".pioenvs"
|
||||
pioenvs_dir.mkdir()
|
||||
subdir = pioenvs_dir / "subdir"
|
||||
subdir.mkdir()
|
||||
test_file = subdir / "test.txt"
|
||||
test_file.write_text("content")
|
||||
|
||||
# Make subdir read-only so files inside can't be deleted
|
||||
os.chmod(subdir, stat.S_IRUSR | stat.S_IXUSR)
|
||||
|
||||
# Setup mocks
|
||||
mock_core.relative_pioenvs_path.return_value = pioenvs_dir
|
||||
mock_core.relative_piolibdeps_path.return_value = tmp_path / ".piolibdeps"
|
||||
mock_core.relative_build_path.return_value = tmp_path / "dependencies.lock"
|
||||
|
||||
try:
|
||||
# Mock os.access in writer module to return True (writable)
|
||||
# This simulates a case where the error is NOT due to read-only permissions
|
||||
# so the error handler should re-raise instead of trying to fix permissions
|
||||
with (
|
||||
patch("esphome.writer.os.access", return_value=True),
|
||||
pytest.raises(PermissionError),
|
||||
):
|
||||
clean_build()
|
||||
finally:
|
||||
# Cleanup - restore write permission so tmp_path cleanup works
|
||||
os.chmod(subdir, stat.S_IRWXU)
|
||||
|
||||
Reference in New Issue
Block a user