mirror of
https://github.com/esphome/esphome.git
synced 2026-01-15 06:27:41 -07:00
Compare commits
1 Commits
dev
...
sockaddr_h
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7f3606ef6 |
@@ -31,8 +31,7 @@ void AlarmControlPanel::publish_state(AlarmControlPanelState state) {
|
||||
this->last_update_ = millis();
|
||||
if (state != this->current_state_) {
|
||||
auto prev_state = this->current_state_;
|
||||
ESP_LOGD(TAG, "'%s' >> %s (was %s)", this->get_name().c_str(),
|
||||
LOG_STR_ARG(alarm_control_panel_state_to_string(state)),
|
||||
ESP_LOGD(TAG, "Set state to: %s, previous: %s", LOG_STR_ARG(alarm_control_panel_state_to_string(state)),
|
||||
LOG_STR_ARG(alarm_control_panel_state_to_string(prev_state)));
|
||||
this->current_state_ = state;
|
||||
// Single state callback - triggers check get_state() for specific states
|
||||
|
||||
@@ -44,7 +44,7 @@ bool BinarySensor::set_new_state(const optional<bool> &new_state) {
|
||||
#if defined(USE_BINARY_SENSOR) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_binary_sensor_update(this);
|
||||
#endif
|
||||
ESP_LOGD(TAG, "'%s' >> %s", this->get_name().c_str(), ONOFFMAYBE(new_state));
|
||||
ESP_LOGD(TAG, "'%s': %s", this->get_name().c_str(), ONOFFMAYBE(new_state));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -436,7 +436,7 @@ void Climate::save_state_() {
|
||||
}
|
||||
|
||||
void Climate::publish_state() {
|
||||
ESP_LOGD(TAG, "'%s' >>", this->name_.c_str());
|
||||
ESP_LOGD(TAG, "'%s' - Sending state:", this->name_.c_str());
|
||||
auto traits = this->get_traits();
|
||||
|
||||
ESP_LOGD(TAG, " Mode: %s", LOG_STR_ARG(climate_mode_to_string(this->mode)));
|
||||
|
||||
@@ -153,7 +153,7 @@ void Cover::publish_state(bool save) {
|
||||
this->position = clamp(this->position, 0.0f, 1.0f);
|
||||
this->tilt = clamp(this->tilt, 0.0f, 1.0f);
|
||||
|
||||
ESP_LOGD(TAG, "'%s' >>", this->name_.c_str());
|
||||
ESP_LOGD(TAG, "'%s' - Publishing:", this->name_.c_str());
|
||||
auto traits = this->get_traits();
|
||||
if (traits.get_supports_position()) {
|
||||
ESP_LOGD(TAG, " Position: %.0f%%", this->position * 100.0f);
|
||||
|
||||
@@ -30,7 +30,7 @@ void DateEntity::publish_state() {
|
||||
return;
|
||||
}
|
||||
this->set_has_state(true);
|
||||
ESP_LOGD(TAG, "'%s' >> %d-%d-%d", this->get_name().c_str(), this->year_, this->month_, this->day_);
|
||||
ESP_LOGD(TAG, "'%s': Sending date %d-%d-%d", this->get_name().c_str(), this->year_, this->month_, this->day_);
|
||||
this->state_callback_.call();
|
||||
#if defined(USE_DATETIME_DATE) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_date_update(this);
|
||||
|
||||
@@ -45,8 +45,8 @@ void DateTimeEntity::publish_state() {
|
||||
return;
|
||||
}
|
||||
this->set_has_state(true);
|
||||
ESP_LOGD(TAG, "'%s' >> %04u-%02u-%02u %02d:%02d:%02d", this->get_name().c_str(), this->year_, this->month_,
|
||||
this->day_, this->hour_, this->minute_, this->second_);
|
||||
ESP_LOGD(TAG, "'%s': Sending datetime %04u-%02u-%02u %02d:%02d:%02d", this->get_name().c_str(), this->year_,
|
||||
this->month_, this->day_, this->hour_, this->minute_, this->second_);
|
||||
this->state_callback_.call();
|
||||
#if defined(USE_DATETIME_DATETIME) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_datetime_update(this);
|
||||
|
||||
@@ -26,7 +26,8 @@ void TimeEntity::publish_state() {
|
||||
return;
|
||||
}
|
||||
this->set_has_state(true);
|
||||
ESP_LOGD(TAG, "'%s' >> %02d:%02d:%02d", this->get_name().c_str(), this->hour_, this->minute_, this->second_);
|
||||
ESP_LOGD(TAG, "'%s': Sending time %02d:%02d:%02d", this->get_name().c_str(), this->hour_, this->minute_,
|
||||
this->second_);
|
||||
this->state_callback_.call();
|
||||
#if defined(USE_DATETIME_TIME) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_time_update(this);
|
||||
|
||||
@@ -22,7 +22,7 @@ void Event::trigger(const std::string &event_type) {
|
||||
return;
|
||||
}
|
||||
this->last_event_type_ = found;
|
||||
ESP_LOGD(TAG, "'%s' >> '%s'", this->get_name().c_str(), this->last_event_type_);
|
||||
ESP_LOGD(TAG, "'%s' Triggered event '%s'", this->get_name().c_str(), this->last_event_type_);
|
||||
this->event_callback_.call(event_type);
|
||||
#if defined(USE_EVENT) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_event(this);
|
||||
|
||||
@@ -201,7 +201,7 @@ void Fan::publish_state() {
|
||||
auto traits = this->get_traits();
|
||||
|
||||
ESP_LOGD(TAG,
|
||||
"'%s' >>\n"
|
||||
"'%s' - Sending state:\n"
|
||||
" State: %s",
|
||||
this->name_.c_str(), ONOFF(this->state));
|
||||
if (traits.supports_speed()) {
|
||||
|
||||
@@ -665,10 +665,15 @@ async def write_image(config, all_frames=False):
|
||||
if is_svg_file(path):
|
||||
import resvg_py
|
||||
|
||||
resize = resize or (None, None)
|
||||
image_data = resvg_py.svg_to_bytes(
|
||||
svg_path=str(path), width=resize[0], height=resize[1], dpi=100
|
||||
)
|
||||
if resize:
|
||||
width, height = resize
|
||||
# resvg-py allows rendering by width/height directly
|
||||
image_data = resvg_py.svg_to_bytes(
|
||||
svg_path=str(path), width=int(width), height=int(height)
|
||||
)
|
||||
else:
|
||||
# Default size
|
||||
image_data = resvg_py.svg_to_bytes(svg_path=str(path))
|
||||
|
||||
# Convert bytes to Pillow Image
|
||||
image = Image.open(io.BytesIO(image_data))
|
||||
|
||||
@@ -52,7 +52,7 @@ void Lock::publish_state(LockState state) {
|
||||
|
||||
this->state = state;
|
||||
this->rtc_.save(&this->state);
|
||||
ESP_LOGD(TAG, "'%s' >> %s", this->name_.c_str(), LOG_STR_ARG(lock_state_to_string(state)));
|
||||
ESP_LOGD(TAG, "'%s': Sending state %s", this->name_.c_str(), LOG_STR_ARG(lock_state_to_string(state)));
|
||||
this->state_callback_.call();
|
||||
#if defined(USE_LOCK) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_lock_update(this);
|
||||
|
||||
@@ -31,7 +31,7 @@ void log_number(const char *tag, const char *prefix, const char *type, Number *o
|
||||
void Number::publish_state(float state) {
|
||||
this->set_has_state(true);
|
||||
this->state = state;
|
||||
ESP_LOGD(TAG, "'%s' >> %.2f", this->get_name().c_str(), state);
|
||||
ESP_LOGD(TAG, "'%s': Sending state %f", this->get_name().c_str(), state);
|
||||
this->state_callback_.call(state);
|
||||
#if defined(USE_NUMBER) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_number_update(this);
|
||||
|
||||
@@ -31,7 +31,7 @@ void Select::publish_state(size_t index) {
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
this->state = option; // Update deprecated member for backward compatibility
|
||||
#pragma GCC diagnostic pop
|
||||
ESP_LOGD(TAG, "'%s' >> %s (%zu)", this->get_name().c_str(), option, index);
|
||||
ESP_LOGD(TAG, "'%s': Sending state %s (index %zu)", this->get_name().c_str(), option, index);
|
||||
this->state_callback_.call(index);
|
||||
#if defined(USE_SELECT) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_select_update(this);
|
||||
|
||||
@@ -126,8 +126,8 @@ float Sensor::get_raw_state() const { return this->raw_state; }
|
||||
void Sensor::internal_send_state_to_frontend(float state) {
|
||||
this->set_has_state(true);
|
||||
this->state = state;
|
||||
ESP_LOGD(TAG, "'%s' >> %.*f %s", this->get_name().c_str(), std::max(0, (int) this->get_accuracy_decimals()), state,
|
||||
this->get_unit_of_measurement_ref().c_str());
|
||||
ESP_LOGD(TAG, "'%s': Sending state %.5f %s with %d decimals of accuracy", this->get_name().c_str(), state,
|
||||
this->get_unit_of_measurement_ref().c_str(), this->get_accuracy_decimals());
|
||||
this->callback_.call(state);
|
||||
#if defined(USE_SENSOR) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_sensor_update(this);
|
||||
|
||||
@@ -107,9 +107,9 @@ std::unique_ptr<Socket> socket_ip_loop_monitored(int type, int protocol) {
|
||||
#endif /* USE_NETWORK_IPV6 */
|
||||
}
|
||||
|
||||
socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const std::string &ip_address, uint16_t port) {
|
||||
socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const char *ip_address, uint16_t port) {
|
||||
#if USE_NETWORK_IPV6
|
||||
if (ip_address.find(':') != std::string::npos) {
|
||||
if (strchr(ip_address, ':') != nullptr) {
|
||||
if (addrlen < sizeof(sockaddr_in6)) {
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
@@ -121,14 +121,14 @@ socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const std::stri
|
||||
|
||||
#ifdef USE_SOCKET_IMPL_BSD_SOCKETS
|
||||
// Use standard inet_pton for BSD sockets
|
||||
if (inet_pton(AF_INET6, ip_address.c_str(), &server->sin6_addr) != 1) {
|
||||
if (inet_pton(AF_INET6, ip_address, &server->sin6_addr) != 1) {
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
// Use LWIP-specific functions
|
||||
ip6_addr_t ip6;
|
||||
inet6_aton(ip_address.c_str(), &ip6);
|
||||
inet6_aton(ip_address, &ip6);
|
||||
memcpy(server->sin6_addr.un.u32_addr, ip6.addr, sizeof(ip6.addr));
|
||||
#endif
|
||||
return sizeof(sockaddr_in6);
|
||||
@@ -141,7 +141,7 @@ socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const std::stri
|
||||
auto *server = reinterpret_cast<sockaddr_in *>(addr);
|
||||
memset(server, 0, sizeof(sockaddr_in));
|
||||
server->sin_family = AF_INET;
|
||||
server->sin_addr.s_addr = inet_addr(ip_address.c_str());
|
||||
server->sin_addr.s_addr = inet_addr(ip_address);
|
||||
server->sin_port = htons(port);
|
||||
return sizeof(sockaddr_in);
|
||||
}
|
||||
|
||||
@@ -87,7 +87,17 @@ std::unique_ptr<Socket> socket_loop_monitored(int domain, int type, int protocol
|
||||
std::unique_ptr<Socket> socket_ip_loop_monitored(int type, int protocol);
|
||||
|
||||
/// Set a sockaddr to the specified address and port for the IP version used by socket_ip().
|
||||
socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const std::string &ip_address, uint16_t port);
|
||||
/// @param addr Destination sockaddr structure
|
||||
/// @param addrlen Size of the addr buffer
|
||||
/// @param ip_address Null-terminated IP address string (IPv4 or IPv6)
|
||||
/// @param port Port number in host byte order
|
||||
/// @return Size of the sockaddr structure used, or 0 on error
|
||||
socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const char *ip_address, uint16_t port);
|
||||
|
||||
/// Convenience overload for std::string (backward compatible).
|
||||
inline socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const std::string &ip_address, uint16_t port) {
|
||||
return set_sockaddr(addr, addrlen, ip_address.c_str(), port);
|
||||
}
|
||||
|
||||
/// Set a sockaddr to the any address and specified port for the IP version used by socket_ip().
|
||||
socklen_t set_sockaddr_any(struct sockaddr *addr, socklen_t addrlen, uint16_t port);
|
||||
|
||||
@@ -62,7 +62,7 @@ void Switch::publish_state(bool state) {
|
||||
if (restore_mode & RESTORE_MODE_PERSISTENT_MASK)
|
||||
this->rtc_.save(&this->state);
|
||||
|
||||
ESP_LOGD(TAG, "'%s' >> %s", this->name_.c_str(), ONOFF(this->state));
|
||||
ESP_LOGD(TAG, "'%s': Sending state %s", this->name_.c_str(), ONOFF(this->state));
|
||||
this->state_callback_.call(this->state);
|
||||
#if defined(USE_SWITCH) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_switch_update(this);
|
||||
|
||||
@@ -20,9 +20,9 @@ void Text::publish_state(const char *state, size_t len) {
|
||||
this->state.assign(state, len);
|
||||
}
|
||||
if (this->traits.get_mode() == TEXT_MODE_PASSWORD) {
|
||||
ESP_LOGD(TAG, "'%s' >> " LOG_SECRET("'%s'"), this->get_name().c_str(), this->state.c_str());
|
||||
ESP_LOGD(TAG, "'%s': Sending state " LOG_SECRET("'%s'"), this->get_name().c_str(), this->state.c_str());
|
||||
} else {
|
||||
ESP_LOGD(TAG, "'%s' >> '%s'", this->get_name().c_str(), this->state.c_str());
|
||||
ESP_LOGD(TAG, "'%s': Sending state %s", this->get_name().c_str(), this->state.c_str());
|
||||
}
|
||||
this->state_callback_.call(this->state);
|
||||
#if defined(USE_TEXT) && defined(USE_CONTROLLER_REGISTRY)
|
||||
|
||||
@@ -116,7 +116,7 @@ void TextSensor::internal_send_state_to_frontend(const char *state, size_t len)
|
||||
|
||||
void TextSensor::notify_frontend_() {
|
||||
this->set_has_state(true);
|
||||
ESP_LOGD(TAG, "'%s' >> '%s'", this->name_.c_str(), this->state.c_str());
|
||||
ESP_LOGD(TAG, "'%s': Sending state '%s'", this->name_.c_str(), this->state.c_str());
|
||||
this->callback_.call(this->state);
|
||||
#if defined(USE_TEXT_SENSOR) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_text_sensor_update(this);
|
||||
|
||||
@@ -10,7 +10,7 @@ static const char *const TAG = "update";
|
||||
|
||||
void UpdateEntity::publish_state() {
|
||||
ESP_LOGD(TAG,
|
||||
"'%s' >>\n"
|
||||
"'%s' - Publishing:\n"
|
||||
" Current Version: %s",
|
||||
this->name_.c_str(), this->update_info_.current_version.c_str());
|
||||
|
||||
|
||||
@@ -133,7 +133,7 @@ void Valve::add_on_state_callback(std::function<void()> &&f) { this->state_callb
|
||||
void Valve::publish_state(bool save) {
|
||||
this->position = clamp(this->position, 0.0f, 1.0f);
|
||||
|
||||
ESP_LOGD(TAG, "'%s' >>", this->name_.c_str());
|
||||
ESP_LOGD(TAG, "'%s' - Publishing:", this->name_.c_str());
|
||||
auto traits = this->get_traits();
|
||||
if (traits.get_supports_position()) {
|
||||
ESP_LOGD(TAG, " Position: %.0f%%", this->position * 100.0f);
|
||||
|
||||
@@ -153,7 +153,7 @@ void WaterHeater::setup() {
|
||||
void WaterHeater::publish_state() {
|
||||
auto traits = this->get_traits();
|
||||
ESP_LOGD(TAG,
|
||||
"'%s' >>\n"
|
||||
"'%s' - Sending state:\n"
|
||||
" Mode: %s",
|
||||
this->name_.c_str(), LOG_STR_ARG(water_heater_mode_to_string(this->mode_)));
|
||||
if (!std::isnan(this->current_temperature_)) {
|
||||
|
||||
@@ -753,6 +753,9 @@ void WebServer::handle_button_request(AsyncWebServerRequest *request, const UrlM
|
||||
}
|
||||
request->send(404);
|
||||
}
|
||||
std::string WebServer::button_state_json_generator(WebServer *web_server, void *source) {
|
||||
return web_server->button_json_((button::Button *) (source), DETAIL_STATE);
|
||||
}
|
||||
std::string WebServer::button_all_json_generator(WebServer *web_server, void *source) {
|
||||
return web_server->button_json_((button::Button *) (source), DETAIL_ALL);
|
||||
}
|
||||
|
||||
@@ -295,7 +295,7 @@ class WebServer : public Controller,
|
||||
/// Handle a button request under '/button/<id>/press'.
|
||||
void handle_button_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
||||
|
||||
// Buttons are stateless, so there is no button_state_json_generator
|
||||
static std::string button_state_json_generator(WebServer *web_server, void *source);
|
||||
static std::string button_all_json_generator(WebServer *web_server, void *source);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="10mm" height="10mm" viewBox="0 0 100 100">
|
||||
<rect x="0" y="0" width="100" height="100" fill="#00FF00"/>
|
||||
<circle cx="50" cy="50" r="30" fill="#0000FF"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 248 B |
@@ -5,21 +5,17 @@ from __future__ import annotations
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from esphome import config_validation as cv
|
||||
from esphome.components.image import (
|
||||
CONF_INVERT_ALPHA,
|
||||
CONF_OPAQUE,
|
||||
CONF_TRANSPARENCY,
|
||||
CONFIG_SCHEMA,
|
||||
get_all_image_metadata,
|
||||
get_image_metadata,
|
||||
write_image,
|
||||
)
|
||||
from esphome.const import CONF_DITHER, CONF_FILE, CONF_ID, CONF_RAW_DATA_ID, CONF_TYPE
|
||||
from esphome.const import CONF_ID, CONF_RAW_DATA_ID, CONF_TYPE
|
||||
from esphome.core import CORE
|
||||
|
||||
|
||||
@@ -354,52 +350,3 @@ def test_get_all_image_metadata_empty() -> None:
|
||||
"get_all_image_metadata should always return a dict"
|
||||
)
|
||||
# Length could be 0 or more depending on what's in CORE at test time
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_progmem_array():
|
||||
"""Mock progmem_array to avoid needing a proper ID object in tests."""
|
||||
with patch("esphome.components.image.cg.progmem_array") as mock_progmem:
|
||||
mock_progmem.return_value = MagicMock()
|
||||
yield mock_progmem
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_svg_with_mm_dimensions_succeeds(
|
||||
component_config_path: Callable[[str], Path],
|
||||
mock_progmem_array: MagicMock,
|
||||
) -> None:
|
||||
"""Test that SVG files with dimensions in mm are successfully processed."""
|
||||
# Create a config for write_image without CONF_RESIZE
|
||||
config = {
|
||||
CONF_FILE: component_config_path("mm_dimensions.svg"),
|
||||
CONF_TYPE: "BINARY",
|
||||
CONF_TRANSPARENCY: CONF_OPAQUE,
|
||||
CONF_DITHER: "NONE",
|
||||
CONF_INVERT_ALPHA: False,
|
||||
CONF_RAW_DATA_ID: "test_raw_data_id",
|
||||
}
|
||||
|
||||
# This should succeed without raising an error
|
||||
result = await write_image(config)
|
||||
|
||||
# Verify that write_image returns the expected tuple
|
||||
assert isinstance(result, tuple), "write_image should return a tuple"
|
||||
assert len(result) == 6, "write_image should return 6 values"
|
||||
|
||||
prog_arr, width, height, image_type, trans_value, frame_count = result
|
||||
|
||||
# Verify the dimensions are positive integers
|
||||
# At 100 DPI, 10mm = ~39 pixels (10mm * 100dpi / 25.4mm_per_inch)
|
||||
assert isinstance(width, int), "Width should be an integer"
|
||||
assert isinstance(height, int), "Height should be an integer"
|
||||
assert width > 0, "Width should be positive"
|
||||
assert height > 0, "Height should be positive"
|
||||
assert frame_count == 1, "Single image should have frame_count of 1"
|
||||
# Verify we got reasonable dimensions from the mm-based SVG
|
||||
assert 30 < width < 50, (
|
||||
f"Width should be around 39 pixels for 10mm at 100dpi, got {width}"
|
||||
)
|
||||
assert 30 < height < 50, (
|
||||
f"Height should be around 39 pixels for 10mm at 100dpi, got {height}"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user