Library refactorization of deCONZ (#23725)
* Improved sensors * Lib update signalling * Replace reason with changed * Move imports to top of file * Add support for secondary temperature reported by some Xiaomi devices * Bump dependency to v59
This commit is contained in:
parent
0ba54ee9b7
commit
31b2f331db
19 changed files with 104 additions and 83 deletions
|
@ -164,6 +164,7 @@ async def async_unload_entry(hass, config_entry):
|
||||||
if not hass.data[DOMAIN]:
|
if not hass.data[DOMAIN]:
|
||||||
hass.services.async_remove(DOMAIN, SERVICE_DECONZ)
|
hass.services.async_remove(DOMAIN, SERVICE_DECONZ)
|
||||||
hass.services.async_remove(DOMAIN, SERVICE_DEVICE_REFRESH)
|
hass.services.async_remove(DOMAIN, SERVICE_DEVICE_REFRESH)
|
||||||
|
|
||||||
elif gateway.master:
|
elif gateway.master:
|
||||||
await async_populate_options(hass, config_entry)
|
await async_populate_options(hass, config_entry)
|
||||||
new_master_gateway = next(iter(hass.data[DOMAIN].values()))
|
new_master_gateway = next(iter(hass.data[DOMAIN].values()))
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
"""Support for deCONZ binary sensors."""
|
"""Support for deCONZ binary sensors."""
|
||||||
|
from pydeconz.sensor import Presence, Vibration
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import BinarySensorDevice
|
from homeassistant.components.binary_sensor import BinarySensorDevice
|
||||||
from homeassistant.const import ATTR_BATTERY_LEVEL
|
from homeassistant.const import ATTR_BATTERY_LEVEL, ATTR_TEMPERATURE
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
|
|
||||||
|
@ -15,7 +17,7 @@ ATTR_VIBRATIONSTRENGTH = 'vibrationstrength'
|
||||||
|
|
||||||
async def async_setup_platform(
|
async def async_setup_platform(
|
||||||
hass, config, async_add_entities, discovery_info=None):
|
hass, config, async_add_entities, discovery_info=None):
|
||||||
"""Old way of setting up deCONZ binary sensors."""
|
"""Old way of setting up deCONZ platforms."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,12 +28,11 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
@callback
|
@callback
|
||||||
def async_add_sensor(sensors):
|
def async_add_sensor(sensors):
|
||||||
"""Add binary sensor from deCONZ."""
|
"""Add binary sensor from deCONZ."""
|
||||||
from pydeconz.sensor import DECONZ_BINARY_SENSOR
|
|
||||||
entities = []
|
entities = []
|
||||||
|
|
||||||
for sensor in sensors:
|
for sensor in sensors:
|
||||||
|
|
||||||
if sensor.type in DECONZ_BINARY_SENSOR and \
|
if sensor.BINARY and \
|
||||||
not (not gateway.allow_clip_sensor and
|
not (not gateway.allow_clip_sensor and
|
||||||
sensor.type.startswith('CLIP')):
|
sensor.type.startswith('CLIP')):
|
||||||
|
|
||||||
|
@ -49,16 +50,11 @@ class DeconzBinarySensor(DeconzDevice, BinarySensorDevice):
|
||||||
"""Representation of a deCONZ binary sensor."""
|
"""Representation of a deCONZ binary sensor."""
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_update_callback(self, reason):
|
def async_update_callback(self, force_update=False):
|
||||||
"""Update the sensor's state.
|
"""Update the sensor's state."""
|
||||||
|
changed = set(self._device.changed_keys)
|
||||||
If reason is that state is updated,
|
keys = {'battery', 'on', 'reachable', 'state'}
|
||||||
or reachable has changed or battery has changed.
|
if force_update or any(key in changed for key in keys):
|
||||||
"""
|
|
||||||
if reason['state'] or \
|
|
||||||
'reachable' in reason['attr'] or \
|
|
||||||
'battery' in reason['attr'] or \
|
|
||||||
'on' in reason['attr']:
|
|
||||||
self.async_schedule_update_ha_state()
|
self.async_schedule_update_ha_state()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -69,26 +65,33 @@ class DeconzBinarySensor(DeconzDevice, BinarySensorDevice):
|
||||||
@property
|
@property
|
||||||
def device_class(self):
|
def device_class(self):
|
||||||
"""Return the class of the sensor."""
|
"""Return the class of the sensor."""
|
||||||
return self._device.sensor_class
|
return self._device.SENSOR_CLASS
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def icon(self):
|
def icon(self):
|
||||||
"""Return the icon to use in the frontend."""
|
"""Return the icon to use in the frontend."""
|
||||||
return self._device.sensor_icon
|
return self._device.SENSOR_ICON
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_state_attributes(self):
|
def device_state_attributes(self):
|
||||||
"""Return the state attributes of the sensor."""
|
"""Return the state attributes of the sensor."""
|
||||||
from pydeconz.sensor import PRESENCE, VIBRATION
|
|
||||||
attr = {}
|
attr = {}
|
||||||
if self._device.battery:
|
if self._device.battery:
|
||||||
attr[ATTR_BATTERY_LEVEL] = self._device.battery
|
attr[ATTR_BATTERY_LEVEL] = self._device.battery
|
||||||
|
|
||||||
if self._device.on is not None:
|
if self._device.on is not None:
|
||||||
attr[ATTR_ON] = self._device.on
|
attr[ATTR_ON] = self._device.on
|
||||||
if self._device.type in PRESENCE and self._device.dark is not None:
|
|
||||||
|
if self._device.secondary_temperature is not None:
|
||||||
|
attr[ATTR_TEMPERATURE] = self._device.secondary_temperature
|
||||||
|
|
||||||
|
if self._device.type in Presence.ZHATYPE and \
|
||||||
|
self._device.dark is not None:
|
||||||
attr[ATTR_DARK] = self._device.dark
|
attr[ATTR_DARK] = self._device.dark
|
||||||
elif self._device.type in VIBRATION:
|
|
||||||
|
elif self._device.type in Vibration.ZHATYPE:
|
||||||
attr[ATTR_ORIENTATION] = self._device.orientation
|
attr[ATTR_ORIENTATION] = self._device.orientation
|
||||||
attr[ATTR_TILTANGLE] = self._device.tiltangle
|
attr[ATTR_TILTANGLE] = self._device.tiltangle
|
||||||
attr[ATTR_VIBRATIONSTRENGTH] = self._device.vibrationstrength
|
attr[ATTR_VIBRATIONSTRENGTH] = self._device.vibrationstrength
|
||||||
|
|
||||||
return attr
|
return attr
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
"""Support for deCONZ climate devices."""
|
"""Support for deCONZ climate devices."""
|
||||||
|
from pydeconz.sensor import Thermostat
|
||||||
|
|
||||||
from homeassistant.components.climate import ClimateDevice
|
from homeassistant.components.climate import ClimateDevice
|
||||||
from homeassistant.components.climate.const import (
|
from homeassistant.components.climate.const import (
|
||||||
SUPPORT_ON_OFF, SUPPORT_TARGET_TEMPERATURE)
|
SUPPORT_ON_OFF, SUPPORT_TARGET_TEMPERATURE)
|
||||||
|
@ -12,6 +14,12 @@ from .deconz_device import DeconzDevice
|
||||||
from .gateway import get_gateway_from_config_entry
|
from .gateway import get_gateway_from_config_entry
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_platform(
|
||||||
|
hass, config, async_add_entities, discovery_info=None):
|
||||||
|
"""Old way of setting up deCONZ platforms."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
"""Set up the deCONZ climate devices.
|
"""Set up the deCONZ climate devices.
|
||||||
|
|
||||||
|
@ -22,12 +30,11 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
@callback
|
@callback
|
||||||
def async_add_climate(sensors):
|
def async_add_climate(sensors):
|
||||||
"""Add climate devices from deCONZ."""
|
"""Add climate devices from deCONZ."""
|
||||||
from pydeconz.sensor import THERMOSTAT
|
|
||||||
entities = []
|
entities = []
|
||||||
|
|
||||||
for sensor in sensors:
|
for sensor in sensors:
|
||||||
|
|
||||||
if sensor.type in THERMOSTAT and \
|
if sensor.type in Thermostat.ZHATYPE and \
|
||||||
not (not gateway.allow_clip_sensor and
|
not (not gateway.allow_clip_sensor and
|
||||||
sensor.type.startswith('CLIP')):
|
sensor.type.startswith('CLIP')):
|
||||||
|
|
||||||
|
@ -59,7 +66,7 @@ class DeconzThermostat(DeconzDevice, ClimateDevice):
|
||||||
@property
|
@property
|
||||||
def is_on(self):
|
def is_on(self):
|
||||||
"""Return true if on."""
|
"""Return true if on."""
|
||||||
return self._device.on
|
return self._device.state_on
|
||||||
|
|
||||||
async def async_turn_on(self):
|
async def async_turn_on(self):
|
||||||
"""Turn on switch."""
|
"""Turn on switch."""
|
||||||
|
|
|
@ -4,6 +4,10 @@ import asyncio
|
||||||
import async_timeout
|
import async_timeout
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from pydeconz.errors import ResponseError, RequestError
|
||||||
|
from pydeconz.utils import (
|
||||||
|
async_discovery, async_get_api_key, async_get_bridgeid)
|
||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PORT
|
from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PORT
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
|
@ -54,8 +58,6 @@ class DeconzFlowHandler(config_entries.ConfigFlow):
|
||||||
If more than one bridge is found let user choose bridge to link.
|
If more than one bridge is found let user choose bridge to link.
|
||||||
If no bridge is found allow user to manually input configuration.
|
If no bridge is found allow user to manually input configuration.
|
||||||
"""
|
"""
|
||||||
from pydeconz.utils import async_discovery
|
|
||||||
|
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
for bridge in self.bridges:
|
for bridge in self.bridges:
|
||||||
if bridge[CONF_HOST] == user_input[CONF_HOST]:
|
if bridge[CONF_HOST] == user_input[CONF_HOST]:
|
||||||
|
@ -101,8 +103,6 @@ class DeconzFlowHandler(config_entries.ConfigFlow):
|
||||||
|
|
||||||
async def async_step_link(self, user_input=None):
|
async def async_step_link(self, user_input=None):
|
||||||
"""Attempt to link with the deCONZ bridge."""
|
"""Attempt to link with the deCONZ bridge."""
|
||||||
from pydeconz.errors import ResponseError, RequestError
|
|
||||||
from pydeconz.utils import async_get_api_key
|
|
||||||
errors = {}
|
errors = {}
|
||||||
|
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
|
@ -127,8 +127,6 @@ class DeconzFlowHandler(config_entries.ConfigFlow):
|
||||||
|
|
||||||
async def _create_entry(self):
|
async def _create_entry(self):
|
||||||
"""Create entry for gateway."""
|
"""Create entry for gateway."""
|
||||||
from pydeconz.utils import async_get_bridgeid
|
|
||||||
|
|
||||||
if CONF_BRIDGEID not in self.deconz_config:
|
if CONF_BRIDGEID not in self.deconz_config:
|
||||||
session = aiohttp_client.async_get_clientsession(self.hass)
|
session = aiohttp_client.async_get_clientsession(self.hass)
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ ZIGBEE_SPEC = ['lumi.curtain']
|
||||||
|
|
||||||
async def async_setup_platform(
|
async def async_setup_platform(
|
||||||
hass, config, async_add_entities, discovery_info=None):
|
hass, config, async_add_entities, discovery_info=None):
|
||||||
"""Unsupported way of setting up deCONZ covers."""
|
"""Old way of setting up deCONZ platforms."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ class DeconzDevice(Entity):
|
||||||
self.unsub_dispatcher()
|
self.unsub_dispatcher()
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_update_callback(self, reason):
|
def async_update_callback(self, force_update=False):
|
||||||
"""Update the device's state."""
|
"""Update the device's state."""
|
||||||
self.async_schedule_update_ha_state()
|
self.async_schedule_update_ha_state()
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import async_timeout
|
import async_timeout
|
||||||
|
|
||||||
|
from pydeconz import DeconzSession, errors
|
||||||
|
from pydeconz.sensor import Switch
|
||||||
|
|
||||||
from homeassistant.exceptions import ConfigEntryNotReady
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
from homeassistant.const import CONF_EVENT, CONF_HOST, CONF_ID
|
from homeassistant.const import CONF_EVENT, CONF_HOST, CONF_ID
|
||||||
from homeassistant.core import EventOrigin, callback
|
from homeassistant.core import EventOrigin, callback
|
||||||
|
@ -126,8 +129,7 @@ class DeconzGateway:
|
||||||
def async_connection_status_callback(self, available):
|
def async_connection_status_callback(self, available):
|
||||||
"""Handle signals of gateway connection status."""
|
"""Handle signals of gateway connection status."""
|
||||||
self.available = available
|
self.available = available
|
||||||
async_dispatcher_send(self.hass, self.event_reachable,
|
async_dispatcher_send(self.hass, self.event_reachable, True)
|
||||||
{'state': True, 'attr': 'reachable'})
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_event_new_device(self, device_type):
|
def async_event_new_device(self, device_type):
|
||||||
|
@ -145,9 +147,8 @@ class DeconzGateway:
|
||||||
@callback
|
@callback
|
||||||
def async_add_remote(self, sensors):
|
def async_add_remote(self, sensors):
|
||||||
"""Set up remote from deCONZ."""
|
"""Set up remote from deCONZ."""
|
||||||
from pydeconz.sensor import SWITCH as DECONZ_REMOTE
|
|
||||||
for sensor in sensors:
|
for sensor in sensors:
|
||||||
if sensor.type in DECONZ_REMOTE and \
|
if sensor.type in Switch.ZHATYPE and \
|
||||||
not (not self.allow_clip_sensor and
|
not (not self.allow_clip_sensor and
|
||||||
sensor.type.startswith('CLIP')):
|
sensor.type.startswith('CLIP')):
|
||||||
self.events.append(DeconzEvent(self.hass, sensor))
|
self.events.append(DeconzEvent(self.hass, sensor))
|
||||||
|
@ -187,8 +188,6 @@ class DeconzGateway:
|
||||||
async def get_gateway(hass, config, async_add_device_callback,
|
async def get_gateway(hass, config, async_add_device_callback,
|
||||||
async_connection_status_callback):
|
async_connection_status_callback):
|
||||||
"""Create a gateway object and verify configuration."""
|
"""Create a gateway object and verify configuration."""
|
||||||
from pydeconz import DeconzSession, errors
|
|
||||||
|
|
||||||
session = aiohttp_client.async_get_clientsession(hass)
|
session = aiohttp_client.async_get_clientsession(hass)
|
||||||
|
|
||||||
deconz = DeconzSession(hass.loop, session, **config,
|
deconz = DeconzSession(hass.loop, session, **config,
|
||||||
|
@ -232,8 +231,8 @@ class DeconzEvent:
|
||||||
self._device = None
|
self._device = None
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_update_callback(self, reason):
|
def async_update_callback(self, force_update=False):
|
||||||
"""Fire the event if reason is that state is updated."""
|
"""Fire the event if reason is that state is updated."""
|
||||||
if reason['state']:
|
if 'state' in self._device.changed_keys:
|
||||||
data = {CONF_ID: self._id, CONF_EVENT: self._device.state}
|
data = {CONF_ID: self._id, CONF_EVENT: self._device.state}
|
||||||
self._hass.bus.async_fire(self._event, data, EventOrigin.remote)
|
self._hass.bus.async_fire(self._event, data, EventOrigin.remote)
|
||||||
|
|
|
@ -15,7 +15,7 @@ from .gateway import get_gateway_from_config_entry
|
||||||
|
|
||||||
async def async_setup_platform(
|
async def async_setup_platform(
|
||||||
hass, config, async_add_entities, discovery_info=None):
|
hass, config, async_add_entities, discovery_info=None):
|
||||||
"""Old way of setting up deCONZ lights and group."""
|
"""Old way of setting up deCONZ platforms."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/components/deconz",
|
"documentation": "https://www.home-assistant.io/components/deconz",
|
||||||
"requirements": [
|
"requirements": [
|
||||||
"pydeconz==58"
|
"pydeconz==59"
|
||||||
],
|
],
|
||||||
"dependencies": [],
|
"dependencies": [],
|
||||||
"codeowners": [
|
"codeowners": [
|
||||||
|
|
|
@ -9,7 +9,7 @@ from .gateway import get_gateway_from_config_entry
|
||||||
|
|
||||||
async def async_setup_platform(
|
async def async_setup_platform(
|
||||||
hass, config, async_add_entities, discovery_info=None):
|
hass, config, async_add_entities, discovery_info=None):
|
||||||
"""Old way of setting up deCONZ scenes."""
|
"""Old way of setting up deCONZ platforms."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
"""Support for deCONZ sensors."""
|
"""Support for deCONZ sensors."""
|
||||||
|
from pydeconz.sensor import LightLevel, Switch
|
||||||
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_BATTERY_LEVEL, ATTR_VOLTAGE, DEVICE_CLASS_BATTERY)
|
ATTR_BATTERY_LEVEL, ATTR_TEMPERATURE, ATTR_VOLTAGE, DEVICE_CLASS_BATTERY)
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
from homeassistant.util import slugify
|
from homeassistant.util import slugify
|
||||||
|
@ -16,7 +18,7 @@ ATTR_EVENT_ID = 'event_id'
|
||||||
|
|
||||||
async def async_setup_platform(
|
async def async_setup_platform(
|
||||||
hass, config, async_add_entities, discovery_info=None):
|
hass, config, async_add_entities, discovery_info=None):
|
||||||
"""Old way of setting up deCONZ sensors."""
|
"""Old way of setting up deCONZ platforms."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,17 +29,15 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
@callback
|
@callback
|
||||||
def async_add_sensor(sensors):
|
def async_add_sensor(sensors):
|
||||||
"""Add sensors from deCONZ."""
|
"""Add sensors from deCONZ."""
|
||||||
from pydeconz.sensor import (
|
|
||||||
DECONZ_SENSOR, SWITCH as DECONZ_REMOTE)
|
|
||||||
entities = []
|
entities = []
|
||||||
|
|
||||||
for sensor in sensors:
|
for sensor in sensors:
|
||||||
|
|
||||||
if sensor.type in DECONZ_SENSOR and \
|
if not sensor.BINARY and \
|
||||||
not (not gateway.allow_clip_sensor and
|
not (not gateway.allow_clip_sensor and
|
||||||
sensor.type.startswith('CLIP')):
|
sensor.type.startswith('CLIP')):
|
||||||
|
|
||||||
if sensor.type in DECONZ_REMOTE:
|
if sensor.type in Switch.ZHATYPE:
|
||||||
if sensor.battery:
|
if sensor.battery:
|
||||||
entities.append(DeconzBattery(sensor, gateway))
|
entities.append(DeconzBattery(sensor, gateway))
|
||||||
|
|
||||||
|
@ -56,16 +56,11 @@ class DeconzSensor(DeconzDevice):
|
||||||
"""Representation of a deCONZ sensor."""
|
"""Representation of a deCONZ sensor."""
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_update_callback(self, reason):
|
def async_update_callback(self, force_update=False):
|
||||||
"""Update the sensor's state.
|
"""Update the sensor's state."""
|
||||||
|
changed = set(self._device.changed_keys)
|
||||||
If reason is that state is updated,
|
keys = {'battery', 'on', 'reachable', 'state'}
|
||||||
or reachable has changed or battery has changed.
|
if force_update or any(key in changed for key in keys):
|
||||||
"""
|
|
||||||
if reason['state'] or \
|
|
||||||
'reachable' in reason['attr'] or \
|
|
||||||
'battery' in reason['attr'] or \
|
|
||||||
'on' in reason['attr']:
|
|
||||||
self.async_schedule_update_ha_state()
|
self.async_schedule_update_ha_state()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -76,34 +71,42 @@ class DeconzSensor(DeconzDevice):
|
||||||
@property
|
@property
|
||||||
def device_class(self):
|
def device_class(self):
|
||||||
"""Return the class of the sensor."""
|
"""Return the class of the sensor."""
|
||||||
return self._device.sensor_class
|
return self._device.SENSOR_CLASS
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def icon(self):
|
def icon(self):
|
||||||
"""Return the icon to use in the frontend."""
|
"""Return the icon to use in the frontend."""
|
||||||
return self._device.sensor_icon
|
return self._device.SENSOR_ICON
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unit_of_measurement(self):
|
def unit_of_measurement(self):
|
||||||
"""Return the unit of measurement of this sensor."""
|
"""Return the unit of measurement of this sensor."""
|
||||||
return self._device.sensor_unit
|
return self._device.SENSOR_UNIT
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_state_attributes(self):
|
def device_state_attributes(self):
|
||||||
"""Return the state attributes of the sensor."""
|
"""Return the state attributes of the sensor."""
|
||||||
from pydeconz.sensor import LIGHTLEVEL
|
|
||||||
attr = {}
|
attr = {}
|
||||||
if self._device.battery:
|
if self._device.battery:
|
||||||
attr[ATTR_BATTERY_LEVEL] = self._device.battery
|
attr[ATTR_BATTERY_LEVEL] = self._device.battery
|
||||||
|
|
||||||
if self._device.on is not None:
|
if self._device.on is not None:
|
||||||
attr[ATTR_ON] = self._device.on
|
attr[ATTR_ON] = self._device.on
|
||||||
if self._device.type in LIGHTLEVEL and self._device.dark is not None:
|
|
||||||
|
if self._device.secondary_temperature is not None:
|
||||||
|
attr[ATTR_TEMPERATURE] = self._device.secondary_temperature
|
||||||
|
|
||||||
|
if self._device.type in LightLevel.ZHATYPE and \
|
||||||
|
self._device.dark is not None:
|
||||||
attr[ATTR_DARK] = self._device.dark
|
attr[ATTR_DARK] = self._device.dark
|
||||||
|
|
||||||
if self.unit_of_measurement == 'Watts':
|
if self.unit_of_measurement == 'Watts':
|
||||||
attr[ATTR_CURRENT] = self._device.current
|
attr[ATTR_CURRENT] = self._device.current
|
||||||
attr[ATTR_VOLTAGE] = self._device.voltage
|
attr[ATTR_VOLTAGE] = self._device.voltage
|
||||||
if self._device.sensor_class == 'daylight':
|
|
||||||
|
if self._device.SENSOR_CLASS == 'daylight':
|
||||||
attr[ATTR_DAYLIGHT] = self._device.daylight
|
attr[ATTR_DAYLIGHT] = self._device.daylight
|
||||||
|
|
||||||
return attr
|
return attr
|
||||||
|
|
||||||
|
|
||||||
|
@ -118,9 +121,11 @@ class DeconzBattery(DeconzDevice):
|
||||||
self._unit_of_measurement = "%"
|
self._unit_of_measurement = "%"
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_update_callback(self, reason):
|
def async_update_callback(self, force_update=False):
|
||||||
"""Update the battery's state, if needed."""
|
"""Update the battery's state, if needed."""
|
||||||
if 'reachable' in reason['attr'] or 'battery' in reason['attr']:
|
changed = set(self._device.changed_keys)
|
||||||
|
keys = {'battery', 'reachable'}
|
||||||
|
if force_update or any(key in changed for key in keys):
|
||||||
self.async_schedule_update_ha_state()
|
self.async_schedule_update_ha_state()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -10,7 +10,7 @@ from .gateway import get_gateway_from_config_entry
|
||||||
|
|
||||||
async def async_setup_platform(
|
async def async_setup_platform(
|
||||||
hass, config, async_add_entities, discovery_info=None):
|
hass, config, async_add_entities, discovery_info=None):
|
||||||
"""Old way of setting up deCONZ switches."""
|
"""Old way of setting up deCONZ platforms."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1048,7 +1048,7 @@ pydaikin==1.4.6
|
||||||
pydanfossair==0.1.0
|
pydanfossair==0.1.0
|
||||||
|
|
||||||
# homeassistant.components.deconz
|
# homeassistant.components.deconz
|
||||||
pydeconz==58
|
pydeconz==59
|
||||||
|
|
||||||
# homeassistant.components.zwave
|
# homeassistant.components.zwave
|
||||||
pydispatcher==2.0.5
|
pydispatcher==2.0.5
|
||||||
|
|
|
@ -230,7 +230,7 @@ pyHS100==0.3.5
|
||||||
pyblackbird==0.5
|
pyblackbird==0.5
|
||||||
|
|
||||||
# homeassistant.components.deconz
|
# homeassistant.components.deconz
|
||||||
pydeconz==58
|
pydeconz==59
|
||||||
|
|
||||||
# homeassistant.components.zwave
|
# homeassistant.components.zwave
|
||||||
pydispatcher==2.0.5
|
pydispatcher==2.0.5
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
"""deCONZ binary sensor platform tests."""
|
"""deCONZ binary sensor platform tests."""
|
||||||
from unittest.mock import Mock, patch
|
from unittest.mock import Mock, patch
|
||||||
|
|
||||||
|
from tests.common import mock_coro
|
||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
from homeassistant.components import deconz
|
from homeassistant.components import deconz
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||||
|
@ -8,8 +10,6 @@ from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
import homeassistant.components.binary_sensor as binary_sensor
|
import homeassistant.components.binary_sensor as binary_sensor
|
||||||
|
|
||||||
from tests.common import mock_coro
|
|
||||||
|
|
||||||
|
|
||||||
SENSOR = {
|
SENSOR = {
|
||||||
"1": {
|
"1": {
|
||||||
|
@ -104,6 +104,7 @@ async def test_add_new_sensor(hass):
|
||||||
sensor = Mock()
|
sensor = Mock()
|
||||||
sensor.name = 'name'
|
sensor.name = 'name'
|
||||||
sensor.type = 'ZHAPresence'
|
sensor.type = 'ZHAPresence'
|
||||||
|
sensor.BINARY = True
|
||||||
sensor.register_async_callback = Mock()
|
sensor.register_async_callback = Mock()
|
||||||
async_dispatcher_send(
|
async_dispatcher_send(
|
||||||
hass, gateway.async_event_new_device('sensor'), [sensor])
|
hass, gateway.async_event_new_device('sensor'), [sensor])
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
"""deCONZ climate platform tests."""
|
"""deCONZ climate platform tests."""
|
||||||
|
from copy import deepcopy
|
||||||
from unittest.mock import Mock, patch
|
from unittest.mock import Mock, patch
|
||||||
|
|
||||||
import asynctest
|
import asynctest
|
||||||
|
@ -18,9 +19,9 @@ SENSOR = {
|
||||||
"id": "Climate 1 id",
|
"id": "Climate 1 id",
|
||||||
"name": "Climate 1 name",
|
"name": "Climate 1 name",
|
||||||
"type": "ZHAThermostat",
|
"type": "ZHAThermostat",
|
||||||
"state": {"on": True, "temperature": 2260},
|
"state": {"on": True, "temperature": 2260, "valve": 30},
|
||||||
"config": {"battery": 100, "heatsetpoint": 2200, "mode": "auto",
|
"config": {"battery": 100, "heatsetpoint": 2200, "mode": "auto",
|
||||||
"offset": 10, "reachable": True, "valve": 30},
|
"offset": 10, "reachable": True},
|
||||||
"uniqueid": "00:00:00:00:00:00:00:00-00"
|
"uniqueid": "00:00:00:00:00:00:00:00-00"
|
||||||
},
|
},
|
||||||
"2": {
|
"2": {
|
||||||
|
@ -97,7 +98,7 @@ async def test_no_sensors(hass):
|
||||||
|
|
||||||
async def test_climate_devices(hass):
|
async def test_climate_devices(hass):
|
||||||
"""Test successful creation of sensor entities."""
|
"""Test successful creation of sensor entities."""
|
||||||
gateway = await setup_gateway(hass, {"sensors": SENSOR})
|
gateway = await setup_gateway(hass, {"sensors": deepcopy(SENSOR)})
|
||||||
assert "climate.climate_1_name" in gateway.deconz_ids
|
assert "climate.climate_1_name" in gateway.deconz_ids
|
||||||
assert "sensor.sensor_2_name" not in gateway.deconz_ids
|
assert "sensor.sensor_2_name" not in gateway.deconz_ids
|
||||||
assert len(hass.states.async_all()) == 1
|
assert len(hass.states.async_all()) == 1
|
||||||
|
@ -138,7 +139,7 @@ async def test_climate_devices(hass):
|
||||||
|
|
||||||
async def test_verify_state_update(hass):
|
async def test_verify_state_update(hass):
|
||||||
"""Test that state update properly."""
|
"""Test that state update properly."""
|
||||||
gateway = await setup_gateway(hass, {"sensors": SENSOR})
|
gateway = await setup_gateway(hass, {"sensors": deepcopy(SENSOR)})
|
||||||
assert "climate.climate_1_name" in gateway.deconz_ids
|
assert "climate.climate_1_name" in gateway.deconz_ids
|
||||||
|
|
||||||
thermostat = hass.states.get('climate.climate_1_name')
|
thermostat = hass.states.get('climate.climate_1_name')
|
||||||
|
@ -149,7 +150,7 @@ async def test_verify_state_update(hass):
|
||||||
"e": "changed",
|
"e": "changed",
|
||||||
"r": "sensors",
|
"r": "sensors",
|
||||||
"id": "1",
|
"id": "1",
|
||||||
"config": {"on": False}
|
"state": {"on": False}
|
||||||
}
|
}
|
||||||
gateway.api.async_event_handler(state_update)
|
gateway.api.async_event_handler(state_update)
|
||||||
|
|
||||||
|
@ -158,6 +159,8 @@ async def test_verify_state_update(hass):
|
||||||
|
|
||||||
thermostat = hass.states.get('climate.climate_1_name')
|
thermostat = hass.states.get('climate.climate_1_name')
|
||||||
assert thermostat.state == 'off'
|
assert thermostat.state == 'off'
|
||||||
|
assert gateway.api.sensors['1'].changed_keys == \
|
||||||
|
{'state', 'r', 't', 'on', 'e', 'id'}
|
||||||
|
|
||||||
|
|
||||||
async def test_add_new_climate_device(hass):
|
async def test_add_new_climate_device(hass):
|
||||||
|
|
|
@ -43,7 +43,7 @@ async def test_flow_works(hass, aioclient_mock):
|
||||||
|
|
||||||
async def test_user_step_bridge_discovery_fails(hass, aioclient_mock):
|
async def test_user_step_bridge_discovery_fails(hass, aioclient_mock):
|
||||||
"""Test config flow works when discovery fails."""
|
"""Test config flow works when discovery fails."""
|
||||||
with patch('pydeconz.utils.async_discovery',
|
with patch('homeassistant.components.deconz.config_flow.async_discovery',
|
||||||
side_effect=asyncio.TimeoutError):
|
side_effect=asyncio.TimeoutError):
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
config_flow.DOMAIN,
|
config_flow.DOMAIN,
|
||||||
|
@ -158,8 +158,9 @@ async def test_link_no_api_key(hass):
|
||||||
config_flow.CONF_PORT: 80
|
config_flow.CONF_PORT: 80
|
||||||
}
|
}
|
||||||
|
|
||||||
with patch('pydeconz.utils.async_get_api_key',
|
with patch(
|
||||||
side_effect=pydeconz.errors.ResponseError):
|
'homeassistant.components.deconz.config_flow.async_get_api_key',
|
||||||
|
side_effect=pydeconz.errors.ResponseError):
|
||||||
result = await flow.async_step_link(user_input={})
|
result = await flow.async_step_link(user_input={})
|
||||||
|
|
||||||
assert result['type'] == 'form'
|
assert result['type'] == 'form'
|
||||||
|
@ -275,8 +276,9 @@ async def test_create_entry_timeout(hass, aioclient_mock):
|
||||||
config_flow.CONF_API_KEY: '1234567890ABCDEF'
|
config_flow.CONF_API_KEY: '1234567890ABCDEF'
|
||||||
}
|
}
|
||||||
|
|
||||||
with patch('pydeconz.utils.async_get_bridgeid',
|
with patch(
|
||||||
side_effect=asyncio.TimeoutError):
|
'homeassistant.components.deconz.config_flow.async_get_bridgeid',
|
||||||
|
side_effect=asyncio.TimeoutError):
|
||||||
result = await flow._create_entry()
|
result = await flow._create_entry()
|
||||||
|
|
||||||
assert result['type'] == 'abort'
|
assert result['type'] == 'abort'
|
||||||
|
|
|
@ -223,7 +223,8 @@ async def test_update_event():
|
||||||
remote.name = 'Name'
|
remote.name = 'Name'
|
||||||
|
|
||||||
event = gateway.DeconzEvent(hass, remote)
|
event = gateway.DeconzEvent(hass, remote)
|
||||||
event.async_update_callback({'state': True})
|
remote.changed_keys = {'state': True}
|
||||||
|
event.async_update_callback()
|
||||||
|
|
||||||
assert len(hass.bus.async_fire.mock_calls) == 1
|
assert len(hass.bus.async_fire.mock_calls) == 1
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
"""deCONZ sensor platform tests."""
|
"""deCONZ sensor platform tests."""
|
||||||
from unittest.mock import Mock, patch
|
from unittest.mock import Mock, patch
|
||||||
|
|
||||||
|
from tests.common import mock_coro
|
||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
from homeassistant.components import deconz
|
from homeassistant.components import deconz
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||||
|
@ -8,8 +10,6 @@ from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
import homeassistant.components.sensor as sensor
|
import homeassistant.components.sensor as sensor
|
||||||
|
|
||||||
from tests.common import mock_coro
|
|
||||||
|
|
||||||
|
|
||||||
SENSOR = {
|
SENSOR = {
|
||||||
"1": {
|
"1": {
|
||||||
|
@ -142,6 +142,7 @@ async def test_add_new_sensor(hass):
|
||||||
sensor = Mock()
|
sensor = Mock()
|
||||||
sensor.name = 'name'
|
sensor.name = 'name'
|
||||||
sensor.type = 'ZHATemperature'
|
sensor.type = 'ZHATemperature'
|
||||||
|
sensor.BINARY = False
|
||||||
sensor.register_async_callback = Mock()
|
sensor.register_async_callback = Mock()
|
||||||
async_dispatcher_send(
|
async_dispatcher_send(
|
||||||
hass, gateway.async_event_new_device('sensor'), [sensor])
|
hass, gateway.async_event_new_device('sensor'), [sensor])
|
||||||
|
|
Loading…
Add table
Reference in a new issue