diff --git a/homeassistant/components/wake_on_lan/__init__.py b/homeassistant/components/wake_on_lan/__init__.py index aae640381a2..a8ad7637c6e 100644 --- a/homeassistant/components/wake_on_lan/__init__.py +++ b/homeassistant/components/wake_on_lan/__init__.py @@ -1,16 +1,27 @@ """Support for sending Wake-On-LAN magic packets.""" +import asyncio +from collections.abc import Coroutine from functools import partial import logging +from typing import Any import voluptuous as vol import wakeonlan -from homeassistant.const import CONF_BROADCAST_ADDRESS, CONF_BROADCAST_PORT, CONF_MAC +from homeassistant.const import ( + CONF_BROADCAST_ADDRESS, + CONF_BROADCAST_PORT, + CONF_HOST, + CONF_MAC, + CONF_NAME, + Platform, +) from homeassistant.core import HomeAssistant, ServiceCall +from homeassistant.helpers import discovery import homeassistant.helpers.config_validation as cv from homeassistant.helpers.typing import ConfigType -from .const import DOMAIN +from .const import CONF_OFF_ACTION, DEFAULT_NAME, DOMAIN _LOGGER = logging.getLogger(__name__) @@ -24,9 +35,41 @@ WAKE_ON_LAN_SEND_MAGIC_PACKET_SCHEMA = vol.Schema( } ) +SWITCH_SCHEMA = vol.Schema( + { + vol.Required(CONF_MAC): cv.string, + vol.Optional(CONF_BROADCAST_ADDRESS): cv.string, + vol.Optional(CONF_BROADCAST_PORT): cv.port, + vol.Optional(CONF_HOST): cv.string, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_OFF_ACTION): cv.SCRIPT_SCHEMA, + } +) + + +CONFIG_SCHEMA = vol.Schema( + {vol.Optional(DOMAIN): vol.All(cv.ensure_list, [vol.Schema(SWITCH_SCHEMA)])}, + extra=vol.ALLOW_EXTRA, +) + async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the wake on LAN component.""" + load_coroutines: list[Coroutine[Any, Any, None]] = [] + if wol_config := config.get(DOMAIN): + for switch_config in wol_config: + load_coroutines.append( + discovery.async_load_platform( + hass, + Platform.SWITCH, + DOMAIN, + switch_config, + config, + ) + ) + + if load_coroutines: + await asyncio.gather(*load_coroutines) async def send_magic_packet(call: ServiceCall) -> None: """Send magic packet to wake up a device.""" diff --git a/homeassistant/components/wake_on_lan/const.py b/homeassistant/components/wake_on_lan/const.py index 14f2bd0263f..a954be33943 100644 --- a/homeassistant/components/wake_on_lan/const.py +++ b/homeassistant/components/wake_on_lan/const.py @@ -1,2 +1,6 @@ """Constants for the Wake-On-LAN component.""" DOMAIN = "wake_on_lan" +CONF_OFF_ACTION = "turn_off" + +DEFAULT_NAME = "Wake on LAN" +DEFAULT_PING_TIMEOUT = 1 diff --git a/homeassistant/components/wake_on_lan/strings.json b/homeassistant/components/wake_on_lan/strings.json new file mode 100644 index 00000000000..275f2e30fff --- /dev/null +++ b/homeassistant/components/wake_on_lan/strings.json @@ -0,0 +1,8 @@ +{ + "issues": { + "moved_yaml": { + "title": "The Wake on Lan YAML configuration has been moved", + "description": "Configuring Wake on Lan using YAML has been moved to integration key.\n\nYour existing YAML configuration will be working for 2 more versions.\n\nMigrate your YAML configuration to the integration key according to the documentation." + } + } +} diff --git a/homeassistant/components/wake_on_lan/switch.py b/homeassistant/components/wake_on_lan/switch.py index 446df402c87..af4f15b79ba 100644 --- a/homeassistant/components/wake_on_lan/switch.py +++ b/homeassistant/components/wake_on_lan/switch.py @@ -23,18 +23,14 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue from homeassistant.helpers.script import Script from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from .const import DOMAIN +from .const import CONF_OFF_ACTION, DEFAULT_NAME, DEFAULT_PING_TIMEOUT, DOMAIN _LOGGER = logging.getLogger(__name__) -CONF_OFF_ACTION = "turn_off" - -DEFAULT_NAME = "Wake on LAN" -DEFAULT_PING_TIMEOUT = 1 - PLATFORM_SCHEMA = PARENT_PLATFORM_SCHEMA.extend( { vol.Required(CONF_MAC): cv.string, @@ -47,21 +43,33 @@ PLATFORM_SCHEMA = PARENT_PLATFORM_SCHEMA.extend( ) -def setup_platform( +async def async_setup_platform( hass: HomeAssistant, config: ConfigType, - add_entities: AddEntitiesCallback, + async_add_entities: AddEntitiesCallback, discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up a wake on lan switch.""" - broadcast_address: str | None = config.get(CONF_BROADCAST_ADDRESS) - broadcast_port: int | None = config.get(CONF_BROADCAST_PORT) - host: str | None = config.get(CONF_HOST) - mac_address: str = config[CONF_MAC] - name: str = config[CONF_NAME] - off_action: list[Any] | None = config.get(CONF_OFF_ACTION) + if (switch_config := discovery_info) is None: + async_create_issue( + hass, + DOMAIN, + "moved_yaml", + breaks_in_ha_version="2022.12.0", + is_fixable=False, + severity=IssueSeverity.WARNING, + translation_key="moved_yaml", + ) + switch_config = config - add_entities( + broadcast_address: str | None = switch_config.get(CONF_BROADCAST_ADDRESS) + broadcast_port: int | None = switch_config.get(CONF_BROADCAST_PORT) + host: str | None = switch_config.get(CONF_HOST) + mac_address: str = switch_config[CONF_MAC] + name: str = switch_config[CONF_NAME] + off_action: list[Any] | None = switch_config.get(CONF_OFF_ACTION) + + async_add_entities( [ WolSwitch( hass, diff --git a/homeassistant/components/wake_on_lan/translations/en.json b/homeassistant/components/wake_on_lan/translations/en.json new file mode 100644 index 00000000000..3d37fa4b9ba --- /dev/null +++ b/homeassistant/components/wake_on_lan/translations/en.json @@ -0,0 +1,8 @@ +{ + "issues": { + "moved_yaml": { + "description": "Configuring Wake on Lan using YAML has been moved to integration key.\n\nYour existing YAML configuration will be working for 2 more versions.\n\nMigrate your YAML configuration to the integration key according to the documentation.", + "title": "The Wake on Lan YAML configuration has been moved" + } + } +} \ No newline at end of file diff --git a/tests/components/wake_on_lan/conftest.py b/tests/components/wake_on_lan/conftest.py new file mode 100644 index 00000000000..5f92cf34e37 --- /dev/null +++ b/tests/components/wake_on_lan/conftest.py @@ -0,0 +1,13 @@ +"""Test fixtures for Wake on Lan.""" +from __future__ import annotations + +from unittest.mock import patch + +import pytest + + +@pytest.fixture(autouse=True) +def mock_send_magic_packet(): + """Mock magic packet.""" + with patch("wakeonlan.send_magic_packet") as mock_send: + yield mock_send diff --git a/tests/components/wake_on_lan/test_init.py b/tests/components/wake_on_lan/test_init.py index 3ec7a53a436..0c61538a835 100644 --- a/tests/components/wake_on_lan/test_init.py +++ b/tests/components/wake_on_lan/test_init.py @@ -1,14 +1,26 @@ """Tests for Wake On LAN component.""" +from __future__ import annotations + +import subprocess from unittest.mock import patch import pytest import voluptuous as vol +from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN from homeassistant.components.wake_on_lan import DOMAIN, SERVICE_SEND_MAGIC_PACKET +from homeassistant.const import ( + ATTR_ENTITY_ID, + SERVICE_TURN_OFF, + SERVICE_TURN_ON, + STATE_OFF, + STATE_ON, +) +from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component -async def test_send_magic_packet(hass): +async def test_send_magic_packet(hass: HomeAssistant) -> None: """Test of send magic packet service call.""" with patch("homeassistant.components.wake_on_lan.wakeonlan") as mocked_wakeonlan: mac = "aa:bb:cc:dd:ee:ff" @@ -65,3 +77,53 @@ async def test_send_magic_packet(hass): assert len(mocked_wakeonlan.mock_calls) == 4 assert mocked_wakeonlan.mock_calls[-1][1][0] == mac assert not mocked_wakeonlan.mock_calls[-1][2] + + +async def test_setup_from_integration_yaml(hass: HomeAssistant) -> None: + """Test setup from integration yaml.""" + assert await async_setup_component( + hass, + DOMAIN, + { + DOMAIN: [ + { + "mac": "00-01-02-03-04-05", + "host": "validhostname", + }, + { + "mac": "06-07-08-09-0A-0B", + "host": "validhostname2", + "name": "Friendly Name", + }, + ], + }, + ) + await hass.async_block_till_done() + + state = hass.states.get("switch.wake_on_lan") + state2 = hass.states.get("switch.friendly_name") + + assert state.state == STATE_OFF + assert state2.state == STATE_OFF + + with patch.object(subprocess, "call", return_value=0): + + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "switch.wake_on_lan"}, + blocking=True, + ) + + state = hass.states.get("switch.wake_on_lan") + assert state.state == STATE_ON + + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: "switch.wake_on_lan"}, + blocking=True, + ) + + state = hass.states.get("switch.wake_on_lan") + assert state.state == STATE_ON