diff --git a/homeassistant/components/harmony/const.py b/homeassistant/components/harmony/const.py index 69ef2cb66c9..f474783b736 100644 --- a/homeassistant/components/harmony/const.py +++ b/homeassistant/components/harmony/const.py @@ -5,7 +5,7 @@ from homeassistant.const import Platform DOMAIN = "harmony" SERVICE_SYNC = "sync" SERVICE_CHANGE_CHANNEL = "change_channel" -PLATFORMS = [Platform.REMOTE, Platform.SELECT, Platform.SWITCH] +PLATFORMS = [Platform.REMOTE, Platform.SELECT] UNIQUE_ID = "unique_id" ACTIVITY_POWER_OFF = "PowerOff" HARMONY_OPTIONS_UPDATE = "harmony_options_update" diff --git a/homeassistant/components/harmony/manifest.json b/homeassistant/components/harmony/manifest.json index 8acc4307d1f..d37801376ec 100644 --- a/homeassistant/components/harmony/manifest.json +++ b/homeassistant/components/harmony/manifest.json @@ -3,7 +3,7 @@ "name": "Logitech Harmony Hub", "codeowners": ["@ehendrix23", "@bdraco", "@mkeesey", "@Aohzan"], "config_flow": true, - "dependencies": ["remote", "switch"], + "dependencies": ["remote"], "documentation": "https://www.home-assistant.io/integrations/harmony", "iot_class": "local_push", "loggers": ["aioharmony", "slixmpp"], diff --git a/homeassistant/components/harmony/strings.json b/homeassistant/components/harmony/strings.json index 444097395c9..e13573a9ea3 100644 --- a/homeassistant/components/harmony/strings.json +++ b/homeassistant/components/harmony/strings.json @@ -46,16 +46,6 @@ } } }, - "issues": { - "deprecated_switches": { - "title": "The Logitech Harmony switch platform is being removed", - "description": "Using the switch platform to change the current activity is now deprecated and will be removed in a future version of Home Assistant.\n\nPlease adjust any automations or scripts that use switch entities to instead use the select entity." - }, - "deprecated_switches_entity": { - "title": "Deprecated Harmony entity detected in {info}", - "description": "Your Harmony entity `{entity}` is being used in `{info}`. A select entity is available and should be used going forward.\n\nPlease adjust `{info}` to fix this issue." - } - }, "services": { "sync": { "name": "Sync", diff --git a/homeassistant/components/harmony/switch.py b/homeassistant/components/harmony/switch.py deleted file mode 100644 index 0cb07e5cb1e..00000000000 --- a/homeassistant/components/harmony/switch.py +++ /dev/null @@ -1,110 +0,0 @@ -"""Support for Harmony Hub activities.""" - -import logging -from typing import Any, cast - -from homeassistant.components.automation import automations_with_entity -from homeassistant.components.script import scripts_with_entity -from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN, SwitchEntity -from homeassistant.config_entries import ConfigEntry -from homeassistant.core import HassJob, HomeAssistant, callback -from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue - -from .const import DOMAIN, HARMONY_DATA -from .data import HarmonyData -from .entity import HarmonyEntity -from .subscriber import HarmonyCallback - -_LOGGER = logging.getLogger(__name__) - - -async def async_setup_entry( - hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback -) -> None: - """Set up harmony activity switches.""" - data: HarmonyData = hass.data[DOMAIN][entry.entry_id][HARMONY_DATA] - - async_add_entities( - (HarmonyActivitySwitch(activity, data) for activity in data.activities), True - ) - - -class HarmonyActivitySwitch(HarmonyEntity, SwitchEntity): - """Switch representation of a Harmony activity.""" - - def __init__(self, activity: dict, data: HarmonyData) -> None: - """Initialize HarmonyActivitySwitch class.""" - super().__init__(data=data) - self._activity_name = self._attr_name = activity["label"] - self._activity_id = activity["id"] - self._attr_entity_registry_enabled_default = False - self._attr_unique_id = f"activity_{self._activity_id}" - self._attr_device_info = self._data.device_info(DOMAIN) - - @property - def is_on(self) -> bool: - """Return if the current activity is the one for this switch.""" - _, activity_name = self._data.current_activity - return activity_name == self._activity_name - - async def async_turn_on(self, **kwargs: Any) -> None: - """Start this activity.""" - async_create_issue( - self.hass, - DOMAIN, - "deprecated_switches", - breaks_in_ha_version="2024.6.0", - is_fixable=False, - severity=IssueSeverity.WARNING, - translation_key="deprecated_switches", - ) - await self._data.async_start_activity(self._activity_name) - - async def async_turn_off(self, **kwargs: Any) -> None: - """Stop this activity.""" - async_create_issue( - self.hass, - DOMAIN, - "deprecated_switches", - breaks_in_ha_version="2024.6.0", - is_fixable=False, - severity=IssueSeverity.WARNING, - translation_key="deprecated_switches", - ) - await self._data.async_power_off() - - async def async_added_to_hass(self) -> None: - """Call when entity is added to hass.""" - activity_update_job = HassJob(self._async_activity_update) - self.async_on_remove( - self._data.async_subscribe( - HarmonyCallback( - connected=HassJob(self.async_got_connected), - disconnected=HassJob(self.async_got_disconnected), - activity_starting=activity_update_job, - activity_started=activity_update_job, - config_updated=None, - ) - ) - ) - entity_automations = automations_with_entity(self.hass, self.entity_id) - entity_scripts = scripts_with_entity(self.hass, self.entity_id) - for item in entity_automations + entity_scripts: - async_create_issue( - self.hass, - DOMAIN, - f"deprecated_switches_{self.entity_id}_{item}", - breaks_in_ha_version="2024.6.0", - is_fixable=False, - severity=IssueSeverity.WARNING, - translation_key="deprecated_switches_entity", - translation_placeholders={ - "entity": f"{SWITCH_DOMAIN}.{cast(str, self.name).lower().replace(' ', '_')}", - "info": item, - }, - ) - - @callback - def _async_activity_update(self, activity_info: tuple) -> None: - self.async_write_ha_state() diff --git a/tests/components/harmony/test_switch.py b/tests/components/harmony/test_switch.py deleted file mode 100644 index 0cfc0e5bead..00000000000 --- a/tests/components/harmony/test_switch.py +++ /dev/null @@ -1,203 +0,0 @@ -"""Test the Logitech Harmony Hub activity switches.""" - -from datetime import timedelta - -import pytest - -from homeassistant.components import automation, script -from homeassistant.components.automation import automations_with_entity -from homeassistant.components.harmony.const import DOMAIN -from homeassistant.components.script import scripts_with_entity -from homeassistant.components.switch import ( - DOMAIN as SWITCH_DOMAIN, - SERVICE_TURN_OFF, - SERVICE_TURN_ON, -) -from homeassistant.const import ( - ATTR_ENTITY_ID, - CONF_HOST, - CONF_NAME, - STATE_OFF, - STATE_ON, - STATE_UNAVAILABLE, -) -from homeassistant.core import HomeAssistant -from homeassistant.helpers import entity_registry as er -import homeassistant.helpers.issue_registry as ir -from homeassistant.setup import async_setup_component -from homeassistant.util import utcnow - -from .const import ENTITY_PLAY_MUSIC, ENTITY_REMOTE, ENTITY_WATCH_TV, HUB_NAME - -from tests.common import MockConfigEntry, async_fire_time_changed - - -async def test_connection_state_changes( - harmony_client, - mock_hc, - hass: HomeAssistant, - mock_write_config, - entity_registry: er.EntityRegistry, -) -> None: - """Ensure connection changes are reflected in the switch states.""" - entry = MockConfigEntry( - domain=DOMAIN, data={CONF_HOST: "192.0.2.0", CONF_NAME: HUB_NAME} - ) - - entry.add_to_hass(hass) - await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() - - # check if switch entities are disabled by default - assert not hass.states.get(ENTITY_WATCH_TV) - assert not hass.states.get(ENTITY_PLAY_MUSIC) - - # enable switch entities - entity_registry.async_update_entity(ENTITY_WATCH_TV, disabled_by=None) - entity_registry.async_update_entity(ENTITY_PLAY_MUSIC, disabled_by=None) - await hass.config_entries.async_reload(entry.entry_id) - await hass.async_block_till_done() - - # mocks start with current activity == Watch TV - assert hass.states.is_state(ENTITY_WATCH_TV, STATE_ON) - assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF) - - harmony_client.mock_disconnection() - await hass.async_block_till_done() - - # Entities do not immediately show as unavailable - assert hass.states.is_state(ENTITY_WATCH_TV, STATE_ON) - assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF) - - future_time = utcnow() + timedelta(seconds=10) - async_fire_time_changed(hass, future_time) - await hass.async_block_till_done() - assert hass.states.is_state(ENTITY_WATCH_TV, STATE_UNAVAILABLE) - assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_UNAVAILABLE) - - harmony_client.mock_reconnection() - await hass.async_block_till_done() - - assert hass.states.is_state(ENTITY_WATCH_TV, STATE_ON) - assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF) - - harmony_client.mock_disconnection() - harmony_client.mock_reconnection() - future_time = utcnow() + timedelta(seconds=10) - async_fire_time_changed(hass, future_time) - - await hass.async_block_till_done() - assert hass.states.is_state(ENTITY_WATCH_TV, STATE_ON) - assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF) - - -async def test_switch_toggles( - mock_hc, - hass: HomeAssistant, - mock_write_config, - entity_registry: er.EntityRegistry, - mock_config_entry: MockConfigEntry, -) -> None: - """Ensure calls to the switch modify the harmony state.""" - - mock_config_entry.add_to_hass(hass) - await hass.config_entries.async_setup(mock_config_entry.entry_id) - await hass.async_block_till_done() - - # enable switch entities - entity_registry.async_update_entity(ENTITY_WATCH_TV, disabled_by=None) - entity_registry.async_update_entity(ENTITY_PLAY_MUSIC, disabled_by=None) - await hass.config_entries.async_reload(mock_config_entry.entry_id) - await hass.async_block_till_done() - - # mocks start with current activity == Watch TV - assert hass.states.is_state(ENTITY_REMOTE, STATE_ON) - assert hass.states.is_state(ENTITY_WATCH_TV, STATE_ON) - assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF) - - # turn off watch tv switch - await _toggle_switch_and_wait(hass, SERVICE_TURN_OFF, ENTITY_WATCH_TV) - assert hass.states.is_state(ENTITY_REMOTE, STATE_OFF) - assert hass.states.is_state(ENTITY_WATCH_TV, STATE_OFF) - assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF) - - # turn on play music switch - await _toggle_switch_and_wait(hass, SERVICE_TURN_ON, ENTITY_PLAY_MUSIC) - assert hass.states.is_state(ENTITY_REMOTE, STATE_ON) - assert hass.states.is_state(ENTITY_WATCH_TV, STATE_OFF) - assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_ON) - - # turn on watch tv switch - await _toggle_switch_and_wait(hass, SERVICE_TURN_ON, ENTITY_WATCH_TV) - assert hass.states.is_state(ENTITY_REMOTE, STATE_ON) - assert hass.states.is_state(ENTITY_WATCH_TV, STATE_ON) - assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF) - - -async def _toggle_switch_and_wait(hass, service_name, entity): - await hass.services.async_call( - SWITCH_DOMAIN, - service_name, - {ATTR_ENTITY_ID: entity}, - blocking=True, - ) - await hass.async_block_till_done() - - -@pytest.mark.usefixtures("entity_registry_enabled_by_default") -async def test_create_issue( - harmony_client, - mock_hc, - hass: HomeAssistant, - mock_write_config, - issue_registry: ir.IssueRegistry, -) -> None: - """Test we create an issue when an automation or script is using a deprecated entity.""" - assert await async_setup_component( - hass, - automation.DOMAIN, - { - automation.DOMAIN: { - "alias": "test", - "trigger": {"platform": "state", "entity_id": ENTITY_WATCH_TV}, - "action": {"service": "switch.turn_on", "entity_id": ENTITY_WATCH_TV}, - } - }, - ) - assert await async_setup_component( - hass, - script.DOMAIN, - { - script.DOMAIN: { - "test": { - "sequence": [ - { - "service": "switch.turn_on", - "data": {"entity_id": ENTITY_WATCH_TV}, - }, - ], - } - } - }, - ) - - entry = MockConfigEntry( - domain=DOMAIN, data={CONF_HOST: "192.0.2.0", CONF_NAME: HUB_NAME} - ) - - entry.add_to_hass(hass) - await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() - - assert automations_with_entity(hass, ENTITY_WATCH_TV)[0] == "automation.test" - assert scripts_with_entity(hass, ENTITY_WATCH_TV)[0] == "script.test" - - assert issue_registry.async_get_issue(DOMAIN, "deprecated_switches") - assert issue_registry.async_get_issue( - DOMAIN, "deprecated_switches_switch.guest_room_watch_tv_automation.test" - ) - assert issue_registry.async_get_issue( - DOMAIN, "deprecated_switches_switch.guest_room_watch_tv_script.test" - ) - - assert len(issue_registry.issues) == 3