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.
This commit is contained in:
ehendrix23 2020-06-19 20:50:42 -06:00 committed by GitHub
parent 5642027ffb
commit 2fd6431cff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 53 additions and 20 deletions

View file

@ -13,7 +13,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.dispatcher import async_dispatcher_send 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 from .remote import HarmonyRemote
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -38,11 +38,18 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
name = entry.data[CONF_NAME] name = entry.data[CONF_NAME]
activity = entry.options.get(ATTR_ACTIVITY) activity = entry.options.get(ATTR_ACTIVITY)
delay_secs = entry.options.get(ATTR_DELAY_SECS, DEFAULT_DELAY_SECS) 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") harmony_conf_file = hass.config.path(f"harmony_{entry.unique_id}.conf")
try: try:
device = HarmonyRemote( 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() connected_ok = await device.connect()
except (asyncio.TimeoutError, ValueError, AttributeError): except (asyncio.TimeoutError, ValueError, AttributeError):

View file

@ -14,7 +14,7 @@ from homeassistant.components.remote import (
from homeassistant.const import CONF_HOST, CONF_NAME from homeassistant.const import CONF_HOST, CONF_NAME
from homeassistant.core import callback from homeassistant.core import callback
from .const import DOMAIN, UNIQUE_ID from .const import ATTR_ACTIVITY_NOTIFY, DOMAIN, UNIQUE_ID
from .util import ( from .util import (
find_best_name_for_remote, find_best_name_for_remote,
find_unique_id_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] options[ATTR_ACTIVITY] = user_input[ATTR_ACTIVITY]
if ATTR_DELAY_SECS in user_input: if ATTR_DELAY_SECS in user_input:
options[ATTR_DELAY_SECS] = user_input[ATTR_DELAY_SECS] 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 return options
@ -190,6 +192,10 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
vol.Optional( vol.Optional(
ATTR_ACTIVITY, default=self.config_entry.options.get(ATTR_ACTIVITY), ATTR_ACTIVITY, default=self.config_entry.options.get(ATTR_ACTIVITY),
): vol.In(remote.activity_names), ): 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) return self.async_show_form(step_id="init", data_schema=data_schema)

View file

@ -6,3 +6,4 @@ PLATFORMS = ["remote"]
UNIQUE_ID = "unique_id" UNIQUE_ID = "unique_id"
ACTIVITY_POWER_OFF = "PowerOff" ACTIVITY_POWER_OFF = "PowerOff"
HARMONY_OPTIONS_UPDATE = "harmony_options_update" HARMONY_OPTIONS_UPDATE = "harmony_options_update"
ATTR_ACTIVITY_NOTIFY = "activity_notify"

View file

@ -2,7 +2,7 @@
"domain": "harmony", "domain": "harmony",
"name": "Logitech Harmony Hub", "name": "Logitech Harmony Hub",
"documentation": "https://www.home-assistant.io/integrations/harmony", "documentation": "https://www.home-assistant.io/integrations/harmony",
"requirements": ["aioharmony==0.2.4"], "requirements": ["aioharmony==0.2.5"],
"codeowners": ["@ehendrix23", "@bramkragten", "@bdraco"], "codeowners": ["@ehendrix23", "@bramkragten", "@bdraco"],
"ssdp": [ "ssdp": [
{ {

View file

@ -3,12 +3,9 @@ import asyncio
import json import json
import logging import logging
from aioharmony.const import ClientCallbackType
import aioharmony.exceptions as aioexc import aioharmony.exceptions as aioexc
from aioharmony.harmonyapi import ( from aioharmony.harmonyapi import HarmonyAPI as HarmonyClient, SendCommandDevice
ClientCallbackType,
HarmonyAPI as HarmonyClient,
SendCommandDevice,
)
import voluptuous as vol import voluptuous as vol
from homeassistant.components import remote from homeassistant.components import remote
@ -31,6 +28,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect
from .const import ( from .const import (
ACTIVITY_POWER_OFF, ACTIVITY_POWER_OFF,
ATTR_ACTIVITY_NOTIFY,
DOMAIN, DOMAIN,
HARMONY_OPTIONS_UPDATE, HARMONY_OPTIONS_UPDATE,
SERVICE_CHANGE_CHANNEL, SERVICE_CHANGE_CHANNEL,
@ -128,7 +126,9 @@ async def async_setup_entry(
class HarmonyRemote(remote.RemoteEntity): class HarmonyRemote(remote.RemoteEntity):
"""Remote representation used to control a Harmony device.""" """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.""" """Initialize HarmonyRemote class."""
self._name = name self._name = name
self.host = host self.host = host
@ -140,6 +140,7 @@ class HarmonyRemote(remote.RemoteEntity):
self.delay_secs = delay_secs self.delay_secs = delay_secs
self._available = False self._available = False
self._unique_id = unique_id self._unique_id = unique_id
self._activity_notify = activity_notify
@property @property
def activity_names(self): def activity_names(self):
@ -162,16 +163,29 @@ class HarmonyRemote(remote.RemoteEntity):
if ATTR_ACTIVITY in data: if ATTR_ACTIVITY in data:
self.default_activity = data[ATTR_ACTIVITY] 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): async def async_added_to_hass(self):
"""Complete the initialization.""" """Complete the initialization."""
_LOGGER.debug("%s: Harmony Hub added", self._name) _LOGGER.debug("%s: Harmony Hub added", self._name)
# Register the callbacks # Register the callbacks
self._client.callbacks = ClientCallbackType( self._update_callbacks()
new_activity=self.new_activity,
config_updated=self.new_config,
connect=self.got_connected,
disconnect=self.got_disconnected,
)
self.async_on_remove( self.async_on_remove(
async_dispatcher_connect( async_dispatcher_connect(

View file

@ -28,7 +28,8 @@
"description": "Adjust Harmony Hub Options", "description": "Adjust Harmony Hub Options",
"data": { "data": {
"activity": "The default activity to execute when none is specified.", "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."
} }
} }
} }

View file

@ -27,7 +27,8 @@
"init": { "init": {
"data": { "data": {
"activity": "The default activity to execute when none is specified.", "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" "description": "Adjust Harmony Hub Options"
} }

View file

@ -181,7 +181,7 @@ aioftp==0.12.0
aioguardian==0.2.3 aioguardian==0.2.3
# homeassistant.components.harmony # homeassistant.components.harmony
aioharmony==0.2.4 aioharmony==0.2.5
# homeassistant.components.homekit_controller # homeassistant.components.homekit_controller
aiohomekit[IP]==0.2.38 aiohomekit[IP]==0.2.38

View file

@ -85,7 +85,7 @@ aiofreepybox==0.0.8
aioguardian==0.2.3 aioguardian==0.2.3
# homeassistant.components.harmony # homeassistant.components.harmony
aioharmony==0.2.4 aioharmony==0.2.5
# homeassistant.components.homekit_controller # homeassistant.components.homekit_controller
aiohomekit[IP]==0.2.38 aiohomekit[IP]==0.2.38

View file

@ -66,6 +66,7 @@ async def test_form_import(hass):
"name": "friend", "name": "friend",
"activity": "Watch TV", "activity": "Watch TV",
"delay_secs": 0.9, "delay_secs": 0.9,
"activity_notify": True,
"unique_id": "555234534543", "unique_id": "555234534543",
}, },
) )
@ -78,6 +79,7 @@ async def test_form_import(hass):
"name": "friend", "name": "friend",
"activity": "Watch TV", "activity": "Watch TV",
"delay_secs": 0.9, "delay_secs": 0.9,
"activity_notify": True,
} }
# It is not possible to import options at this time # It is not possible to import options at this time
# so they end up in the config entry data and are # so they end up in the config entry data and are
@ -148,6 +150,7 @@ async def test_form_cannot_connect(hass):
"name": "friend", "name": "friend",
"activity": "Watch TV", "activity": "Watch TV",
"delay_secs": 0.2, "delay_secs": 0.2,
"activity_notify": True,
}, },
) )