-
-
Notifications
You must be signed in to change notification settings - Fork 32.6k
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
Add deCONZ component #10321
Add deCONZ component #10321
Conversation
|
||
from homeassistant.components.light import (Light, | ||
ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_RGB_COLOR, ATTR_XY_COLOR, | ||
SUPPORT_BRIGHTNESS, SUPPORT_COLOR_TEMP, SUPPORT_RGB_COLOR, SUPPORT_XY_COLOR) |
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.
continuation line under-indented for visual indent
line too long (80 > 79 characters)
import logging | ||
|
||
from homeassistant.components.light import (Light, | ||
ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_RGB_COLOR, ATTR_XY_COLOR, |
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.
continuation line under-indented for visual indent
import asyncio | ||
import logging | ||
|
||
from homeassistant.components.light import (Light, |
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.
'homeassistant.components.light.ATTR_XY_COLOR' imported but unused
eecc13b
to
4d4d2e4
Compare
from homeassistant.helpers.dispatcher import ( | ||
async_dispatcher_send, async_dispatcher_connect) | ||
from homeassistant.helpers.entity import Entity | ||
from homeassistant.util import slugify |
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.
'homeassistant.util.slugify' imported but unused
homeassistant/components/deconz.py
Outdated
DOMAIN = 'deconz' | ||
|
||
DECONZ_DATA = 'deconz_data' | ||
CONFIG_FILE = 'deconz.conf' |
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 think the path to the config file should be configurable.
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 just the name, the read_json, load_json gets the full path from HASS. From my understanding this is standard behaviour.
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.
As, I see what that file is for.
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.
If it's not meant for users to manually read/write it, prefix the name with a .
homeassistant/components/deconz.py
Outdated
vol.Required(CONF_HOST): cv.string, | ||
vol.Optional(CONF_API_KEY): cv.string, | ||
vol.Optional(CONF_PORT, default=80): cv.port, | ||
vol.Optional(TYPE_AS_EVENT, default=['ZHASwitch']): |
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.
What is this supposed to do? It looks strange, and it's not documented in the accompanying documentation PR.
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.
Normal behaviour for momentary sensors are to be events, I'd like to have an opening in getting it as a sensor entity instead by disabling it. I'm just keeping it configurable.
homeassistant/components/deconz.py
Outdated
DECONZ_DATA = 'deconz_data' | ||
CONFIG_FILE = 'deconz.conf' | ||
|
||
TYPE_AS_EVENT = 'type_as_event' |
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.
For naming reasons, this should probably also start with CONF_.
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.
Sure, good catch!
homeassistant/components/deconz.py
Outdated
def async_setup(hass, config): | ||
"""Setup services for Deconz.""" | ||
deconz_config = config[DOMAIN] | ||
config_file = load_json(hass.config.path(CONFIG_FILE)) |
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.
Not sure, but: Since this is a coroutine, it runs in the event loop, and load_json
probably blocks. Is there an asynchroneous way of reading the JSON?
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.
There is no similar async version but from what I could see in Discord, Balloob didn't think this was an issue.
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.
Okay, great! 👍
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.
uuuh what. This is a really big issue. NEVER do I/O inside the event loop.
It is not a big issue that no async version exists, because one does never do I/O inside the event loop. Instead, do yield from hass.async_add_job(load_json, hass.config.path(CONFIG_FILE))
. Although, if you are not familiar with how asyncio works, it is preferred that you don't write async components. Write sync ones instead.
homeassistant/components/deconz.py
Outdated
Store API key in deconz.conf. | ||
""" | ||
from pydeconz.utils import get_api_key | ||
deconz_config[CONF_USERNAME] = call.data.get(CONF_USERNAME, 'delight') |
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 that the default values for username / password should be hard coded here, but rather added as default value to the config schema. Nobody's going to look for the default value 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.
I guess I could create a second config schema for the service? Since the username and password is only relevant for the service to generate the API key. Not sure what is best :)
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.
Oh, I just now realize that this is a service! Why do you make this a service? The component cannot work before that service was called / the API key was generated, can it? So how about this workflow:
- On startup, check if an API key was generated
- If so, initialize the component
- If not, retrieve username / password from the config, generate a new API key
- If that worked, initialize the component
- If it didn't, fail the initialization
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 had it like that initially, but then I thought that I don't want the user to need to remove the username and password afterwords. Originally I created a notification in the GUI for the user to write down and add to the configuration.yaml. This is the opposite in that after initially specifying the component you are not required to add any additional configuration for the component.
But in the end I guess it is a matter of taste.
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'm gonna think a bit more about this, you have some valid points!
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 changed it to more as you suggested
homeassistant/components/deconz.py
Outdated
deconz_config[CONF_USERNAME] = call.data.get(CONF_USERNAME, 'delight') | ||
deconz_config[CONF_PASSWORD] = call.data.get(CONF_PASSWORD, 'delight') | ||
api_key = yield from get_api_key(hass.loop, **deconz_config) | ||
if not save_json( |
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.
Same as above - is there an async way of doing 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.
See previous comment.
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.
👍
homeassistant/components/deconz.py
Outdated
else: | ||
hass.services.async_register( | ||
DOMAIN, 'generate_api_key', generate_api_key) | ||
_LOGGER.warning("Deconz needs API key to set up session. See 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.
If the component can't do anything without an API key, this should probably be an error, not just a warning (i.e., return False
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.
An error is a bit extreme since you still intentionally have to enable the component. Which means that if you don't specify the API key you have the intent of generating one using the service which will only load when the API key is missing.
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.
Yes, I agree that a failure would be too hard if you have to call the generate_api_key
service manually. But with the workflow from above, that should not be necessary, should it?
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.
Absolutely. See the other post for my reasoning.
homeassistant/components/deconz.py
Outdated
DOMAIN, | ||
deconz_config, | ||
config)) | ||
deconz.start() |
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.
Does this block?
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.
No, it is a asyncio transport class.
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.
👍
homeassistant/components/deconz.py
Outdated
|
||
|
||
class DeconzEvent(object): | ||
"""When you want signals instead of 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.
I'm afraid I don't understand what that means. :(
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 understand this since you only had time to spend on this file ;).
It is a event class that will take a device sensor and generate events in hass instead of creating a sensor entity.
I will improve the pydocstring.
data = {'on': True} | ||
|
||
if ATTR_BRIGHTNESS in kwargs: | ||
data['bri'] = kwargs[ATTR_BRIGHTNESS] |
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 might want to move this down below RGB so one can specify both and have the RGB brightness overridden.
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 hear you.
Regarding the event / entity thing: I think I got it now. I wonder though what the advantage of the "event mode" is over having an entity. If you have an entity for every sensor, you still get an event every time the sensor's state changes (a Also, from what I know it's pretty uncommon to not represent physical devices by entities, but by a stream of events instead. I think if my assumption above is correct (that the event mode doesn't give you any "extra features") I would be in favor of just using the "entity mode". Reduces code complexity and makes this component behave more like all other components. |
This specific thing I've had a long talk with another author of a Deconz component as well as Balloob to get a proper decision. I can agree that some people would prefer entities for all devices, that's why I left the 'undocumented' configuration possibility to alter this from the default behaviour. |
@property | ||
def unit_of_measurement(self): | ||
"""Unit of measurement of this entity.""" | ||
return self._sensor.sensor_unit |
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 think you should translate here into home assistant - standardized units such as TEMP_CELSIUS
(which is defined as 'C'
, while pydeconz would use 'Celsius'
). The same probably holds for other units?
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.
Good catch! Too quick on that one.
return self._sensor.sensor_icon | ||
|
||
@property | ||
def unit_of_measurement(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.
See the other sensor file - maybe translation would be benefical here, although I am not sure what unit a binary sensor should have.
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.
No that is true, copy paste issue...
def device_state_attributes(self): | ||
"""Return the state attributes of the sensor.""" | ||
attr = { | ||
'battery_level': self._sensor.battery, |
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.
ATTR_BATTERY_LEVEL
from const.py
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.
Good point!
'manufacturer': self._sensor.manufacturer, | ||
'model_number': self._sensor.modelid, | ||
'reachable': self._sensor.reachable, | ||
'uniqueid': self._sensor.uniqueid, |
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.
ATTR_ID
from const.py
?
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.
Good point!
@property | ||
def device_state_attributes(self): | ||
"""Return the state attributes of the sensor.""" | ||
attr = { |
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.
For the attribute names that cannot be imported from const.py
, I think it would be cleaner to define your own ATTR_*
constants in the component, so that you have one central place where they are defined and could be changed.
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.
Sure
@property | ||
def device_state_attributes(self): | ||
"""Return the state attributes of the sensor.""" | ||
if self._light.type != 'LightGroup': |
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.
See binary sensor.
Edit: If you're in a hurryWhat follows is a long thread of me not understanding that the "sensors" sometimes represent clickable buttons instead of real sensors, and having the (on the first glance somewhat weird-looking) "event instead of entity" approach is probably a good idea. If you're in a hurry you can probably fast-forward to #10321 (comment) Original commentMy preference for "entity only" is less because of what the user might want, but because of (in my eyes unnecessarily) added code complexity. I mean, you could remove two out of five classes (the event class and the battery class), you would not have the (somewhat strange, I think) "battery" entities, … Also, breaking the "an phyiscal device is an entity" rule makes your component unusable with some other components, for example the recorder, mqtt statestream, anything that wants to do templating, anything that records historical data, … But if @balloob is on okay with this, it's probably okay. 😉 |
Valid points |
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 just had another quick discussion with @balloob , and we think that physical sensors should always be represented by an entity, instead of "hiding" them, generating custom events plus a battery sensor.
We have to try to keep a certain consistency throughout the home assistant code base to ensure maintainability. Since we're not able to see the advantages, we think the added code complexity and different approach from all other sensor platforms is not worth it.
Sure, as long as there isn't a third change back to events later on ;) |
Just to make sure: We're really talking about sensors here, not some buttons that are internally called "sensors", are we? |
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.
Nice!
@MartinHjelmare what's left for this PR to be merged? |
I'm happy but our CLA bot seems to have troubles processing this PR. My guess is the PR simply has too many commits. Try squashing the older commits into one commit if you are comfortable with git. Otherwise you can make a new branch and copy your changes manually and open a new PR and link to this PR. |
I will look into how to squash commits tomorrow |
@balloob do you have any knowledge about the issue mentioned by MartinHjelmare? |
Bump dependency to v23 Use only HASS aiohttp session Change when to use 'deconz' or domain or deconz data Clean up unused logger defines Remove unnecessary return values Fix faulty references to component documentation Move callback registration to after entity has been initialized by HASS Less inception style on pydocs ;) Simplify loading platforms by using a for loop Added voluptous schema for service Yaml file is for deconz only, no need to have the domain present Remove domain constraint when creating event title
b83d816
to
b0582b1
Compare
@MartinHjelmare I squashed a few commits, less than a 100 commits seems to be the sweet spot. But there seems to be a bigger issue with travis atm |
@Kane610 I've restarted the build. Let's see what happens. |
Great @MartinHjelmare ! It is ready for merging 🍾 |
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.
Great!
So...I'm not sure I'm doing this correctly, but I have a bug report for this component :) I have merged this pull request with my current installation of 0.60 and have gotten the deCONZ integration running just fine. Except for one type of sensor I have; the Aqara Temperature/Humidity sensor. When this is added to HA from deCONZ it is added as three sensors. One for temperature, one for humidity and one for airpressure. They are added as sensor.[name in deconz], sensor.[name in deconz]_2 and sensor.[name in deconz]_3 The problem is that this order is seeminly random. After restarting HA, the sensor that previously was temperature might be the humidity or airpressure sensor. Attaching two screenshots to illustrate. The first image is before restarting HA, the second one is after restarting. |
@ScuttleSE this is known. It is expected that the user name their devices uniquely. |
How though? This is one device in deCONZ named temp_1_balcony which in HA gets named sensor.temp_1_balcony, sensor.temp_1_balcony_2 and sensor.temp_1_balcony_3 |
@ScuttleSE either through the API, which you can use the deconz service in hass for, I think you can also use the new web frontend phoscon to do it. I'm also planning on looking in to doing frontend support later on for deconz. Lets take further discussions on the forums, this PR is closed. |
@Kane610 But...maybe I am misunderstanding here... :) I have named the device temp_1_balcony using the phoscon webthing, that's where I added the sensor too, but there is only an option to name the whole device there, not the three "sub-sensors". When the device gets sucked into HA through the API, it gets added as three separate devices, and HA seems to add the extensions _2 and _3 to two of the sensors at random to be able to differentiate between them |
As I said, lets take further discussions in the forum https://community.home-assistant.io/t/conbee-zigbee-stick-and-ha-will-this-work-together/ |
Never mind, it just disappeared |
I don't understand how that could be, have you posted earlier in the forums? |
Please open an issue if you suspect a bug. If you need help please use our help channels: Merged PRs should not be used for support, bug reports or to discuss the implementation. Thanks! |
Description:
Component supporting Deconz (lights, sensors) rest API and web sockets for push notification.
The Implementation is more or less code complete.
The Component supports all devices (on a theoretical level) supported by deCONZ. There are lists of verified devices in the documentation.
Pull request in home-assistant.github.io with documentation: home-assistant/home-assistant.io#3967
Pull request in home-assistant-polymer with new logo for configurator: home-assistant/frontend#689
Example entry for
configuration.yaml
(if applicable):If you don't have a API key and want to generate one use this config and then follow the steps in the GUI:
The component will automatically create an API key and put it into deconz.conf.
If you prefer you can use auto discovery component instead and follow instructions in GUI.
Checklist:
If user exposed functionality or configuration variables are added/changed:
If the code communicates with devices, web services, or third-party tools:
tox
run successfully. Your PR cannot be merged unless tests passREQUIREMENTS
variable (example).requirements_all.txt
by runningscript/gen_requirements_all.py
..coveragerc
.