From 2fd6431cff00663d370acc0fe9465ae8651e769e Mon Sep 17 00:00:00 2001 From: ehendrix23 Date: Fri, 19 Jun 2020 20:50:42 -0600 Subject: [PATCH] Allow harmony activity change on start of switching activity (#36699) * Allow activity change on start of switch Allow activity to be updated when a switch to a new activity is initiated instead of when it is completed. * Updates based on feedback Some items are not required to be done as YAML is not used anymore. Cleaned-up some code. * Fix for change on how to set callbacks How callbacks are set now one has to set the new_activity and new_activity_starting as well, even just with None. * Added callback update Added so that when it is changed in the UI the callbacks will be changed as well. * Added test cases for notify setting Added test cases for config flow to test new setting for activity notifications. --- homeassistant/components/harmony/__init__.py | 11 +++++- .../components/harmony/config_flow.py | 8 +++- homeassistant/components/harmony/const.py | 1 + .../components/harmony/manifest.json | 2 +- homeassistant/components/harmony/remote.py | 38 +++++++++++++------ homeassistant/components/harmony/strings.json | 3 +- .../components/harmony/translations/en.json | 3 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/harmony/test_config_flow.py | 3 ++ 10 files changed, 53 insertions(+), 20 deletions(-) diff --git a/homeassistant/components/harmony/__init__.py b/homeassistant/components/harmony/__init__.py index 540e39f8f44..f08d4dcd151 100644 --- a/homeassistant/components/harmony/__init__.py +++ b/homeassistant/components/harmony/__init__.py @@ -13,7 +13,7 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers.dispatcher import async_dispatcher_send -from .const import DOMAIN, HARMONY_OPTIONS_UPDATE, PLATFORMS +from .const import ATTR_ACTIVITY_NOTIFY, DOMAIN, HARMONY_OPTIONS_UPDATE, PLATFORMS from .remote import HarmonyRemote _LOGGER = logging.getLogger(__name__) @@ -38,11 +38,18 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): name = entry.data[CONF_NAME] activity = entry.options.get(ATTR_ACTIVITY) delay_secs = entry.options.get(ATTR_DELAY_SECS, DEFAULT_DELAY_SECS) + activity_notify = entry.options.get(ATTR_ACTIVITY_NOTIFY, False) harmony_conf_file = hass.config.path(f"harmony_{entry.unique_id}.conf") try: device = HarmonyRemote( - name, entry.unique_id, address, activity, harmony_conf_file, delay_secs + name, + entry.unique_id, + address, + activity, + harmony_conf_file, + delay_secs, + activity_notify, ) connected_ok = await device.connect() except (asyncio.TimeoutError, ValueError, AttributeError): diff --git a/homeassistant/components/harmony/config_flow.py b/homeassistant/components/harmony/config_flow.py index 8d43b2d69ca..8487509407c 100644 --- a/homeassistant/components/harmony/config_flow.py +++ b/homeassistant/components/harmony/config_flow.py @@ -14,7 +14,7 @@ from homeassistant.components.remote import ( from homeassistant.const import CONF_HOST, CONF_NAME from homeassistant.core import callback -from .const import DOMAIN, UNIQUE_ID +from .const import ATTR_ACTIVITY_NOTIFY, DOMAIN, UNIQUE_ID from .util import ( find_best_name_for_remote, find_unique_id_for_remote, @@ -162,6 +162,8 @@ def _options_from_user_input(user_input): options[ATTR_ACTIVITY] = user_input[ATTR_ACTIVITY] if ATTR_DELAY_SECS in user_input: options[ATTR_DELAY_SECS] = user_input[ATTR_DELAY_SECS] + if ATTR_ACTIVITY_NOTIFY in user_input: + options[ATTR_ACTIVITY_NOTIFY] = user_input[ATTR_ACTIVITY_NOTIFY] return options @@ -190,6 +192,10 @@ class OptionsFlowHandler(config_entries.OptionsFlow): vol.Optional( ATTR_ACTIVITY, default=self.config_entry.options.get(ATTR_ACTIVITY), ): vol.In(remote.activity_names), + vol.Optional( + ATTR_ACTIVITY_NOTIFY, + default=self.config_entry.options.get(ATTR_ACTIVITY_NOTIFY, False), + ): vol.Coerce(bool), } ) return self.async_show_form(step_id="init", data_schema=data_schema) diff --git a/homeassistant/components/harmony/const.py b/homeassistant/components/harmony/const.py index 4cd5dce0af5..dcb4f74912f 100644 --- a/homeassistant/components/harmony/const.py +++ b/homeassistant/components/harmony/const.py @@ -6,3 +6,4 @@ PLATFORMS = ["remote"] UNIQUE_ID = "unique_id" ACTIVITY_POWER_OFF = "PowerOff" HARMONY_OPTIONS_UPDATE = "harmony_options_update" +ATTR_ACTIVITY_NOTIFY = "activity_notify" diff --git a/homeassistant/components/harmony/manifest.json b/homeassistant/components/harmony/manifest.json index 0c030caa04c..40f88ad19ef 100644 --- a/homeassistant/components/harmony/manifest.json +++ b/homeassistant/components/harmony/manifest.json @@ -2,7 +2,7 @@ "domain": "harmony", "name": "Logitech Harmony Hub", "documentation": "https://www.home-assistant.io/integrations/harmony", - "requirements": ["aioharmony==0.2.4"], + "requirements": ["aioharmony==0.2.5"], "codeowners": ["@ehendrix23", "@bramkragten", "@bdraco"], "ssdp": [ { diff --git a/homeassistant/components/harmony/remote.py b/homeassistant/components/harmony/remote.py index 25b68b42e72..d5d8eb5773f 100644 --- a/homeassistant/components/harmony/remote.py +++ b/homeassistant/components/harmony/remote.py @@ -3,12 +3,9 @@ import asyncio import json import logging +from aioharmony.const import ClientCallbackType import aioharmony.exceptions as aioexc -from aioharmony.harmonyapi import ( - ClientCallbackType, - HarmonyAPI as HarmonyClient, - SendCommandDevice, -) +from aioharmony.harmonyapi import HarmonyAPI as HarmonyClient, SendCommandDevice import voluptuous as vol from homeassistant.components import remote @@ -31,6 +28,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from .const import ( ACTIVITY_POWER_OFF, + ATTR_ACTIVITY_NOTIFY, DOMAIN, HARMONY_OPTIONS_UPDATE, SERVICE_CHANGE_CHANNEL, @@ -128,7 +126,9 @@ async def async_setup_entry( class HarmonyRemote(remote.RemoteEntity): """Remote representation used to control a Harmony device.""" - def __init__(self, name, unique_id, host, activity, out_path, delay_secs): + def __init__( + self, name, unique_id, host, activity, out_path, delay_secs, activity_notify + ): """Initialize HarmonyRemote class.""" self._name = name self.host = host @@ -140,6 +140,7 @@ class HarmonyRemote(remote.RemoteEntity): self.delay_secs = delay_secs self._available = False self._unique_id = unique_id + self._activity_notify = activity_notify @property def activity_names(self): @@ -162,16 +163,29 @@ class HarmonyRemote(remote.RemoteEntity): if ATTR_ACTIVITY in data: self.default_activity = data[ATTR_ACTIVITY] + if ATTR_ACTIVITY_NOTIFY in data: + self._activity_notify = data[ATTR_ACTIVITY_NOTIFY] + self._update_callbacks() + + def _update_callbacks(self): + callbacks = { + "config_updated": self.new_config, + "connect": self.got_connected, + "disconnect": self.got_disconnected, + "new_activity_starting": None, + "new_activity": None, + } + if self._activity_notify: + callbacks["new_activity_starting"] = self.new_activity + else: + callbacks["new_activity"] = self.new_activity + self._client.callbacks = ClientCallbackType(**callbacks) + async def async_added_to_hass(self): """Complete the initialization.""" _LOGGER.debug("%s: Harmony Hub added", self._name) # Register the callbacks - self._client.callbacks = ClientCallbackType( - new_activity=self.new_activity, - config_updated=self.new_config, - connect=self.got_connected, - disconnect=self.got_disconnected, - ) + self._update_callbacks() self.async_on_remove( async_dispatcher_connect( diff --git a/homeassistant/components/harmony/strings.json b/homeassistant/components/harmony/strings.json index 86de34672be..053d5cea8bd 100644 --- a/homeassistant/components/harmony/strings.json +++ b/homeassistant/components/harmony/strings.json @@ -28,7 +28,8 @@ "description": "Adjust Harmony Hub Options", "data": { "activity": "The default activity to execute when none is specified.", - "delay_secs": "The delay between sending commands." + "delay_secs": "The delay between sending commands.", + "activity_notify": "Update current activity on start of activity switch." } } } diff --git a/homeassistant/components/harmony/translations/en.json b/homeassistant/components/harmony/translations/en.json index ce13e79e279..f004ab153cf 100644 --- a/homeassistant/components/harmony/translations/en.json +++ b/homeassistant/components/harmony/translations/en.json @@ -27,7 +27,8 @@ "init": { "data": { "activity": "The default activity to execute when none is specified.", - "delay_secs": "The delay between sending commands." + "delay_secs": "The delay between sending commands.", + "activity_notify": "Update current activity on start of activity switch." }, "description": "Adjust Harmony Hub Options" } diff --git a/requirements_all.txt b/requirements_all.txt index 90cf5f581a8..2b87208b8bb 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -181,7 +181,7 @@ aioftp==0.12.0 aioguardian==0.2.3 # homeassistant.components.harmony -aioharmony==0.2.4 +aioharmony==0.2.5 # homeassistant.components.homekit_controller aiohomekit[IP]==0.2.38 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 24ac9114088..a34eee393b8 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -85,7 +85,7 @@ aiofreepybox==0.0.8 aioguardian==0.2.3 # homeassistant.components.harmony -aioharmony==0.2.4 +aioharmony==0.2.5 # homeassistant.components.homekit_controller aiohomekit[IP]==0.2.38 diff --git a/tests/components/harmony/test_config_flow.py b/tests/components/harmony/test_config_flow.py index 079923330e2..812e3414ea9 100644 --- a/tests/components/harmony/test_config_flow.py +++ b/tests/components/harmony/test_config_flow.py @@ -66,6 +66,7 @@ async def test_form_import(hass): "name": "friend", "activity": "Watch TV", "delay_secs": 0.9, + "activity_notify": True, "unique_id": "555234534543", }, ) @@ -78,6 +79,7 @@ async def test_form_import(hass): "name": "friend", "activity": "Watch TV", "delay_secs": 0.9, + "activity_notify": True, } # It is not possible to import options at this time # so they end up in the config entry data and are @@ -148,6 +150,7 @@ async def test_form_cannot_connect(hass): "name": "friend", "activity": "Watch TV", "delay_secs": 0.2, + "activity_notify": True, }, )