1
0
mirror of https://github.com/esphome/esphome.git synced 2025-09-02 03:12:20 +01:00

Merge pull request #10228 from esphome/bump-2025.8.0b2

2025.8.0b2
This commit is contained in:
Jesse Hills
2025-08-15 08:46:15 +12:00
committed by GitHub
10 changed files with 74 additions and 12 deletions

View File

@@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 2025.8.0b1
PROJECT_NUMBER = 2025.8.0b2
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a

View File

@@ -208,11 +208,11 @@ void ESPNowComponent::enable_() {
esp_wifi_connectionless_module_set_wake_interval(CONFIG_ESPNOW_WAKE_INTERVAL);
#endif
this->state_ = ESPNOW_STATE_ENABLED;
for (auto peer : this->peers_) {
this->add_peer(peer.address);
}
this->state_ = ESPNOW_STATE_ENABLED;
}
void ESPNowComponent::disable() {
@@ -407,7 +407,7 @@ esp_err_t ESPNowComponent::add_peer(const uint8_t *peer) {
}
if (memcmp(peer, this->own_address_, ESP_NOW_ETH_ALEN) == 0) {
this->mark_failed();
this->status_momentary_warning("peer-add-failed");
return ESP_ERR_INVALID_MAC;
}

View File

@@ -16,6 +16,7 @@ from esphome.components.esp32.const import (
import esphome.config_validation as cv
from esphome.const import (
CONF_ADVANCED,
CONF_DISABLED,
CONF_FRAMEWORK,
CONF_ID,
CONF_MODE,
@@ -102,6 +103,7 @@ def get_config_schema(config):
cv.Optional(CONF_MODE, default=modes[0]): cv.one_of(*modes, lower=True),
cv.Optional(CONF_ENABLE_ECC, default=False): cv.boolean,
cv.Optional(CONF_SPEED, default=speeds[0]): cv.one_of(*speeds, upper=True),
cv.Optional(CONF_DISABLED, default=False): cv.boolean,
}
)(config)
@@ -112,6 +114,8 @@ FINAL_VALIDATE_SCHEMA = validate_psram_mode
async def to_code(config):
if config[CONF_DISABLED]:
return
if CORE.using_arduino:
cg.add_build_flag("-DBOARD_HAS_PSRAM")
if config[CONF_MODE] == TYPE_OCTAL:

View File

@@ -393,10 +393,13 @@ def icon(value):
)
def sub_device_id(value: str | None) -> core.ID:
def sub_device_id(value: str | None) -> core.ID | None:
# Lazy import to avoid circular imports
from esphome.core.config import Device
if not value:
return None
return use_id(Device)(value)

View File

@@ -4,7 +4,7 @@ from enum import Enum
from esphome.enum import StrEnum
__version__ = "2025.8.0b1"
__version__ = "2025.8.0b2"
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
VALID_SUBSTITUTIONS_CHARACTERS = (

View File

@@ -77,8 +77,8 @@ async def setup_entity(var: MockObj, config: ConfigType, platform: str) -> None:
"""
# Get device info
device_name: str | None = None
if CONF_DEVICE_ID in config:
device_id_obj: ID = config[CONF_DEVICE_ID]
device_id_obj: ID | None
if device_id_obj := config.get(CONF_DEVICE_ID):
device: MockObj = await get_variable(device_id_obj)
add(var.set_device(device))
# Get device name for object ID calculation
@@ -199,8 +199,8 @@ def entity_duplicate_validator(platform: str) -> Callable[[ConfigType], ConfigTy
# Get device name if entity is on a sub-device
device_name = None
device_id = "" # Empty string for main device
if CONF_DEVICE_ID in config:
device_id_obj = config[CONF_DEVICE_ID]
device_id_obj: ID | None
if device_id_obj := config.get(CONF_DEVICE_ID):
device_name = device_id_obj.id
# Use the device ID string directly for uniqueness
device_id = device_id_obj.id

View File

@@ -11,8 +11,8 @@ pyserial==3.5
platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile
esptool==5.0.2
click==8.1.7
esphome-dashboard==20250514.0
aioesphomeapi==38.2.1
esphome-dashboard==20250814.0
aioesphomeapi==39.0.0
zeroconf==0.147.0
puremagic==1.30
ruamel.yaml==0.18.14 # dashboard_import

View File

@@ -55,6 +55,12 @@ sensor:
lambda: return 4.0;
update_interval: 0.1s
- platform: template
name: Living Room Sensor
device_id: ""
lambda: return 5.0;
update_interval: 0.1s
# Switches with the same name on different devices to test device_id lookup
switch:
# Switch with no device_id (defaults to 0)
@@ -96,3 +102,23 @@ switch:
- logger.log: "Turning on Test Switch on Motion Detector"
turn_off_action:
- logger.log: "Turning off Test Switch on Motion Detector"
- platform: template
name: Living Room Blank Switch
device_id: ""
id: test_switch_blank_living_room
optimistic: true
turn_on_action:
- logger.log: "Turning on Living Room Blank Switch"
turn_off_action:
- logger.log: "Turning off Living Room Blank Switch"
- platform: template
name: Living Room None Switch
device_id:
id: test_switch_none_living_room
optimistic: true
turn_on_action:
- logger.log: "Turning on Living Room None Switch"
turn_off_action:
- logger.log: "Turning off Living Room None Switch"

View File

@@ -132,6 +132,7 @@ async def test_areas_and_devices(
"Temperature Sensor Reading": temp_sensor.device_id,
"Motion Detector Status": motion_detector.device_id,
"Smart Switch Power": smart_switch.device_id,
"Living Room Sensor": 0, # Main device
}
for entity in sensor_entities:
@@ -160,6 +161,18 @@ async def test_areas_and_devices(
"Should have a switch with device_id 0 (main device)"
)
# Verify extra switches with blank and none device_id are correctly available
extra_switches = [
e for e in switch_entities if e.name.startswith("Living Room")
]
assert len(extra_switches) == 2, (
f"Expected 2 extra switches for Living Room, got {len(extra_switches)}"
)
extra_switch_device_ids = [e.device_id for e in extra_switches]
assert all(d == 0 for d in extra_switch_device_ids), (
"All extra switches should have device_id 0 (main device)"
)
# Wait for initial states to be received for all switches
await asyncio.wait_for(initial_states_future, timeout=2.0)

View File

@@ -689,3 +689,19 @@ def test_entity_duplicate_validator_internal_entities() -> None:
Invalid, match=r"Duplicate sensor entity with name 'Temperature' found"
):
validator(config4)
def test_empty_or_null_device_id_on_entity() -> None:
"""Test that empty or null device IDs are handled correctly."""
# Create validator for sensor platform
validator = entity_duplicate_validator("sensor")
# Entity with empty device_id should pass
config1 = {CONF_NAME: "Battery", CONF_DEVICE_ID: ""}
validated1 = validator(config1)
assert validated1 == config1
# Entity with None device_id should pass
config2 = {CONF_NAME: "Temperature", CONF_DEVICE_ID: None}
validated2 = validator(config2)
assert validated2 == config2