Skip to content

Commit

Permalink
Merge pull request #25 from JackJPowell/Development
Browse files Browse the repository at this point in the history
Development
  • Loading branch information
JackJPowell authored Mar 12, 2024
2 parents 1b7fd06 + f68f9f1 commit f7a161b
Show file tree
Hide file tree
Showing 10 changed files with 282 additions and 70 deletions.
20 changes: 18 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,18 @@ After the device is configured, the integration will expose 22 entities plus the
- The ability to install Remote Two firmware from within home assistant is implemented but currently disabled.
- Switches
- A switch is created for every activity defined that is not apart of an activity group.
- An option exists to create a switch for each activity regardless of activity group.
- Select
- A select is created for every activity group defined.
- An option exists to suppress the creation of activity groups
- Button
- A button is available to restart the remote.
- Remote
- A remote is available to send pre-configured IR commands from the dock (See Below). It also provides a select to activate an activity and extra state about the status of activities and media player entities
- Media player
- A media player entity is created providing controls and information about currently playing media. If multiple media player entities are active, the integration attempts to select the most appropriate based on activity and recency.
- You can override this behavior by selecting a different media source from the sources menu in the Media Player control
- You can override this behavior by selecting a different media source from the sound mode menu in the Media Player control
- Options exist to create a media player per activity group or per activity.
- A reminder: The controls are acting solely on the entity that is being displayed and not the activity that is running. For instance, if the media player doesn't control your volume, e.g. your receiver does, adjusting the volume via the media player controls will not have the desired effect.
- Number

Expand All @@ -109,9 +112,22 @@ target:

> [!TIP] > **device:** will match the case-sensitive name of your remote defined in the web configurator on the remote page. **command** will match the case-senstitive name of the pre-defined (custom or codeset) command defined for that remote. **num_repeats** is optional.
## Options

Additional options have been added to the intergration for further customization:

- Activity Options:
- Create all activities as switches
- Suppress the creation of activity groups as selects (best combined with the previous option)
- Media Player Options:
- Create a global media player
- Create a media player for each activity group on your remote
- Create a media player for each activity on your remote

## Zeroconf

Your Remote Two will now be automatically discovered on the network. If you have already configured the remote two integration, you can ignore this discovery.
Your Remote Two will now be automatically discovered on the network.
**Due to insufficient data in the zeroconf broadcast, home assistant is not able to determine if a newly discovered remote has already been configured. This will present itself as another prompt to configure your Remote Two. If you have already configured your remote, you can ignore this discovery.**

## Future Ideas

Expand Down
109 changes: 84 additions & 25 deletions custom_components/unfoldedcircle/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,23 @@
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.components.zeroconf import ZeroconfServiceInfo
from homeassistant.config_entries import ConfigEntry, ConfigFlow, OptionsFlow
from homeassistant.config_entries import ConfigEntry, ConfigFlow
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_NAME, CONF_PORT
from homeassistant.core import callback
from homeassistant.data_entry_flow import FlowResult
from homeassistant.exceptions import HomeAssistantError
from pyUnfoldedCircleRemote.const import AUTH_APIKEY_NAME
from pyUnfoldedCircleRemote.remote import AuthenticationError, Remote

from .const import CONF_SERIAL, DOMAIN
from .const import (
CONF_ACTIVITIES_AS_SWITCHES,
CONF_ACTIVITY_GROUP_MEDIA_ENTITIES,
CONF_ACTIVITY_MEDIA_ENTITIES,
CONF_GLOBAL_MEDIA_ENTITY,
CONF_SERIAL,
CONF_SUPPRESS_ACTIVITIY_GROUPS,
DOMAIN,
)

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -66,11 +75,6 @@ async def validate_input(data: dict[str, Any], host: str = "") -> dict[str, Any]
if remote.mac_address:
mac_address = remote.mac_address.replace(":", "").lower()

# If you cannot connect:
# throw CannotConnect
# If the authentication is wrong:
# InvalidAuth

# Return info that you want to store in the config entry.
return {
"title": remote.name,
Expand Down Expand Up @@ -98,11 +102,13 @@ def __init__(self) -> None:
self.api_keyname: str = None
self.discovery_info: dict[str, Any] = {}

# @staticmethod
# @callback
# def async_get_options_flow(config_entry: ConfigEntry) -> UnfoldedCircleRemoteOptionsFlowHandler:
# """Get the options flow for this handler."""
# return UnfoldedCircleRemoteOptionsFlowHandler(config_entry)
@staticmethod
@callback
def async_get_options_flow(
config_entry: ConfigEntry,
):
"""Get the options flow for this handler."""
return UnfoldedCircleRemoteOptionsFlowHandler(config_entry)

async def async_step_zeroconf(self, discovery_info: ZeroconfServiceInfo):
"""Handle zeroconf discovery."""
Expand Down Expand Up @@ -199,10 +205,6 @@ async def async_step_zeroconf_confirm(
data=info,
)

# self.context["title_placeholders"] = {
# "name": f"{self.discovery.product_name} ({self.discovery.serial})"
# }

return self.async_show_form(
step_id="zeroconf_confirm",
data_schema=STEP_ZEROCONF_DATA_SCHEMA,
Expand Down Expand Up @@ -307,25 +309,82 @@ async def async_step_reauth_confirm(
)


class UnfoldedCircleRemoteOptionsFlowHandler(OptionsFlow):
class UnfoldedCircleRemoteOptionsFlowHandler(config_entries.OptionsFlow):
"""Handle Unfolded Circle Remote options."""

def __init__(self, config_entry: ConfigEntry) -> None:
def __init__(self, config_entry: config_entries.ConfigEntry) -> None:
"""Initialize options flow."""
self.config_entry = config_entry
self.options = dict(config_entry.options)

async def async_step_init(
self, user_input: dict[str, int] | None = None
) -> FlowResult:
"""Manage Unfolded Circle options."""
async def async_step_init(self, user_input=None): # pylint: disable=unused-argument
"""Manage the options."""
return await self.async_step_activities()

async def async_step_media_player(self, user_input=None):
"""Handle a flow initialized by the user."""
if user_input is not None:
return self.async_create_entry(title="", data=user_input)
self.options.update(user_input)
return await self._update_options()

return self.async_show_form(
step_id="init",
data_schema=STEP_USER_DATA_SCHEMA,
step_id="media_player",
data_schema=vol.Schema(
{
vol.Optional(
CONF_GLOBAL_MEDIA_ENTITY,
default=self.config_entry.options.get(
CONF_GLOBAL_MEDIA_ENTITY, True
),
): bool,
vol.Optional(
CONF_ACTIVITY_GROUP_MEDIA_ENTITIES,
default=self.config_entry.options.get(
CONF_ACTIVITY_GROUP_MEDIA_ENTITIES, False
),
): bool,
vol.Optional(
CONF_ACTIVITY_MEDIA_ENTITIES,
default=self.config_entry.options.get(
CONF_ACTIVITY_MEDIA_ENTITIES, False
),
): bool,
}
),
last_step=True,
)

async def async_step_activities(self, user_input=None):
"""Handle options step two flow initialized by the user."""
if user_input is not None:
self.options.update(user_input)
return await self.async_step_media_player()

return self.async_show_form(
step_id="activities",
data_schema=vol.Schema(
{
vol.Optional(
CONF_ACTIVITIES_AS_SWITCHES,
default=self.config_entry.options.get(
CONF_ACTIVITIES_AS_SWITCHES, False
),
): bool,
vol.Optional(
CONF_SUPPRESS_ACTIVITIY_GROUPS,
default=self.config_entry.options.get(
CONF_SUPPRESS_ACTIVITIY_GROUPS, False
),
): bool,
}
),
last_step=False,
)

async def _update_options(self):
"""Update config entry options."""
return self.async_create_entry(title="", data=self.options)


class CannotConnect(HomeAssistantError):
"""Error to indicate we cannot connect."""
Expand Down
5 changes: 5 additions & 0 deletions custom_components/unfoldedcircle/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
DOMAIN = "unfoldedcircle"

CONF_SERIAL = "serial"
CONF_ACTIVITY_GROUP_MEDIA_ENTITIES = "activity_group_media_entities"
CONF_GLOBAL_MEDIA_ENTITY = "global_media_entity"
CONF_ACTIVITY_MEDIA_ENTITIES = "activity_media_entities"
CONF_ACTIVITIES_AS_SWITCHES = "activities_as_switches"
CONF_SUPPRESS_ACTIVITIY_GROUPS = "suppress_activity_groups"
DEVICE_SCAN_INTERVAL = timedelta(seconds=30)
UNFOLDED_CIRCLE_COORDINATOR = "unfolded_circle_coordinator"
UNFOLDED_CIRCLE_API = "unfolded_circle_api"
2 changes: 1 addition & 1 deletion custom_components/unfoldedcircle/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"codeowners": ["@jackjpowell"],
"config_flow": true,
"dependencies": ["network", "zeroconf"],
"version": "0.6.0",
"version": "0.6.1",
"documentation": "https://github.com/JackJPowell/hass-unfoldedcircle",
"homekit": {},
"integration_type": "device",
Expand Down
Loading

0 comments on commit f7a161b

Please sign in to comment.