diff --git a/homeassistant/components/climate/ecobee.py b/homeassistant/components/climate/ecobee.py index 5d78aeb8597..21a2c89c31a 100644 --- a/homeassistant/components/climate/ecobee.py +++ b/homeassistant/components/climate/ecobee.py @@ -116,9 +116,6 @@ class Thermostat(ClimateDevice): return self.target_temperature_low elif self.operation_mode == 'cool': return self.target_temperature_high - else: - return (self.target_temperature_low + - self.target_temperature_high) / 2 @property def target_temperature_low(self): @@ -223,19 +220,27 @@ class Thermostat(ClimateDevice): """Set new target temperature.""" if kwargs.get(ATTR_TEMPERATURE) is not None: temperature = kwargs.get(ATTR_TEMPERATURE) - low_temp = temperature - 1 - high_temp = temperature + 1 + low_temp = int(temperature) + high_temp = int(temperature) if kwargs.get(ATTR_TARGET_TEMP_LOW) is not None and \ kwargs.get(ATTR_TARGET_TEMP_HIGH) is not None: - low_temp = kwargs.get(ATTR_TARGET_TEMP_LOW) - high_temp = kwargs.get(ATTR_TARGET_TEMP_HIGH) + high_temp = int(kwargs.get(ATTR_TARGET_TEMP_LOW)) + low_temp = int(kwargs.get(ATTR_TARGET_TEMP_HIGH)) if self.hold_temp: self.data.ecobee.set_hold_temp(self.thermostat_index, low_temp, high_temp, "indefinite") + _LOGGER.debug("Setting ecobee hold_temp to: low=%s, is=%s, " + "high=%s, is=%s", low_temp, isinstance( + low_temp, (int, float)), high_temp, + isinstance(high_temp, (int, float))) else: self.data.ecobee.set_hold_temp(self.thermostat_index, low_temp, high_temp) + _LOGGER.debug("Setting ecobee temp to: low=%s, is=%s, " + "high=%s, is=%s", low_temp, isinstance( + low_temp, (int, float)), high_temp, + isinstance(high_temp, (int, float))) def set_operation_mode(self, operation_mode): """Set HVAC mode (auto, auxHeatOnly, cool, heat, off).""" diff --git a/homeassistant/components/cover/wink.py b/homeassistant/components/cover/wink.py index 9b76e234303..59676b1f6a7 100644 --- a/homeassistant/components/cover/wink.py +++ b/homeassistant/components/cover/wink.py @@ -28,8 +28,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None): pywink.set_bearer_token(token) - add_devices(WinkCoverDevice(shade) for shade, door in + add_devices(WinkCoverDevice(shade) for shade in pywink.get_shades()) + add_devices(WinkCoverDevice(door) for door in + pywink.get_garage_doors()) class WinkCoverDevice(WinkDevice, CoverDevice): diff --git a/homeassistant/components/device_tracker/automatic.py b/homeassistant/components/device_tracker/automatic.py index 7855323ba06..27bd9c6b477 100644 --- a/homeassistant/components/device_tracker/automatic.py +++ b/homeassistant/components/device_tracker/automatic.py @@ -15,12 +15,11 @@ from homeassistant.components.device_tracker import (PLATFORM_SCHEMA, ATTR_ATTRIBUTES) from homeassistant.const import CONF_USERNAME, CONF_PASSWORD import homeassistant.helpers.config_validation as cv -from homeassistant.util import Throttle, datetime as dt_util +from homeassistant.helpers.event import track_utc_time_change +from homeassistant.util import datetime as dt_util _LOGGER = logging.getLogger(__name__) -MIN_TIME_BETWEEN_SCANS = timedelta(seconds=30) - CONF_CLIENT_ID = 'client_id' CONF_SECRET = 'secret' CONF_DEVICES = 'devices' @@ -53,7 +52,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ def setup_scanner(hass, config: dict, see): """Validate the configuration and return an Automatic scanner.""" try: - AutomaticDeviceScanner(config, see) + AutomaticDeviceScanner(hass, config, see) except requests.HTTPError as err: _LOGGER.error(str(err)) return False @@ -61,11 +60,14 @@ def setup_scanner(hass, config: dict, see): return True +# pylint: disable=too-many-instance-attributes +# pylint: disable=too-few-public-methods class AutomaticDeviceScanner(object): """A class representing an Automatic device.""" - def __init__(self, config: dict, see) -> None: + def __init__(self, hass, config: dict, see) -> None: """Initialize the automatic device scanner.""" + self.hass = hass self._devices = config.get(CONF_DEVICES, None) self._access_token_payload = { 'username': config.get(CONF_USERNAME), @@ -81,20 +83,10 @@ class AutomaticDeviceScanner(object): self.last_trips = {} self.see = see - self.scan_devices() - - def scan_devices(self): - """Scan for new devices and return a list with found device IDs.""" self._update_info() - return [item['id'] for item in self.last_results] - - def get_device_name(self, device): - """Get the device name from id.""" - vehicle = [item['display_name'] for item in self.last_results - if item['id'] == device] - - return vehicle[0] + track_utc_time_change(self.hass, self._update_info, + second=range(0, 60, 30)) def _update_headers(self): """Get the access token from automatic.""" @@ -114,10 +106,9 @@ class AutomaticDeviceScanner(object): 'Authorization': 'Bearer {}'.format(access_token) } - @Throttle(MIN_TIME_BETWEEN_SCANS) - def _update_info(self) -> None: + def _update_info(self, now=None) -> None: """Update the device info.""" - _LOGGER.info('Updating devices') + _LOGGER.debug('Updating devices %s', now) self._update_headers() response = requests.get(URL_VEHICLES, headers=self._headers) diff --git a/homeassistant/components/pilight.py b/homeassistant/components/pilight.py index 764b972d393..3475a6be65a 100644 --- a/homeassistant/components/pilight.py +++ b/homeassistant/components/pilight.py @@ -10,7 +10,6 @@ import socket import voluptuous as vol import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.config_validation import ensure_list from homeassistant.const import ( EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, CONF_HOST, CONF_PORT, CONF_WHITELIST) @@ -29,7 +28,10 @@ EVENT = 'pilight_received' # The pilight code schema depends on the protocol # Thus only require to have the protocol information -RF_CODE_SCHEMA = vol.Schema({vol.Required(ATTR_PROTOCOL): cv.string}, +# Ensure that protocol is in a list otherwise segfault in pilight-daemon +# https://github.com/pilight/pilight/issues/296 +RF_CODE_SCHEMA = vol.Schema({vol.Required(ATTR_PROTOCOL): + vol.All(cv.ensure_list, [cv.string])}, extra=vol.ALLOW_EXTRA) SERVICE_NAME = 'send' @@ -71,12 +73,9 @@ def setup(hass, config): def send_code(call): """Send RF code to the pilight-daemon.""" - message_data = call.data - - # Patch data because of bug: - # https://github.com/pilight/pilight/issues/296 - # Protocol has to be in a list otherwise segfault in pilight-daemon - message_data['protocol'] = ensure_list(message_data['protocol']) + # Change type to dict from mappingproxy + # since data has to be JSON serializable + message_data = dict(call.data) try: pilight_client.send_code(message_data) diff --git a/homeassistant/components/recorder/__init__.py b/homeassistant/components/recorder/__init__.py index 5e4415d81a0..8f373700165 100644 --- a/homeassistant/components/recorder/__init__.py +++ b/homeassistant/components/recorder/__init__.py @@ -20,6 +20,7 @@ from homeassistant.core import HomeAssistant from homeassistant.const import (EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, EVENT_STATE_CHANGED, EVENT_TIME_CHANGED, MATCH_ALL) +import homeassistant.helpers.config_validation as cv from homeassistant.helpers.event import track_point_in_utc_time from homeassistant.helpers.typing import ConfigType, QueryType import homeassistant.util.dt as dt_util @@ -40,10 +41,9 @@ QUERY_RETRY_WAIT = 0.1 CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ - vol.Optional(CONF_PURGE_DAYS): vol.All(vol.Coerce(int), - vol.Range(min=1)), - # pylint: disable=no-value-for-parameter - vol.Optional(CONF_DB_URL): vol.Url(), + vol.Optional(CONF_PURGE_DAYS): + vol.All(vol.Coerce(int), vol.Range(min=1)), + vol.Optional(CONF_DB_URL): cv.string, }) }, extra=vol.ALLOW_EXTRA) diff --git a/homeassistant/const.py b/homeassistant/const.py index 797fd3108b9..392a10aedb9 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 28 -PATCH_VERSION = '1' +PATCH_VERSION = '2' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 4) diff --git a/homeassistant/helpers/state.py b/homeassistant/helpers/state.py index 9c6e797acd1..4935251db7d 100644 --- a/homeassistant/helpers/state.py +++ b/homeassistant/helpers/state.py @@ -29,10 +29,10 @@ from homeassistant.const import ( SERVICE_CLOSE, SERVICE_LOCK, SERVICE_MEDIA_PAUSE, SERVICE_MEDIA_PLAY, SERVICE_MEDIA_SEEK, SERVICE_MOVE_DOWN, SERVICE_MOVE_UP, SERVICE_OPEN, SERVICE_TURN_OFF, SERVICE_TURN_ON, SERVICE_UNLOCK, SERVICE_VOLUME_MUTE, - SERVICE_VOLUME_SET, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, - STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED, STATE_CLOSED, STATE_LOCKED, - STATE_OFF, STATE_ON, STATE_OPEN, STATE_PAUSED, STATE_PLAYING, - STATE_UNKNOWN, STATE_UNLOCKED) + SERVICE_VOLUME_SET, SERVICE_OPEN_COVER, SERVICE_CLOSE_COVER, + STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, + STATE_ALARM_TRIGGERED, STATE_CLOSED, STATE_LOCKED, STATE_OFF, STATE_ON, + STATE_OPEN, STATE_PAUSED, STATE_PLAYING, STATE_UNKNOWN, STATE_UNLOCKED) from homeassistant.core import State _LOGGER = logging.getLogger(__name__) @@ -77,6 +77,8 @@ SERVICE_TO_STATE = { SERVICE_OPEN: STATE_OPEN, SERVICE_MOVE_UP: STATE_OPEN, SERVICE_MOVE_DOWN: STATE_CLOSED, + SERVICE_OPEN_COVER: STATE_OPEN, + SERVICE_CLOSE_COVER: STATE_CLOSED } diff --git a/tests/components/device_tracker/test_automatic.py b/tests/components/device_tracker/test_automatic.py index e026d91a43c..2e476ac742d 100644 --- a/tests/components/device_tracker/test_automatic.py +++ b/tests/components/device_tracker/test_automatic.py @@ -6,8 +6,9 @@ import unittest from unittest.mock import patch from homeassistant.components.device_tracker.automatic import ( - URL_AUTHORIZE, URL_VEHICLES, URL_TRIPS, setup_scanner, - AutomaticDeviceScanner) + URL_AUTHORIZE, URL_VEHICLES, URL_TRIPS, setup_scanner) + +from tests.common import get_test_home_assistant _LOGGER = logging.getLogger(__name__) @@ -205,6 +206,7 @@ class TestAutomatic(unittest.TestCase): def setUp(self): """Set up test data.""" + self.hass = get_test_home_assistant() def tearDown(self): """Tear down test data.""" @@ -221,7 +223,7 @@ class TestAutomatic(unittest.TestCase): 'secret': CLIENT_SECRET } - self.assertFalse(setup_scanner(None, config, self.see_mock)) + self.assertFalse(setup_scanner(self.hass, config, self.see_mock)) @patch('requests.get', side_effect=mocked_requests) @patch('requests.post', side_effect=mocked_requests) @@ -235,20 +237,4 @@ class TestAutomatic(unittest.TestCase): 'secret': CLIENT_SECRET } - self.assertTrue(setup_scanner(None, config, self.see_mock)) - - @patch('requests.get', side_effect=mocked_requests) - @patch('requests.post', side_effect=mocked_requests) - def test_device_attributes(self, mock_get, mock_post): - """Test device attributes are set on load.""" - config = { - 'platform': 'automatic', - 'username': VALID_USERNAME, - 'password': PASSWORD, - 'client_id': CLIENT_ID, - 'secret': CLIENT_SECRET - } - - scanner = AutomaticDeviceScanner(config, self.see_mock) - - self.assertEqual(DISPLAY_NAME, scanner.get_device_name('vid')) + self.assertTrue(setup_scanner(self.hass, config, self.see_mock))