Add Konnected component with support for discovery, binary sensor, and switch#13670
Conversation
|
Hi @heythisisnate, It seems you haven't yet signed a CLA. Please do so here. Once you do that we will be able to review and accept this pull request. Thanks! |
There was a problem hiding this comment.
line too long (101 > 79 characters)
There was a problem hiding this comment.
line too long (101 > 79 characters)
There was a problem hiding this comment.
line too long (111 > 79 characters)
There was a problem hiding this comment.
line too long (102 > 79 characters)
There was a problem hiding this comment.
line too long (123 > 79 characters)
There was a problem hiding this comment.
continuation line over-indented for visual indent
|
This is my first Home Assistant PR. This code was written in collaboration with @emosenkis (squashed and cleaned up commits for easy reading). Can anyone help us out with a review? |
There was a problem hiding this comment.
Looks like that this is required. We have a validation helper for strings in homeassistant.helpers.config_validation.
There was a problem hiding this comment.
The DOMAIN contains a schema as well.
There was a problem hiding this comment.
Please check const.py if there is already a defined constant.
There was a problem hiding this comment.
Is 'supersecret' the default? If yes, then pass it in the validation.
There was a problem hiding this comment.
I opted to remove the default and make it required for the user to set.
There was a problem hiding this comment.
continuation line over-indented for visual indent
There was a problem hiding this comment.
continuation line over-indented for visual indent
|
Thank you for the feedback @fabaff. I think I've addressed all of the concerns mentioned. |
b051cca to
0fc832f
Compare
|
I just rebased on the latest |
|
It would be great to get this accepted and merged in ASAP. Konnected is the whole reason I'm beginning to migrate from SmartThings! |
|
I'm excited for this module! I tried it out and it works flawlessly! |
There was a problem hiding this comment.
The default seems to be set by the configuration schema:
vol.Required(CONF_TYPE, default='motion'): DEVICE_CLASSES_SCHEMA,There was a problem hiding this comment.
This seems overly verbose for an info log message... I would consider it a debug message if at all.
There was a problem hiding this comment.
The binary sensor doesn't seem to have an update method, so passing True here has no effect and should be removed.
There was a problem hiding this comment.
This debug message also seems quite over-verbose. The async_schedule_update_ha_state() will already make a state changed debug message for you.
There was a problem hiding this comment.
If the user doesn't provide pin nor zone, this config validation will still pass even though it's clearly invalid. See has_at_least_one_key for potentially fixing this.
There was a problem hiding this comment.
Nice. thanks for that tip.
There was a problem hiding this comment.
Suggestion: Please use the new async/await syntax if you can. We have Python 3.5 now and I've seen a few PRs getting comments like this one. See http://stackabuse.com/python-async-await-tutorial/ for a simple tutorial.
There was a problem hiding this comment.
Ok, I will read up on it. I'm pretty new to Python so I'm sure that I just modeled this after some other code that maybe wasn't up date.
There was a problem hiding this comment.
Just a style suggestion: it's weird to read code where sometimes 'devices' is used and sometimes CONF_DEVICES. I would recommend using one of them and sticking to it IMO.
There was a problem hiding this comment.
This also seems like a non-info log message. Especially with info messages, I don't expect to see raw python dictionaries in the logs.
There was a problem hiding this comment.
These log messages should IMO not be info. Home Assistant's default is to print every log message above and including info. If someone needs these log messages they can still manually set the log level for this component using the logger component.
There was a problem hiding this comment.
You're right, these were really meant for debugging discovery. I will change them to debug.
There was a problem hiding this comment.
This is not safe to timing attacks. See hmac.compare_digest for safe auth token checks.
There was a problem hiding this comment.
If this integration requires discovery: to be enabled as the docs state, it might be good to have it in DEPENDENCIES too I think.
e9e7129 to
6c4e399
Compare
|
Assuming the constants, Would that mean it could be released as part of 0.70 @syssi? |
|
I hope so. :-P I believe the merge window closes on friday. |
|
Awesome! Nate has been great about turning the fixes around quickly. So it sounds like we'll be getting native integration in 0.70. Thank you @syssi! |
|
@syssi Thank you SO much for the review! 🙌 Konnected users who are beta testing this: I've changed the |
|
@heythisisnate Sorry, but you've to move the const to the top of your component. The const.py is just used for prominent consts. ;-) |
|
@syssi Oh, gotcha. I misunderstood. Give me 5 minutes. |
|
@syssi updated |
|
Do you like to add your component and name to the CODEOWNERS file? It basically means you'll automatically get notified for all changes in your source files. |
|
@syssi Nice. Added. |
syssi
left a comment
There was a problem hiding this comment.
LGTM. There are some more consts which should be introduced on the long run IMO.
| def save_data(self): | ||
| """Save the device configuration to `hass.data`. | ||
|
|
||
| TODO: This can probably be refactored and tidied up. |
There was a problem hiding this comment.
Since this is a new component we should make sure to have it as ready as possible. At the least remove todo comment, at best follow the todo 👍
There was a problem hiding this comment.
I've removed the TODO comment. There will certainly be more work done on this component in the near future and opportunities to refactor later. This code works and has been beta tested for more than a month, so I don't think it's prudent to change right this moment or hold up an initial release.
|
Thank you for the approval @syssi. So what happens next? Do I have to merge this or does somebody from the core team do the merge into |
|
Woohoo! 🎉 Thanks for the merge. @syssi is it possible for you to un-block the documentation PR for merge?: EDIT: Thanks!! |
MartinHjelmare
left a comment
There was a problem hiding this comment.
Please open a new PR, to at least address the I/O being done inside coroutines.
| import logging | ||
|
|
||
| from homeassistant.components.binary_sensor import BinarySensorDevice | ||
| from homeassistant.components.konnected import (DOMAIN, PIN_TO_ZONE) |
There was a problem hiding this comment.
We're not allowed to overwrite the binary sensor domain with the konnected component domain. Instead import it like so:
from homeassistant.components.konnected import (DOMAIN as KONNECTED_DOMAIN, ...)| """Return the device class.""" | ||
| return self._device_class | ||
|
|
||
| @asyncio.coroutine |
There was a problem hiding this comment.
We're now using Python 3.5 async syntax, ie async def.
|
|
||
| import konnected | ||
| self.client = konnected.Client(host, str(port)) | ||
| self.status = self.client.get_status() |
There was a problem hiding this comment.
This call is making a request, ie I/O, and we're inside a coroutine here. That's not allowed. Probably change async_device_discovered to be non async as well as KonnectedDevice. If the interface library is not async, there's usually no point in making the home assistant component async.
There was a problem hiding this comment.
Hmm, I think this was all originally synchronous but at the suggestion of another reviewer I made these into coroutines: #13670 (comment)
So now I'm thoroughly confused as to the best way to resolve. Seems like either I make everything synchronous or change the konnected library to use asyncio http. Which is preferred? Pros/cons of each?
There was a problem hiding this comment.
Async is preferred, but I suggest you wait with that. It's probably easier to just make the home assistant component use the sync api for this release. Then you have time later to convert to async.
| if (desired_sensor_configuration != current_sensor_configuration) or \ | ||
| (current_actuator_config != desired_actuator_config): | ||
| _LOGGER.debug('pushing settings to device %s', self.device_id) | ||
| self.client.put_settings( |
There was a problem hiding this comment.
This is also making a request inside a coroutine.
| def sensor_configuration(self): | ||
| """Return the configuration map for syncing sensors.""" | ||
| return [{'pin': p} for p in | ||
| self.stored_configuration[CONF_BINARY_SENSORS].keys()] |
There was a problem hiding this comment.
.keys() is not needed when iterating a dict.
| return self.json_message('uninitialized sensor/actuator', | ||
| status_code=HTTP_INTERNAL_SERVER_ERROR) | ||
|
|
||
| await entity.async_set_state(state) |
There was a problem hiding this comment.
Entities should usually not be accessed directly from outside their platform. That's against the design philosophy of home assistant. Instead, we have the dispatch helper for this purpose, to send an update from the component to platform entities.
It's ok to store a key that references a specific device or entity, eg the entity_id in a dict to be able to know if the entity needs an update and send the update to the correct target with the dispatcher. But we shouldn't store the entities themselves so that they are accessible from outside the platform.
There was a problem hiding this comment.
How do I get the entity_id to store in a dict? It's unclear to me where this is set or how to retrieve it. self.entity_id returns None from inside the entity class. I must be missing something here.
There was a problem hiding this comment.
You might not need the entity_id. You don't need it specifically to use the dispatcher. I just mentioned it as an example identifier of an entity.
There's two parts to the dispatcher, a connect function and a send function. First you connect a target function to a target key. Then you send an update to the target key which will call all connected target functions of the target key.
The target key can be anything that is specific enough for what you want to target. If you want to target a specific entity, the entity_id is probably good. You could also use the builtin id function on the entity instance to get a unique id of the entity instance, if you don't need the key to be persistent between restarts.
You want to connect the entity target method from the entity coroutine method async_added_to_hass. At this point, the entity_id will be available on the entity, ie it will be something other than None.
There was a problem hiding this comment.
I understand conceptually.
Could you point me to a component that uses this pattern? This would help me tremendously.
| def _set_state(self, state): | ||
| self._state = state | ||
| self._data[ATTR_STATE] = state | ||
| self.schedule_update_ha_state() |
There was a problem hiding this comment.
Is there no feedback from the device when it changes state? If there is no feedback, the entity should be marked to use assumed_state, ie set that entity property to True.
If there is feedback after a state change, we shouldn't update state from the turn_on/turn_off methods, but let the feedback handle that, if it's done within a couple of seconds.
Description:
Adds a component for Konnected devices. Konnected is an open source application that runs on a NodeMCU ESP8266. Konnected develops hardware designed for connecting traditional wired alarm system sensors and sirens to home automation.
Pull request in home-assistant.github.io with documentation: home-assistant/home-assistant.io#5102
Example entry for
configuration.yaml: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:
REQUIREMENTSvariable (example).requirements_all.txtby runningscript/gen_requirements_all.py..coveragerc.