Merge pull request #21508 from home-assistant/rc

0.88.2
This commit is contained in:
Paulus Schoutsen 2019-02-27 16:19:26 -08:00 committed by GitHub
commit 63fda29eaf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 131 additions and 17 deletions

View File

@ -151,6 +151,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
device['name'] = '{} {}'.format(device['id'], ipaddr)
device[ATTR_MODE] = MODE_RGBW
device[CONF_PROTOCOL] = None
device[CONF_CUSTOM_EFFECT] = None
light = FluxLight(device)
lights.append(light)

View File

@ -176,7 +176,7 @@ class PersonManager:
CONF_ID: uuid.uuid4().hex,
CONF_NAME: name,
CONF_USER_ID: user_id,
CONF_DEVICE_TRACKERS: device_trackers,
CONF_DEVICE_TRACKERS: device_trackers or [],
}
self.storage_data[person[CONF_ID]] = person
self._async_schedule_save()
@ -337,6 +337,12 @@ class Person(RestoreEntity):
if state:
self._parse_source_state(state)
if self.hass.is_running:
# Update person now if hass is already running.
self.person_updated()
else:
# Wait for hass start to not have race between person
# and device trackers finishing setup.
@callback
def person_start_hass(now):
self.person_updated()

View File

@ -31,6 +31,11 @@ def _platform_validator(config):
platform = importlib.import_module(
'homeassistant.components.scene.{}'.format(
config[CONF_PLATFORM]))
except ImportError:
try:
platform = importlib.import_module(
'homeassistant.components.{}.scene'.format(
config[CONF_PLATFORM]))
except ImportError:
raise vol.Invalid('Invalid platform specified') from None

View File

@ -1,13 +1,14 @@
"""Support for climate devices through the SmartThings cloud API."""
import asyncio
from typing import Optional, Sequence
import logging
from typing import Iterable, Optional, Sequence
from homeassistant.components.climate import (
ATTR_OPERATION_MODE, ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW,
STATE_AUTO, STATE_COOL, STATE_ECO, STATE_HEAT, SUPPORT_FAN_MODE,
SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE,
SUPPORT_TARGET_TEMPERATURE_HIGH, SUPPORT_TARGET_TEMPERATURE_LOW,
ClimateDevice)
ClimateDevice, DOMAIN as CLIMATE_DOMAIN)
from homeassistant.const import (
ATTR_TEMPERATURE, STATE_OFF, TEMP_CELSIUS, TEMP_FAHRENHEIT)
@ -38,6 +39,8 @@ UNIT_MAP = {
'F': TEMP_FAHRENHEIT
}
_LOGGER = logging.getLogger(__name__)
async def async_setup_platform(
hass, config, async_add_entities, discovery_info=None):
@ -50,7 +53,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
broker = hass.data[DOMAIN][DATA_BROKERS][config_entry.entry_id]
async_add_entities(
[SmartThingsThermostat(device) for device in broker.devices.values()
if broker.any_assigned(device.device_id, 'climate')])
if broker.any_assigned(device.device_id, CLIMATE_DOMAIN)], True)
def get_capabilities(capabilities: Sequence[str]) -> Optional[Sequence[str]]:
@ -90,6 +93,8 @@ class SmartThingsThermostat(SmartThingsEntity, ClimateDevice):
"""Init the class."""
super().__init__(device)
self._supported_features = self._determine_features()
self._current_operation = None
self._operations = None
def _determine_features(self):
from pysmartthings import Capability
@ -127,6 +132,7 @@ class SmartThingsThermostat(SmartThingsEntity, ClimateDevice):
if operation_state:
mode = STATE_TO_MODE[operation_state]
await self._device.set_thermostat_mode(mode, set_status=True)
await self.async_update()
# Heat/cool setpoint
heating_setpoint = None
@ -151,6 +157,33 @@ class SmartThingsThermostat(SmartThingsEntity, ClimateDevice):
# the entity state ahead of receiving the confirming push updates
self.async_schedule_update_ha_state(True)
async def async_update(self):
"""Update the attributes of the climate device."""
thermostat_mode = self._device.status.thermostat_mode
self._current_operation = MODE_TO_STATE.get(thermostat_mode)
if self._current_operation is None:
_LOGGER.debug('Device %s (%s) returned an invalid'
'thermostat mode: %s', self._device.label,
self._device.device_id, thermostat_mode)
supported_modes = self._device.status.supported_thermostat_modes
if isinstance(supported_modes, Iterable):
operations = set()
for mode in supported_modes:
state = MODE_TO_STATE.get(mode)
if state is not None:
operations.add(state)
else:
_LOGGER.debug('Device %s (%s) returned an invalid '
'supported thermostat mode: %s',
self._device.label, self._device.device_id,
mode)
self._operations = operations
else:
_LOGGER.debug('Device %s (%s) returned invalid supported '
'thermostat modes: %s', self._device.label,
self._device.device_id, supported_modes)
@property
def current_fan_mode(self):
"""Return the fan setting."""
@ -164,7 +197,7 @@ class SmartThingsThermostat(SmartThingsEntity, ClimateDevice):
@property
def current_operation(self):
"""Return current operation ie. heat, cool, idle."""
return MODE_TO_STATE[self._device.status.thermostat_mode]
return self._current_operation
@property
def current_temperature(self):
@ -187,8 +220,7 @@ class SmartThingsThermostat(SmartThingsEntity, ClimateDevice):
@property
def operation_list(self):
"""Return the list of available operation modes."""
return {MODE_TO_STATE[mode] for mode in
self._device.status.supported_thermostat_modes}
return self._operations
@property
def supported_features(self):

View File

@ -16,7 +16,7 @@ from homeassistant.helpers.entity import Entity
from homeassistant.helpers.event import async_track_point_in_utc_time
from homeassistant.util.dt import utcnow
REQUIREMENTS = ['PyXiaomiGateway==0.11.1']
REQUIREMENTS = ['PyXiaomiGateway==0.11.2']
_LOGGER = logging.getLogger(__name__)

View File

@ -99,8 +99,10 @@ class XiaomiGatewayLight(XiaomiDevice, Light):
if self._write_to_hub(self._sid, **{self._data_key: rgbhex}):
self._state = True
self.schedule_update_ha_state()
def turn_off(self, **kwargs):
"""Turn the light off."""
if self._write_to_hub(self._sid, **{self._data_key: 0}):
self._state = False
self.schedule_update_ha_state()

View File

@ -2,7 +2,7 @@
"""Constants used by Home Assistant components."""
MAJOR_VERSION = 0
MINOR_VERSION = 88
PATCH_VERSION = '1'
PATCH_VERSION = '2'
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
REQUIRED_PYTHON_VER = (3, 5, 3)

View File

@ -26,3 +26,7 @@ pycrypto==1000000000.0.0
# Contains code to modify Home Assistant to work around our rules
python-systemair-savecair==1000000000.0.0
# Newer version causes pylint to take forever
# https://github.com/timothycrosley/isort/issues/848
isort==4.3.4

View File

@ -63,7 +63,7 @@ PySwitchbot==0.5
PyTransportNSW==0.1.1
# homeassistant.components.xiaomi_aqara
PyXiaomiGateway==0.11.1
PyXiaomiGateway==0.11.2
# homeassistant.components.rpi_gpio
# RPi.GPIO==0.6.5

View File

@ -157,6 +157,10 @@ pycrypto==1000000000.0.0
# Contains code to modify Home Assistant to work around our rules
python-systemair-savecair==1000000000.0.0
# Newer version causes pylint to take forever
# https://github.com/timothycrosley/isort/issues/848
isort==4.3.4
"""

View File

@ -101,6 +101,7 @@ async def test_valid_invalid_user_ids(hass, hass_admin_user):
async def test_setup_tracker(hass, hass_admin_user):
"""Test set up person with one device tracker."""
hass.state = CoreState.not_running
user_id = hass_admin_user.id
config = {DOMAIN: {
'id': '1234', 'name': 'tracked person', 'user_id': user_id,
@ -148,6 +149,7 @@ async def test_setup_tracker(hass, hass_admin_user):
async def test_setup_two_trackers(hass, hass_admin_user):
"""Test set up person with two device trackers."""
hass.state = CoreState.not_running
user_id = hass_admin_user.id
config = {DOMAIN: {
'id': '1234', 'name': 'tracked person', 'user_id': user_id,
@ -191,6 +193,7 @@ async def test_setup_two_trackers(hass, hass_admin_user):
async def test_ignore_unavailable_states(hass, hass_admin_user):
"""Test set up person with two device trackers, one unavailable."""
hass.state = CoreState.not_running
user_id = hass_admin_user.id
config = {DOMAIN: {
'id': '1234', 'name': 'tracked person', 'user_id': user_id,
@ -234,7 +237,7 @@ async def test_restore_home_state(hass, hass_admin_user):
ATTR_SOURCE: DEVICE_TRACKER, ATTR_USER_ID: user_id}
state = State('person.tracked_person', 'home', attrs)
mock_restore_cache(hass, (state, ))
hass.state = CoreState.starting
hass.state = CoreState.not_running
mock_component(hass, 'recorder')
config = {DOMAIN: {
'id': '1234', 'name': 'tracked person', 'user_id': user_id,
@ -263,6 +266,21 @@ async def test_duplicate_ids(hass, hass_admin_user):
assert hass.states.get('person.test_user_2') is None
async def test_create_person_during_run(hass):
"""Test that person is updated if created while hass is running."""
config = {DOMAIN: {}}
assert await async_setup_component(hass, DOMAIN, config)
hass.states.async_set(DEVICE_TRACKER, 'home')
await hass.async_block_till_done()
await hass.components.person.async_create_person(
'tracked person', device_trackers=[DEVICE_TRACKER])
await hass.async_block_till_done()
state = hass.states.get('person.tracked_person')
assert state.state == 'home'
async def test_load_person_storage(hass, hass_admin_user, storage_setup):
"""Test set up person from storage."""
state = hass.states.get('person.tracked_person')

View File

@ -19,7 +19,7 @@ from homeassistant.components.climate import (
from homeassistant.components.smartthings import climate
from homeassistant.components.smartthings.const import DOMAIN
from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, ATTR_TEMPERATURE)
ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, ATTR_TEMPERATURE, STATE_UNKNOWN)
from .conftest import setup_platform
@ -95,6 +95,25 @@ def thermostat_fixture(device_factory):
return device
@pytest.fixture(name="buggy_thermostat")
def buggy_thermostat_fixture(device_factory):
"""Fixture returns a buggy thermostat."""
device = device_factory(
"Buggy Thermostat",
capabilities=[
Capability.temperature_measurement,
Capability.thermostat_cooling_setpoint,
Capability.thermostat_heating_setpoint,
Capability.thermostat_mode],
status={
Attribute.thermostat_mode: 'heating',
Attribute.cooling_setpoint: 74,
Attribute.heating_setpoint: 68}
)
device.status.attributes[Attribute.temperature] = Status(70, 'F', None)
return device
async def test_async_setup_platform():
"""Test setup platform does nothing (it uses config entries)."""
await climate.async_setup_platform(None, None, None)
@ -152,6 +171,29 @@ async def test_thermostat_entity_state(hass, thermostat):
assert state.attributes[ATTR_CURRENT_HUMIDITY] == 34
async def test_buggy_thermostat_entity_state(hass, buggy_thermostat):
"""Tests the state attributes properly match the thermostat type."""
await setup_platform(hass, CLIMATE_DOMAIN, buggy_thermostat)
state = hass.states.get('climate.buggy_thermostat')
assert state.state == STATE_UNKNOWN
assert state.attributes[ATTR_SUPPORTED_FEATURES] == \
SUPPORT_OPERATION_MODE | SUPPORT_TARGET_TEMPERATURE_HIGH | \
SUPPORT_TARGET_TEMPERATURE_LOW | SUPPORT_TARGET_TEMPERATURE
assert ATTR_OPERATION_LIST not in state.attributes
assert state.attributes[ATTR_TEMPERATURE] is None
assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 21.1 # celsius
async def test_buggy_thermostat_invalid_mode(hass, buggy_thermostat):
"""Tests when an invalid operation mode is included."""
buggy_thermostat.status.update_attribute_value(
Attribute.supported_thermostat_modes,
['heat', 'emergency heat', 'other'])
await setup_platform(hass, CLIMATE_DOMAIN, buggy_thermostat)
state = hass.states.get('climate.buggy_thermostat')
assert state.attributes[ATTR_OPERATION_LIST] == {'heat'}
async def test_set_fan_mode(hass, thermostat):
"""Test the fan mode is set successfully."""
await setup_platform(hass, CLIMATE_DOMAIN, thermostat)