mirror of
https://github.com/esphome/esphome.git
synced 2025-09-13 16:52:18 +01:00
Merge branch 'multi_device' into integration
This commit is contained in:
@@ -2,7 +2,9 @@ esphome:
|
||||
debug_scheduler: true
|
||||
platformio_options:
|
||||
board_build.flash_mode: dio
|
||||
area: testing
|
||||
area:
|
||||
id: testing_area
|
||||
name: Testing Area
|
||||
on_boot:
|
||||
logger.log: on_boot
|
||||
on_shutdown:
|
||||
@@ -17,4 +19,20 @@ esphome:
|
||||
version: "1.1"
|
||||
on_update:
|
||||
logger.log: on_update
|
||||
areas:
|
||||
- id: another_area
|
||||
name: Another area
|
||||
devices:
|
||||
- id: other_device
|
||||
name: Another device
|
||||
area_id: another_area
|
||||
- id: test_device
|
||||
name: Test device in main area
|
||||
area_id: testing_area # Reference the main area (not in areas)
|
||||
- id: no_area_device
|
||||
name: Device without area # This device has no area_id
|
||||
|
||||
binary_sensor:
|
||||
- platform: template
|
||||
name: Other device sensor
|
||||
device_id: other_device
|
||||
|
@@ -12,7 +12,7 @@
|
||||
using namespace esphome;
|
||||
|
||||
void setup() {
|
||||
App.pre_setup("livingroom", "LivingRoom", "LivingRoomArea", "comment", __DATE__ ", " __TIME__, false);
|
||||
App.pre_setup("livingroom", "LivingRoom", "comment", __DATE__ ", " __TIME__, false);
|
||||
auto *log = new logger::Logger(115200, 512); // NOLINT
|
||||
log->pre_setup();
|
||||
log->set_uart_selection(logger::UART_SELECTION_UART0);
|
||||
|
57
tests/integration/fixtures/areas_and_devices.yaml
Normal file
57
tests/integration/fixtures/areas_and_devices.yaml
Normal file
@@ -0,0 +1,57 @@
|
||||
esphome:
|
||||
name: areas-devices-test
|
||||
# Define top-level area
|
||||
area:
|
||||
id: living_room_area
|
||||
name: Living Room
|
||||
# Define additional areas
|
||||
areas:
|
||||
- id: bedroom_area
|
||||
name: Bedroom
|
||||
- id: kitchen_area
|
||||
name: Kitchen
|
||||
# Define devices with area assignments
|
||||
devices:
|
||||
- id: light_controller_device
|
||||
name: Light Controller
|
||||
area_id: living_room_area # Uses top-level area
|
||||
- id: temp_sensor_device
|
||||
name: Temperature Sensor
|
||||
area_id: bedroom_area
|
||||
- id: motion_detector_device
|
||||
name: Motion Detector
|
||||
area_id: living_room_area # Reuses top-level area
|
||||
- id: smart_switch_device
|
||||
name: Smart Switch
|
||||
area_id: kitchen_area
|
||||
|
||||
host:
|
||||
api:
|
||||
logger:
|
||||
|
||||
# Sensors assigned to different devices
|
||||
sensor:
|
||||
- platform: template
|
||||
name: Light Controller Sensor
|
||||
device_id: light_controller_device
|
||||
lambda: return 1.0;
|
||||
update_interval: 0.1s
|
||||
|
||||
- platform: template
|
||||
name: Temperature Sensor Reading
|
||||
device_id: temp_sensor_device
|
||||
lambda: return 2.0;
|
||||
update_interval: 0.1s
|
||||
|
||||
- platform: template
|
||||
name: Motion Detector Status
|
||||
device_id: motion_detector_device
|
||||
lambda: return 3.0;
|
||||
update_interval: 0.1s
|
||||
|
||||
- platform: template
|
||||
name: Smart Switch Power
|
||||
device_id: smart_switch_device
|
||||
lambda: return 4.0;
|
||||
update_interval: 0.1s
|
||||
|
15
tests/integration/fixtures/legacy_area.yaml
Normal file
15
tests/integration/fixtures/legacy_area.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
esphome:
|
||||
name: legacy-area-test
|
||||
# Using legacy string-based area configuration
|
||||
area: Master Bedroom
|
||||
|
||||
host:
|
||||
api:
|
||||
logger:
|
||||
|
||||
# Simple sensor to ensure the device compiles and runs
|
||||
sensor:
|
||||
- platform: template
|
||||
name: Test Sensor
|
||||
lambda: return 42.0;
|
||||
update_interval: 1s
|
121
tests/integration/test_areas_and_devices.py
Normal file
121
tests/integration/test_areas_and_devices.py
Normal file
@@ -0,0 +1,121 @@
|
||||
"""Integration test for areas and devices feature."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
|
||||
from aioesphomeapi import EntityState
|
||||
import pytest
|
||||
|
||||
from .types import APIClientConnectedFactory, RunCompiledFunction
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_areas_and_devices(
|
||||
yaml_config: str,
|
||||
run_compiled: RunCompiledFunction,
|
||||
api_client_connected: APIClientConnectedFactory,
|
||||
) -> None:
|
||||
"""Test areas and devices configuration with entity mapping."""
|
||||
async with run_compiled(yaml_config), api_client_connected() as client:
|
||||
# Get device info which includes areas and devices
|
||||
device_info = await client.device_info()
|
||||
assert device_info is not None
|
||||
|
||||
# Verify areas are reported
|
||||
areas = device_info.areas
|
||||
assert len(areas) >= 2, f"Expected at least 2 areas, got {len(areas)}"
|
||||
|
||||
# Find our specific areas
|
||||
main_area = next((a for a in areas if a.name == "Living Room"), None)
|
||||
bedroom_area = next((a for a in areas if a.name == "Bedroom"), None)
|
||||
kitchen_area = next((a for a in areas if a.name == "Kitchen"), None)
|
||||
|
||||
assert main_area is not None, "Living Room area not found"
|
||||
assert bedroom_area is not None, "Bedroom area not found"
|
||||
assert kitchen_area is not None, "Kitchen area not found"
|
||||
|
||||
# Verify devices are reported
|
||||
devices = device_info.devices
|
||||
assert len(devices) >= 4, f"Expected at least 4 devices, got {len(devices)}"
|
||||
|
||||
# Find our specific devices
|
||||
light_controller = next(
|
||||
(d for d in devices if d.name == "Light Controller"), None
|
||||
)
|
||||
temp_sensor = next((d for d in devices if d.name == "Temperature Sensor"), None)
|
||||
motion_detector = next(
|
||||
(d for d in devices if d.name == "Motion Detector"), None
|
||||
)
|
||||
smart_switch = next((d for d in devices if d.name == "Smart Switch"), None)
|
||||
|
||||
assert light_controller is not None, "Light Controller device not found"
|
||||
assert temp_sensor is not None, "Temperature Sensor device not found"
|
||||
assert motion_detector is not None, "Motion Detector device not found"
|
||||
assert smart_switch is not None, "Smart Switch device not found"
|
||||
|
||||
# Verify device area assignments
|
||||
assert light_controller.area_id == main_area.area_id, (
|
||||
"Light Controller should be in Living Room"
|
||||
)
|
||||
assert temp_sensor.area_id == bedroom_area.area_id, (
|
||||
"Temperature Sensor should be in Bedroom"
|
||||
)
|
||||
assert motion_detector.area_id == main_area.area_id, (
|
||||
"Motion Detector should be in Living Room"
|
||||
)
|
||||
assert smart_switch.area_id == kitchen_area.area_id, (
|
||||
"Smart Switch should be in Kitchen"
|
||||
)
|
||||
|
||||
# Verify suggested_area is set to the top-level area name
|
||||
assert device_info.suggested_area == "Living Room", (
|
||||
f"Expected suggested_area to be 'Living Room', got '{device_info.suggested_area}'"
|
||||
)
|
||||
|
||||
# Get entity list to verify device_id mapping
|
||||
entities = await client.list_entities_services()
|
||||
|
||||
# Collect sensor entities
|
||||
sensor_entities = [e for e in entities[0] if hasattr(e, "device_id")]
|
||||
assert len(sensor_entities) >= 4, (
|
||||
f"Expected at least 4 sensor entities, got {len(sensor_entities)}"
|
||||
)
|
||||
|
||||
# Subscribe to states to get sensor values
|
||||
loop = asyncio.get_running_loop()
|
||||
states: dict[int, EntityState] = {}
|
||||
states_future: asyncio.Future[bool] = loop.create_future()
|
||||
|
||||
def on_state(state: EntityState) -> None:
|
||||
states[state.key] = state
|
||||
# Check if we have all expected sensor states
|
||||
if len(states) >= 4 and not states_future.done():
|
||||
states_future.set_result(True)
|
||||
|
||||
client.subscribe_states(on_state)
|
||||
|
||||
# Wait for sensor states
|
||||
try:
|
||||
await asyncio.wait_for(states_future, timeout=10.0)
|
||||
except asyncio.TimeoutError:
|
||||
pytest.fail(
|
||||
f"Did not receive all sensor states within 10 seconds. "
|
||||
f"Received {len(states)} states"
|
||||
)
|
||||
|
||||
# Verify we have sensor entities with proper device_id assignments
|
||||
device_id_mapping = {
|
||||
"Light Controller Sensor": light_controller.device_id,
|
||||
"Temperature Sensor Reading": temp_sensor.device_id,
|
||||
"Motion Detector Status": motion_detector.device_id,
|
||||
"Smart Switch Power": smart_switch.device_id,
|
||||
}
|
||||
|
||||
for entity in sensor_entities:
|
||||
if entity.name in device_id_mapping:
|
||||
expected_device_id = device_id_mapping[entity.name]
|
||||
assert entity.device_id == expected_device_id, (
|
||||
f"{entity.name} has device_id {entity.device_id}, "
|
||||
f"expected {expected_device_id}"
|
||||
)
|
41
tests/integration/test_legacy_area.py
Normal file
41
tests/integration/test_legacy_area.py
Normal file
@@ -0,0 +1,41 @@
|
||||
"""Integration test for legacy string-based area configuration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from .types import APIClientConnectedFactory, RunCompiledFunction
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_legacy_area(
|
||||
yaml_config: str,
|
||||
run_compiled: RunCompiledFunction,
|
||||
api_client_connected: APIClientConnectedFactory,
|
||||
) -> None:
|
||||
"""Test legacy string-based area configuration."""
|
||||
async with run_compiled(yaml_config), api_client_connected() as client:
|
||||
# Get device info which includes areas
|
||||
device_info = await client.device_info()
|
||||
assert device_info is not None
|
||||
|
||||
# Verify the area is reported (should be converted to structured format)
|
||||
areas = device_info.areas
|
||||
assert len(areas) == 1, f"Expected exactly 1 area, got {len(areas)}"
|
||||
|
||||
# Find the area - should be slugified from "Master Bedroom"
|
||||
area = areas[0]
|
||||
assert area.name == "Master Bedroom", (
|
||||
f"Expected area name 'Master Bedroom', got '{area.name}'"
|
||||
)
|
||||
|
||||
# Verify area.id is set (it should be a hash)
|
||||
assert area.area_id > 0, "Area ID should be a positive hash value"
|
||||
|
||||
# The suggested_area field should be set for backward compatibility
|
||||
assert device_info.suggested_area == "Master Bedroom", (
|
||||
f"Expected suggested_area to be 'Master Bedroom', got '{device_info.suggested_area}'"
|
||||
)
|
||||
|
||||
# Verify deprecated warning would have been logged during compilation
|
||||
# (We can't check logs directly in integration tests, but the code should work)
|
256
tests/unit_tests/core/test_config.py
Normal file
256
tests/unit_tests/core/test_config.py
Normal file
@@ -0,0 +1,256 @@
|
||||
"""Unit tests for core config functionality including areas and devices."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from esphome import config, config_validation as cv, core, yaml_util
|
||||
from esphome.config import Config
|
||||
from esphome.const import CONF_AREA, CONF_AREAS, CONF_DEVICES
|
||||
from esphome.core import CORE
|
||||
from esphome.core.config import Area, validate_area_config
|
||||
|
||||
FIXTURES_DIR = Path(__file__).parent.parent / "fixtures" / "core" / "config"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def yaml_file(tmp_path: Path) -> Callable[[str], str]:
|
||||
"""Create a temporary YAML file for testing."""
|
||||
|
||||
def _yaml_file(content: str) -> str:
|
||||
yaml_path = tmp_path / "test.yaml"
|
||||
yaml_path.write_text(content)
|
||||
return str(yaml_path)
|
||||
|
||||
return _yaml_file
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def reset_core():
|
||||
"""Reset CORE after each test."""
|
||||
yield
|
||||
CORE.reset()
|
||||
|
||||
|
||||
def load_config_from_yaml(
|
||||
yaml_file: Callable[[str], str], yaml_content: str
|
||||
) -> Config | None:
|
||||
"""Load configuration from YAML content."""
|
||||
yaml_path = yaml_file(yaml_content)
|
||||
parsed_yaml = yaml_util.load_yaml(yaml_path)
|
||||
|
||||
# Mock yaml_util.load_yaml to return our parsed content
|
||||
with (
|
||||
patch.object(yaml_util, "load_yaml", return_value=parsed_yaml),
|
||||
patch.object(CORE, "config_path", yaml_path),
|
||||
):
|
||||
return config.read_config({})
|
||||
|
||||
|
||||
def load_config_from_fixture(
|
||||
yaml_file: Callable[[str], str], fixture_name: str
|
||||
) -> Config | None:
|
||||
"""Load configuration from a fixture file."""
|
||||
fixture_path = FIXTURES_DIR / fixture_name
|
||||
yaml_content = fixture_path.read_text()
|
||||
return load_config_from_yaml(yaml_file, yaml_content)
|
||||
|
||||
|
||||
def test_validate_area_config_with_string() -> None:
|
||||
"""Test that string area config is converted to structured format."""
|
||||
result: dict[str, Any] = validate_area_config("Living Room")
|
||||
|
||||
assert isinstance(result, dict)
|
||||
assert "id" in result
|
||||
assert "name" in result
|
||||
assert result["name"] == "Living Room"
|
||||
assert isinstance(result["id"], core.ID)
|
||||
assert result["id"].is_declaration
|
||||
assert not result["id"].is_manual
|
||||
|
||||
|
||||
def test_validate_area_config_with_dict() -> None:
|
||||
"""Test that structured area config passes through unchanged."""
|
||||
area_id = cv.declare_id(Area)("test_area")
|
||||
input_config: dict[str, Any] = {
|
||||
"id": area_id,
|
||||
"name": "Test Area",
|
||||
}
|
||||
|
||||
result: dict[str, Any] = validate_area_config(input_config)
|
||||
|
||||
assert result == input_config
|
||||
assert result["id"] == area_id
|
||||
assert result["name"] == "Test Area"
|
||||
|
||||
|
||||
def test_device_with_valid_area_id(yaml_file: Callable[[str], str]) -> None:
|
||||
"""Test that device with valid area_id works correctly."""
|
||||
result = load_config_from_fixture(yaml_file, "valid_area_device.yaml")
|
||||
assert result is not None
|
||||
|
||||
esphome_config = result["esphome"]
|
||||
|
||||
# Verify areas were parsed correctly
|
||||
assert CONF_AREAS in esphome_config
|
||||
areas = esphome_config[CONF_AREAS]
|
||||
assert len(areas) == 1
|
||||
assert areas[0]["id"].id == "bedroom_area"
|
||||
assert areas[0]["name"] == "Bedroom"
|
||||
|
||||
# Verify devices were parsed correctly
|
||||
assert CONF_DEVICES in esphome_config
|
||||
devices = esphome_config[CONF_DEVICES]
|
||||
assert len(devices) == 1
|
||||
assert devices[0]["id"].id == "test_device"
|
||||
assert devices[0]["name"] == "Test Device"
|
||||
assert devices[0]["area_id"].id == "bedroom_area"
|
||||
|
||||
|
||||
def test_multiple_areas_and_devices(yaml_file: Callable[[str], str]) -> None:
|
||||
"""Test multiple areas and devices configuration."""
|
||||
result = load_config_from_fixture(yaml_file, "multiple_areas_devices.yaml")
|
||||
assert result is not None
|
||||
|
||||
esphome_config = result["esphome"]
|
||||
|
||||
# Verify main area
|
||||
assert CONF_AREA in esphome_config
|
||||
main_area = esphome_config[CONF_AREA]
|
||||
assert main_area["id"].id == "main_area"
|
||||
assert main_area["name"] == "Main Area"
|
||||
|
||||
# Verify additional areas
|
||||
assert CONF_AREAS in esphome_config
|
||||
areas = esphome_config[CONF_AREAS]
|
||||
assert len(areas) == 2
|
||||
area_ids = {area["id"].id for area in areas}
|
||||
assert area_ids == {"area1", "area2"}
|
||||
|
||||
# Verify devices
|
||||
assert CONF_DEVICES in esphome_config
|
||||
devices = esphome_config[CONF_DEVICES]
|
||||
assert len(devices) == 3
|
||||
|
||||
# Check device-area associations
|
||||
device_area_map = {dev["id"].id: dev["area_id"].id for dev in devices}
|
||||
assert device_area_map == {
|
||||
"device1": "main_area",
|
||||
"device2": "area1",
|
||||
"device3": "area2",
|
||||
}
|
||||
|
||||
|
||||
def test_legacy_string_area(
|
||||
yaml_file: Callable[[str], str], caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
"""Test legacy string area configuration with deprecation warning."""
|
||||
result = load_config_from_fixture(yaml_file, "legacy_string_area.yaml")
|
||||
assert result is not None
|
||||
|
||||
esphome_config = result["esphome"]
|
||||
|
||||
# Verify the string was converted to structured format
|
||||
assert CONF_AREA in esphome_config
|
||||
area = esphome_config[CONF_AREA]
|
||||
assert isinstance(area, dict)
|
||||
assert area["name"] == "Living Room"
|
||||
assert isinstance(area["id"], core.ID)
|
||||
assert area["id"].is_declaration
|
||||
assert not area["id"].is_manual
|
||||
|
||||
|
||||
def test_area_id_collision(
|
||||
yaml_file: Callable[[str], str], capsys: pytest.CaptureFixture[str]
|
||||
) -> None:
|
||||
"""Test that duplicate area IDs are detected."""
|
||||
result = load_config_from_fixture(yaml_file, "area_id_collision.yaml")
|
||||
assert result is None
|
||||
|
||||
# Check for the specific error message in stdout
|
||||
captured = capsys.readouterr()
|
||||
# Exact duplicates are now caught by IDPassValidationStep
|
||||
assert "ID duplicate_id redefined! Check esphome->area->id." in captured.out
|
||||
|
||||
|
||||
def test_device_without_area(yaml_file: Callable[[str], str]) -> None:
|
||||
"""Test that devices without area_id work correctly."""
|
||||
result = load_config_from_fixture(yaml_file, "device_without_area.yaml")
|
||||
assert result is not None
|
||||
|
||||
esphome_config = result["esphome"]
|
||||
|
||||
# Verify device was parsed
|
||||
assert CONF_DEVICES in esphome_config
|
||||
devices = esphome_config[CONF_DEVICES]
|
||||
assert len(devices) == 1
|
||||
|
||||
device = devices[0]
|
||||
assert device["id"].id == "test_device"
|
||||
assert device["name"] == "Test Device"
|
||||
|
||||
# Verify no area_id is present
|
||||
assert "area_id" not in device
|
||||
|
||||
|
||||
def test_device_with_invalid_area_id(
|
||||
yaml_file: Callable[[str], str], capsys: pytest.CaptureFixture[str]
|
||||
) -> None:
|
||||
"""Test that device with non-existent area_id fails validation."""
|
||||
result = load_config_from_fixture(yaml_file, "device_invalid_area.yaml")
|
||||
assert result is None
|
||||
|
||||
# Check for the specific error message in stdout
|
||||
captured = capsys.readouterr()
|
||||
print(captured.out)
|
||||
assert (
|
||||
"Couldn't find ID 'nonexistent_area'. Please check you have defined an ID with that name in your configuration."
|
||||
in captured.out
|
||||
)
|
||||
|
||||
|
||||
def test_device_id_hash_collision(
|
||||
yaml_file: Callable[[str], str], capsys: pytest.CaptureFixture[str]
|
||||
) -> None:
|
||||
"""Test that device IDs with hash collisions are detected."""
|
||||
result = load_config_from_fixture(yaml_file, "device_id_collision.yaml")
|
||||
assert result is None
|
||||
|
||||
# Check for the specific error message about hash collision
|
||||
captured = capsys.readouterr()
|
||||
# The error message shows the ID that collides and includes the hash value
|
||||
assert (
|
||||
"Device ID 'd6ka' with hash 3082558663 collides with existing device ID 'test_2258'"
|
||||
in captured.out
|
||||
)
|
||||
|
||||
|
||||
def test_area_id_hash_collision(
|
||||
yaml_file: Callable[[str], str], capsys: pytest.CaptureFixture[str]
|
||||
) -> None:
|
||||
"""Test that area IDs with hash collisions are detected."""
|
||||
result = load_config_from_fixture(yaml_file, "area_id_hash_collision.yaml")
|
||||
assert result is None
|
||||
|
||||
# Check for the specific error message about hash collision
|
||||
captured = capsys.readouterr()
|
||||
# The error message shows the ID that collides and includes the hash value
|
||||
assert (
|
||||
"Area ID 'd6ka' with hash 3082558663 collides with existing area ID 'test_2258'"
|
||||
in captured.out
|
||||
)
|
||||
|
||||
|
||||
def test_device_duplicate_id(
|
||||
yaml_file: Callable[[str], str], capsys: pytest.CaptureFixture[str]
|
||||
) -> None:
|
||||
"""Test that duplicate device IDs are detected by IDPassValidationStep."""
|
||||
result = load_config_from_fixture(yaml_file, "device_duplicate_id.yaml")
|
||||
assert result is None
|
||||
|
||||
# Check for the specific error message from IDPassValidationStep
|
||||
captured = capsys.readouterr()
|
||||
assert "ID duplicate_device redefined!" in captured.out
|
10
tests/unit_tests/fixtures/core/config/area_id_collision.yaml
Normal file
10
tests/unit_tests/fixtures/core/config/area_id_collision.yaml
Normal file
@@ -0,0 +1,10 @@
|
||||
esphome:
|
||||
name: test-collision
|
||||
area:
|
||||
id: duplicate_id
|
||||
name: Area 1
|
||||
areas:
|
||||
- id: duplicate_id
|
||||
name: Area 2
|
||||
|
||||
host:
|
@@ -0,0 +1,10 @@
|
||||
esphome:
|
||||
name: test
|
||||
areas:
|
||||
- id: test_2258
|
||||
name: "Area 1"
|
||||
- id: d6ka
|
||||
name: "Area 2"
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
@@ -0,0 +1,10 @@
|
||||
esphome:
|
||||
name: test
|
||||
devices:
|
||||
- id: duplicate_device
|
||||
name: "Device 1"
|
||||
- id: duplicate_device
|
||||
name: "Device 2"
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
@@ -0,0 +1,10 @@
|
||||
esphome:
|
||||
name: test
|
||||
devices:
|
||||
- id: test_2258
|
||||
name: "Device 1"
|
||||
- id: d6ka
|
||||
name: "Device 2"
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
@@ -0,0 +1,12 @@
|
||||
esphome:
|
||||
name: test
|
||||
areas:
|
||||
- id: valid_area
|
||||
name: "Valid Area"
|
||||
devices:
|
||||
- id: test_device
|
||||
name: "Test Device"
|
||||
area_id: nonexistent_area
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
@@ -0,0 +1,7 @@
|
||||
esphome:
|
||||
name: test-device-no-area
|
||||
devices:
|
||||
- id: test_device
|
||||
name: Test Device
|
||||
|
||||
host:
|
@@ -0,0 +1,5 @@
|
||||
esphome:
|
||||
name: test-legacy-area
|
||||
area: Living Room
|
||||
|
||||
host:
|
@@ -0,0 +1,22 @@
|
||||
esphome:
|
||||
name: test-multiple
|
||||
area:
|
||||
id: main_area
|
||||
name: Main Area
|
||||
areas:
|
||||
- id: area1
|
||||
name: Area 1
|
||||
- id: area2
|
||||
name: Area 2
|
||||
devices:
|
||||
- id: device1
|
||||
name: Device 1
|
||||
area_id: main_area
|
||||
- id: device2
|
||||
name: Device 2
|
||||
area_id: area1
|
||||
- id: device3
|
||||
name: Device 3
|
||||
area_id: area2
|
||||
|
||||
host:
|
11
tests/unit_tests/fixtures/core/config/valid_area_device.yaml
Normal file
11
tests/unit_tests/fixtures/core/config/valid_area_device.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
esphome:
|
||||
name: test-valid-area
|
||||
areas:
|
||||
- id: bedroom_area
|
||||
name: Bedroom
|
||||
devices:
|
||||
- id: test_device
|
||||
name: Test Device
|
||||
area_id: bedroom_area
|
||||
|
||||
host:
|
Reference in New Issue
Block a user