Skip to content

Config Flow and Entity registry support for Monoprice#30337

Merged
balloob merged 12 commits into
home-assistant:devfrom
OnFreund:monoprice_entity_registry
Mar 22, 2020
Merged

Config Flow and Entity registry support for Monoprice#30337
balloob merged 12 commits into
home-assistant:devfrom
OnFreund:monoprice_entity_registry

Conversation

@OnFreund
Copy link
Copy Markdown
Contributor

@OnFreund OnFreund commented Dec 31, 2019

Breaking Change:

Monoprice is now configured through the UI. Please remove the configuration from YAML, and add the Monoprice integration via the Integrations page.

Description:

Monoprice is now configured through the UI, with device and entity registry support.

Pull request with documentation for home-assistant.io (if applicable): home-assistant/home-assistant.io#12480

Checklist:

  • The code change is tested and works locally.
  • Local tests pass with tox. Your PR cannot be merged unless tests pass
  • There is no commented out code in this PR.
  • I have followed the development checklist

If user exposed functionality or configuration variables are added/changed:

If the code does not interact with devices:

  • Tests have been added to verify that the new code works.

@probot-home-assistant
Copy link
Copy Markdown

Hey there @etsinko, mind taking a look at this pull request as its been labeled with a integration (monoprice) you are listed as a codeowner for? Thanks!

@OnFreund
Copy link
Copy Markdown
Contributor Author

OnFreund commented Dec 31, 2019

I'm planning a few changes to this integration to bring it up to date with HA (looks like it hasn't really been touched much in the past couple of years), but this change can stand on its own and is the least controversial of those changes, so even if the rest seems off, I believe this is a good one.
Future changes I plan:

  • Device registry support, so that the different zones can be put in an Areas.
  • Config flow support (maybe with options flow for source names), and make the entities fully managed by the registry, rather than through configuration.
  • Multiple (named) snapshots.
  • Leveraging the recently updated Scene functionality as the implementation for said snapshots.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't work because one could configure two devices via 2 different serial ports. Can you get a unique ID from the device ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The device does not have a unique id. I thought about using the serial port address as the prefix, but:

  1. The current implementation also doesn't seem to support more than one instance (or at least has confusing functionality with snapshots).
  2. These devices can chain together if you have 2 or 3, so having two unique devices, each connected via serial, should be very rare.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And of course that would mean that a change to the serial address would create new entities.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need a better solution for Home Assistant to deal with these cases. In it's current form we cannot accept this PR, as it means we might have to break it in the future when it will be possible to get unique IDs per device.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I'm open to other ideas. Here's my thinking:

Some axioms first:

  • The device itself does not have any unique ID
  • The zones within it do have a unique ID relative to the device
  • If you have several of them, up to 3 devices can be chained together so they look like a single device to HA. A deployment with two distinct instances seems like an extremely rare occasion.
  • The existing implementation wasn't designed for multiple instances, but might partially work (I don't really have a way to test it).

Here are my humble suggestions:

  1. Create some sort of mechanism for "singleton" devices that can only have one instance in HA. Some other components can clearly fit this category (e.g. Sun), and I'm pretty sure there are also a lot of integrations that might not support multiple instances (many integrations in the "Hub" category are likely candidates, for example). This will limit functionality, but ensure no errors are caused as a result of multiple instances in integrations that don't support it. This will require core changes though, and can not be implemented just within this PR.
  2. Add the serial address as a prefix to the unique ID. As mentioned, this will mean new entities if the address changes (but then again, if this was configured through a config entry, a change in address would likely have caused new entities as well).
  3. Allow the user to define a prefix per instance. In essence, this means the user is controlling the unique id of each device. This is the simplest and cleanest implementation, I believe, and could be generalized as a pattern.
  4. Once the configuration switches over to a config entry, store a random value in the config entry as a prefix. As long as it's the same config entry, the entities will stay the same.

What do you think?

@OnFreund
Copy link
Copy Markdown
Contributor Author

OnFreund commented Jan 5, 2020

@balloob gave it more thought, and I really like solution 3 - it also has the benefit of being very explicit (after all, the user cares about the "logical uniqueness" of the device, not its "physical uniqueness", so you can replace the device, and keep the same connections, and entity ids will stay the same, yet if you choose a new namespace, it'll create new entities).
Additionally, the namespace allows targeting of entities belonging to a specific instance in service calls (so you can save/restore a snapshot for a specific instance by passing a namespace to the service instead of entity ids - but that's stuff for a future PR).
The downside is that it's a breaking change - everyone with a monoprice configuration will have to add a namespace.

I also think I know how to fix snapshots in case of multiple instances - will fix this in a separate PR once this is approved.

@balloob
Copy link
Copy Markdown
Member

balloob commented Jan 6, 2020

I like option 3, but we should first decide on that in the architecture repository. We dont' change architecture rules in PRs to the Python repo.

@OnFreund
Copy link
Copy Markdown
Contributor Author

OnFreund commented Jan 7, 2020

No problem. I thought the arch repo was just when changes to core are required, but I opened this home-assistant/architecture#333, including a potential core implementation.

@MartinHjelmare
Copy link
Copy Markdown
Member

Is there no serial number for the device? PySerial supports getting the serial number of a connected device. Did you try this?

@OnFreund
Copy link
Copy Markdown
Contributor Author

OnFreund commented Jan 7, 2020

@MartinHjelmare from what I understand PySerial can only obtain the serial number for USB devices:

USB specific data, these are all None if it is not an USB device (or the platform does not support extended info).

https://pyserial.readthedocs.io/en/latest/tools.html

In this case, even if I obtain a serial number, it will most likely represent the USB to RS232 adapter, rather than the actual device.

@MartinHjelmare
Copy link
Copy Markdown
Member

Does it matter? If the adapter is required it could be seen as part of the device.

@OnFreund
Copy link
Copy Markdown
Contributor Author

OnFreund commented Jan 7, 2020

It's not required (you can use a COM port, RS232 hat, tunnel over the network with ser2net, etc...), but even if it was, I don't think it's the right approach. It will be like using the serial ID of a USB splitter hub for a USB device - these are cheap adapters and replacing them should not create new entities.

@OnFreund
Copy link
Copy Markdown
Contributor Author

@balloob:
The outcome of home-assistant/architecture#333 means that we can't have YAML configuration, and this must be a breaking change. As a result, I'm going to completely rewrite the configuration schema here.
Most of it should be easy to drop, as it's simply defining entities and their names, which the entity registry can handle.
The part I'm not sure how to handle is the sources. Changing the names (or enabling/disabling) should not require a new config entry (which is a breaking change for up to 18 devices/entities), but they don't change often enough, so an HA restart, for example, is acceptable (and is required today anyway, so not a regression).
One option I can think of is an external configuration file (like the Harmony integration). It's cumbersome, and means you need to add the integration, wait for the file to be created, edit it, and then restart HA.
Another alternative is the Options flow, but documentation for it is very sparse, and I don't have any integration that supports it, so I'm not even sure if it's the right approach and how does it actually function.
The third alternative I can think of is creating entities for them, which doesn't seem right given that they don't hold any state or perform any action.

Any guidance?

@balloob
Copy link
Copy Markdown
Member

balloob commented Feb 18, 2020

It's possible to have YAML configuration that is imported from the component setup. https://github.com/home-assistant/home-assistant/blob/2e5161997f03554639b8d1f270a73be869950f2e/homeassistant/components/hue/__init__.py#L92-L102

I think that the options flow is the right way to go. We have just updated the demo integration to have an options flow, including the new multi-select. Add demo: to your YAML file and try it out.

@OnFreund
Copy link
Copy Markdown
Contributor Author

@balloob Thanks, I'll check out the demo flow.
As for importing from YAML, as I mentioned in the arch topic, it requires some form of unique id, which creates a chicken and egg problem. That's one of the reasons I'm not a big fan of the outcome of that topic.

@OnFreund OnFreund changed the title Entity registry support for Monoprice [WIP] Entity registry support for Monoprice Feb 22, 2020
@OnFreund OnFreund force-pushed the monoprice_entity_registry branch from 4e7adce to e3f8876 Compare February 22, 2020 18:09
@OnFreund
Copy link
Copy Markdown
Contributor Author

@balloob I added the config flow and modified the tests. I have one failing test: tests/components/monoprice/test_media_player.py:208 test_update[pyloop], with

homeassistant.exceptions.ServiceNotFound: Unable to find service homeassistant/update_entity

Not sure how to fix this.

@OnFreund
Copy link
Copy Markdown
Contributor Author

As for the options flow, I'll add that in a future PR.

@balloob
Copy link
Copy Markdown
Member

balloob commented Feb 25, 2020

Make sure to remove the [wip] tag if it's ready for review.

@OnFreund OnFreund changed the title [WIP] Entity registry support for Monoprice Entity registry support for Monoprice Feb 25, 2020
@OnFreund OnFreund force-pushed the monoprice_entity_registry branch from 774fc0d to 552d117 Compare February 26, 2020 22:47
@codecov
Copy link
Copy Markdown

codecov Bot commented Feb 26, 2020

Codecov Report

Merging #30337 into dev will increase coverage by 0.01%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff             @@
##              dev   #30337      +/-   ##
==========================================
+ Coverage   94.74%   94.76%   +0.01%     
==========================================
  Files         772      776       +4     
  Lines       55911    56220     +309     
==========================================
+ Hits        52975    53278     +303     
- Misses       2936     2942       +6
Impacted Files Coverage Δ
homeassistant/generated/config_flows.py 100% <ø> (ø) ⬆️
homeassistant/components/monoprice/config_flow.py 100% <100%> (ø)
homeassistant/components/monoprice/const.py 100% <100%> (ø) ⬆️
homeassistant/components/monoprice/media_player.py 96.69% <100%> (+2.78%) ⬆️
homeassistant/components/lovelace/const.py 94.59% <0%> (-5.41%) ⬇️
homeassistant/helpers/storage.py 91.81% <0%> (-3.42%) ⬇️
homeassistant/components/lovelace/resources.py 92.3% <0%> (-1.81%) ⬇️
homeassistant/components/hue/bridge.py 70.68% <0%> (-1.39%) ⬇️
...ssistant/components/islamic_prayer_times/sensor.py 94.62% <0%> (-0.06%) ⬇️
... and 49 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 16dba3f...0c4689c. Read the comment docs.

@OnFreund OnFreund changed the title Entity registry support for Monoprice [WIP] Entity registry support for Monoprice Feb 27, 2020
@OnFreund OnFreund changed the title [WIP] Entity registry support for Monoprice Entity registry support for Monoprice Mar 14, 2020
@OnFreund OnFreund requested a review from balloob March 14, 2020 20:26
@OnFreund
Copy link
Copy Markdown
Contributor Author

@balloob any chance you can review this soon? There's a follow up options flow PR, and I'm hoping we can get both together into 0.108. Thanks

@OnFreund OnFreund changed the title Entity registry support for Monoprice Config Flow and Entity registry support for Monoprice Mar 19, 2020

try:
monoprice = get_monoprice(port)
monoprice = await hass.async_add_executor_job(get_monoprice, port)
Copy link
Copy Markdown
Member

@balloob balloob Mar 22, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we do this in __init__.py we can raise ConfigEntryNotReady if you get a SerialException so it will automatically retry later.

Copy link
Copy Markdown
Member

@MartinHjelmare MartinHjelmare Mar 22, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use get_async_monoprice instead?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MartinHjelmare that's my goal, and the code changes are relatively straightforward. The reason I'm not doing this yet is that pymonoprice is dependent on pyserial-asyncio for its async implementation, which does not seem to be maintained and is using old-style coroutine syntax. I'm afraid it's not going to work with future python versions.

monoprice = get_monoprice(port)
monoprice = await hass.async_add_executor_job(get_monoprice, port)
except SerialException:
_LOGGER.error("Error connecting to Monoprice controller")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You want to add the port in case you have multiple set up.

Copy link
Copy Markdown
Member

@balloob balloob left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! 2 tiny comments but they don't need to be addressed in this PR so I'll go ahead and merge this.

@balloob balloob merged commit e8bd1b9 into home-assistant:dev Mar 22, 2020
"""Error to indicate we cannot connect."""


class InvalidAuth(exceptions.HomeAssistantError):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't used.



def setup_platform(hass, config, add_entities, discovery_info=None):
async def async_setup_entry(hass, config_entry, async_add_devices):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rename async_add_devices to async_add_entities.

async def async_setup_entry(hass, config_entry, async_add_devices):
"""Set up the Monoprice 6-zone amplifier platform."""
port = config.get(CONF_PORT)
port = config_entry.data.get(CONF_PORT)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Port is required so we can use dict[key].

}
sources = _get_sources(config_entry.data.get(CONF_SOURCES))

devices = []
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rename devices to entities.

@MartinHjelmare
Copy link
Copy Markdown
Member

It looks like there's no docs PR linked. Please link a docs PR since the config has changed.

@OnFreund
Copy link
Copy Markdown
Contributor Author

@MartinHjelmare I created a PR for the docs:
home-assistant/home-assistant.io#12480

@OnFreund OnFreund mentioned this pull request Mar 22, 2020
20 tasks
@OnFreund
Copy link
Copy Markdown
Contributor Author

@MartinHjelmare @balloob I addressed your comments in a new PR: #33133

@OnFreund OnFreund deleted the monoprice_entity_registry branch March 22, 2020 12:28
@OnFreund OnFreund mentioned this pull request Mar 22, 2020
20 tasks
@lock lock Bot locked and limited conversation to collaborators Mar 27, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants