-
-
Notifications
You must be signed in to change notification settings - Fork 31.1k
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
deCONZ - Support device registry #16115
Changes from 7 commits
8dbda5a
82c0745
38b51a1
bb8eb38
454c2da
7069980
15ba6ab
16a7191
7fcfc74
7b48826
4d3c28c
df79e77
33405d8
067c0f8
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 |
---|---|---|
|
@@ -10,6 +10,8 @@ | |
DATA_DECONZ_ID, DATA_DECONZ_UNSUB) | ||
from homeassistant.const import ATTR_BATTERY_LEVEL | ||
from homeassistant.core import callback | ||
from homeassistant.helpers.device_registry import ( | ||
CONNECTION_ZIGBEE, IDENTIFIER_MAC) | ||
from homeassistant.helpers.dispatcher import async_dispatcher_connect | ||
|
||
DEPENDENCIES = ['deconz'] | ||
|
@@ -113,3 +115,21 @@ def device_state_attributes(self): | |
if self._sensor.type in PRESENCE and self._sensor.dark is not None: | ||
attr[ATTR_DARK] = self._sensor.dark | ||
return attr | ||
|
||
@property | ||
def device(self): | ||
"""Description for device registry.""" | ||
if (self._sensor.uniqueid is not None and | ||
self._sensor.uniqueid.count(':') == 7): | ||
mac = self._sensor.uniqueid.split('-', 1)[0] | ||
else: | ||
return None | ||
dev = { | ||
'connection': [[CONNECTION_ZIGBEE, mac]], | ||
'identifiers': [[IDENTIFIER_MAC, mac]], | ||
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 should not be an identifier MAC. There is already a connection one. Is there a serial number for this sensor? In that case you could add Also MAC should never be used as just a standalone type, as there is Network MAC, Zigbee MAC etc. 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. Good point with DECONZ_DOMAIN since it should only map inside of deconz context 👍 Previously you asked if the sensor had a mac, I will go back to using serial :) Should I just call them NETWORK_MAC and ZIGBEE_MAC? 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. yes |
||
'manufacturer': self._sensor.manufacturer, | ||
'model': self._sensor.modelid, | ||
'name': self._sensor.name, | ||
'sw_version': self._sensor.swversion, | ||
} | ||
return dev |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,7 @@ | |
CONF_ID, CONF_PORT, EVENT_HOMEASSISTANT_STOP) | ||
from homeassistant.core import EventOrigin, callback | ||
from homeassistant.helpers import aiohttp_client, config_validation as cv | ||
from homeassistant.helpers.device_registry import CONNECTION_MAC | ||
from homeassistant.helpers.dispatcher import ( | ||
async_dispatcher_connect, async_dispatcher_send) | ||
from homeassistant.util import slugify | ||
|
@@ -23,7 +24,7 @@ | |
CONF_ALLOW_CLIP_SENSOR, CONFIG_FILE, DATA_DECONZ_EVENT, | ||
DATA_DECONZ_ID, DATA_DECONZ_UNSUB, DOMAIN, _LOGGER) | ||
|
||
REQUIREMENTS = ['pydeconz==43'] | ||
REQUIREMENTS = ['pydeconz==44'] | ||
|
||
CONFIG_SCHEMA = vol.Schema({ | ||
DOMAIN: vol.Schema({ | ||
|
@@ -119,6 +120,14 @@ def async_add_remote(sensors): | |
|
||
deconz.start() | ||
|
||
device_registry = await \ | ||
hass.helpers.device_registry.async_get_registry() | ||
device_registry.async_get_or_create( | ||
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. indentation contains mixed spaces and tabs 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. As this example might be copied a lot, I would prefer that we use named arguments to clarify what is what: device_registry.async_get_or_create(
identifiers=[[DOMAIN, deconz.config.bridgeid]],
manufacturer='Dresden Elektronik',
# etc
) |
||
connection=[[CONNECTION_MAC, deconz.config.mac]], | ||
identifiers=[[DOMAIN, deconz.config.bridgeid]], | ||
manufacturer='Dresden Elektronik', model=deconz.config.modelid, | ||
name=deconz.config.name, sw_version=deconz.config.swversion) | ||
|
||
async def async_configure(call): | ||
"""Set attribute of device in deCONZ. | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,8 @@ | |
SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, SUPPORT_EFFECT, | ||
SUPPORT_FLASH, SUPPORT_TRANSITION, Light) | ||
from homeassistant.core import callback | ||
from homeassistant.helpers.device_registry import ( | ||
CONNECTION_ZIGBEE, IDENTIFIER_MAC) | ||
from homeassistant.helpers.dispatcher import async_dispatcher_connect | ||
import homeassistant.util.color as color_util | ||
|
||
|
@@ -199,3 +201,21 @@ def device_state_attributes(self): | |
if self._light.type == 'LightGroup': | ||
attributes['all_on'] = self._light.all_on | ||
return attributes | ||
|
||
@property | ||
def device(self): | ||
"""Description for device registry.""" | ||
if (self._light.uniqueid is not None and | ||
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. Same |
||
self._light.uniqueid.count(':') == 7): | ||
mac = self._light.uniqueid.split('-', 1)[0] | ||
else: | ||
return None | ||
dev = { | ||
'connection': [[CONNECTION_ZIGBEE, mac]], | ||
'identifiers': [[IDENTIFIER_MAC, mac]], | ||
'manufacturer': self._light.manufacturer, | ||
'model': self._light.modelid, | ||
'name': self._light.name, | ||
'sw_version': self._light.swversion, | ||
} | ||
return dev |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,8 @@ | |
from homeassistant.const import ( | ||
ATTR_BATTERY_LEVEL, ATTR_VOLTAGE, DEVICE_CLASS_BATTERY) | ||
from homeassistant.core import callback | ||
from homeassistant.helpers.device_registry import ( | ||
CONNECTION_ZIGBEE, IDENTIFIER_MAC) | ||
from homeassistant.helpers.dispatcher import async_dispatcher_connect | ||
from homeassistant.helpers.entity import Entity | ||
from homeassistant.util import slugify | ||
|
@@ -134,6 +136,24 @@ def device_state_attributes(self): | |
attr[ATTR_DAYLIGHT] = self._sensor.daylight | ||
return attr | ||
|
||
@property | ||
def device(self): | ||
"""Description for device registry.""" | ||
if (self._sensor.uniqueid is not None and | ||
self._sensor.uniqueid.count(':') == 7): | ||
mac = self._sensor.uniqueid.split('-', 1)[0] | ||
else: | ||
return None | ||
dev = { | ||
'connection': [[CONNECTION_ZIGBEE, mac]], | ||
'identifiers': [[IDENTIFIER_MAC, mac]], | ||
'manufacturer': self._sensor.manufacturer, | ||
'model': self._sensor.modelid, | ||
'name': self._sensor.name, | ||
'sw_version': self._sensor.swversion, | ||
} | ||
return dev | ||
|
||
|
||
class DeconzBattery(Entity): | ||
"""Battery class for when a device is only represented as an event.""" | ||
|
@@ -192,3 +212,21 @@ def device_state_attributes(self): | |
ATTR_EVENT_ID: slugify(self._device.name), | ||
} | ||
return attr | ||
|
||
@property | ||
def device(self): | ||
"""Description for device registry.""" | ||
if (self._device.uniqueid is not None and | ||
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. Same |
||
self._device.uniqueid.count(':') == 7): | ||
mac = self._device.uniqueid.split('-', 1)[0] | ||
else: | ||
return None | ||
dev = { | ||
'connection': [[CONNECTION_ZIGBEE, mac]], | ||
'identifiers': [[IDENTIFIER_MAC, mac]], | ||
'manufacturer': self._device.manufacturer, | ||
'model': self._device.modelid, | ||
'name': self._device.name, | ||
'sw_version': self._device.swversion, | ||
} | ||
return dev |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,8 @@ | |
POWER_PLUGS, SIRENS) | ||
from homeassistant.components.switch import SwitchDevice | ||
from homeassistant.core import callback | ||
from homeassistant.helpers.device_registry import ( | ||
CONNECTION_ZIGBEE, IDENTIFIER_MAC) | ||
from homeassistant.helpers.dispatcher import async_dispatcher_connect | ||
|
||
DEPENDENCIES = ['deconz'] | ||
|
@@ -79,6 +81,24 @@ def should_poll(self): | |
"""No polling needed.""" | ||
return False | ||
|
||
@property | ||
def device(self): | ||
"""Description for device registry.""" | ||
if (self._switch.uniqueid is not None and | ||
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. Same |
||
self._switch.uniqueid.count(':') == 7): | ||
mac = self._switch.uniqueid.split('-', 1)[0] | ||
else: | ||
return None | ||
dev = { | ||
'connection': [[CONNECTION_ZIGBEE, mac]], | ||
'identifiers': [[IDENTIFIER_MAC, mac]], | ||
'manufacturer': self._switch.manufacturer, | ||
'model': self._switch.modelid, | ||
'name': self._switch.name, | ||
'sw_version': self._switch.swversion, | ||
} | ||
return dev | ||
|
||
|
||
class DeconzPowerPlug(DeconzSwitch): | ||
"""Representation of power plugs from deCONZ.""" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,8 +5,17 @@ | |
from homeassistant.setup import async_setup_component | ||
from homeassistant.components import deconz | ||
|
||
from tests.common import mock_coro | ||
|
||
from tests.common import mock_coro, MockConfigEntry | ||
|
||
CONFIG = { | ||
"config": { | ||
"bridgeid": "0123456789ABCDEF", | ||
"mac": "12:34:56:78:90:ab", | ||
"modelid": "deCONZ", | ||
"name": "Phoscon", | ||
"swversion": "2.05.35" | ||
} | ||
} | ||
|
||
async def test_config_with_host_passed_to_config_entry(hass): | ||
"""Test that configured options for a host are loaded via config entry.""" | ||
|
@@ -93,8 +102,11 @@ async def test_setup_entry_successful(hass): | |
entry.data = {'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF'} | ||
with patch.object(hass, 'async_create_task') as mock_add_job, \ | ||
patch.object(hass, 'config_entries') as mock_config_entries, \ | ||
patch('pydeconz.DeconzSession.async_load_parameters', | ||
return_value=mock_coro(True)): | ||
patch('pydeconz.DeconzSession.async_get_state', | ||
return_value=mock_coro(CONFIG)), \ | ||
patch('pydeconz.DeconzSession.start', return_value=True), \ | ||
patch('homeassistant.helpers.device_registry.async_get_registry', | ||
return_value=mock_coro(Mock())): | ||
assert await deconz.async_setup_entry(hass, entry) is True | ||
assert hass.data[deconz.DOMAIN] | ||
assert hass.data[deconz.DATA_DECONZ_ID] == {} | ||
|
@@ -115,10 +127,13 @@ async def test_setup_entry_successful(hass): | |
|
||
async def test_unload_entry(hass): | ||
"""Test being able to unload an entry.""" | ||
entry = Mock() | ||
entry.data = {'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF'} | ||
with patch('pydeconz.DeconzSession.async_load_parameters', | ||
return_value=mock_coro(True)): | ||
entry = MockConfigEntry(domain=deconz.DOMAIN, data={ | ||
'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF' | ||
}) | ||
entry.add_to_hass(hass) | ||
with patch('pydeconz.DeconzSession.async_get_state', | ||
return_value=mock_coro(CONFIG)), \ | ||
patch('pydeconz.DeconzSession.start', return_value=True): | ||
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. continuation line over-indented for visual indent |
||
assert await deconz.async_setup_entry(hass, entry) is True | ||
assert deconz.DATA_DECONZ_EVENT in hass.data | ||
hass.data[deconz.DATA_DECONZ_EVENT].append(Mock()) | ||
|
@@ -132,6 +147,9 @@ async def test_unload_entry(hass): | |
|
||
async def test_add_new_device(hass): | ||
"""Test adding a new device generates a signal for platforms.""" | ||
entry = Mock() | ||
entry.data = {'host': '1.2.3.4', 'port': 80, | ||
'api_key': '1234567890ABCDEF', 'allow_clip_sensor': False} | ||
new_event = { | ||
"t": "event", | ||
"e": "added", | ||
|
@@ -147,11 +165,10 @@ async def test_add_new_device(hass): | |
"type": "ZHASwitch" | ||
} | ||
} | ||
entry = Mock() | ||
entry.data = {'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF'} | ||
with patch.object(deconz, 'async_dispatcher_send') as mock_dispatch_send, \ | ||
patch('pydeconz.DeconzSession.async_load_parameters', | ||
return_value=mock_coro(True)): | ||
patch('pydeconz.DeconzSession.async_get_state', | ||
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. continuation line over-indented for visual indent |
||
return_value=mock_coro(CONFIG)), \ | ||
patch('pydeconz.DeconzSession.start', return_value=True): | ||
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. continuation line over-indented for visual indent |
||
assert await deconz.async_setup_entry(hass, entry) is True | ||
hass.data[deconz.DOMAIN].async_event_handler(new_event) | ||
await hass.async_block_till_done() | ||
|
@@ -162,15 +179,16 @@ async def test_add_new_device(hass): | |
async def test_add_new_remote(hass): | ||
"""Test new added device creates a new remote.""" | ||
entry = Mock() | ||
entry.data = {'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF'} | ||
entry.data = {'host': '1.2.3.4', 'port': 80, | ||
'api_key': '1234567890ABCDEF', 'allow_clip_sensor': False} | ||
remote = Mock() | ||
remote.name = 'name' | ||
remote.type = 'ZHASwitch' | ||
remote.register_async_callback = Mock() | ||
with patch('pydeconz.DeconzSession.async_load_parameters', | ||
return_value=mock_coro(True)): | ||
with patch('pydeconz.DeconzSession.async_get_state', | ||
return_value=mock_coro(CONFIG)), \ | ||
patch('pydeconz.DeconzSession.start', return_value=True): | ||
assert await deconz.async_setup_entry(hass, entry) is True | ||
|
||
async_dispatcher_send(hass, 'deconz_new_sensor', [remote]) | ||
await hass.async_block_till_done() | ||
assert len(hass.data[deconz.DATA_DECONZ_EVENT]) == 1 | ||
|
@@ -185,8 +203,9 @@ async def test_do_not_allow_clip_sensor(hass): | |
remote.name = 'name' | ||
remote.type = 'CLIPSwitch' | ||
remote.register_async_callback = Mock() | ||
with patch('pydeconz.DeconzSession.async_load_parameters', | ||
return_value=mock_coro(True)): | ||
with patch('pydeconz.DeconzSession.async_get_state', | ||
return_value=mock_coro(CONFIG)), \ | ||
patch('pydeconz.DeconzSession.start', return_value=True): | ||
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. continuation line over-indented for visual indent |
||
assert await deconz.async_setup_entry(hass, entry) is True | ||
|
||
async_dispatcher_send(hass, 'deconz_new_sensor', [remote]) | ||
|
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.
Can you inverse this logic to make the method a bit easier to read
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 can evaluate positive on the counter instead. Then leave return None to be at the end of the method instead.