-
-
Notifications
You must be signed in to change notification settings - Fork 31.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dynalite Integration #27841
Dynalite Integration #27841
Conversation
I had a pull request Dynalite Integration #27621 but because I pulled from the upstream, the pull request included many commits. recreated the fork and added my files cleanly. it passes all the local tests. please let me know if this is in the right direction. If it is, i can start working on the documentation |
Hi, there was no activity on the new pull request. Do I need to do something to re add it to the queue? |
It's Hacktoberfest...we are dealing with approx. 300 open PRs across the organisation. Thus, it can take a while till the review starts. |
Is this still in the queue for review? I’d be very interested in using this in mainline release, rather than persisting with my own fork. Thanks Ziv. |
I would love to have it reviewed if possible. Not sure why no one picked it up. Was just about the send another question... Note that there is much more functionality that I added in my module, but was suggested to split it into small pull requests, so anxious to have it there and start adding to it. Ziv |
pinging again. can someone look at it or did i forget to do something to add it to the queues? |
if not await bridge.async_setup(): | ||
LOGGER.error("bridge.async_setup failed") | ||
return False | ||
hass.data[DOMAIN][host] = bridge |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should store each bridge by entry.entry_id
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please explain? Not sure what you mean by this. Is there any documentation or example of how this is done correctly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok. used the pattern from component tradfri. hopefully it is ok now
# Configure the dynalite devices | ||
self.dynalite_devices = DynaliteDevices( | ||
config=self.config, | ||
loop=hass.loop, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Python is moving away from passing in loops. Why do we need to pass in the loop here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done. thanks
async def async_setup(self, tries=0): | ||
"""Set up a Dynalite bridge.""" | ||
LOGGER.debug( | ||
"component bridge async_setup - %s", pprint.pformat(self.config_entry.data) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is expensive. You're always going to pretty format the config entry data, even if it's not going to be logged. Please remove this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed
|
||
async def async_step_import(self, import_info): | ||
"""Import a new bridge as a config entry.""" | ||
LOGGER.debug("async_step_import - %s", pprint.pformat(import_info)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove all usage of pprint
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed
CONF_AREA_CREATE_AUTO = "auto" | ||
CONF_BRIDGES = "bridges" | ||
|
||
ENTITY_CATEGORIES = ["light", "switch", "cover"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't seem to be used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you forget to push the change?
from .const import DOMAIN, LOGGER | ||
|
||
|
||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can remove this. It was never in Home Assistant under the old way.
"""Return true if this entity should be hidden from UI.""" | ||
return self._device.hidden | ||
|
||
@callback |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not a callback.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
@@ -0,0 +1,9 @@ | |||
{ | |||
"domain": "dynalite", | |||
"name": "Direct Integration for Philips Dynalite", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"name": "Direct Integration for Philips Dynalite", | |
"name": "Philips Dynalite", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changed
"domain": "dynalite", | ||
"name": "Direct Integration for Philips Dynalite", | ||
"config_flow": true, | ||
"documentation": "", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add url to docs link on website.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how do i know the docs link? Wouldn't it be merged at the same time this pull request goes into the code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is pull request 10866 in the docs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docs link is https://www.home-assistant.io/integrations/<domain>/
Hey Ziv, Thanks for your pull request. It took a while but we're slowly going through our backlog. I left some comments. Most of the things already look very good ! |
thanks. Looking at the comments now and will implement them |
I also rebased it (per the docs instructions) so it is now working off the latest dev version |
This comment has been minimized.
This comment has been minimized.
ok. all the requested changes have been implemented |
Thanks!
From: Paulus Schoutsen <notifications@github.com>
Sent: Monday, February 10, 2020 11:16 PM
To: home-assistant/home-assistant <home-assistant@noreply.github.com>
Cc: Ziv <zivk@hotmail.com>; Author <author@noreply.github.com>
Subject: Re: [home-assistant/home-assistant] Dynalite Integration (#27841)
@balloob approved this pull request.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub<#27841>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AD5UNSYFW6DA6R23CJ3A3PDRCG7Y5ANCNFSM4JCDQVQQ>.
|
from homeassistant.core import callback | ||
|
||
from .const import DATA_CONFIGS, DOMAIN, LOGGER | ||
from .light import DynaliteLight |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're leaking entity implementation details from the light platform to the bridge module. That's not a good pattern. The platform details should stay within the platform.
It's ok to extract a common function that's used by multiple platforms but the platform should still be responsible for creating and adding entities etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed. will be pushed shortly
if same_hub_entries: | ||
await asyncio.wait( | ||
[ | ||
self.hass.config_entries.async_remove(entry_id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a legacy pattern. We now have the unique_id api for config flow and config entries.
https://developers.home-assistant.io/docs/en/config_entries_config_flow_handler.html#unique-ids
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is quite difficult to understand the config_flow. Do you know if there is any example component that has the most simple config flow (only initiated from configuration.yaml)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think there's an integration with config flow and only import step that uses the unique_id api. That api is still quite new.
Often the import step just forwards the config to the user step, since the user step often can accept a subset of the items in the config import.
The rainmachine config flow is quite simple and has just import + user step. It doesn't use the unique_id api yet though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found a simple flow with just import + user step and unique_id api: gdacs.
LOGGER.debug("Illegal device category %s", device.category) | ||
continue | ||
added_entities.append(entity) | ||
self.all_entities[entity.unique_id] = entity |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should not store the entity instances outside their platforms in a shared container. It's ok to store a reference to the entity, eg the entity_id, to keep track of added entities.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed. will be pushed shortly
|
||
for device in devices: | ||
if device.category == "light": | ||
entity = DynaliteLight(device, self) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The entity should be created and added within its platform.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed. will be pushed shortly
@callback | ||
def try_schedule_ha(self): | ||
"""Schedule update HA state if configured.""" | ||
if self.hass: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a bad sign that we have to have this check. Entity state update methods should not be called from outside the entity. That's error prone, and that's why this check is needed.
Instead, if we need to tell an entity to update from outside its platform, we can use our dispatch helper to signal the entity to update.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed. will be pushed shortly
else: | ||
LOGGER.info("Disconnected from dynalite host") | ||
for uid in self.all_entities: | ||
self.all_entities[uid].try_schedule_ha() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use our dispatch helper to signal the entity method instead.
See eg the tuya integration for an example how to use the dispatch helper:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed. will be pushed shortly
self.host = config_entry.data[CONF_HOST] | ||
if self.host not in hass.data[DOMAIN][DATA_CONFIGS]: | ||
LOGGER.info("invalid host - %s", self.host) | ||
raise BridgeError(f"invalid host - {self.host}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We want to avoid side effects like logging or raising an error in init method. Please move that outside.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
config = hass.data[DOMAIN][DATA_CONFIGS].get(host) | ||
|
||
if config is None: | ||
LOGGER.error("__init async_setup_entry empty config for host %s", host) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should not allow this to happen. This should be caught earlier in the config flow.
Can you please explain? Not sure what you mean by the leak.
Also saw your other comments. Will work on all of them tonight, but this one is one that I didn’t understand.
From: Martin Hjelmare <notifications@github.com>
Sent: Tuesday, February 11, 2020 12:41 AM
To: home-assistant/home-assistant <home-assistant@noreply.github.com>
Cc: Ziv <zivk@hotmail.com>; Author <author@noreply.github.com>
Subject: Re: [home-assistant/home-assistant] Dynalite Integration (#27841)
@MartinHjelmare commented on this pull request.
________________________________
In homeassistant/components/dynalite/bridge.py<#27841 (comment)>:
@@ -0,0 +1,118 @@
+"""Code to handle a Dynalite bridge."""
+
+from dynalite_devices_lib import DynaliteDevices
+from dynalite_lib import CONF_ALL
+
+from homeassistant.const import CONF_HOST
+from homeassistant.core import callback
+
+from .const import DATA_CONFIGS, DOMAIN, LOGGER
+from .light import DynaliteLight
We're leaking entity implementation details from the light platform to the bridge module. That's not a good pattern. The platform details should stay within the platform.
It's ok to extract a common function that's used by multiple platforms but the platform should still be responsible for creating and adding entities etc.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub<#27841>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AD5UNS7ZGG2UEIEHOHEMAY3RCHJZJANCNFSM4JCDQVQQ>.
|
BTW, if there is a good reference platform to examine and learn from, please tell me and I will look into it. Tried modeling it after the hue platform which is similar in the sense of having a bridge that manages many devices but can look at other design patterns
On 11 Feb 2020, at 9:01, Ziv Kedem <zivk@hotmail.com> wrote:
Can you please explain? Not sure what you mean by the leak.
Also saw your other comments. Will work on all of them tonight, but this one is one that I didn’t understand.
From: Martin Hjelmare <notifications@github.com>
Sent: Tuesday, February 11, 2020 12:41 AM
To: home-assistant/home-assistant <home-assistant@noreply.github.com>
Cc: Ziv <zivk@hotmail.com>; Author <author@noreply.github.com>
Subject: Re: [home-assistant/home-assistant] Dynalite Integration (#27841)
@MartinHjelmare commented on this pull request.
________________________________
In homeassistant/components/dynalite/bridge.py<#27841 (comment)>:
@@ -0,0 +1,118 @@
+"""Code to handle a Dynalite bridge."""
+
+from dynalite_devices_lib import DynaliteDevices
+from dynalite_lib import CONF_ALL
+
+from homeassistant.const import CONF_HOST
+from homeassistant.core import callback
+
+from .const import DATA_CONFIGS, DOMAIN, LOGGER
+from .light import DynaliteLight
We're leaking entity implementation details from the light platform to the bridge module. That's not a good pattern. The platform details should stay within the platform.
It's ok to extract a common function that's used by multiple platforms but the platform should still be responsible for creating and adding entities etc.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub<#27841>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AD5UNS7ZGG2UEIEHOHEMAY3RCHJZJANCNFSM4JCDQVQQ>.
|
The platform entity creation and addition should stay within the platform and entities should not be accessed directly outside their platform. We're currently breaking that design pattern here. That's what I meant with "leak". |
It looks like the dynalite is using a push based model for updating state. It's good to take the model for updates into account when searching for examples. Smartthings uses push model and our dispatch helper to update entities. Entities don't seem to be discovered there though. Zha has a discovery function, using the dispatch helper, in each platform that gets called to add new entities when a new device is discovered. Mysensors is using a gateway and push model with dispatch helper but doesn't use config entry api, so is a bit dated. Unfortunately, the integrations with highest code quality and patterns is often not the simplest and may be hard to read as a clean example. |
Deconz is good example. |
That’s ok. I like the challenge…
I was looking for the interfaces such as the dispatch and signals. It seems that async_dispatcher_connect is used, but I cannot find any documentation about it online. Did I miss something?
From: Martin Hjelmare <notifications@github.com>
Sent: Tuesday, February 11, 2020 10:46 AM
To: home-assistant/home-assistant <home-assistant@noreply.github.com>
Cc: Ziv <zivk@hotmail.com>; Author <author@noreply.github.com>
Subject: Re: [home-assistant/home-assistant] Dynalite Integration (#27841)
It looks like the dynalite is using a push based model for updating state. It's good to take the model for updates into account when searching for examples.
Smartthings uses push model and our dispatch helper to update entities. Entities don't seem to be discovered there though.
Zha has a discovery function, using the dispatch helper, in each platform that gets called to add new entities when a new device is discovered.
Mysensors is using a gateway and push model with dispatch helper but doesn't use config entry api, so is a bit dated.
Unfortunately, the integrations with highest code quality and patterns is often not the simplest and may be hard to read as a clean example.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub<#27841>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AD5UNSZBLTGFKTSLQLII3XTRCJQSZANCNFSM4JCDQVQQ>.
|
figured out the dispatcher from the examples provided. Will implement all these comments and send a new pull request soon with them |
Thanks. Very helpful.
My flow is even simpler as I don’t have the user option so was able to take out most of the code
I pushed it with a PR #31760. It is still going through the checks, but all the fixes should be there
From: Martin Hjelmare <notifications@github.com>
Sent: Wednesday, February 12, 2020 10:34 PM
To: home-assistant/home-assistant <home-assistant@noreply.github.com>
Cc: Ziv <zivk@hotmail.com>; Author <author@noreply.github.com>
Subject: Re: [home-assistant/home-assistant] Dynalite Integration (#27841)
@MartinHjelmare commented on this pull request.
________________________________
In homeassistant/components/dynalite/config_flow.py<#27841 (comment)>:
+ """Return a config entry from an initialized bridge."""
+ LOGGER.debug("entry_from_bridge - %s", host)
+ # Remove all other entries of hubs with same ID or host
+
+ same_hub_entries = [
+ entry.entry_id
+ for entry in self.hass.config_entries.async_entries(DOMAIN)
+ if entry.data[CONF_HOST] == host
+ ]
+
+ LOGGER.debug("entry_from_bridge same_hub - %s", same_hub_entries)
+
+ if same_hub_entries:
+ await asyncio.wait(
+ [
+ self.hass.config_entries.async_remove(entry_id)
I found a simple flow with just import + user step and unique_id api: gdacs.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub<#27841>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AD5UNSZN5EJYCSZP6FR7TKTRCRMLJANCNFSM4JCDQVQQ>.
|
I believe I am good there. Followed the instructions from the tutorial.
If you can check out the code in the new PR it would be great. Should address all your comments
From: Martin Hjelmare <notifications@github.com>
Sent: Wednesday, February 12, 2020 10:31 PM
To: home-assistant/home-assistant <home-assistant@noreply.github.com>
Cc: Ziv <zivk@hotmail.com>; Author <author@noreply.github.com>
Subject: Re: [home-assistant/home-assistant] Dynalite Integration (#27841)
@MartinHjelmare commented on this pull request.
________________________________
In homeassistant/components/dynalite/config_flow.py<#27841 (comment)>:
+ """Return a config entry from an initialized bridge."""
+ LOGGER.debug("entry_from_bridge - %s", host)
+ # Remove all other entries of hubs with same ID or host
+
+ same_hub_entries = [
+ entry.entry_id
+ for entry in self.hass.config_entries.async_entries(DOMAIN)
+ if entry.data[CONF_HOST] == host
+ ]
+
+ LOGGER.debug("entry_from_bridge same_hub - %s", same_hub_entries)
+
+ if same_hub_entries:
+ await asyncio.wait(
+ [
+ self.hass.config_entries.async_remove(entry_id)
I don't think there's an integration with config flow and only import step that uses the unique_id api. That api is still quite new.
Often the import step just forwards the config to the user step, since the user step often can accept a subset of the items in the config import.
The rainmachine config flow is quite simple and has just import + user step. It doesn't use the unique_id api yet though.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub<#27841>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AD5UNS5UOZKHRWOY6T5I2ELRCRMBRANCNFSM4JCDQVQQ>.
|
Breaking Change:
Not a breaking change. Only adds a new integration
Description:
Opened a new pull request to clean up fork
I used Troy Kelly's library and component as a starting point, added several features to them, and am now able to control my full house which is based on Dynalite. I know that no other open-source system supports it, so will be glad if it can go into the mainstream, so others can use it as well. Will also be happy to make fixes to issues that come.
Since I am new to open-source development, wanted to understand what is needed to get this merged. I documented the basic features, that are in this pull request and will continue to add the features and documentation, but was asked to do it in parts so each PR is small.
I tried to follow the guidelines in the dev docs, but I am sure that there are some things that I missed. If this is interesting to add, please let me know if there is anything other than the documentation that needs fixing and i will take care of it quickly.
Best,
Ziv
Related issue (if applicable): fixes #
Pull request with documentation for home-assistant.io (if applicable): home-assistant/home-assistant.io#10866
Example entry for
configuration.yaml
(if applicable):Checklist:
tox
. Your PR cannot be merged unless tests passIf user exposed functionality or configuration variables are added/changed:
If the code communicates with devices, web services, or third-party tools:
python3 -m script.hassfest
.requirements_all.txt
by runningpython3 -m script.gen_requirements_all
..coveragerc
.If the code does not interact with devices: