From 0fdf1391e210f4e7aefb2b0e79ac6a0a549fb35a Mon Sep 17 00:00:00 2001 From: Erik Eriksson Date: Thu, 9 Feb 2017 18:00:18 +0100 Subject: [PATCH 01/18] Don't thow exception if connection to server is lost (#5775) --- homeassistant/components/sensor/tellduslive.py | 4 +++- homeassistant/components/tellduslive.py | 2 +- requirements_all.txt | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/sensor/tellduslive.py b/homeassistant/components/sensor/tellduslive.py index b7f3cf60892..9bebbe6e3dc 100644 --- a/homeassistant/components/sensor/tellduslive.py +++ b/homeassistant/components/sensor/tellduslive.py @@ -85,7 +85,9 @@ class TelldusLiveSensor(TelldusLiveEntity): @property def state(self): """Return the state of the sensor.""" - if self._type == SENSOR_TYPE_TEMP: + if not self.available: + return None + elif self._type == SENSOR_TYPE_TEMP: return self._value_as_temperature elif self._type == SENSOR_TYPE_HUMIDITY: return self._value_as_humidity diff --git a/homeassistant/components/tellduslive.py b/homeassistant/components/tellduslive.py index 259d4e1becb..84ab96841d9 100644 --- a/homeassistant/components/tellduslive.py +++ b/homeassistant/components/tellduslive.py @@ -17,7 +17,7 @@ import voluptuous as vol DOMAIN = 'tellduslive' -REQUIREMENTS = ['tellduslive==0.3.0'] +REQUIREMENTS = ['tellduslive==0.3.2'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 12aaaab9666..21c5538c76b 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -645,7 +645,7 @@ steamodd==4.21 tellcore-py==1.1.2 # homeassistant.components.tellduslive -tellduslive==0.3.0 +tellduslive==0.3.2 # homeassistant.components.sensor.temper temperusb==1.5.1 From 60f85b1e094f0a54675abe519ba6ebd3ef5686ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre=20St=C3=A5hl?= Date: Thu, 9 Feb 2017 22:25:06 +0100 Subject: [PATCH 02/18] Handle connection errors when connecting to Apple TVs (#5829) * Handle connection errors when connecting to Apple TVs Also bump pyatv to 0.1.2 which fixes a request leak. * Fix pylint error * Fix import order --- homeassistant/components/media_player/apple_tv.py | 5 ++++- requirements_all.txt | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/media_player/apple_tv.py b/homeassistant/components/media_player/apple_tv.py index 19700e2f8d7..a4632c5a12d 100644 --- a/homeassistant/components/media_player/apple_tv.py +++ b/homeassistant/components/media_player/apple_tv.py @@ -8,6 +8,7 @@ import asyncio import logging import hashlib +import aiohttp import voluptuous as vol from homeassistant.components.media_player import ( @@ -21,7 +22,7 @@ import homeassistant.helpers.config_validation as cv import homeassistant.util.dt as dt_util -REQUIREMENTS = ['pyatv==0.1.1'] +REQUIREMENTS = ['pyatv==0.1.2'] _LOGGER = logging.getLogger(__name__) @@ -128,6 +129,8 @@ class AppleTvDevice(MediaPlayerDevice): self._playing = playing except exceptions.AuthenticationError as ex: _LOGGER.warning('%s (bad login id?)', str(ex)) + except aiohttp.errors.ClientOSError as ex: + _LOGGER.error('failed to connect to Apple TV (%s)', str(ex)) except asyncio.TimeoutError: _LOGGER.warning('timed out while connecting to Apple TV') diff --git a/requirements_all.txt b/requirements_all.txt index 21c5538c76b..e67826fa1f6 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -418,7 +418,7 @@ pyasn1-modules==0.0.8 pyasn1==0.2.2 # homeassistant.components.media_player.apple_tv -pyatv==0.1.1 +pyatv==0.1.2 # homeassistant.components.device_tracker.bbox # homeassistant.components.sensor.bbox From 7259082de59dcec83ab08b9487e26c2324c158bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre=20St=C3=A5hl?= Date: Thu, 9 Feb 2017 23:07:46 +0100 Subject: [PATCH 03/18] Reuse default aiohttp session (#5836) --- homeassistant/components/media_player/apple_tv.py | 6 ++++-- requirements_all.txt | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/media_player/apple_tv.py b/homeassistant/components/media_player/apple_tv.py index a4632c5a12d..2f47b1ba0a8 100644 --- a/homeassistant/components/media_player/apple_tv.py +++ b/homeassistant/components/media_player/apple_tv.py @@ -18,11 +18,12 @@ from homeassistant.components.media_player import ( from homeassistant.const import ( STATE_IDLE, STATE_PAUSED, STATE_PLAYING, STATE_STANDBY, CONF_HOST, CONF_NAME, EVENT_HOMEASSISTANT_STOP) +from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv import homeassistant.util.dt as dt_util -REQUIREMENTS = ['pyatv==0.1.2'] +REQUIREMENTS = ['pyatv==0.1.3'] _LOGGER = logging.getLogger(__name__) @@ -62,7 +63,8 @@ def async_setup_platform(hass, config, async_add_entities, hass.data[DATA_APPLE_TV].append(host) details = pyatv.AppleTVDevice(name, host, login_id) - atv = pyatv.connect_to_apple_tv(details, hass.loop) + session = async_get_clientsession(hass) + atv = pyatv.connect_to_apple_tv(details, hass.loop, session=session) entity = AppleTvDevice(atv, name) @asyncio.coroutine diff --git a/requirements_all.txt b/requirements_all.txt index e67826fa1f6..605bc2b0de9 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -418,7 +418,7 @@ pyasn1-modules==0.0.8 pyasn1==0.2.2 # homeassistant.components.media_player.apple_tv -pyatv==0.1.2 +pyatv==0.1.3 # homeassistant.components.device_tracker.bbox # homeassistant.components.sensor.bbox From 3f87d28616de97acb09c35a73af1f547969ebbe3 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 10 Feb 2017 02:31:20 +0100 Subject: [PATCH 04/18] Update aiohttp 1.3.1 (#5838) --- requirements_all.txt | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements_all.txt b/requirements_all.txt index 605bc2b0de9..07943439eb6 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -6,7 +6,7 @@ pip>=7.0.0 jinja2>=2.8 voluptuous==0.9.3 typing>=3,<4 -aiohttp==1.3 +aiohttp==1.3.1 async_timeout==1.1.0 # homeassistant.components.nuimo_controller diff --git a/setup.py b/setup.py index b9d5d3460fb..b6b679315e1 100755 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ REQUIRES = [ 'jinja2>=2.8', 'voluptuous==0.9.3', 'typing>=3,<4', - 'aiohttp==1.3', + 'aiohttp==1.3.1', 'async_timeout==1.1.0', ] From 7bf7c727d1582fb82ac726a41fbd04d587a2fd5c Mon Sep 17 00:00:00 2001 From: Philipp Schmitt Date: Fri, 10 Feb 2017 02:57:19 +0100 Subject: [PATCH 05/18] Refactoring and JSON decode error handling (#5826) * Refactoring and JSON decode error handling * Catch ValueError instead of simplejson.scanner.JSONDecodeError --- homeassistant/components/zoneminder.py | 43 ++++++++++++-------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/homeassistant/components/zoneminder.py b/homeassistant/components/zoneminder.py index 4920a5a6ce2..0fbe807fb2a 100644 --- a/homeassistant/components/zoneminder.py +++ b/homeassistant/components/zoneminder.py @@ -5,7 +5,6 @@ For more details about this component, please refer to the documentation at https://home-assistant.io/components/zoneminder/ """ import logging -import json from urllib.parse import urljoin import requests @@ -79,46 +78,44 @@ def login(): ZM['url'] + 'api/host/getVersion.json', cookies=ZM['cookies'], timeout=DEFAULT_TIMEOUT) - if req.status_code != requests.codes.ok: + if not req.ok: _LOGGER.error("Connection error logging into ZoneMinder") return False return True -# pylint: disable=no-member -def get_state(api_url): - """Get a state from the ZoneMinder API service.""" +def _zm_request(method, api_url, data=None): + """Perform a Zoneminder request.""" # Since the API uses sessions that expire, sometimes we need to re-auth # if the call fails. for _ in range(LOGIN_RETRIES): - req = requests.get(urljoin(ZM['url'], api_url), cookies=ZM['cookies'], - timeout=DEFAULT_TIMEOUT) + req = requests.request( + method, urljoin(ZM['url'], api_url), data=data, + cookies=ZM['cookies'], timeout=DEFAULT_TIMEOUT) - if req.status_code != requests.codes.ok: + if not req.ok: login() else: break + else: _LOGGER.exception("Unable to get API response from ZoneMinder") - return json.loads(req.text) + try: + return req.json() + except ValueError: + _LOGGER.exception('JSON decode exception caught while attempting to ' + 'decode "%s"', req.text) + + +# pylint: disable=no-member +def get_state(api_url): + """Get a state from the ZoneMinder API service.""" + return _zm_request('get', api_url) # pylint: disable=no-member def change_state(api_url, post_data): """Update a state using the Zoneminder API.""" - for _ in range(LOGIN_RETRIES): - req = requests.post( - urljoin(ZM['url'], api_url), data=post_data, cookies=ZM['cookies'], - timeout=DEFAULT_TIMEOUT) - - if req.status_code != requests.codes.ok: - login() - else: - break - - else: - _LOGGER.exception("Unable to get API response from ZoneMinder") - - return json.loads(req.text) + return _zm_request('post', api_url, data=post_data) From 3d7b79f523d0c2d040adfe2378ba79958045b4fc Mon Sep 17 00:00:00 2001 From: Adam Mills Date: Thu, 9 Feb 2017 21:17:17 -0500 Subject: [PATCH 06/18] [recorder] Add tests for full schema migration (#5831) * [recorder] Add tests for full schema migration * Remove leftover code * Fix duplicate creation of sqlalchemy Index object * It's that kind of day... * Improve models_original docstring --- homeassistant/components/recorder/__init__.py | 7 +- tests/components/recorder/models_original.py | 163 ++++++++++++++++++ tests/components/recorder/test_init.py | 58 ++++++- 3 files changed, 216 insertions(+), 12 deletions(-) create mode 100644 tests/components/recorder/models_original.py diff --git a/homeassistant/components/recorder/__init__.py b/homeassistant/components/recorder/__init__.py index 14b9fe11574..8d3213a4805 100644 --- a/homeassistant/components/recorder/__init__.py +++ b/homeassistant/components/recorder/__init__.py @@ -345,15 +345,16 @@ class Recorder(threading.Thread): def _apply_update(self, new_version): """Perform operations to bring schema up to date.""" - from sqlalchemy import Index, Table + from sqlalchemy import Table import homeassistant.components.recorder.models as models if new_version == 1: def create_index(table_name, column_name): """Create an index for the specified table and column.""" table = Table(table_name, models.Base.metadata) - index_name = "_".join(("ix", table_name, column_name)) - index = Index(index_name, getattr(table.c, column_name)) + name = "_".join(("ix", table_name, column_name)) + # Look up the index object that was created from the models + index = next(idx for idx in table.indexes if idx.name == name) _LOGGER.debug("Creating index for table %s column %s", table_name, column_name) index.create(self.engine) diff --git a/tests/components/recorder/models_original.py b/tests/components/recorder/models_original.py new file mode 100644 index 00000000000..31ec5ee7ed7 --- /dev/null +++ b/tests/components/recorder/models_original.py @@ -0,0 +1,163 @@ +"""Models for SQLAlchemy. + +This file contains the original models definitions before schema tracking was +implemented. It is used to test the schema migration logic. +""" + +import json +from datetime import datetime +import logging + +from sqlalchemy import (Boolean, Column, DateTime, ForeignKey, Index, Integer, + String, Text, distinct) +from sqlalchemy.ext.declarative import declarative_base + +import homeassistant.util.dt as dt_util +from homeassistant.core import Event, EventOrigin, State, split_entity_id +from homeassistant.remote import JSONEncoder + +# SQLAlchemy Schema +# pylint: disable=invalid-name +Base = declarative_base() + +_LOGGER = logging.getLogger(__name__) + + +class Events(Base): # type: ignore + """Event history data.""" + + __tablename__ = 'events' + event_id = Column(Integer, primary_key=True) + event_type = Column(String(32), index=True) + event_data = Column(Text) + origin = Column(String(32)) + time_fired = Column(DateTime(timezone=True)) + created = Column(DateTime(timezone=True), default=datetime.utcnow) + + @staticmethod + def from_event(event): + """Create an event database object from a native event.""" + return Events(event_type=event.event_type, + event_data=json.dumps(event.data, cls=JSONEncoder), + origin=str(event.origin), + time_fired=event.time_fired) + + def to_native(self): + """Convert to a natve HA Event.""" + try: + return Event( + self.event_type, + json.loads(self.event_data), + EventOrigin(self.origin), + _process_timestamp(self.time_fired) + ) + except ValueError: + # When json.loads fails + _LOGGER.exception("Error converting to event: %s", self) + return None + + +class States(Base): # type: ignore + """State change history.""" + + __tablename__ = 'states' + state_id = Column(Integer, primary_key=True) + domain = Column(String(64)) + entity_id = Column(String(255)) + state = Column(String(255)) + attributes = Column(Text) + event_id = Column(Integer, ForeignKey('events.event_id')) + last_changed = Column(DateTime(timezone=True), default=datetime.utcnow) + last_updated = Column(DateTime(timezone=True), default=datetime.utcnow) + created = Column(DateTime(timezone=True), default=datetime.utcnow) + + __table_args__ = (Index('states__state_changes', + 'last_changed', 'last_updated', 'entity_id'), + Index('states__significant_changes', + 'domain', 'last_updated', 'entity_id'), ) + + @staticmethod + def from_event(event): + """Create object from a state_changed event.""" + entity_id = event.data['entity_id'] + state = event.data.get('new_state') + + dbstate = States(entity_id=entity_id) + + # State got deleted + if state is None: + dbstate.state = '' + dbstate.domain = split_entity_id(entity_id)[0] + dbstate.attributes = '{}' + dbstate.last_changed = event.time_fired + dbstate.last_updated = event.time_fired + else: + dbstate.domain = state.domain + dbstate.state = state.state + dbstate.attributes = json.dumps(dict(state.attributes), + cls=JSONEncoder) + dbstate.last_changed = state.last_changed + dbstate.last_updated = state.last_updated + + return dbstate + + def to_native(self): + """Convert to an HA state object.""" + try: + return State( + self.entity_id, self.state, + json.loads(self.attributes), + _process_timestamp(self.last_changed), + _process_timestamp(self.last_updated) + ) + except ValueError: + # When json.loads fails + _LOGGER.exception("Error converting row to state: %s", self) + return None + + +class RecorderRuns(Base): # type: ignore + """Representation of recorder run.""" + + __tablename__ = 'recorder_runs' + run_id = Column(Integer, primary_key=True) + start = Column(DateTime(timezone=True), default=datetime.utcnow) + end = Column(DateTime(timezone=True)) + closed_incorrect = Column(Boolean, default=False) + created = Column(DateTime(timezone=True), default=datetime.utcnow) + + def entity_ids(self, point_in_time=None): + """Return the entity ids that existed in this run. + + Specify point_in_time if you want to know which existed at that point + in time inside the run. + """ + from sqlalchemy.orm.session import Session + + session = Session.object_session(self) + + assert session is not None, 'RecorderRuns need to be persisted' + + query = session.query(distinct(States.entity_id)).filter( + States.last_updated >= self.start) + + if point_in_time is not None: + query = query.filter(States.last_updated < point_in_time) + elif self.end is not None: + query = query.filter(States.last_updated < self.end) + + return [row[0] for row in query] + + def to_native(self): + """Return self, native format is this model.""" + return self + + +def _process_timestamp(ts): + """Process a timestamp into datetime object.""" + if ts is None: + return None + elif ts.tzinfo is None: + return dt_util.UTC.localize(ts) + else: + return dt_util.as_utc(ts) diff --git a/tests/components/recorder/test_init.py b/tests/components/recorder/test_init.py index ce395044d11..0bfa3a20997 100644 --- a/tests/components/recorder/test_init.py +++ b/tests/components/recorder/test_init.py @@ -6,15 +6,18 @@ import unittest from unittest.mock import patch, call, MagicMock import pytest +from sqlalchemy import create_engine + from homeassistant.core import callback from homeassistant.const import MATCH_ALL from homeassistant.components import recorder from homeassistant.bootstrap import setup_component from tests.common import get_test_home_assistant +from tests.components.recorder import models_original -class TestRecorder(unittest.TestCase): - """Test the recorder module.""" +class BaseTestRecorder(unittest.TestCase): + """Base class for common recorder tests.""" def setUp(self): # pylint: disable=invalid-name """Setup things to be run when tests are started.""" @@ -87,6 +90,10 @@ class TestRecorder(unittest.TestCase): time_fired=timestamp, )) + +class TestRecorder(BaseTestRecorder): + """Test the recorder module.""" + def test_saving_state(self): """Test saving and restoring a state.""" entity_id = 'test.recorder' @@ -205,15 +212,48 @@ class TestRecorder(unittest.TestCase): with self.assertRaises(ValueError): recorder._INSTANCE._apply_update(-1) + +def create_engine_test(*args, **kwargs): + """Test version of create_engine that initializes with old schema. + + This simulates an existing db with the old schema. + """ + engine = create_engine(*args, **kwargs) + models_original.Base.metadata.create_all(engine) + return engine + + +class TestMigrateRecorder(BaseTestRecorder): + """Test recorder class that starts with an original schema db.""" + + @patch('sqlalchemy.create_engine', new=create_engine_test) + @patch('homeassistant.components.recorder.Recorder._migrate_schema') + def setUp(self, migrate): # pylint: disable=invalid-name + """Setup things to be run when tests are started. + + create_engine is patched to create a db that starts with the old + schema. + + _migrate_schema is mocked to ensure it isn't run, so we can test it + below. + """ + super().setUp() + def test_schema_update_calls(self): # pylint: disable=no-self-use """Test that schema migrations occurr in correct order.""" - test_version = recorder.models.SchemaChanges(schema_version=0) - with recorder.session_scope() as session: - session.add(test_version) - with patch.object(recorder._INSTANCE, '_apply_update') as update: - recorder._INSTANCE._migrate_schema() - update.assert_has_calls([call(version+1) for version in range( - 0, recorder.models.SCHEMA_VERSION)]) + with patch.object(recorder._INSTANCE, '_apply_update') as update: + recorder._INSTANCE._migrate_schema() + update.assert_has_calls([call(version+1) for version in range( + 0, recorder.models.SCHEMA_VERSION)]) + + def test_schema_migrate(self): # pylint: disable=no-self-use + """Test the full schema migration logic. + + We're just testing that the logic can execute successfully here without + throwing exceptions. Maintaining a set of assertions based on schema + inspection could quickly become quite cumbersome. + """ + recorder._INSTANCE._migrate_schema() @pytest.fixture From 2b62e9f008a59cdd3132a501e2b09aff98d4330a Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 10 Feb 2017 12:30:44 +0200 Subject: [PATCH 07/18] Fix zwave helper getter not to fail on some None results. (#5845) --- homeassistant/components/zwave/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/zwave/__init__.py b/homeassistant/components/zwave/__init__.py index 8fcfd3897a7..867a0f96062 100755 --- a/homeassistant/components/zwave/__init__.py +++ b/homeassistant/components/zwave/__init__.py @@ -646,6 +646,7 @@ class ZWaveDeviceEntity(Entity): method, class_id, index, label, data, member, kwargs) values = self._value.node.get_values(**kwargs).values() _LOGGER.debug('values=%s', values) + results = None if not values: return None for value in values: From cee389f62106ad95013c4f6a69e163496a9a0b31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Br=C3=A6dstrup?= Date: Fri, 10 Feb 2017 12:00:28 +0100 Subject: [PATCH 08/18] D-Link switch version bump of external library (#5843) --- homeassistant/components/switch/dlink.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/switch/dlink.py b/homeassistant/components/switch/dlink.py index 3e1f7db3ddb..11aff81a0d5 100644 --- a/homeassistant/components/switch/dlink.py +++ b/homeassistant/components/switch/dlink.py @@ -15,7 +15,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.const import TEMP_CELSIUS, STATE_UNKNOWN REQUIREMENTS = ['https://github.com/LinuxChristian/pyW215/archive/' - 'v0.3.7.zip#pyW215==0.3.7'] + 'v0.4.zip#pyW215==0.4'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 07943439eb6..229ec1229fd 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -198,7 +198,7 @@ hikvision==0.4 # http://github.com/adafruit/Adafruit_Python_DHT/archive/310c59b0293354d07d94375f1365f7b9b9110c7d.zip#Adafruit_DHT==1.3.0 # homeassistant.components.switch.dlink -https://github.com/LinuxChristian/pyW215/archive/v0.3.7.zip#pyW215==0.3.7 +https://github.com/LinuxChristian/pyW215/archive/v0.4.zip#pyW215==0.4 # homeassistant.components.media_player.webostv # homeassistant.components.notify.webostv From 0f6aed16a2b89ec4fd8aa42e8deff5f53baf7666 Mon Sep 17 00:00:00 2001 From: Teemu R Date: Fri, 10 Feb 2017 14:45:31 +0100 Subject: [PATCH 09/18] bump python-yeelight version (#5850) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an optional extended description… --- homeassistant/components/light/yeelight.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/light/yeelight.py b/homeassistant/components/light/yeelight.py index 616bae94a91..5eae4c66bb6 100644 --- a/homeassistant/components/light/yeelight.py +++ b/homeassistant/components/light/yeelight.py @@ -22,7 +22,7 @@ from homeassistant.components.light import ( Light, PLATFORM_SCHEMA) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['yeelight==0.2.1'] +REQUIREMENTS = ['yeelight==0.2.2'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 229ec1229fd..34f74ca6bd9 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -711,7 +711,7 @@ yahoo-finance==1.4.0 yahooweather==0.8 # homeassistant.components.light.yeelight -yeelight==0.2.1 +yeelight==0.2.2 # homeassistant.components.light.zengge zengge==0.2 From a071cd21f2c1b30affb5f9cc86b6a60fe65b0515 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Vran=C3=ADk?= Date: Fri, 10 Feb 2017 14:56:49 +0100 Subject: [PATCH 10/18] version bump (#5846) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an optional extended description… --- homeassistant/components/hdmi_cec.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/hdmi_cec.py b/homeassistant/components/hdmi_cec.py index 11a3f0f2d02..a154bdf609e 100644 --- a/homeassistant/components/hdmi_cec.py +++ b/homeassistant/components/hdmi_cec.py @@ -26,7 +26,7 @@ from homeassistant.const import (EVENT_HOMEASSISTANT_START, STATE_UNKNOWN, from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['pyCEC==0.4.12'] +REQUIREMENTS = ['pyCEC==0.4.13'] DOMAIN = 'hdmi_cec' diff --git a/requirements_all.txt b/requirements_all.txt index 34f74ca6bd9..695b7144fa3 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -403,7 +403,7 @@ pwaqi==1.4 py-cpuinfo==0.2.3 # homeassistant.components.hdmi_cec -pyCEC==0.4.12 +pyCEC==0.4.13 # homeassistant.components.switch.tplink pyHS100==0.2.3 From eaa6392535b55f49106b12693e8804a9ab589db1 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 10 Feb 2017 17:51:08 +0100 Subject: [PATCH 11/18] Fix check_config script. (#5853) --- homeassistant/scripts/check_config.py | 1 + tests/test_bootstrap.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/scripts/check_config.py b/homeassistant/scripts/check_config.py index b1ecaaa57ba..154754c667a 100644 --- a/homeassistant/scripts/check_config.py +++ b/homeassistant/scripts/check_config.py @@ -33,6 +33,7 @@ MOCKS = { } SILENCE = ( 'homeassistant.bootstrap.clear_secret_cache', + 'homeassistant.bootstrap.async_register_signal_handling', 'homeassistant.core._LOGGER.info', 'homeassistant.loader._LOGGER.info', 'homeassistant.bootstrap._LOGGER.info', diff --git a/tests/test_bootstrap.py b/tests/test_bootstrap.py index 0a1e3633916..0fede71a63d 100644 --- a/tests/test_bootstrap.py +++ b/tests/test_bootstrap.py @@ -58,7 +58,7 @@ class TestBootstrap: autospec=True) @mock.patch('homeassistant.util.location.detect_location_info', autospec=True, return_value=None) - @mock.patch('homeassistant.helpers.signal.async_register_signal_handling') + @mock.patch('homeassistant.bootstrap.async_register_signal_handling') def test_from_config_file(self, mock_upgrade, mock_detect, mock_signal): """Test with configuration file.""" components = ['browser', 'conversation', 'script'] @@ -290,7 +290,7 @@ class TestBootstrap: assert 'comp' not in self.hass.config.components @mock.patch('homeassistant.bootstrap.enable_logging') - @mock.patch('homeassistant.helpers.signal.async_register_signal_handling') + @mock.patch('homeassistant.bootstrap.async_register_signal_handling') def test_home_assistant_core_config_validation(self, log_mock, sig_mock): """Test if we pass in wrong information for HA conf.""" # Extensive HA conf validation testing is done in test_config.py From 0e6ab3ace6fa8f62e8dbcbe57546244349eabe58 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 10 Feb 2017 09:30:13 -0800 Subject: [PATCH 12/18] Update frontend (#5855) --- homeassistant/components/frontend/version.py | 2 +- .../frontend/www_static/frontend.html | 2 +- .../frontend/www_static/frontend.html.gz | Bin 138154 -> 138153 bytes .../www_static/home-assistant-polymer | 2 +- .../frontend/www_static/service_worker.js | 2 +- .../frontend/www_static/service_worker.js.gz | Bin 2338 -> 2337 bytes 6 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/version.py b/homeassistant/components/frontend/version.py index 4f8b6aed2d0..40cda09ae24 100644 --- a/homeassistant/components/frontend/version.py +++ b/homeassistant/components/frontend/version.py @@ -2,7 +2,7 @@ FINGERPRINTS = { "core.js": "adfeb513cf650acf763e284d76a48d6b", - "frontend.html": "ae96f5256a562f35a652f31560a3b550", + "frontend.html": "43340b2369646b779e04a9925c225ab4", "mdi.html": "c1dde43ccf5667f687c418fc8daf9668", "micromarkdown-js.html": "93b5ec4016f0bba585521cf4d18dec1a", "panels/ha-panel-dev-event.html": "5c82300b3cf543a92cf4297506e450e7", diff --git a/homeassistant/components/frontend/www_static/frontend.html b/homeassistant/components/frontend/www_static/frontend.html index 70a7063556b..31f8a0789dd 100644 --- a/homeassistant/components/frontend/www_static/frontend.html +++ b/homeassistant/components/frontend/www_static/frontend.html @@ -600,4 +600,4 @@ this.currentTarget=t,this.defaultPrevented=!1,this.eventPhase=Event.AT_TARGET,th this.hass.callService('media_player', service, serviceData); }, }); -}()); \ No newline at end of file +}()); \ No newline at end of file diff --git a/homeassistant/components/frontend/www_static/frontend.html.gz b/homeassistant/components/frontend/www_static/frontend.html.gz index 2e368d5b7f80e50e9f04a54b610ee75669fe1a4e..2790cc1ddf447ff91abfdf445239ca8e335103ea 100644 GIT binary patch delta 10641 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 delta 10642 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

MwGAF$n!IN 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 Z Date: Fri, 10 Feb 2017 22:22:15 +0200 Subject: [PATCH 13/18] [recorder] Run end model changed in session scope (#5858) --- homeassistant/components/recorder/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/recorder/__init__.py b/homeassistant/components/recorder/__init__.py index 8d3213a4805..66eb06f4d27 100644 --- a/homeassistant/components/recorder/__init__.py +++ b/homeassistant/components/recorder/__init__.py @@ -421,8 +421,8 @@ class Recorder(threading.Thread): def _close_run(self): """Save end time for current run.""" - self._run.end = dt_util.utcnow() with session_scope() as session: + self._run.end = dt_util.utcnow() self._commit(session, self._run) self._run = None From 849ae9903cccff7bed5056c1abb999c29183ca0b Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 10 Feb 2017 12:55:59 -0800 Subject: [PATCH 14/18] Recorder run can be None (#5854) --- homeassistant/components/recorder/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/recorder/__init__.py b/homeassistant/components/recorder/__init__.py index 66eb06f4d27..b227a8ce76a 100644 --- a/homeassistant/components/recorder/__init__.py +++ b/homeassistant/components/recorder/__init__.py @@ -124,7 +124,8 @@ def run_information(point_in_time: Optional[datetime]=None): res = query(recorder_runs).filter( (recorder_runs.start < point_in_time) & (recorder_runs.end > point_in_time)).first() - session.expunge(res) + if res: + session.expunge(res) return res From bb043c47f8e6b60b783096ee3f2a64a90dfab2a1 Mon Sep 17 00:00:00 2001 From: Johan Bloemberg Date: Sat, 11 Feb 2017 00:24:07 +0100 Subject: [PATCH 15/18] Rflink update and small refactor. (#5789) * Use same pattern for device defaults in both platforms. * Update Rflink that passes loop downstream. * Update requirements. --- homeassistant/components/rflink.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/rflink.py b/homeassistant/components/rflink.py index 13696318e01..9a9e6d1145f 100644 --- a/homeassistant/components/rflink.py +++ b/homeassistant/components/rflink.py @@ -36,7 +36,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity import voluptuous as vol -REQUIREMENTS = ['rflink==0.0.24'] +REQUIREMENTS = ['rflink==0.0.28'] DOMAIN = 'rflink' diff --git a/requirements_all.txt b/requirements_all.txt index 695b7144fa3..fff2eaef756 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -586,7 +586,7 @@ qnapstats==0.2.1 radiotherm==1.2 # homeassistant.components.rflink -rflink==0.0.24 +rflink==0.0.28 # homeassistant.components.switch.rpi_rf # rpi-rf==0.9.6 From 3fb70afb1439a433d2fef57260cc610aa9d9769c Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Fri, 10 Feb 2017 23:51:19 -0500 Subject: [PATCH 16/18] Avoid traceback for Amcrest cameras/firmware that does not have the software_information API call (#5865) * Avoid traceback for Amcrest cameras/firmware that does not have the software_information API call * Make lint happy --- homeassistant/components/sensor/amcrest.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/sensor/amcrest.py b/homeassistant/components/sensor/amcrest.py index 08d551b8fde..f250905e952 100644 --- a/homeassistant/components/sensor/amcrest.py +++ b/homeassistant/components/sensor/amcrest.py @@ -122,10 +122,18 @@ class AmcrestSensor(Entity): def update(self): """Get the latest data and updates the state.""" - version, build_date = self._camera.software_information - self._attrs['Build Date'] = build_date.split('=')[-1] - self._attrs['Serial Number'] = self._camera.serial_number - self._attrs['Version'] = version.split('=')[-1] + try: + version, build_date = self._camera.software_information + self._attrs['Build Date'] = build_date.split('=')[-1] + self._attrs['Version'] = version.split('=')[-1] + except ValueError: + self._attrs['Build Date'] = 'Not Available' + self._attrs['Version'] = 'Not Available' + + try: + self._attrs['Serial Number'] = self._camera.serial_number + except ValueError: + self._attrs['Serial Number'] = 'Not Available' if self._sensor_type == 'motion_detector': self._state = self._camera.is_motion_detected From d8a34877d4f94332b41d8ee2c4b7c1b9248369ca Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 10 Feb 2017 20:55:52 -0800 Subject: [PATCH 17/18] Version bump to 0.38 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index eda303a552e..358294dde4d 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 38 -PATCH_VERSION = '0.dev0' +PATCH_VERSION = '0' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 4, 2) From b59b42db2c72c28d5ce76a8ba7bac601e7de00ee Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 11 Feb 2017 19:58:20 -0800 Subject: [PATCH 18/18] 0.38.1 (#5889) * Upgrade AppleTV dep to 0.1.4 * Version bump to 0.38.1 * Update logbook in frontend --- homeassistant/components/frontend/version.py | 2 +- .../www_static/panels/ha-panel-logbook.html | 2 +- .../panels/ha-panel-logbook.html.gz | Bin 8001 -> 8006 bytes .../components/media_player/apple_tv.py | 2 +- homeassistant/const.py | 2 +- requirements_all.txt | 2 +- 6 files changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/frontend/version.py b/homeassistant/components/frontend/version.py index 40cda09ae24..859a9e52bbd 100644 --- a/homeassistant/components/frontend/version.py +++ b/homeassistant/components/frontend/version.py @@ -12,7 +12,7 @@ FINGERPRINTS = { "panels/ha-panel-dev-template.html": "2b618508510afa5281c9ecae0c3a3dbd", "panels/ha-panel-history.html": "8955c1d093a2c417c89ed90dd627c7d3", "panels/ha-panel-iframe.html": "d920f0aa3c903680f2f8795e2255daab", - "panels/ha-panel-logbook.html": "f36297a894524518fa70883f264492b0", + "panels/ha-panel-logbook.html": "7eb06cf9fdeab6683bcd755276f571aa", "panels/ha-panel-map.html": "9c8c7924ba8f731560c9f4093835cc26", "websocket_test.html": "575de64b431fe11c3785bf96d7813450" } diff --git a/homeassistant/components/frontend/www_static/panels/ha-panel-logbook.html b/homeassistant/components/frontend/www_static/panels/ha-panel-logbook.html index 7b5c0f6d33c..1335ff844be 100644 --- a/homeassistant/components/frontend/www_static/panels/ha-panel-logbook.html +++ b/homeassistant/components/frontend/www_static/panels/ha-panel-logbook.html @@ -1,4 +1,4 @@

\ No newline at end of file + */.pika-single{z-index:9999;display:block;position:relative;color:#333;background:#fff;border:1px solid #ccc;border-bottom-color:#bbb;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}.pika-single:after,.pika-single:before{content:" ";display:table}.pika-single:after{clear:both}.pika-single.is-hidden{display:none}.pika-single.is-bound{position:absolute;box-shadow:0 5px 15px -5px rgba(0,0,0,.5)}.pika-lendar{float:left;width:240px;margin:8px}.pika-title{position:relative;text-align:center}.pika-label{display:inline-block;position:relative;z-index:9999;overflow:hidden;margin:0;padding:5px 3px;font-size:14px;line-height:20px;font-weight:700;background-color:#fff}.pika-title select{cursor:pointer;position:absolute;z-index:9998;margin:0;left:0;top:5px;filter:alpha(opacity=0);opacity:0}.pika-next,.pika-prev{display:block;cursor:pointer;position:relative;outline:0;border:0;padding:0;width:20px;height:30px;text-indent:20px;white-space:nowrap;overflow:hidden;background-color:transparent;background-position:center center;background-repeat:no-repeat;background-size:75% 75%;opacity:.5}.pika-next:hover,.pika-prev:hover{opacity:1}.is-rtl .pika-next,.pika-prev{float:left;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg==)}.is-rtl .pika-prev,.pika-next{float:right;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII=)}.pika-next.is-disabled,.pika-prev.is-disabled{cursor:default;opacity:.2}.pika-select{display:inline-block}.pika-table{width:100%;border-collapse:collapse;border-spacing:0;border:0}.pika-table td,.pika-table th{width:14.285714285714286%;padding:0}.pika-table th{color:#999;font-size:12px;line-height:25px;font-weight:700;text-align:center}.pika-button{cursor:pointer;display:block;box-sizing:border-box;-moz-box-sizing:border-box;outline:0;border:0;margin:0;width:100%;padding:5px;color:#666;font-size:12px;line-height:15px;text-align:right;background:#f5f5f5}.pika-week{font-size:11px;color:#999}.is-today .pika-button{color:#3af;font-weight:700}.is-selected .pika-button{color:#fff;font-weight:700;background:#3af;box-shadow:inset 0 1px 3px #178fe5;border-radius:3px}.is-inrange .pika-button{background:#D5E9F7}.is-startrange .pika-button{color:#fff;background:#6CB31D;box-shadow:none;border-radius:3px}.is-endrange .pika-button{color:#fff;background:#3af;box-shadow:none;border-radius:3px}.is-disabled .pika-button,.is-outside-current-month .pika-button{pointer-events:none;cursor:default;color:#999;opacity:.3}.pika-button:hover{color:#fff;background:#ff8000;box-shadow:none;border-radius:3px}.pika-table abbr{border-bottom:none;cursor:help}} \ No newline at end of file diff --git a/homeassistant/components/frontend/www_static/panels/ha-panel-logbook.html.gz b/homeassistant/components/frontend/www_static/panels/ha-panel-logbook.html.gz index c81ae0bc4545bec3ebb536adf50165d822f0ac1d..5c3f2b97bd8376771fa8ec1a362d8587dc2de9f6 100644 GIT binary patch delta 571 zcmX?Tcg&7mzMF&N;hp&#*(GJ`FU{!k7pmB>B`hZRfy9@)vo#jUglaS&(vxvcFLsVD ze3c+wF-8Bi=)RchyoRag_FN8l-cY<((9wU#gngDrKF5BT^1ZZH?Q+XD@5&uTp;n)w zxsU${v={3>v_Dj=$5hF=qOx=U3uc8Cy-Q4(s_o93)@(CPHucxF zT&m{2o>IKlT&>+TAY73-=UYn9@%MTRT2Eg$EZVi_e*4FR>3lCY9X4}J{7_xB|Js9jZ63LS5e#P z>te*`{b^hFvSC`)#g>pii`LDO`TEg(u53uHrKB(M|XUTS5swK7(e^${(A{2!OkO~6hq+0ScRlmASR z?{}49TM$#D?fmCadn0b#TAjGJ>PXZ|gN`2MdD)_3d(YK> zJ+brJ?1_h!Caj*oSXO1U^6_>thN++O9Rg$Ty*&KmfqHw%!Nl1M41U<|D_pO|x@M=I zcjlyIIsdQjUP-5pI=p%!d`|fN3=WRfFQ1lIoXa$>@-BD3=U@Bt(7Qd;mt_Brld4{< zu>OhWMaL+wb*byu-gLHE^L(c)LyE_pMVrqb*vC*G)F|)PG4YlWPy0Ff3ntEyPnU>a zayTdNbv8nb-O6oO>I4t9mX>uA6QtRAXD!Hal9ZiRBRffE{WW11_0W&;mmbgN)e&l3 zd+KV$N(ryT>)fj@Oi_(JD|)GC)|rQAFM4}jH*^gT+CBf<#ivjI+WPB?7HsX$I62!t zG?aGeHB+4*YfCz#VyQz%6Ur6xpmsR?_Kk}udm_u zuQR_~V&%Oz!$~VG@Va;3uLrZwaje?M;B~4yeC97*-6KErCkr)QSbyr-{mW73LQQtc z7hJa2E$K^X40#^2n&r(R^?NRlSYMPszZEcj$(P!LPnZ6Ad@<%{wJMv)SzYO@`mgO4 hlOJ~*|9$1AX>fbO{dIR2