From 725e1ddfc116ff6f06e5d7574321ffc96d30faf8 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 23 Mar 2018 14:15:31 -0700 Subject: [PATCH 01/14] Update translations --- .../.translations/nl.json | 3 ++- .../.translations/vi.json | 24 +++++++++++++++++++ .../components/hue/.translations/de.json | 2 +- .../components/hue/.translations/pl.json | 2 +- .../sensor/.translations/season.vi.json | 8 +++++++ 5 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 homeassistant/components/config_entry_example/.translations/vi.json create mode 100644 homeassistant/components/sensor/.translations/season.vi.json diff --git a/homeassistant/components/config_entry_example/.translations/nl.json b/homeassistant/components/config_entry_example/.translations/nl.json index 10469dd0804..7b52ac88cf0 100644 --- a/homeassistant/components/config_entry_example/.translations/nl.json +++ b/homeassistant/components/config_entry_example/.translations/nl.json @@ -18,6 +18,7 @@ "description": "Voer een naam in voor het testen van de entiteit.", "title": "Naam van de entiteit" } - } + }, + "title": "Voorbeeld van de config vermelding" } } \ No newline at end of file diff --git a/homeassistant/components/config_entry_example/.translations/vi.json b/homeassistant/components/config_entry_example/.translations/vi.json new file mode 100644 index 00000000000..e40c4d38e9f --- /dev/null +++ b/homeassistant/components/config_entry_example/.translations/vi.json @@ -0,0 +1,24 @@ +{ + "config": { + "error": { + "invalid_object_id": "ID \u0111\u1ed1i t\u01b0\u1ee3ng kh\u00f4ng h\u1ee3p l\u1ec7" + }, + "step": { + "init": { + "data": { + "object_id": "ID \u0111\u1ed1i t\u01b0\u1ee3ng" + }, + "description": "Xin vui l\u00f2ng nh\u1eadp m\u1ed9t object_id cho th\u1eed nghi\u1ec7m th\u1ef1c th\u1ec3.", + "title": "Ch\u1ecdn id \u0111\u1ed1i t\u01b0\u1ee3ng" + }, + "name": { + "data": { + "name": "T\u00ean" + }, + "description": "Xin vui l\u00f2ng nh\u1eadp t\u00ean cho th\u1eed nghi\u1ec7m th\u1ef1c th\u1ec3.", + "title": "T\u00ean c\u1ee7a th\u1ef1c th\u1ec3" + } + }, + "title": "V\u00ed d\u1ee5 v\u1ec1 c\u1ea5u h\u00ecnh th\u1ef1c th\u1ec3" + } +} \ No newline at end of file diff --git a/homeassistant/components/hue/.translations/de.json b/homeassistant/components/hue/.translations/de.json index b7094d91528..f11af7756c7 100644 --- a/homeassistant/components/hue/.translations/de.json +++ b/homeassistant/components/hue/.translations/de.json @@ -6,7 +6,7 @@ "no_bridges": "Philips Hue Bridges entdeckt" }, "error": { - "linking": "Unbekannte Link-Fehler aufgetreten.", + "linking": "Unbekannter Link-Fehler aufgetreten.", "register_failed": "Registrieren fehlgeschlagen, bitte versuche es erneut" }, "step": { diff --git a/homeassistant/components/hue/.translations/pl.json b/homeassistant/components/hue/.translations/pl.json index cdd26a5b4b2..e364b7033a1 100644 --- a/homeassistant/components/hue/.translations/pl.json +++ b/homeassistant/components/hue/.translations/pl.json @@ -17,7 +17,7 @@ "title": "Wybierz mostek Hue" }, "link": { - "description": "Naci\u015bnij przycisk na mostku, aby zarejestrowa\u0107 Philips Hue z Home Assistant. ", + "description": "Naci\u015bnij przycisk na mostku, aby zarejestrowa\u0107 Philips Hue z Home Assistant.", "title": "Hub Link" } }, diff --git a/homeassistant/components/sensor/.translations/season.vi.json b/homeassistant/components/sensor/.translations/season.vi.json new file mode 100644 index 00000000000..a3bb21dee27 --- /dev/null +++ b/homeassistant/components/sensor/.translations/season.vi.json @@ -0,0 +1,8 @@ +{ + "state": { + "autumn": "M\u00f9a thu", + "spring": "M\u00f9a xu\u00e2n", + "summer": "M\u00f9a h\u00e8", + "winter": "M\u00f9a \u0111\u00f4ng" + } +} \ No newline at end of file From 101b39300b9fcbb01fdc9f237caf37a3a386189a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 23 Mar 2018 14:17:36 -0700 Subject: [PATCH 02/14] Version bump to 0.66.0.beta0 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 4ce2f503ad6..b0be1933ffe 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 66 -PATCH_VERSION = '0.dev0' +PATCH_VERSION = '0.beta0' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From 8e14e803cb985943b83339091b8ecb2965cf309a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 23 Mar 2018 14:27:05 -0700 Subject: [PATCH 03/14] Fix release script --- script/release | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/release b/script/release index 65a6339cedc..17d5ad9370d 100755 --- a/script/release +++ b/script/release @@ -21,9 +21,9 @@ fi CURRENT_BRANCH=`git rev-parse --abbrev-ref HEAD` -if [ "$CURRENT_BRANCH" != "master" ] +if [ "$CURRENT_BRANCH" != "master" ] && [ "$CURRENT_BRANCH" != "rc" ] then - echo "You have to be on the master branch to release." + echo "You have to be on the master or rc branch to release." exit 1 fi From 8a204fd15bc9217974b6656af93c1eceab8ab499 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 25 Mar 2018 18:10:59 -0700 Subject: [PATCH 04/14] Bump frontend to 20180326.0 --- homeassistant/components/frontend/__init__.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index 9107e64a040..dad07c87cb6 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -24,7 +24,7 @@ from homeassistant.core import callback from homeassistant.helpers.translation import async_get_translations from homeassistant.loader import bind_hass -REQUIREMENTS = ['home-assistant-frontend==20180322.0'] +REQUIREMENTS = ['home-assistant-frontend==20180326.0'] DOMAIN = 'frontend' DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log'] diff --git a/requirements_all.txt b/requirements_all.txt index 52833969872..017449bfeca 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -356,7 +356,7 @@ hipnotify==1.0.8 holidays==0.9.4 # homeassistant.components.frontend -home-assistant-frontend==20180322.0 +home-assistant-frontend==20180326.0 # homeassistant.components.homematicip_cloud homematicip==0.8 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index d2c1df2d3bf..fda514af007 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -81,7 +81,7 @@ hbmqtt==0.9.1 holidays==0.9.4 # homeassistant.components.frontend -home-assistant-frontend==20180322.0 +home-assistant-frontend==20180326.0 # homeassistant.components.influxdb # homeassistant.components.sensor.influxdb From 0d48a8eec68bcb226df3e8abfafafa40e04ea786 Mon Sep 17 00:00:00 2001 From: Patrick Hofmann Date: Sun, 25 Mar 2018 23:25:28 +0200 Subject: [PATCH 05/14] Security fix & lock for HomeMatic (#11980) * HomeMatic KeyMatic device become a real lock component * Adds supported features to lock component. Locks may are capable to open the door latch. If component is support it, the SUPPORT_OPENING bitmask can be supplied in the supported_features property. * hound improvements. * Travis improvements. * Improvements from review process * Simplifies is_locked method * Adds an openable lock in the lock demo component * removes blank line * Adds test for openable demo lock and lint and reviewer improvements. * adds new line... * Comment end with a period. * Additional blank line. * Mock service based testing, lint fixes * Update description --- .../components/homematic/__init__.py | 9 ++- homeassistant/components/lock/__init__.py | 33 ++++++++++- homeassistant/components/lock/demo.py | 19 +++++- homeassistant/components/lock/homematic.py | 58 +++++++++++++++++++ tests/components/lock/test_demo.py | 12 +++- 5 files changed, 121 insertions(+), 10 deletions(-) create mode 100644 homeassistant/components/lock/homematic.py diff --git a/homeassistant/components/homematic/__init__.py b/homeassistant/components/homematic/__init__.py index 1accf038575..c542cd9e88e 100644 --- a/homeassistant/components/homematic/__init__.py +++ b/homeassistant/components/homematic/__init__.py @@ -33,6 +33,7 @@ DISCOVER_SENSORS = 'homematic.sensor' DISCOVER_BINARY_SENSORS = 'homematic.binary_sensor' DISCOVER_COVER = 'homematic.cover' DISCOVER_CLIMATE = 'homematic.climate' +DISCOVER_LOCKS = 'homematic.locks' ATTR_DISCOVER_DEVICES = 'devices' ATTR_PARAM = 'param' @@ -59,7 +60,7 @@ SERVICE_SET_INSTALL_MODE = 'set_install_mode' HM_DEVICE_TYPES = { DISCOVER_SWITCHES: [ 'Switch', 'SwitchPowermeter', 'IOSwitch', 'IPSwitch', 'RFSiren', - 'IPSwitchPowermeter', 'KeyMatic', 'HMWIOSwitch', 'Rain', 'EcoLogic'], + 'IPSwitchPowermeter', 'HMWIOSwitch', 'Rain', 'EcoLogic'], DISCOVER_LIGHTS: ['Dimmer', 'KeyDimmer', 'IPKeyDimmer'], DISCOVER_SENSORS: [ 'SwitchPowermeter', 'Motion', 'MotionV2', 'RemoteMotion', 'MotionIP', @@ -78,7 +79,8 @@ HM_DEVICE_TYPES = { 'MotionIP', 'RemoteMotion', 'WeatherSensor', 'TiltSensor', 'IPShutterContact', 'HMWIOSwitch', 'MaxShutterContact', 'Rain', 'WiredSensor', 'PresenceIP'], - DISCOVER_COVER: ['Blind', 'KeyBlind', 'IPKeyBlind', 'IPKeyBlindTilt'] + DISCOVER_COVER: ['Blind', 'KeyBlind', 'IPKeyBlind', 'IPKeyBlindTilt'], + DISCOVER_LOCKS: ['KeyMatic'] } HM_IGNORE_DISCOVERY_NODE = [ @@ -464,7 +466,8 @@ def _system_callback_handler(hass, config, src, *args): ('cover', DISCOVER_COVER), ('binary_sensor', DISCOVER_BINARY_SENSORS), ('sensor', DISCOVER_SENSORS), - ('climate', DISCOVER_CLIMATE)): + ('climate', DISCOVER_CLIMATE), + ('lock', DISCOVER_LOCKS)): # Get all devices of a specific type found_devices = _get_devices( hass, discovery_type, addresses, interface) diff --git a/homeassistant/components/lock/__init__.py b/homeassistant/components/lock/__init__.py index d03bbebd696..b3e4ac8f0ff 100644 --- a/homeassistant/components/lock/__init__.py +++ b/homeassistant/components/lock/__init__.py @@ -18,7 +18,7 @@ from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa import homeassistant.helpers.config_validation as cv from homeassistant.const import ( ATTR_CODE, ATTR_CODE_FORMAT, ATTR_ENTITY_ID, STATE_LOCKED, STATE_UNLOCKED, - STATE_UNKNOWN, SERVICE_LOCK, SERVICE_UNLOCK) + STATE_UNKNOWN, SERVICE_LOCK, SERVICE_UNLOCK, SERVICE_OPEN) from homeassistant.components import group ATTR_CHANGED_BY = 'changed_by' @@ -39,6 +39,9 @@ LOCK_SERVICE_SCHEMA = vol.Schema({ vol.Optional(ATTR_CODE): cv.string, }) +# Bitfield of features supported by the lock entity +SUPPORT_OPEN = 1 + _LOGGER = logging.getLogger(__name__) PROP_TO_ATTR = { @@ -78,6 +81,18 @@ def unlock(hass, entity_id=None, code=None): hass.services.call(DOMAIN, SERVICE_UNLOCK, data) +@bind_hass +def open_lock(hass, entity_id=None, code=None): + """Open all or specified locks.""" + data = {} + if code: + data[ATTR_CODE] = code + if entity_id: + data[ATTR_ENTITY_ID] = entity_id + + hass.services.call(DOMAIN, SERVICE_OPEN, data) + + @asyncio.coroutine def async_setup(hass, config): """Track states and offer events for locks.""" @@ -97,6 +112,8 @@ def async_setup(hass, config): for entity in target_locks: if service.service == SERVICE_LOCK: yield from entity.async_lock(code=code) + elif service.service == SERVICE_OPEN: + yield from entity.async_open(code=code) else: yield from entity.async_unlock(code=code) @@ -113,6 +130,9 @@ def async_setup(hass, config): hass.services.async_register( DOMAIN, SERVICE_LOCK, async_handle_lock_service, schema=LOCK_SERVICE_SCHEMA) + hass.services.async_register( + DOMAIN, SERVICE_OPEN, async_handle_lock_service, + schema=LOCK_SERVICE_SCHEMA) return True @@ -158,6 +178,17 @@ class LockDevice(Entity): """ return self.hass.async_add_job(ft.partial(self.unlock, **kwargs)) + def open(self, **kwargs): + """Open the door latch.""" + raise NotImplementedError() + + def async_open(self, **kwargs): + """Open the door latch. + + This method must be run in the event loop and returns a coroutine. + """ + return self.hass.async_add_job(ft.partial(self.open, **kwargs)) + @property def state_attributes(self): """Return the state attributes.""" diff --git a/homeassistant/components/lock/demo.py b/homeassistant/components/lock/demo.py index aca25e7e16d..d561dd333ab 100644 --- a/homeassistant/components/lock/demo.py +++ b/homeassistant/components/lock/demo.py @@ -4,7 +4,7 @@ Demo lock platform that has two fake locks. For more details about this platform, please refer to the documentation https://home-assistant.io/components/demo/ """ -from homeassistant.components.lock import LockDevice +from homeassistant.components.lock import LockDevice, SUPPORT_OPEN from homeassistant.const import (STATE_LOCKED, STATE_UNLOCKED) @@ -13,17 +13,19 @@ def setup_platform(hass, config, add_devices, discovery_info=None): """Set up the Demo lock platform.""" add_devices([ DemoLock('Front Door', STATE_LOCKED), - DemoLock('Kitchen Door', STATE_UNLOCKED) + DemoLock('Kitchen Door', STATE_UNLOCKED), + DemoLock('Openable Lock', STATE_LOCKED, True) ]) class DemoLock(LockDevice): """Representation of a Demo lock.""" - def __init__(self, name, state): + def __init__(self, name, state, openable=False): """Initialize the lock.""" self._name = name self._state = state + self._openable = openable @property def should_poll(self): @@ -49,3 +51,14 @@ class DemoLock(LockDevice): """Unlock the device.""" self._state = STATE_UNLOCKED self.schedule_update_ha_state() + + def open(self, **kwargs): + """Open the door latch.""" + self._state = STATE_UNLOCKED + self.schedule_update_ha_state() + + @property + def supported_features(self): + """Flag supported features.""" + if self._openable: + return SUPPORT_OPEN diff --git a/homeassistant/components/lock/homematic.py b/homeassistant/components/lock/homematic.py new file mode 100644 index 00000000000..0d70849e37e --- /dev/null +++ b/homeassistant/components/lock/homematic.py @@ -0,0 +1,58 @@ +""" +Support for Homematic lock. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/lock.homematic/ +""" +import logging +from homeassistant.components.lock import LockDevice, SUPPORT_OPEN +from homeassistant.components.homematic import HMDevice, ATTR_DISCOVER_DEVICES +from homeassistant.const import STATE_UNKNOWN + + +_LOGGER = logging.getLogger(__name__) + +DEPENDENCIES = ['homematic'] + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Set up the Homematic lock platform.""" + if discovery_info is None: + return + + devices = [] + for conf in discovery_info[ATTR_DISCOVER_DEVICES]: + devices.append(HMLock(conf)) + + add_devices(devices) + + +class HMLock(HMDevice, LockDevice): + """Representation of a Homematic lock aka KeyMatic.""" + + @property + def is_locked(self): + """Return true if the lock is locked.""" + return not bool(self._hm_get_state()) + + def lock(self, **kwargs): + """Lock the lock.""" + self._hmdevice.lock() + + def unlock(self, **kwargs): + """Unlock the lock.""" + self._hmdevice.unlock() + + def open(self, **kwargs): + """Open the door latch.""" + self._hmdevice.open() + + def _init_data_struct(self): + """Generate the data dictionary (self._data) from metadata.""" + self._state = "STATE" + self._data.update({self._state: STATE_UNKNOWN}) + + @property + def supported_features(self): + """Flag supported features.""" + return SUPPORT_OPEN diff --git a/tests/components/lock/test_demo.py b/tests/components/lock/test_demo.py index 12007d2b8ad..1d774248f35 100644 --- a/tests/components/lock/test_demo.py +++ b/tests/components/lock/test_demo.py @@ -4,11 +4,10 @@ import unittest from homeassistant.setup import setup_component from homeassistant.components import lock -from tests.common import get_test_home_assistant - - +from tests.common import get_test_home_assistant, mock_service FRONT = 'lock.front_door' KITCHEN = 'lock.kitchen_door' +OPENABLE_LOCK = 'lock.openable_lock' class TestLockDemo(unittest.TestCase): @@ -48,3 +47,10 @@ class TestLockDemo(unittest.TestCase): self.hass.block_till_done() self.assertFalse(lock.is_locked(self.hass, FRONT)) + + def test_opening(self): + """Test the opening of a lock.""" + calls = mock_service(self.hass, lock.DOMAIN, lock.SERVICE_OPEN) + lock.open_lock(self.hass, OPENABLE_LOCK) + self.hass.block_till_done() + self.assertEqual(1, len(calls)) From a08293cff7facd2a204f5fd8e84aff0db9d43cd4 Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Sun, 25 Mar 2018 01:12:26 +0100 Subject: [PATCH 06/14] Log invalid templates in script delays (#13423) * Log invalid templates in script delays * Abort on error * Remove unused import --- homeassistant/helpers/script.py | 15 ++++++++++----- tests/helpers/test_script.py | 26 ++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/homeassistant/helpers/script.py b/homeassistant/helpers/script.py index abfdde8c8e1..f2ae36e7fd0 100644 --- a/homeassistant/helpers/script.py +++ b/homeassistant/helpers/script.py @@ -97,11 +97,16 @@ class Script(): delay = action[CONF_DELAY] - if isinstance(delay, template.Template): - delay = vol.All( - cv.time_period, - cv.positive_timedelta)( - delay.async_render(variables)) + try: + if isinstance(delay, template.Template): + delay = vol.All( + cv.time_period, + cv.positive_timedelta)( + delay.async_render(variables)) + except (TemplateError, vol.Invalid) as ex: + _LOGGER.error("Error rendering '%s' delay template: %s", + self.name, ex) + break unsub = async_track_point_in_utc_time( self.hass, async_script_delay, diff --git a/tests/helpers/test_script.py b/tests/helpers/test_script.py index a8ae20ad69b..4297ca26e7d 100644 --- a/tests/helpers/test_script.py +++ b/tests/helpers/test_script.py @@ -218,6 +218,32 @@ class TestScriptHelper(unittest.TestCase): assert not script_obj.is_running assert len(events) == 2 + def test_delay_invalid_template(self): + """Test the delay as a template that fails.""" + event = 'test_event' + events = [] + + @callback + def record_event(event): + """Add recorded event to set.""" + events.append(event) + + self.hass.bus.listen(event, record_event) + + script_obj = script.Script(self.hass, cv.SCRIPT_SCHEMA([ + {'event': event}, + {'delay': '{{ invalid_delay }}'}, + {'delay': {'seconds': 5}}, + {'event': event}])) + + with mock.patch.object(script, '_LOGGER') as mock_logger: + script_obj.run() + self.hass.block_till_done() + assert mock_logger.error.called + + assert not script_obj.is_running + assert len(events) == 1 + def test_cancel_while_delay(self): """Test the cancelling while the delay is present.""" event = 'test_event' From 444805df103619f9aa52591a11ff1d4fb6e224b0 Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Sun, 25 Mar 2018 09:47:10 +0200 Subject: [PATCH 07/14] LimitlessLED hs_color fixes (#13425) --- homeassistant/components/light/limitlessled.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/light/limitlessled.py b/homeassistant/components/light/limitlessled.py index 5a6a0a34959..bb84b3a6fed 100644 --- a/homeassistant/components/light/limitlessled.py +++ b/homeassistant/components/light/limitlessled.py @@ -197,7 +197,7 @@ class LimitlessLEDGroup(Light): self._is_on = (last_state.state == STATE_ON) self._brightness = last_state.attributes.get('brightness') self._temperature = last_state.attributes.get('color_temp') - self._color = last_state.attributes.get('rgb_color') + self._color = last_state.attributes.get('hs_color') @property def should_poll(self): @@ -336,4 +336,4 @@ class LimitlessLEDGroup(Light): def limitlessled_color(self): """Convert Home Assistant HS list to RGB Color tuple.""" from limitlessled import Color - return Color(color_hs_to_RGB(*tuple(self._color))) + return Color(*color_hs_to_RGB(*tuple(self._color))) From 24d299e266833a2d22369500bb41d91c058f333d Mon Sep 17 00:00:00 2001 From: a-andre Date: Mon, 26 Mar 2018 03:07:26 +0200 Subject: [PATCH 08/14] Hyperion: fix typo (#13429) --- homeassistant/components/light/hyperion.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/light/hyperion.py b/homeassistant/components/light/hyperion.py index e5a4bd18115..8ba2329af7e 100644 --- a/homeassistant/components/light/hyperion.py +++ b/homeassistant/components/light/hyperion.py @@ -215,7 +215,7 @@ class Hyperion(Light): pass led_color = response['info']['activeLedColor'] - if not led_color or led_color[0]['RGB value'] == [0, 0, 0]: + if not led_color or led_color[0]['RGB Value'] == [0, 0, 0]: # Get the active effect if response['info'].get('activeEffects'): self._rgb_color = [175, 0, 255] @@ -234,8 +234,7 @@ class Hyperion(Light): self._effect = None else: # Get the RGB color - self._rgb_color =\ - response['info']['activeLedColor'][0]['RGB Value'] + self._rgb_color = led_color[0]['RGB Value'] self._brightness = max(self._rgb_color) self._rgb_mem = [int(round(float(x)*255/self._brightness)) for x in self._rgb_color] From 60f6109cbfdfb180a853b8dd6cadc6c7503d8c9e Mon Sep 17 00:00:00 2001 From: cdce8p <30130371+cdce8p@users.noreply.github.com> Date: Sun, 25 Mar 2018 12:53:15 +0200 Subject: [PATCH 09/14] HomeKit: Bugfix & improved logging (#13431) * Bugfix & improved logging * Removed logging statements * Removed logging test --- homeassistant/components/homekit/__init__.py | 4 ---- homeassistant/components/homekit/type_covers.py | 1 + homeassistant/components/homekit/type_lights.py | 4 ++++ .../components/homekit/type_security_systems.py | 1 + homeassistant/components/homekit/type_switches.py | 1 + homeassistant/components/homekit/type_thermostats.py | 4 ++++ tests/components/homekit/test_get_accessories.py | 11 ----------- 7 files changed, 11 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/homekit/__init__.py b/homeassistant/components/homekit/__init__.py index 02449607bf2..4854a828e41 100644 --- a/homeassistant/components/homekit/__init__.py +++ b/homeassistant/components/homekit/__init__.py @@ -73,8 +73,6 @@ async def async_setup(hass, config): def get_accessory(hass, state, aid, config): """Take state and return an accessory object if supported.""" - _LOGGER.debug('', - state.entity_id, aid, config) if not aid: _LOGGER.warning('The entitiy "%s" is not supported, since it ' 'generates an invalid aid, please change it.', @@ -129,8 +127,6 @@ def get_accessory(hass, state, aid, config): _LOGGER.debug('Add "%s" as "%s"', state.entity_id, 'Switch') return TYPES['Switch'](hass, state.entity_id, state.name, aid=aid) - _LOGGER.warning('The entity "%s" is not supported yet', - state.entity_id) return None diff --git a/homeassistant/components/homekit/type_covers.py b/homeassistant/components/homekit/type_covers.py index 36cfa4d635a..7616ef05fdf 100644 --- a/homeassistant/components/homekit/type_covers.py +++ b/homeassistant/components/homekit/type_covers.py @@ -46,6 +46,7 @@ class WindowCovering(HomeAccessory): def move_cover(self, value): """Move cover to value if call came from HomeKit.""" + self.char_target_position.set_value(value, should_callback=False) if value != self.current_position: _LOGGER.debug('%s: Set position to %d', self._entity_id, value) self.homekit_target = value diff --git a/homeassistant/components/homekit/type_lights.py b/homeassistant/components/homekit/type_lights.py index c723fcc08a6..2415bb1a4df 100644 --- a/homeassistant/components/homekit/type_lights.py +++ b/homeassistant/components/homekit/type_lights.py @@ -71,6 +71,7 @@ class Light(HomeAccessory): _LOGGER.debug('%s: Set state to %d', self._entity_id, value) self._flag[CHAR_ON] = True + self.char_on.set_value(value, should_callback=False) if value == 1: self._hass.components.light.turn_on(self._entity_id) @@ -81,6 +82,7 @@ class Light(HomeAccessory): """Set brightness if call came from HomeKit.""" _LOGGER.debug('%s: Set brightness to %d', self._entity_id, value) self._flag[CHAR_BRIGHTNESS] = True + self.char_brightness.set_value(value, should_callback=False) self._hass.components.light.turn_on( self._entity_id, brightness_pct=value) @@ -88,6 +90,7 @@ class Light(HomeAccessory): """Set saturation if call came from HomeKit.""" _LOGGER.debug('%s: Set saturation to %d', self._entity_id, value) self._flag[CHAR_SATURATION] = True + self.char_saturation.set_value(value, should_callback=False) self._saturation = value self.set_color() @@ -95,6 +98,7 @@ class Light(HomeAccessory): """Set hue if call came from HomeKit.""" _LOGGER.debug('%s: Set hue to %d', self._entity_id, value) self._flag[CHAR_HUE] = True + self.char_hue.set_value(value, should_callback=False) self._hue = value self.set_color() diff --git a/homeassistant/components/homekit/type_security_systems.py b/homeassistant/components/homekit/type_security_systems.py index 1d47160f9d2..146fca95b53 100644 --- a/homeassistant/components/homekit/type_security_systems.py +++ b/homeassistant/components/homekit/type_security_systems.py @@ -54,6 +54,7 @@ class SecuritySystem(HomeAccessory): _LOGGER.debug('%s: Set security state to %d', self._entity_id, value) self.flag_target_state = True + self.char_target_state.set_value(value, should_callback=False) hass_value = HOMEKIT_TO_HASS[value] service = STATE_TO_SERVICE[hass_value] diff --git a/homeassistant/components/homekit/type_switches.py b/homeassistant/components/homekit/type_switches.py index fd3291ffe23..1f19893d0be 100644 --- a/homeassistant/components/homekit/type_switches.py +++ b/homeassistant/components/homekit/type_switches.py @@ -36,6 +36,7 @@ class Switch(HomeAccessory): _LOGGER.debug('%s: Set switch state to %s', self._entity_id, value) self.flag_target_state = True + self.char_on.set_value(value, should_callback=False) service = SERVICE_TURN_ON if value else SERVICE_TURN_OFF self._hass.services.call(self._domain, service, {ATTR_ENTITY_ID: self._entity_id}) diff --git a/homeassistant/components/homekit/type_thermostats.py b/homeassistant/components/homekit/type_thermostats.py index b73b492ba74..3f545e90eb3 100644 --- a/homeassistant/components/homekit/type_thermostats.py +++ b/homeassistant/components/homekit/type_thermostats.py @@ -97,6 +97,7 @@ class Thermostat(HomeAccessory): def set_heat_cool(self, value): """Move operation mode to value if call came from HomeKit.""" + self.char_target_heat_cool.set_value(value, should_callback=False) if value in HC_HOMEKIT_TO_HASS: _LOGGER.debug('%s: Set heat-cool to %d', self._entity_id, value) self.heat_cool_flag_target_state = True @@ -109,6 +110,7 @@ class Thermostat(HomeAccessory): _LOGGER.debug('%s: Set cooling threshold temperature to %.2f', self._entity_id, value) self.coolingthresh_flag_target_state = True + self.char_cooling_thresh_temp.set_value(value, should_callback=False) low = self.char_heating_thresh_temp.value self._hass.components.climate.set_temperature( entity_id=self._entity_id, target_temp_high=value, @@ -119,6 +121,7 @@ class Thermostat(HomeAccessory): _LOGGER.debug('%s: Set heating threshold temperature to %.2f', self._entity_id, value) self.heatingthresh_flag_target_state = True + self.char_heating_thresh_temp.set_value(value, should_callback=False) # Home assistant always wants to set low and high at the same time high = self.char_cooling_thresh_temp.value self._hass.components.climate.set_temperature( @@ -130,6 +133,7 @@ class Thermostat(HomeAccessory): _LOGGER.debug('%s: Set target temperature to %.2f', self._entity_id, value) self.temperature_flag_target_state = True + self.char_target_temp.set_value(value, should_callback=False) self._hass.components.climate.set_temperature( temperature=value, entity_id=self._entity_id) diff --git a/tests/components/homekit/test_get_accessories.py b/tests/components/homekit/test_get_accessories.py index e6dbe1ff729..ee7baae2755 100644 --- a/tests/components/homekit/test_get_accessories.py +++ b/tests/components/homekit/test_get_accessories.py @@ -16,17 +16,6 @@ _LOGGER = logging.getLogger(__name__) CONFIG = {} -def test_get_accessory_invalid(caplog): - """Test with unsupported component.""" - assert get_accessory(None, State('test.unsupported', 'on'), 2, None) \ - is None - assert caplog.records[1].levelname == 'WARNING' - - assert get_accessory(None, State('test.test', 'on'), None, None) \ - is None - assert caplog.records[3].levelname == 'WARNING' - - class TestGetAccessories(unittest.TestCase): """Methods to test the get_accessory method.""" From 22cefc7e640bee867296a98c9098470f52aa052b Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Sun, 25 Mar 2018 12:51:11 +0200 Subject: [PATCH 10/14] Improve detection of entity names in templates (#13432) * Improve detection of entity names in templates * Only test variables --- homeassistant/helpers/template.py | 5 +++-- tests/helpers/test_template.py | 6 ++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index 3dd65aa362c..28ab4e9bfa0 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -13,7 +13,7 @@ from jinja2.sandbox import ImmutableSandboxedEnvironment from homeassistant.const import ( ATTR_LATITUDE, ATTR_LONGITUDE, ATTR_UNIT_OF_MEASUREMENT, MATCH_ALL, STATE_UNKNOWN) -from homeassistant.core import State +from homeassistant.core import State, valid_entity_id from homeassistant.exceptions import TemplateError from homeassistant.helpers import location as loc_helper from homeassistant.loader import bind_hass, get_component @@ -73,7 +73,8 @@ def extract_entities(template, variables=None): extraction_final.append(result[0]) if variables and result[1] in variables and \ - isinstance(variables[result[1]], str): + isinstance(variables[result[1]], str) and \ + valid_entity_id(variables[result[1]]): extraction_final.append(variables[result[1]]) if extraction_final: diff --git a/tests/helpers/test_template.py b/tests/helpers/test_template.py index 47e46bae3c7..def06ea9284 100644 --- a/tests/helpers/test_template.py +++ b/tests/helpers/test_template.py @@ -836,6 +836,12 @@ is_state_attr('device_tracker.phone_2', 'battery', 40) "{{ is_state(trigger.entity_id, 'off') }}", {'trigger': {'entity_id': 'input_boolean.switch'}})) + self.assertEqual( + MATCH_ALL, + template.extract_entities( + "{{ is_state('media_player.' ~ where , 'playing') }}", + {'where': 'livingroom'})) + @asyncio.coroutine def test_state_with_unit(hass): From 93b9ec0b0f904b0f20b5c1b8b922e68806196de9 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 25 Mar 2018 18:04:20 -0700 Subject: [PATCH 11/14] Add version bump script (#13447) * Add version bump script * Lint --- script/version_bump.py | 135 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100755 script/version_bump.py diff --git a/script/version_bump.py b/script/version_bump.py new file mode 100755 index 00000000000..0cd02ddbfcb --- /dev/null +++ b/script/version_bump.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +"""Helper script to bump the current version.""" +import argparse +import re + +from homeassistant import const + + +PARSE_PATCH = r'(?P\d+)(\.(?P\D+)(?P\d+))?' + + +def format_patch(patch_parts): + """Format the patch parts back into a patch string.""" + return '{patch}.{prerel}{prerelversion}'.format(**patch_parts) + + +def bump_version(cur_major, cur_minor, cur_patch, bump_type): + """Return a new version given a current version and action.""" + patch_parts = re.match(PARSE_PATCH, cur_patch).groupdict() + patch_parts['patch'] = int(patch_parts['patch']) + if patch_parts['prerelversion'] is not None: + patch_parts['prerelversion'] = int(patch_parts['prerelversion']) + + if bump_type == 'release_patch': + # Convert 0.67.3 to 0.67.4 + # Convert 0.67.3.beta5 to 0.67.3 + # Convert 0.67.3.dev0 to 0.67.3 + new_major = cur_major + new_minor = cur_minor + + if patch_parts['prerel'] is None: + new_patch = str(patch_parts['patch'] + 1) + else: + new_patch = str(patch_parts['patch']) + + elif bump_type == 'dev': + # Convert 0.67.3 to 0.67.4.dev0 + # Convert 0.67.3.beta5 to 0.67.4.dev0 + # Convert 0.67.3.dev0 to 0.67.3.dev1 + new_major = cur_major + + if patch_parts['prerel'] == 'dev': + new_minor = cur_minor + patch_parts['prerelversion'] += 1 + new_patch = format_patch(patch_parts) + else: + new_minor = cur_minor + 1 + new_patch = '0.dev0' + + elif bump_type == 'beta': + # Convert 0.67.5 to 0.67.8.beta0 + # Convert 0.67.0.dev0 to 0.67.0.beta0 + # Convert 0.67.5.beta4 to 0.67.5.beta5 + new_major = cur_major + new_minor = cur_minor + + if patch_parts['prerel'] is None: + patch_parts['patch'] += 1 + patch_parts['prerel'] = 'beta' + patch_parts['prerelversion'] = 0 + + elif patch_parts['prerel'] == 'beta': + patch_parts['prerelversion'] += 1 + + elif patch_parts['prerel'] == 'dev': + patch_parts['prerel'] = 'beta' + patch_parts['prerelversion'] = 0 + + else: + raise Exception('Can only bump from beta or no prerel version') + + new_patch = format_patch(patch_parts) + + return new_major, new_minor, new_patch + + +def write_version(major, minor, patch): + """Update Home Assistant constant file with new version.""" + with open('homeassistant/const.py') as fil: + content = fil.read() + + content = re.sub('MAJOR_VERSION = .*\n', + 'MAJOR_VERSION = {}\n'.format(major), + content) + content = re.sub('MINOR_VERSION = .*\n', + 'MINOR_VERSION = {}\n'.format(minor), + content) + content = re.sub('PATCH_VERSION = .*\n', + "PATCH_VERSION = '{}'\n".format(patch), + content) + + with open('homeassistant/const.py', 'wt') as fil: + content = fil.write(content) + + +def main(): + """Execute script.""" + parser = argparse.ArgumentParser( + description="Bump version of Home Assistant") + parser.add_argument( + 'type', + help="The type of the bump the version to.", + choices=['beta', 'dev', 'release_patch'], + ) + arguments = parser.parse_args() + write_version(*bump_version(const.MAJOR_VERSION, const.MINOR_VERSION, + const.PATCH_VERSION, arguments.type)) + + +def test_bump_version(): + """Make sure it all works.""" + assert bump_version(0, 56, '0', 'beta') == \ + (0, 56, '1.beta0') + assert bump_version(0, 56, '0.beta3', 'beta') == \ + (0, 56, '0.beta4') + assert bump_version(0, 56, '0.dev0', 'beta') == \ + (0, 56, '0.beta0') + + assert bump_version(0, 56, '3', 'dev') == \ + (0, 57, '0.dev0') + assert bump_version(0, 56, '0.beta3', 'dev') == \ + (0, 57, '0.dev0') + assert bump_version(0, 56, '0.dev0', 'dev') == \ + (0, 56, '0.dev1') + + assert bump_version(0, 56, '3', 'release_patch') == \ + (0, 56, '4') + assert bump_version(0, 56, '3.beta3', 'release_patch') == \ + (0, 56, '3') + assert bump_version(0, 56, '0.dev0', 'release_patch') == \ + (0, 56, '0') + + +if __name__ == '__main__': + main() From 38d2702e3c05d7df42f87c998b8b819e6a1e3c40 Mon Sep 17 00:00:00 2001 From: Cedric Van Goethem Date: Mon, 26 Mar 2018 02:03:23 +0100 Subject: [PATCH 12/14] Add extra check for ESSID field in case there's a wired connection (#13459) --- homeassistant/components/device_tracker/unifi.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/device_tracker/unifi.py b/homeassistant/components/device_tracker/unifi.py index 8663930c4e6..d8a52aaaeb4 100644 --- a/homeassistant/components/device_tracker/unifi.py +++ b/homeassistant/components/device_tracker/unifi.py @@ -98,7 +98,8 @@ class UnifiScanner(DeviceScanner): # Filter clients to provided SSID list if self._ssid_filter: clients = [client for client in clients - if client['essid'] in self._ssid_filter] + if 'essid' in client and + client['essid'] in self._ssid_filter] self._clients = { client['mac']: client From 068b037944ab3ee918430e0607294cadaf521b1f Mon Sep 17 00:00:00 2001 From: Beat <508289+bdurrer@users.noreply.github.com> Date: Mon, 26 Mar 2018 03:02:21 +0200 Subject: [PATCH 13/14] Fix encoding errors in mikrotik device tracker (#13464) --- homeassistant/components/device_tracker/mikrotik.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/device_tracker/mikrotik.py b/homeassistant/components/device_tracker/mikrotik.py index 1d9161c0d45..154fc3d2a63 100644 --- a/homeassistant/components/device_tracker/mikrotik.py +++ b/homeassistant/components/device_tracker/mikrotik.py @@ -73,7 +73,8 @@ class MikrotikScanner(DeviceScanner): self.host, self.username, self.password, - port=int(self.port) + port=int(self.port), + encoding='utf-8' ) try: From a507ed0af8aaa4b6a464cdb9b138f4a8173c404b Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 25 Mar 2018 18:24:16 -0700 Subject: [PATCH 14/14] Version bump to 0.66.0.beta1 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index b0be1933ffe..382323ed534 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 66 -PATCH_VERSION = '0.beta0' +PATCH_VERSION = '0.beta1' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3)