Skip to content

Add Konnected component with support for discovery, binary sensor, and switch#13670

Merged
syssi merged 13 commits into
home-assistant:devfrom
konnected-io:konnected
May 15, 2018
Merged

Add Konnected component with support for discovery, binary sensor, and switch#13670
syssi merged 13 commits into
home-assistant:devfrom
konnected-io:konnected

Conversation

@heythisisnate
Copy link
Copy Markdown
Contributor

@heythisisnate heythisisnate commented Apr 4, 2018

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:

konnected:
  access_token: REPLACE_ME_WITH_A_RANDOM_STRING
  devices:
    - id: 8bcd53
      binary_sensors:
        - zone: 1
          type: door
          name: 'Front Door'
        - zone: 3
          type: motion
          name: 'Test Motion'
      switches:
        - zone: out
          name: siren
    - id: 438a38
      binary_sensors:
        - pin: 1
          type: motion
          name: 'Office Motion'
        - pin: 2
          type: door
          name: 'Office Door'
      switches:
        - pin: 5
          name: 'Garage Door'
          activation: low

Checklist:

  • The code change is tested and works locally.
  • Local tests pass with tox. Your PR cannot be merged unless tests pass

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

If the code communicates with devices, web services, or third-party tools:

  • New dependencies have been added to the REQUIREMENTS variable (example).
  • New dependencies are only imported inside functions that use them (example).
  • New dependencies have been added to requirements_all.txt by running script/gen_requirements_all.py.
  • New files were added to .coveragerc.

@homeassistant
Copy link
Copy Markdown
Contributor

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!

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

line too long (101 > 79 characters)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

line too long (101 > 79 characters)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

line too long (111 > 79 characters)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

line too long (82 > 79 characters)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

line too long (90 > 79 characters)

Comment thread homeassistant/components/konnected.py Outdated
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

line too long (94 > 79 characters)

Comment thread homeassistant/components/konnected.py Outdated
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

line too long (102 > 79 characters)

Comment thread homeassistant/components/konnected.py Outdated
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

line too long (89 > 79 characters)

Comment thread homeassistant/components/konnected.py Outdated
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

line too long (93 > 79 characters)

Comment thread homeassistant/components/konnected.py Outdated
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

line too long (123 > 79 characters)

Comment thread homeassistant/components/konnected.py Outdated
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

continuation line over-indented for visual indent

Comment thread homeassistant/components/konnected.py Outdated
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

missing whitespace after ','

Comment thread homeassistant/components/konnected.py Outdated
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

expected 2 blank lines, found 1

Comment thread homeassistant/components/konnected.py Outdated
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

expected 2 blank lines, found 1

Comment thread homeassistant/components/konnected.py Outdated
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

'aiohttp' imported but unused

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

blank line contains whitespace

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

expected 2 blank lines, found 1

@heythisisnate
Copy link
Copy Markdown
Contributor Author

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?

Comment thread homeassistant/components/konnected.py Outdated
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.

Looks like that this is required. We have a validation helper for strings in homeassistant.helpers.config_validation.

Comment thread homeassistant/components/konnected.py Outdated
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.

The DOMAIN contains a schema as well.

Comment thread homeassistant/components/konnected.py Outdated
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 check const.py if there is already a defined constant.

Comment thread homeassistant/components/konnected.py Outdated
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.

Is 'supersecret' the default? If yes, then pass it in the validation.

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.

I opted to remove the default and make it required for the user to set.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

continuation line over-indented for visual indent

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

continuation line over-indented for visual indent

Comment thread homeassistant/components/konnected.py Outdated
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

line too long (83 > 79 characters)

Comment thread homeassistant/components/konnected.py Outdated
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

line too long (80 > 79 characters)

Comment thread homeassistant/components/konnected.py Outdated
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

line too long (80 > 79 characters)

@heythisisnate
Copy link
Copy Markdown
Contributor Author

Thank you for the feedback @fabaff. I think I've addressed all of the concerns mentioned.

@heythisisnate
Copy link
Copy Markdown
Contributor Author

I just rebased on the latest dev branch and fixed a merge conflict. Is there anything else that I need to here @fabaff?

@pfiggins
Copy link
Copy Markdown

It would be great to get this accepted and merged in ASAP. Konnected is the whole reason I'm beginning to migrate from SmartThings!

@dejanzelic
Copy link
Copy Markdown

I'm excited for this module! I tried it out and it works flawlessly!

Copy link
Copy Markdown
Member

@OttoWinter OttoWinter Apr 21, 2018

Choose a reason for hiding this comment

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

The default seems to be set by the configuration schema:

vol.Required(CONF_TYPE, default='motion'): DEVICE_CLASSES_SCHEMA,

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 seems overly verbose for an info log message... I would consider it a debug message if at all.

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.

The binary sensor doesn't seem to have an update method, so passing True here has no effect and should be removed.

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 debug message also seems quite over-verbose. The async_schedule_update_ha_state() will already make a state changed debug message for you.

Comment thread homeassistant/components/konnected.py Outdated
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.

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.

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.

Nice. thanks for that tip.

Comment thread homeassistant/components/konnected.py Outdated
Copy link
Copy Markdown
Member

@OttoWinter OttoWinter Apr 21, 2018

Choose a reason for hiding this comment

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

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.

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 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.

Comment thread homeassistant/components/konnected.py Outdated
Copy link
Copy Markdown
Member

@OttoWinter OttoWinter Apr 21, 2018

Choose a reason for hiding this comment

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

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.

Comment thread homeassistant/components/konnected.py Outdated
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 also seems like a non-info log message. Especially with info messages, I don't expect to see raw python dictionaries in the logs.

Comment thread homeassistant/components/konnected.py Outdated
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.

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.

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.

You're right, these were really meant for debugging discovery. I will change them to debug.

Comment thread homeassistant/components/konnected.py Outdated
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 is not safe to timing attacks. See hmac.compare_digest for safe auth token checks.

Comment thread homeassistant/components/konnected.py Outdated
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.

If this integration requires discovery: to be enabled as the docs state, it might be good to have it in DEPENDENCIES too I think.

@bradleyscott
Copy link
Copy Markdown

Assuming the constants, Would that mean it could be released as part of 0.70 @syssi?

@syssi
Copy link
Copy Markdown
Member

syssi commented May 15, 2018

I hope so. :-P I believe the merge window closes on friday.

@pfiggins
Copy link
Copy Markdown

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!

@heythisisnate heythisisnate requested a review from a team as a code owner May 15, 2018 14:36
@heythisisnate
Copy link
Copy Markdown
Contributor Author

heythisisnate commented May 15, 2018

@syssi Thank you SO much for the review! 🙌
I've added the constants as suggested. Please have a second look.

Konnected users who are beta testing this: I've changed the auth_token configuration variable to access_token to be consistent with other similar HASS configs and to reuse an existing constant string. I'm updating the documentation accordingly.

@syssi
Copy link
Copy Markdown
Member

syssi commented May 15, 2018

@heythisisnate Sorry, but you've to move the const to the top of your component. The const.py is just used for prominent consts. ;-)

@heythisisnate
Copy link
Copy Markdown
Contributor Author

@syssi Oh, gotcha. I misunderstood. Give me 5 minutes.

@heythisisnate
Copy link
Copy Markdown
Contributor Author

@syssi updated

@syssi
Copy link
Copy Markdown
Member

syssi commented May 15, 2018

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.

@heythisisnate
Copy link
Copy Markdown
Contributor Author

@syssi Nice. Added.

Copy link
Copy Markdown
Member

@syssi syssi left a comment

Choose a reason for hiding this comment

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

LGTM. There are some more consts which should be introduced on the long run IMO.

Comment thread homeassistant/components/konnected.py Outdated
def save_data(self):
"""Save the device configuration to `hass.data`.

TODO: This can probably be refactored and tidied up.
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.

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 👍

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.

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.

@heythisisnate
Copy link
Copy Markdown
Contributor Author

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 dev?

@syssi syssi merged commit de50d5d into home-assistant:dev May 15, 2018
@heythisisnate
Copy link
Copy Markdown
Contributor Author

heythisisnate commented May 15, 2018

Woohoo! 🎉 Thanks for the merge.

@syssi is it possible for you to un-block the documentation PR for merge?:
home-assistant/home-assistant.io#5102

EDIT: Thanks!!

Copy link
Copy Markdown
Member

@MartinHjelmare MartinHjelmare left a comment

Choose a reason for hiding this comment

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

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)
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'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
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'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()
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 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.

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.

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?

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.

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(
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 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()]
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.

.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)
Copy link
Copy Markdown
Member

@MartinHjelmare MartinHjelmare May 15, 2018

Choose a reason for hiding this comment

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

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.

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.

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.

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 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.

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.

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()
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.

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.

@balloob balloob mentioned this pull request May 28, 2018
@home-assistant home-assistant locked and limited conversation to collaborators Sep 5, 2018
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.