Z-Wave Device Registry Support (#17291)

* Add device_registry support for sensor and switch domains

* Add device_registry support for light

* Add device registry to binary_sensor, climate, cover

* Add device registry to zwave fan

* Fix test for config entry loading

* lint

* revert erroneous modification

* Revert device_registry.py change
This commit is contained in:
Charles Garwood 2018-10-16 08:58:25 -04:00 committed by Paulus Schoutsen
parent c6d9ceca63
commit 9c52a3ce22
10 changed files with 189 additions and 31 deletions

View file

@ -7,10 +7,11 @@ https://home-assistant.io/components/binary_sensor.zwave/
import logging
import datetime
import homeassistant.util.dt as dt_util
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.event import track_point_in_time
from homeassistant.components import zwave
from homeassistant.components.zwave import ( # noqa pylint: disable=unused-import
async_setup_platform, workaround)
from homeassistant.components.zwave import workaround
from homeassistant.components.binary_sensor import (
DOMAIN,
BinarySensorDevice)
@ -19,6 +20,23 @@ _LOGGER = logging.getLogger(__name__)
DEPENDENCIES = []
async def async_setup_platform(hass, config, async_add_entities,
discovery_info=None):
"""Old method of setting up Z-Wave binary sensors."""
pass
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up Z-Wave binary sensors from Config Entry."""
@callback
def async_add_binary_sensor(binary_sensor):
"""Add Z-Wave binary sensor."""
async_add_entities([binary_sensor])
async_dispatcher_connect(hass, 'zwave_new_binary_sensor',
async_add_binary_sensor)
def get_device(values, **kwargs):
"""Create Z-Wave entity device."""
device_mapping = workaround.get_device_mapping(values.primary)

View file

@ -6,14 +6,15 @@ https://home-assistant.io/components/climate.zwave/
"""
# Because we do not compile openzwave on CI
import logging
from homeassistant.core import callback
from homeassistant.components.climate import (
DOMAIN, ClimateDevice, STATE_AUTO, STATE_COOL, STATE_HEAT,
SUPPORT_TARGET_TEMPERATURE, SUPPORT_FAN_MODE,
SUPPORT_OPERATION_MODE, SUPPORT_SWING_MODE)
from homeassistant.components.zwave import ( # noqa pylint: disable=unused-import
ZWaveDeviceEntity, async_setup_platform)
from homeassistant.components.zwave import ZWaveDeviceEntity
from homeassistant.const import (
STATE_OFF, TEMP_CELSIUS, TEMP_FAHRENHEIT, ATTR_TEMPERATURE)
from homeassistant.helpers.dispatcher import async_dispatcher_connect
_LOGGER = logging.getLogger(__name__)
@ -42,6 +43,22 @@ STATE_MAPPINGS = {
}
async def async_setup_platform(hass, config, async_add_entities,
discovery_info=None):
"""Old method of setting up Z-Wave climate devices."""
pass
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up Z-Wave Climate device from Config Entry."""
@callback
def async_add_climate(climate):
"""Add Z-Wave Climate Device."""
async_add_entities([climate])
async_dispatcher_connect(hass, 'zwave_new_climate', async_add_climate)
def get_device(hass, values, **kwargs):
"""Create Z-Wave entity device."""
temp_unit = hass.config.units.temperature_unit

View file

@ -5,18 +5,36 @@ For more details about this platform, please refer to the documentation
https://home-assistant.io/components/cover.zwave/
"""
import logging
from homeassistant.core import callback
from homeassistant.components.cover import (
DOMAIN, SUPPORT_OPEN, SUPPORT_CLOSE, ATTR_POSITION)
from homeassistant.components import zwave
from homeassistant.components.zwave import ( # noqa pylint: disable=unused-import
ZWaveDeviceEntity, async_setup_platform, workaround)
from homeassistant.components.zwave import (
ZWaveDeviceEntity, workaround)
from homeassistant.components.cover import CoverDevice
from homeassistant.helpers.dispatcher import async_dispatcher_connect
_LOGGER = logging.getLogger(__name__)
SUPPORT_GARAGE = SUPPORT_OPEN | SUPPORT_CLOSE
async def async_setup_platform(hass, config, async_add_entities,
discovery_info=None):
"""Old method of setting up Z-Wave covers."""
pass
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up Z-Wave Cover from Config Entry."""
@callback
def async_add_cover(cover):
"""Add Z-Wave Cover."""
async_add_entities([cover])
async_dispatcher_connect(hass, 'zwave_new_cover', async_add_cover)
def get_device(hass, values, node_config, **kwargs):
"""Create Z-Wave entity device."""
invert_buttons = node_config.get(zwave.CONF_INVERT_OPENCLOSE_BUTTONS)

View file

@ -7,11 +7,12 @@ https://home-assistant.io/components/fan.zwave/
import logging
import math
from homeassistant.core import callback
from homeassistant.components.fan import (
DOMAIN, FanEntity, SPEED_OFF, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH,
SUPPORT_SET_SPEED)
from homeassistant.components import zwave
from homeassistant.components.zwave import async_setup_platform # noqa pylint: disable=unused-import
from homeassistant.helpers.dispatcher import async_dispatcher_connect
_LOGGER = logging.getLogger(__name__)
@ -35,6 +36,22 @@ SPEED_TO_VALUE = {
}
async def async_setup_platform(hass, config, async_add_entities,
discovery_info=None):
"""Old method of setting up Z-Wave fans."""
pass
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up Z-Wave Fan from Config Entry."""
@callback
def async_add_fan(fan):
"""Add Z-Wave Fan."""
async_add_entities([fan])
async_dispatcher_connect(hass, 'zwave_new_fan', async_add_fan)
def get_device(values, **kwargs):
"""Create Z-Wave entity device."""
return ZwaveFan(values)

View file

@ -7,13 +7,14 @@ https://home-assistant.io/components/light.zwave/
import logging
from threading import Timer
from homeassistant.core import callback
from homeassistant.components.light import (
ATTR_WHITE_VALUE, ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_HS_COLOR,
ATTR_TRANSITION, SUPPORT_BRIGHTNESS, SUPPORT_COLOR_TEMP, SUPPORT_COLOR,
SUPPORT_TRANSITION, SUPPORT_WHITE_VALUE, DOMAIN, Light)
from homeassistant.components import zwave
from homeassistant.components.zwave import async_setup_platform # noqa pylint: disable=unused-import
from homeassistant.const import STATE_OFF, STATE_ON
from homeassistant.helpers.dispatcher import async_dispatcher_connect
import homeassistant.util.color as color_util
_LOGGER = logging.getLogger(__name__)
@ -43,6 +44,22 @@ TEMP_WARM_HASS = (TEMP_COLOR_MAX - TEMP_COLOR_MIN) / 3 * 2 + TEMP_COLOR_MIN
TEMP_COLD_HASS = (TEMP_COLOR_MAX - TEMP_COLOR_MIN) / 3 + TEMP_COLOR_MIN
async def async_setup_platform(hass, config, async_add_entities,
discovery_info=None):
"""Old method of setting up Z-Wave lights."""
pass
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up Z-Wave Light from Config Entry."""
@callback
def async_add_light(light):
"""Add Z-Wave Light."""
async_add_entities([light])
async_dispatcher_connect(hass, 'zwave_new_light', async_add_light)
def get_device(node, values, node_config, **kwargs):
"""Create Z-Wave entity device."""
refresh = node_config.get(zwave.CONF_REFRESH_VALUE)

View file

@ -5,14 +5,31 @@ For more details about this platform, please refer to the documentation
at https://home-assistant.io/components/sensor.zwave/
"""
import logging
from homeassistant.core import callback
from homeassistant.components.sensor import DOMAIN
from homeassistant.components import zwave
from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT
from homeassistant.components.zwave import async_setup_platform # noqa pylint: disable=unused-import
from homeassistant.helpers.dispatcher import async_dispatcher_connect
_LOGGER = logging.getLogger(__name__)
async def async_setup_platform(hass, config, async_add_entities,
discovery_info=None):
"""Old method of setting up Z-Wave sensors."""
pass
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up Z-Wave Sensor from Config Entry."""
@callback
def async_add_sensor(sensor):
"""Add Z-Wave Sensor."""
async_add_entities([sensor])
async_dispatcher_connect(hass, 'zwave_new_sensor', async_add_sensor)
def get_device(node, values, **kwargs):
"""Create Z-Wave entity device."""
# Generic Device mappings

View file

@ -6,13 +6,30 @@ https://home-assistant.io/components/switch.zwave/
"""
import logging
import time
from homeassistant.core import callback
from homeassistant.components.switch import DOMAIN, SwitchDevice
from homeassistant.components import zwave
from homeassistant.components.zwave import workaround, async_setup_platform # noqa pylint: disable=unused-import
from homeassistant.helpers.dispatcher import async_dispatcher_connect
_LOGGER = logging.getLogger(__name__)
async def async_setup_platform(hass, config, async_add_entities,
discovery_info=None):
"""Old method of setting up Z-Wave switches."""
pass
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up Z-Wave Switch from Config Entry."""
@callback
def async_add_switch(switch):
"""Add Z-Wave Switch."""
async_add_entities([switch])
async_dispatcher_connect(hass, 'zwave_new_switch', async_add_switch)
def get_device(values, **kwargs):
"""Create zwave entity device."""
return ZwaveSwitch(values)
@ -25,8 +42,8 @@ class ZwaveSwitch(zwave.ZWaveDeviceEntity, SwitchDevice):
"""Initialize the Z-Wave switch device."""
zwave.ZWaveDeviceEntity.__init__(self, values, DOMAIN)
self.refresh_on_update = (
workaround.get_device_mapping(values.primary) ==
workaround.WORKAROUND_REFRESH_NODE_ON_UPDATE)
zwave.workaround.get_device_mapping(values.primary) ==
zwave.workaround.WORKAROUND_REFRESH_NODE_ON_UPDATE)
self.last_update = time.perf_counter()
self._state = self.values.primary.data

View file

@ -66,6 +66,9 @@ DEFAULT_CONF_INVERT_OPENCLOSE_BUTTONS = False
DEFAULT_CONF_REFRESH_VALUE = False
DEFAULT_CONF_REFRESH_DELAY = 5
SUPPORTED_PLATFORMS = ['binary_sensor', 'climate', 'fan',
'light', 'sensor', 'switch']
RENAME_NODE_SCHEMA = vol.Schema({
vol.Required(const.ATTR_NODE_ID): vol.Coerce(int),
vol.Required(const.ATTR_NAME): cv.string,
@ -224,7 +227,6 @@ async def async_setup_platform(hass, config, async_add_entities,
discovery_info[const.DISCOVERY_DEVICE], None)
if device is None:
return False
async_add_entities([device])
return True
@ -777,6 +779,10 @@ async def async_setup_entry(hass, config_entry):
hass.services.async_register(DOMAIN, const.SERVICE_START_NETWORK,
start_zwave)
for entry_component in SUPPORTED_PLATFORMS:
hass.async_create_task(hass.config_entries.async_forward_entry_setup(
config_entry, entry_component))
return True
@ -928,9 +934,13 @@ class ZWaveDeviceEntityValues():
async def discover_device(component, device, dict_id):
"""Put device in a dictionary and call discovery on it."""
self._hass.data[DATA_DEVICES][dict_id] = device
await discovery.async_load_platform(
self._hass, component, DOMAIN,
{const.DISCOVERY_DEVICE: dict_id}, self._zwave_config)
if component in SUPPORTED_PLATFORMS:
async_dispatcher_send(
self._hass, 'zwave_new_{}'.format(component), device)
else:
await discovery.async_load_platform(
self._hass, component, DOMAIN,
{const.DISCOVERY_DEVICE: dict_id}, self._zwave_config)
if device.unique_id:
self._hass.add_job(discover_device, component, device, dict_id)
@ -1010,6 +1020,18 @@ class ZWaveDeviceEntity(ZWaveBaseEntity):
"""Return a unique ID."""
return self._unique_id
@property
def device_info(self):
"""Return device information."""
return {
'identifiers': {
(DOMAIN, self.node_id)
},
'manufacturer': self.node.manufacturer_name,
'model': self.node.product_name,
'name': node_name(self.node),
}
@property
def name(self):
"""Return the name of the device."""

View file

@ -8,7 +8,7 @@ from homeassistant.helpers.entity import Entity
from .const import (
ATTR_NODE_ID, COMMAND_CLASS_WAKE_UP, ATTR_SCENE_ID, ATTR_SCENE_DATA,
ATTR_BASIC_LEVEL, EVENT_NODE_EVENT, EVENT_SCENE_ACTIVATED,
COMMAND_CLASS_CENTRAL_SCENE)
COMMAND_CLASS_CENTRAL_SCENE, DOMAIN)
from .util import node_name, is_node_parsed
_LOGGER = logging.getLogger(__name__)
@ -110,6 +110,18 @@ class ZWaveNodeEntity(ZWaveBaseEntity):
"""Return unique ID of Z-wave node."""
return self._unique_id
@property
def device_info(self):
"""Return device information."""
return {
'identifiers': {
(DOMAIN, self.node_id)
},
'manufacturer': self.node.manufacturer_name,
'model': self.node.product_name,
'name': node_name(self.node)
}
def network_node_changed(self, node=None, value=None, args=None):
"""Handle a changed node on the network."""
if node and node.node_id != self.node_id:

View file

@ -703,21 +703,24 @@ class TestZWaveDeviceEntityValues(unittest.TestCase):
const.COMMAND_CLASS_SWITCH_BINARY],
}}}
values = zwave.ZWaveDeviceEntityValues(
hass=self.hass,
schema=self.mock_schema,
primary_value=self.primary,
zwave_config=self.zwave_config,
device_config=self.device_config,
registry=self.registry
)
values._check_entity_ready()
self.hass.block_till_done()
with patch.object(zwave, 'async_dispatcher_send') as \
mock_dispatch_send:
assert discovery.async_load_platform.called
assert len(discovery.async_load_platform.mock_calls) == 1
args = discovery.async_load_platform.mock_calls[0][1]
assert args[1] == 'binary_sensor'
values = zwave.ZWaveDeviceEntityValues(
hass=self.hass,
schema=self.mock_schema,
primary_value=self.primary,
zwave_config=self.zwave_config,
device_config=self.device_config,
registry=self.registry
)
values._check_entity_ready()
self.hass.block_till_done()
assert mock_dispatch_send.called
assert len(mock_dispatch_send.mock_calls) == 1
args = mock_dispatch_send.mock_calls[0][1]
assert args[1] == 'zwave_new_binary_sensor'
@patch.object(zwave, 'get_platform')
@patch.object(zwave, 'discovery')