-
-
Notifications
You must be signed in to change notification settings - Fork 37.4k
Add deCONZ component #10321
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
Changes from all commits
4517876
6845f5a
89927fe
d96f520
eff5bf9
f673578
85a27f8
cd19c4e
1fb7050
07c4636
f9e72a4
d4ab28f
c7ec2ef
88e396d
6192018
a0ee176
e46c63f
c78d931
1866cf6
09ff292
de0920a
347cecf
e00375c
2c65b95
f00cd0b
6b2ba8b
e9d1ff1
992241d
92b7c53
c24381e
e9870ed
924a7ef
9230d28
1cdced2
8a08cf7
b217cdf
ec02ca1
44d2337
d216677
e402b87
66aea41
0bfe78d
08d4e67
87972e3
bc8d2b1
aaf0a55
a756a3f
de7bcc9
51fddf8
f10ecb4
d674367
f3d4041
4afa90c
b64216c
8f81605
cf38661
ac0cd59
743ed28
c4e4d4a
d6a6ec6
c4f93c9
1dd7940
85ed7d6
b00cbb2
8da145d
c3c920f
7a34a66
7d091f2
a147f51
bed7978
c06b38d
ce075f7
3474381
f5de075
51c07b6
09d789d
6f57b23
70c5cd9
eb2953b
87af434
2940fa8
7f3ff1f
5ba8593
c833426
098cd01
0f00960
2be977e
acd19e5
2b62ab8
24a367a
7762687
d335d62
f9a280a
e0c5ce1
46de840
a02ce0f
3f82d98
b0582b1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| """ | ||
| Support for deCONZ binary sensor. | ||
|
|
||
| For more details about this component, please refer to the documentation at | ||
| https://home-assistant.io/components/binary_sensor.deconz/ | ||
| """ | ||
|
|
||
| import asyncio | ||
|
|
||
| from homeassistant.components.binary_sensor import BinarySensorDevice | ||
| from homeassistant.components.deconz import DOMAIN as DECONZ_DATA | ||
| from homeassistant.const import ATTR_BATTERY_LEVEL | ||
| from homeassistant.core import callback | ||
|
|
||
| DEPENDENCIES = ['deconz'] | ||
|
|
||
|
|
||
| @asyncio.coroutine | ||
| def async_setup_platform(hass, config, async_add_devices, discovery_info=None): | ||
| """Setup binary sensor for deCONZ component.""" | ||
| if discovery_info is None: | ||
| return | ||
|
|
||
| from pydeconz.sensor import DECONZ_BINARY_SENSOR | ||
| sensors = hass.data[DECONZ_DATA].sensors | ||
| entities = [] | ||
|
|
||
| for sensor in sensors.values(): | ||
| if sensor.type in DECONZ_BINARY_SENSOR: | ||
| entities.append(DeconzBinarySensor(sensor)) | ||
| async_add_devices(entities, True) | ||
|
|
||
|
|
||
| class DeconzBinarySensor(BinarySensorDevice): | ||
| """Representation of a binary sensor.""" | ||
|
|
||
| def __init__(self, sensor): | ||
| """Setup sensor and add update callback to get data from websocket.""" | ||
| self._sensor = sensor | ||
|
|
||
| @asyncio.coroutine | ||
| def async_added_to_hass(self): | ||
| """Subscribe sensors events.""" | ||
| self._sensor.register_async_callback(self.async_update_callback) | ||
|
|
||
| @callback | ||
| def async_update_callback(self, reason): | ||
| """Update the sensor's state. | ||
|
|
||
| If reason is that state is updated, | ||
| or reachable has changed or battery has changed. | ||
| """ | ||
| if reason['state'] or \ | ||
| 'reachable' in reason['attr'] or \ | ||
| 'battery' in reason['attr']: | ||
| self.async_schedule_update_ha_state() | ||
|
|
||
| @property | ||
| def is_on(self): | ||
| """Return true if sensor is on.""" | ||
| return self._sensor.is_tripped | ||
|
|
||
| @property | ||
| def name(self): | ||
| """Return the name of the sensor.""" | ||
| return self._sensor.name | ||
|
|
||
| @property | ||
| def device_class(self): | ||
| """Class of the sensor.""" | ||
| return self._sensor.sensor_class | ||
|
|
||
| @property | ||
| def icon(self): | ||
| """Return the icon to use in the frontend.""" | ||
| return self._sensor.sensor_icon | ||
|
|
||
| @property | ||
| def available(self): | ||
| """Return True if sensor is available.""" | ||
| return self._sensor.reachable | ||
|
|
||
| @property | ||
| def should_poll(self): | ||
| """No polling needed.""" | ||
| return False | ||
|
|
||
| @property | ||
| def device_state_attributes(self): | ||
| """Return the state attributes of the sensor.""" | ||
| from pydeconz.sensor import PRESENCE | ||
| attr = { | ||
| ATTR_BATTERY_LEVEL: self._sensor.battery, | ||
| } | ||
| if self._sensor.type == PRESENCE: | ||
| attr['dark'] = self._sensor.dark | ||
| return attr |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,176 @@ | ||
| """ | ||
| Support for deCONZ devices. | ||
|
|
||
| For more details about this component, please refer to the documentation at | ||
| https://home-assistant.io/components/deconz/ | ||
| """ | ||
|
|
||
| import asyncio | ||
| import logging | ||
| import os | ||
| import voluptuous as vol | ||
|
|
||
| from homeassistant.config import load_yaml_config_file | ||
| from homeassistant.const import ( | ||
| CONF_API_KEY, CONF_HOST, CONF_PORT, EVENT_HOMEASSISTANT_STOP) | ||
| from homeassistant.components.discovery import SERVICE_DECONZ | ||
| from homeassistant.helpers import config_validation as cv | ||
| from homeassistant.helpers import discovery | ||
| from homeassistant.helpers.aiohttp_client import async_get_clientsession | ||
| from homeassistant.util.json import load_json, save_json | ||
|
|
||
| REQUIREMENTS = ['pydeconz==23'] | ||
|
|
||
| _LOGGER = logging.getLogger(__name__) | ||
|
|
||
| DOMAIN = 'deconz' | ||
|
|
||
| CONFIG_FILE = 'deconz.conf' | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please prefix the name with a dot, to make the file hidden, if users shouldn't interact with it, ie
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is no easy way for users to get a hold of the API key if needed, so if possible I'd like to leave this as is.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok. |
||
|
|
||
| CONFIG_SCHEMA = vol.Schema({ | ||
| DOMAIN: vol.Schema({ | ||
| vol.Optional(CONF_HOST): cv.string, | ||
| vol.Optional(CONF_API_KEY): cv.string, | ||
| vol.Optional(CONF_PORT, default=80): cv.port, | ||
| }) | ||
| }, extra=vol.ALLOW_EXTRA) | ||
|
|
||
| SERVICE_FIELD = 'field' | ||
| SERVICE_DATA = 'data' | ||
|
|
||
| SERVICE_SCHEMA = vol.Schema({ | ||
| vol.Required(SERVICE_FIELD): cv.string, | ||
| vol.Required(SERVICE_DATA): cv.string, | ||
| }) | ||
|
|
||
| CONFIG_INSTRUCTIONS = """ | ||
| Unlock your deCONZ gateway to register with Home Assistant. | ||
|
|
||
| 1. [Go to deCONZ system settings](http://{}:{}/edit_system.html) | ||
| 2. Press "Unlock Gateway" button | ||
|
|
||
| [deCONZ platform documentation](https://home-assistant.io/components/deconz/) | ||
| """ | ||
|
|
||
|
|
||
| @asyncio.coroutine | ||
| def async_setup(hass, config): | ||
| """Setup services and configuration for deCONZ component.""" | ||
| result = False | ||
| config_file = yield from hass.async_add_job( | ||
| load_json, hass.config.path(CONFIG_FILE)) | ||
|
|
||
| @asyncio.coroutine | ||
| def async_deconz_discovered(service, discovery_info): | ||
| """Called when deCONZ gateway has been found.""" | ||
| deconz_config = {} | ||
| deconz_config[CONF_HOST] = discovery_info.get(CONF_HOST) | ||
| deconz_config[CONF_PORT] = discovery_info.get(CONF_PORT) | ||
| yield from async_request_configuration(hass, config, deconz_config) | ||
|
|
||
| if config_file: | ||
| result = yield from async_setup_deconz(hass, config, config_file) | ||
|
|
||
| if not result and DOMAIN in config and CONF_HOST in config[DOMAIN]: | ||
| deconz_config = config[DOMAIN] | ||
| if CONF_API_KEY in deconz_config: | ||
| result = yield from async_setup_deconz(hass, config, deconz_config) | ||
| else: | ||
| yield from async_request_configuration(hass, config, deconz_config) | ||
| return True | ||
|
|
||
| if not result: | ||
| discovery.async_listen(hass, SERVICE_DECONZ, async_deconz_discovered) | ||
|
|
||
| return True | ||
|
|
||
|
|
||
| @asyncio.coroutine | ||
| def async_setup_deconz(hass, config, deconz_config): | ||
| """Setup deCONZ session. | ||
|
|
||
| Load config, group, light and sensor data for server information. | ||
| Start websocket for push notification of state changes from deCONZ. | ||
| """ | ||
| from pydeconz import DeconzSession | ||
| websession = async_get_clientsession(hass) | ||
| deconz = DeconzSession(hass.loop, websession, **deconz_config) | ||
| result = yield from deconz.async_load_parameters() | ||
| if result is False: | ||
| _LOGGER.error("Failed to communicate with deCONZ.") | ||
| return False | ||
|
|
||
| hass.data[DOMAIN] = deconz | ||
|
|
||
| for component in ['binary_sensor', 'light', 'scene', 'sensor']: | ||
| hass.async_add_job(discovery.async_load_platform( | ||
| hass, component, DOMAIN, {}, config)) | ||
| deconz.start() | ||
|
|
||
| descriptions = yield from hass.async_add_job( | ||
| load_yaml_config_file, | ||
| os.path.join(os.path.dirname(__file__), 'services.yaml')) | ||
|
|
||
| @asyncio.coroutine | ||
| def async_configure(call): | ||
| """Set attribute of device in deCONZ. | ||
|
|
||
| Field is a string representing a specific device in deCONZ | ||
| e.g. field='/lights/1/state'. | ||
| Data is a json object with what data you want to alter | ||
| e.g. data={'on': true}. | ||
| { | ||
| "field": "/lights/1/state", | ||
| "data": {"on": true} | ||
| } | ||
| See Dresden Elektroniks REST API documentation for details: | ||
| http://dresden-elektronik.github.io/deconz-rest-doc/rest/ | ||
| """ | ||
| deconz = hass.data[DOMAIN] | ||
| field = call.data.get(SERVICE_FIELD) | ||
| data = call.data.get(SERVICE_DATA) | ||
| yield from deconz.async_put_state(field, data) | ||
| hass.services.async_register( | ||
| DOMAIN, 'configure', async_configure, | ||
| descriptions['configure'], schema=SERVICE_SCHEMA) | ||
|
|
||
| hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, deconz.close) | ||
| return True | ||
|
|
||
|
|
||
| @asyncio.coroutine | ||
| def async_request_configuration(hass, config, deconz_config): | ||
| """Request configuration steps from the user.""" | ||
| configurator = hass.components.configurator | ||
|
|
||
| @asyncio.coroutine | ||
| def async_configuration_callback(data): | ||
| """Set up actions to do when our configuration callback is called.""" | ||
| from pydeconz.utils import async_get_api_key | ||
| api_key = yield from async_get_api_key(hass.loop, **deconz_config) | ||
| if api_key: | ||
| deconz_config[CONF_API_KEY] = api_key | ||
| result = yield from async_setup_deconz(hass, config, deconz_config) | ||
| if result: | ||
| yield from hass.async_add_job(save_json, | ||
| hass.config.path(CONFIG_FILE), | ||
| deconz_config) | ||
| configurator.async_request_done(request_id) | ||
| return | ||
| else: | ||
| configurator.async_notify_errors( | ||
| request_id, "Couldn't load configuration.") | ||
| else: | ||
| configurator.async_notify_errors( | ||
| request_id, "Couldn't get an API key.") | ||
| return | ||
|
|
||
| instructions = CONFIG_INSTRUCTIONS.format( | ||
| deconz_config[CONF_HOST], deconz_config[CONF_PORT]) | ||
|
|
||
| request_id = configurator.async_request_config( | ||
| "deCONZ", async_configuration_callback, | ||
| description=instructions, | ||
| entity_picture="/static/images/logo_deconz.jpeg", | ||
| submit_caption="I have unlocked the gateway", | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
|
|
||
| configure: | ||
| description: Set attribute of device in Deconz. See Dresden Elektroniks REST API documentation for details http://dresden-elektronik.github.io/deconz-rest-doc/rest/ | ||
| fields: | ||
| field: | ||
| description: Field is a string representing a specific device in Deconz. | ||
| example: '/lights/1/state' | ||
| data: | ||
| description: Data is a json object with what data you want to alter. | ||
| example: '{"on": true}' |
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.
Is this part of the deconz 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.
It got overridden in a separate PR so I just add it again here out of pure laziness