Merge pull request #12484 from home-assistant/release-0-63-3

0.63.3
This commit is contained in:
Paulus Schoutsen 2018-02-17 15:29:10 -08:00 committed by GitHub
commit 38da81c308
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 68 additions and 71 deletions

View File

@ -29,7 +29,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
})
REQUIREMENTS = ['python-miio==0.3.5']
REQUIREMENTS = ['python-miio==0.3.6']
ATTR_TEMPERATURE = 'temperature'
ATTR_HUMIDITY = 'humidity'

View File

@ -83,9 +83,9 @@ NODE_FILTERS = {
},
'fan': {
'uom': [],
'states': ['on', 'off', 'low', 'medium', 'high'],
'states': ['off', 'low', 'medium', 'high'],
'node_def_id': ['FanLincMotor'],
'insteon_type': ['1.46.']
'insteon_type': []
},
'cover': {
'uom': ['97'],
@ -99,7 +99,7 @@ NODE_FILTERS = {
'node_def_id': ['DimmerLampSwitch', 'DimmerLampSwitch_ADV',
'DimmerSwitchOnly', 'DimmerSwitchOnly_ADV',
'DimmerLampOnly', 'BallastRelayLampSwitch',
'BallastRelayLampSwitch_ADV', 'RelayLampSwitch',
'BallastRelayLampSwitch_ADV',
'RemoteLinc2', 'RemoteLinc2_ADV'],
'insteon_type': ['1.']
},
@ -431,7 +431,10 @@ class ISYDevice(Entity):
def unique_id(self) -> str:
"""Get the unique identifier of the device."""
# pylint: disable=protected-access
return self._node._id
if hasattr(self._node, '_id'):
return self._node._id
return None
@property
def name(self) -> str:

View File

@ -237,7 +237,6 @@ class LightTemplate(Light):
@asyncio.coroutine
def async_update(self):
"""Update the state from the template."""
print("ASYNC UPDATE")
if self._template is not None:
try:
state = self._template.async_render().lower()
@ -262,7 +261,7 @@ class LightTemplate(Light):
self._state = None
if 0 <= int(brightness) <= 255:
self._brightness = brightness
self._brightness = int(brightness)
else:
_LOGGER.error(
'Received invalid brightness : %s' +

View File

@ -30,7 +30,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
})
REQUIREMENTS = ['python-miio==0.3.5']
REQUIREMENTS = ['python-miio==0.3.6']
# The light does not accept cct values < 1
CCT_MIN = 1

View File

@ -370,7 +370,8 @@ class PlexClient(MediaPlayerDevice):
self._is_player_available = False
self._media_position = self._session.viewOffset
self._media_content_id = self._session.ratingKey
self._media_content_rating = self._session.contentRating
self._media_content_rating = getattr(
self._session, 'contentRating', None)
self._set_player_state()

View File

@ -12,8 +12,7 @@ _LOGGER = logging.getLogger(__name__)
def purge_old_data(instance, purge_days):
"""Purge events and states older than purge_days ago."""
from .models import States, Events
from sqlalchemy import orm
from sqlalchemy.sql import exists
from sqlalchemy import func
purge_before = dt_util.utcnow() - timedelta(days=purge_days)
@ -21,18 +20,10 @@ def purge_old_data(instance, purge_days):
# For each entity, the most recent state is protected from deletion
# s.t. we can properly restore state even if the entity has not been
# updated in a long time
states_alias = orm.aliased(States, name='StatesAlias')
protected_states = session.query(States.state_id, States.event_id)\
.filter(~exists()
.where(States.entity_id ==
states_alias.entity_id)
.where(states_alias.last_updated >
States.last_updated))\
.all()
protected_states = session.query(func.max(States.state_id)) \
.group_by(States.entity_id).all()
protected_state_ids = tuple((state[0] for state in protected_states))
protected_event_ids = tuple((state[1] for state in protected_states
if state[1] is not None))
deleted_rows = session.query(States) \
.filter((States.last_updated < purge_before)) \
@ -45,6 +36,13 @@ def purge_old_data(instance, purge_days):
# Otherwise, if the SQL server has "ON DELETE CASCADE" as default, it
# will delete the protected state when deleting its associated
# event. Also, we would be producing NULLed foreign keys otherwise.
protected_events = session.query(States.event_id) \
.filter(States.state_id.in_(protected_state_ids)) \
.filter(States.event_id.isnot(None)) \
.all()
protected_event_ids = tuple((state[0] for state in protected_events))
deleted_rows = session.query(Events) \
.filter((Events.time_fired < purge_before)) \
.filter(~Events.event_id.in_(

View File

@ -21,7 +21,7 @@ from homeassistant.const import (
import homeassistant.helpers.config_validation as cv
from homeassistant.util.dt import utcnow
REQUIREMENTS = ['python-miio==0.3.5']
REQUIREMENTS = ['python-miio==0.3.6']
_LOGGER = logging.getLogger(__name__)

View File

@ -126,6 +126,8 @@ class SQLSensor(Entity):
except sqlalchemy.exc.SQLAlchemyError as err:
_LOGGER.error("Error executing query %s: %s", self._query, err)
return
finally:
sess.close()
for res in result:
_LOGGER.debug(res.items())
@ -141,5 +143,3 @@ class SQLSensor(Entity):
data, None)
else:
self._state = data
sess.close()

View File

@ -7,7 +7,6 @@ https://home-assistant.io/components/sensor.yr/
import asyncio
import logging
from datetime import timedelta
from random import randrange
from xml.parsers.expat import ExpatError
@ -22,16 +21,17 @@ from homeassistant.const import (
ATTR_ATTRIBUTION, CONF_NAME)
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.event import (
async_track_point_in_utc_time, async_track_utc_time_change)
from homeassistant.helpers.event import (async_track_utc_time_change,
async_call_later)
from homeassistant.util import dt as dt_util
REQUIREMENTS = ['xmltodict==0.11.0']
_LOGGER = logging.getLogger(__name__)
CONF_ATTRIBUTION = "Weather forecast from yr.no, delivered by the Norwegian " \
"Meteorological Institute and the NRK."
CONF_ATTRIBUTION = "Weather forecast from met.no, delivered " \
"by the Norwegian Meteorological Institute."
# https://api.met.no/license_data.html
SENSOR_TYPES = {
'symbol': ['Symbol', None],
@ -91,11 +91,8 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
async_add_devices(dev)
weather = YrData(hass, coordinates, forecast, dev)
# Update weather on the hour, spread seconds
async_track_utc_time_change(
hass, weather.async_update, minute=randrange(1, 10),
second=randrange(0, 59))
yield from weather.async_update()
async_track_utc_time_change(hass, weather.updating_devices, minute=31)
yield from weather.fetching_data()
class YrSensor(Entity):
@ -153,50 +150,49 @@ class YrData(object):
self._url = 'https://aa015h6buqvih86i1.api.met.no/'\
'weatherapi/locationforecast/1.9/'
self._urlparams = coordinates
self._nextrun = None
self._forecast = forecast
self.devices = devices
self.data = {}
self.hass = hass
@asyncio.coroutine
def async_update(self, *_):
def fetching_data(self, *_):
"""Get the latest data from yr.no."""
import xmltodict
def try_again(err: str):
"""Retry in 15 minutes."""
_LOGGER.warning("Retrying in 15 minutes: %s", err)
self._nextrun = None
nxt = dt_util.utcnow() + timedelta(minutes=15)
if nxt.minute >= 15:
async_track_point_in_utc_time(self.hass, self.async_update,
nxt)
if self._nextrun is None or dt_util.utcnow() >= self._nextrun:
try:
websession = async_get_clientsession(self.hass)
with async_timeout.timeout(10, loop=self.hass.loop):
resp = yield from websession.get(
self._url, params=self._urlparams)
if resp.status != 200:
try_again('{} returned {}'.format(resp.url, resp.status))
return
text = yield from resp.text()
except (asyncio.TimeoutError, aiohttp.ClientError) as err:
try_again(err)
"""Retry in 15 to 20 minutes."""
minutes = 15 + randrange(6)
_LOGGER.error("Retrying in %i minutes: %s", minutes, err)
async_call_later(self.hass, minutes*60, self.fetching_data)
try:
websession = async_get_clientsession(self.hass)
with async_timeout.timeout(10, loop=self.hass.loop):
resp = yield from websession.get(
self._url, params=self._urlparams)
if resp.status != 200:
try_again('{} returned {}'.format(resp.url, resp.status))
return
text = yield from resp.text()
try:
self.data = xmltodict.parse(text)['weatherdata']
model = self.data['meta']['model']
if '@nextrun' not in model:
model = model[0]
self._nextrun = dt_util.parse_datetime(model['@nextrun'])
except (ExpatError, IndexError) as err:
try_again(err)
return
except (asyncio.TimeoutError, aiohttp.ClientError) as err:
try_again(err)
return
try:
self.data = xmltodict.parse(text)['weatherdata']
except (ExpatError, IndexError) as err:
try_again(err)
return
yield from self.updating_devices()
async_call_later(self.hass, 60*60, self.fetching_data)
@asyncio.coroutine
def updating_devices(self, *_):
"""Find the current data from self.data."""
if not self.data:
return
now = dt_util.utcnow()
forecast_time = now + dt_util.dt.timedelta(hours=self._forecast)

View File

@ -25,7 +25,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
})
REQUIREMENTS = ['python-miio==0.3.5']
REQUIREMENTS = ['python-miio==0.3.6']
ATTR_POWER = 'power'
ATTR_TEMPERATURE = 'temperature'

View File

@ -19,7 +19,7 @@ from homeassistant.const import (
ATTR_ENTITY_ID, CONF_HOST, CONF_NAME, CONF_TOKEN, STATE_OFF, STATE_ON)
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['python-miio==0.3.5']
REQUIREMENTS = ['python-miio==0.3.6']
_LOGGER = logging.getLogger(__name__)

View File

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

View File

@ -922,7 +922,7 @@ python-juicenet==0.0.5
# homeassistant.components.remote.xiaomi_miio
# homeassistant.components.switch.xiaomi_miio
# homeassistant.components.vacuum.xiaomi_miio
python-miio==0.3.5
python-miio==0.3.6
# homeassistant.components.media_player.mpd
python-mpd2==0.5.5

View File

@ -586,7 +586,7 @@ class TestTemplateLight:
state = self.hass.states.get('light.test_template_light')
assert state is not None
assert state.attributes.get('brightness') == '42'
assert state.attributes.get('brightness') == 42
def test_friendly_name(self):
"""Test the accessibility of the friendly_name attribute."""