Create zwave devices on OZW thread and only add them during discovery#6096
Conversation
|
@andrey-git, thanks for your PR! By analyzing the history of the files in this pull request, we identified @turbokongen, @fabaff and @armills to be potential reviewers. |
|
Awesome work. This is exactly what we needed for zwave. 👍 I just have two small suggestions:
|
|
|
You could have OZW add an async job to hass to add it to the dict. entity = get_entity(…)
@callback
def add_entity():
hass.data[DATA_ZWAVE_DICT][entity.entity_id] = entity
hass.add_job(add_entity)(obv replace entity_id with value id etc) |
|
@balloob If we call that before scheduling the platform setup, do we know for sure it will have been added before setup_platform is called? And actually, is there any reason we can't just pass the entity object directly to platform setup? After the call to platform get_device, nothing is touching that object until setup_platform. |
|
@armills everything passed to platform setup must be serializable. This was the first thing I tried. |
|
OK, I wasn't certain. Actually, what you could do is add a job like @balloob said, and have it first update the dict, then call the discovery method. That way we know for certain it happens in the correct order. EDIT: Like this from balloob's previous example. entity = get_entity(…)
@asyncio.coroutine
def add_entity():
hass.data[DATA_ZWAVE_DICT][entity.entity_id] = entity
yield from discovery.async_load_platform(hass, component, DOMAIN, {
...
hass.add_job(add_entity) |
balloob
left a comment
There was a problem hiding this comment.
This looks very good. Just a few minor tweaks.
| _LOGGER.debug('name=%s node_config=%s CONF_REFRESH_VALUE=%s' | ||
| ' CONF_REFRESH_DELAY=%s', name, node_config, | ||
| refresh, delay) | ||
| if value.command_class != zwave.const.COMMAND_CLASS_SWITCH_MULTILEVEL: |
There was a problem hiding this comment.
(for a future PR)
Should this filtering happening inside these methods or should get_device only be called with the correct values? (And thus the filtering done before)
There was a problem hiding this comment.
Some filtering can be removed as it is already done in component. I'll look into it in another PR
| if value.genre != zwave.const.GENRE_USER: | ||
| return | ||
| return None | ||
| if node.has_command_class(zwave.const.COMMAND_CLASS_USER_CODE): |
There was a problem hiding this comment.
Registering these services should stay in setup_platform
There was a problem hiding this comment.
They are registered conditionally by accessing node, so must be done on OZW thread.
| value.disable_poll() | ||
| platform = get_platform(component, DOMAIN) | ||
| device = platform.get_device( | ||
| node=node, value=value, node_config=node_config, hass=hass) |
There was a problem hiding this comment.
We can move node_config out of hass.data since we pass it in here.
I am not a big fan of using **kwargs for get_device. I think that we should standardize it.
get_config(node, value, node_config):
There is no hass there because this all about creating the device. Hass will be set on the entity once added to hass.
There was a problem hiding this comment.
Climate needs hass to pull the default units.
There was a problem hiding this comment.
Also most platforms don't need all 4 - won't it look better if each one would "declare" only what it needs?
| return None | ||
|
|
||
|
|
||
| def setup_platform(hass, config, add_devices, discovery_info=None): |
There was a problem hiding this comment.
I think that you can make a generic setup that you just import for each platform. We should make it async for improved speed:
(to be placed in zwave/__init__.py)
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Generic Z-Wave platform setup."""
if discovery_info is None or zwave.NETWORK is None:
return False
device = zwave.get_device(hass, discovery_info[zwave.const.DISCOVERY_DEVICE])
if device:
yield from async_add_devices([device])
return True
else:
return FalseIn the case of the lock component, you could then wrap this method and register the services.
from homeassistant.components.zwave import async_setup_platform as zwave_async_setup_platform
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
result = yield from zwave_async_setup_platform(…)
if not result or hass.services.has_service('lock', SERVICE_XX):
return
# Register services| def discover_device(component, device, dict_id): | ||
| """Put device in a dictionary and call discovery on it.""" | ||
| hass.data[DATA_ZWAVE_DICT][dict_id] = device | ||
| discovery.load_platform(hass, component, DOMAIN, { |
There was a problem hiding this comment.
We should probably use the async equivalent here, since we're already in the event loop at this point. No sense in creating a new job: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/helpers/discovery.py#L136 There's nothing waiting for discover_device to return.
There was a problem hiding this comment.
load_platform will blow up when used inside the event loop, so yes. We should.
|
Addressed comments, commented on those not addressed. |
|
Something is broken - the devices are not added. I'll investigate tomorrow. |
| if device: | ||
| dict_id = value.value_id | ||
|
|
||
| @callback |
There was a problem hiding this comment.
When this was a callback it was never executed
|
Fixed |
| return None | ||
|
|
||
|
|
||
| @callback |
There was a problem hiding this comment.
This is not a callback: there is a yield in this method.
|
|
||
|
|
||
| @asyncio.coroutine | ||
| def async_setup_platform(hass, config, async_add_devices, discovery_info=None): |
There was a problem hiding this comment.
Why create this method instead of just directly importing it?
from homeassistant.components.zwave import async_setup_platform # noqaThat's all there is to it 👍
There was a problem hiding this comment.
(noqa to tell our linter to sssh)
There was a problem hiding this comment.
Done with # noqa # pylint: disable=unused-import
noqa only affects flake8
| track_point_in_time( | ||
| self._hass, self.async_update_ha_state, | ||
| self.invalidate_after) | ||
| if self.hass: |
There was a problem hiding this comment.
Please use a guard clause:
if not self.hass:
return| platform = get_platform(component, DOMAIN) | ||
| device = platform.get_device( | ||
| node=node, value=value, node_config=node_config, hass=hass) | ||
| if device: |
There was a problem hiding this comment.
Please use a guard clause.
if not device:
return|
Lets not merge this into 0.39 but let it soak in dev after the release is cut. |
|
0.39 has been cut - now ready for merge :) |
Description:
Create zwave devices on OZW thread and only add them during discovery
If the code does not interact with devices:
toxrun successfully. Your PR cannot be merged unless tests pass