Merge pull request #23620 from home-assistant/rc

0.92.2
This commit is contained in:
Pascal Vizeli 2019-05-02 09:49:23 +02:00 committed by GitHub
commit d4ae73ce38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 91 additions and 43 deletions

View File

@ -911,13 +911,17 @@ class _MediaPlayerCapabilities(_AlexaEntity):
return [_DisplayCategory.TV] return [_DisplayCategory.TV]
def interfaces(self): def interfaces(self):
yield _AlexaPowerController(self.entity)
yield _AlexaEndpointHealth(self.hass, self.entity) yield _AlexaEndpointHealth(self.hass, self.entity)
supported = self.entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0) supported = self.entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
if supported & media_player.const.SUPPORT_VOLUME_SET: if supported & media_player.const.SUPPORT_VOLUME_SET:
yield _AlexaSpeaker(self.entity) yield _AlexaSpeaker(self.entity)
power_features = (media_player.SUPPORT_TURN_ON |
media_player.SUPPORT_TURN_OFF)
if supported & power_features:
yield _AlexaPowerController(self.entity)
step_volume_features = (media_player.const.SUPPORT_VOLUME_MUTE | step_volume_features = (media_player.const.SUPPORT_VOLUME_MUTE |
media_player.const.SUPPORT_VOLUME_STEP) media_player.const.SUPPORT_VOLUME_STEP)
if supported & step_volume_features: if supported & step_volume_features:

View File

@ -6,7 +6,6 @@ from html.parser import HTMLParser
from urllib.parse import urlparse, urljoin from urllib.parse import urlparse, urljoin
import aiohttp import aiohttp
from aiohttp.client_exceptions import ClientError
from homeassistant.util.network import is_local from homeassistant.util.network import is_local
@ -81,8 +80,22 @@ async def fetch_redirect_uris(hass, url):
if chunks == 10: if chunks == 10:
break break
except (asyncio.TimeoutError, ClientError) as ex: except asyncio.TimeoutError:
_LOGGER.error("Error while looking up redirect_uri %s: %s", url, ex) _LOGGER.error("Timeout while looking up redirect_uri %s", url)
pass
except aiohttp.client_exceptions.ClientSSLError:
_LOGGER.error("SSL error while looking up redirect_uri %s", url)
pass
except aiohttp.client_exceptions.ClientOSError as ex:
_LOGGER.error("OS error while looking up redirect_uri %s: %s", url,
ex.strerror)
pass
except aiohttp.client_exceptions.ClientConnectionError:
_LOGGER.error(("Low level connection error while looking up "
"redirect_uri %s"), url)
pass
except aiohttp.client_exceptions.ClientError:
_LOGGER.error("Unknown error while looking up redirect_uri %s", url)
pass pass
# Authorization endpoints verifying that a redirect_uri is allowed for use # Authorization endpoints verifying that a redirect_uri is allowed for use

View File

@ -3,7 +3,7 @@
"name": "Cast", "name": "Cast",
"documentation": "https://www.home-assistant.io/components/cast", "documentation": "https://www.home-assistant.io/components/cast",
"requirements": [ "requirements": [
"pychromecast==3.2.0" "pychromecast==3.2.1"
], ],
"dependencies": [], "dependencies": [],
"codeowners": [] "codeowners": []

View File

@ -659,7 +659,7 @@ class CastDevice(MediaPlayerDevice):
self.entity_id, self._cast_info.friendly_name, self.entity_id, self._cast_info.friendly_name,
self._cast_info.host, self._cast_info.port, cast_info) self._cast_info.host, self._cast_info.port, cast_info)
self.async_del_dynamic_group() await self.async_del_dynamic_group()
self._dynamic_group_cast_info = cast_info self._dynamic_group_cast_info = cast_info
# pylint: disable=protected-access # pylint: disable=protected-access

View File

@ -122,6 +122,9 @@ def async_remote_ui_url(hass) -> str:
if not async_is_logged_in(hass): if not async_is_logged_in(hass):
raise CloudNotAvailable raise CloudNotAvailable
if not hass.data[DOMAIN].remote.instance_domain:
raise CloudNotAvailable
return "https://" + hass.data[DOMAIN].remote.instance_domain return "https://" + hass.data[DOMAIN].remote.instance_domain

View File

@ -100,10 +100,13 @@ class CloudClient(Interface):
return google_conf['filter'](entity.entity_id) return google_conf['filter'](entity.entity_id)
username = self._hass.data[DOMAIN].claims["cognito:username"]
self._google_config = ga_h.Config( self._google_config = ga_h.Config(
should_expose=should_expose, should_expose=should_expose,
secure_devices_pin=self._prefs.google_secure_devices_pin, secure_devices_pin=self._prefs.google_secure_devices_pin,
entity_config=google_conf.get(CONF_ENTITY_CONFIG), entity_config=google_conf.get(CONF_ENTITY_CONFIG),
agent_user_id=username,
) )
# Set it to the latest. # Set it to the latest.
@ -149,19 +152,10 @@ class CloudClient(Interface):
if not self._prefs.google_enabled: if not self._prefs.google_enabled:
return ga.turned_off_response(payload) return ga.turned_off_response(payload)
answer = await ga.async_handle_message( return await ga.async_handle_message(
self._hass, self.google_config, self.prefs.cloud_user, payload self._hass, self.google_config, self.prefs.cloud_user, payload
) )
# Fix AgentUserId
try:
cloud = self._hass.data[DOMAIN]
answer['payload']['agentUserId'] = cloud.claims['cognito:username']
except (TypeError, KeyError):
return ga.turned_off_response(payload)
return answer
async def async_webhook_message( async def async_webhook_message(
self, payload: Dict[Any, Any]) -> Dict[Any, Any]: self, payload: Dict[Any, Any]) -> Dict[Any, Any]:
"""Process cloud webhook message to client.""" """Process cloud webhook message to client."""

View File

@ -3,7 +3,7 @@
"name": "Home Assistant Frontend", "name": "Home Assistant Frontend",
"documentation": "https://www.home-assistant.io/components/frontend", "documentation": "https://www.home-assistant.io/components/frontend",
"requirements": [ "requirements": [
"home-assistant-frontend==20190424.0" "home-assistant-frontend==20190427.0"
], ],
"dependencies": [ "dependencies": [
"api", "api",

View File

@ -3,7 +3,7 @@
"name": "Genius Hub", "name": "Genius Hub",
"documentation": "https://www.home-assistant.io/components/geniushub", "documentation": "https://www.home-assistant.io/components/geniushub",
"requirements": [ "requirements": [
"geniushub-client==0.3.6" "geniushub-client==0.4.5"
], ],
"dependencies": [], "dependencies": [],
"codeowners": [] "codeowners": []

View File

@ -20,12 +20,16 @@ class Config:
"""Hold the configuration for Google Assistant.""" """Hold the configuration for Google Assistant."""
def __init__(self, should_expose, def __init__(self, should_expose,
entity_config=None, secure_devices_pin=None): entity_config=None, secure_devices_pin=None,
agent_user_id=None):
"""Initialize the configuration.""" """Initialize the configuration."""
self.should_expose = should_expose self.should_expose = should_expose
self.entity_config = entity_config or {} self.entity_config = entity_config or {}
self.secure_devices_pin = secure_devices_pin self.secure_devices_pin = secure_devices_pin
# Agent User Id to use for query responses
self.agent_user_id = agent_user_id
class RequestData: class RequestData:
"""Hold data associated with a particular request.""" """Hold data associated with a particular request."""

View File

@ -99,7 +99,7 @@ async def async_devices_sync(hass, data, payload):
devices.append(serialized) devices.append(serialized)
response = { response = {
'agentUserId': data.context.user_id, 'agentUserId': data.config.agent_user_id or data.context.user_id,
'devices': devices, 'devices': devices,
} }

View File

@ -195,7 +195,7 @@ class SourceManager:
exc_info=isinstance(error, CommandError)) exc_info=isinstance(error, CommandError))
return return
async def update_sources(event): async def update_sources(event, data):
if event in (const.EVENT_SOURCES_CHANGED, if event in (const.EVENT_SOURCES_CHANGED,
const.EVENT_USER_CHANGED): const.EVENT_USER_CHANGED):
sources = await get_sources() sources = await get_sources()

View File

@ -3,7 +3,7 @@
"name": "Heos", "name": "Heos",
"documentation": "https://www.home-assistant.io/components/heos", "documentation": "https://www.home-assistant.io/components/heos",
"requirements": [ "requirements": [
"pyheos==0.4.1" "pyheos==0.5.1"
], ],
"dependencies": [], "dependencies": [],
"codeowners": [ "codeowners": [

View File

@ -78,7 +78,7 @@ class HeosMediaPlayer(MediaPlayerDevice):
const.CONTROL_PLAY_NEXT: SUPPORT_NEXT_TRACK const.CONTROL_PLAY_NEXT: SUPPORT_NEXT_TRACK
} }
async def _controller_event(self, event): async def _controller_event(self, event, data):
"""Handle controller event.""" """Handle controller event."""
from pyheos import const from pyheos import const
if event == const.EVENT_PLAYERS_CHANGED: if event == const.EVENT_PLAYERS_CHANGED:

View File

@ -32,6 +32,9 @@ class HueLightLevel(GenericHueGaugeSensorEntity):
@property @property
def state(self): def state(self):
"""Return the state of the device.""" """Return the state of the device."""
if self.sensor.lightlevel is None:
return None
# https://developers.meethue.com/develop/hue-api/supported-devices/#clip_zll_lightlevel # https://developers.meethue.com/develop/hue-api/supported-devices/#clip_zll_lightlevel
# Light level in 10000 log10 (lux) +1 measured by sensor. Logarithm # Light level in 10000 log10 (lux) +1 measured by sensor. Logarithm
# scale used because the human eye adjusts to light levels and small # scale used because the human eye adjusts to light levels and small
@ -59,4 +62,7 @@ class HueTemperature(GenericHueGaugeSensorEntity):
@property @property
def state(self): def state(self):
"""Return the state of the device.""" """Return the state of the device."""
if self.sensor.temperature is None:
return None
return self.sensor.temperature / 100 return self.sensor.temperature / 100

View File

@ -3,7 +3,7 @@
"name": "Myq", "name": "Myq",
"documentation": "https://www.home-assistant.io/components/myq", "documentation": "https://www.home-assistant.io/components/myq",
"requirements": [ "requirements": [
"pymyq==1.2.0" "pymyq==1.2.1"
], ],
"dependencies": [], "dependencies": [],
"codeowners": [] "codeowners": []

View File

@ -12,6 +12,8 @@ from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.util import Throttle from homeassistant.util import Throttle
from homeassistant.components.netatmo.const import DATA_NETATMO_AUTH
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
CONF_AREAS = 'areas' CONF_AREAS = 'areas'
@ -54,12 +56,12 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
def setup_platform(hass, config, add_entities, discovery_info=None): def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the access to Netatmo binary sensor.""" """Set up the access to Netatmo binary sensor."""
netatmo = hass.components.netatmo auth = hass.data[DATA_NETATMO_AUTH]
sensors = [] sensors = []
areas = config.get(CONF_AREAS) areas = config.get(CONF_AREAS)
for area_conf in areas: for area_conf in areas:
data = NetatmoPublicData(netatmo.NETATMO_AUTH, data = NetatmoPublicData(auth,
lat_ne=area_conf.get(CONF_LAT_NE), lat_ne=area_conf.get(CONF_LAT_NE),
lon_ne=area_conf.get(CONF_LON_NE), lon_ne=area_conf.get(CONF_LON_NE),
lat_sw=area_conf.get(CONF_LAT_SW), lat_sw=area_conf.get(CONF_LAT_SW),

View File

@ -170,7 +170,7 @@ class TraccarScanner:
if events is not None: if events is not None:
for event in events: for event in events:
device_name = next(( device_name = next((
dev.get('name') for dev in self._api.devices() dev.get('name') for dev in self._api.devices
if dev.get('id') == event['deviceId']), None) if dev.get('id') == event['deviceId']), None)
self._hass.bus.async_fire( self._hass.bus.async_fire(
'traccar_' + self._event_types.get(event["type"]), { 'traccar_' + self._event_types.get(event["type"]), {

View File

@ -3,7 +3,6 @@ from datetime import datetime
import logging import logging
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.helpers.typing import HomeAssistantType from homeassistant.helpers.typing import HomeAssistantType
@ -108,9 +107,6 @@ class UpnpSensor(Entity):
'identifiers': { 'identifiers': {
(DOMAIN_UPNP, self.unique_id) (DOMAIN_UPNP, self.unique_id)
}, },
'connections': {
(dr.CONNECTION_UPNP, self._device.udn)
},
'name': self.name, 'name': self.name,
'manufacturer': self._device.manufacturer, 'manufacturer': self._device.manufacturer,
} }

View File

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

View File

@ -459,7 +459,7 @@ gearbest_parser==1.0.7
geizhals==0.0.9 geizhals==0.0.9
# homeassistant.components.geniushub # homeassistant.components.geniushub
geniushub-client==0.3.6 geniushub-client==0.4.5
# homeassistant.components.geo_json_events # homeassistant.components.geo_json_events
# homeassistant.components.nsw_rural_fire_service_feed # homeassistant.components.nsw_rural_fire_service_feed
@ -548,7 +548,7 @@ hole==0.3.0
holidays==0.9.10 holidays==0.9.10
# homeassistant.components.frontend # homeassistant.components.frontend
home-assistant-frontend==20190424.0 home-assistant-frontend==20190427.0
# homeassistant.components.zwave # homeassistant.components.zwave
homeassistant-pyozw==0.1.4 homeassistant-pyozw==0.1.4
@ -976,7 +976,7 @@ pycfdns==0.0.1
pychannels==1.0.0 pychannels==1.0.0
# homeassistant.components.cast # homeassistant.components.cast
pychromecast==3.2.0 pychromecast==3.2.1
# homeassistant.components.cmus # homeassistant.components.cmus
pycmus==0.1.1 pycmus==0.1.1
@ -1073,7 +1073,7 @@ pygtt==1.1.2
pyhaversion==2.2.1 pyhaversion==2.2.1
# homeassistant.components.heos # homeassistant.components.heos
pyheos==0.4.1 pyheos==0.5.1
# homeassistant.components.hikvision # homeassistant.components.hikvision
pyhik==0.2.2 pyhik==0.2.2
@ -1166,7 +1166,7 @@ pymonoprice==0.3
pymusiccast==0.1.6 pymusiccast==0.1.6
# homeassistant.components.myq # homeassistant.components.myq
pymyq==1.2.0 pymyq==1.2.1
# homeassistant.components.mysensors # homeassistant.components.mysensors
pymysensors==0.18.0 pymysensors==0.18.0

View File

@ -136,7 +136,7 @@ hdate==0.8.7
holidays==0.9.10 holidays==0.9.10
# homeassistant.components.frontend # homeassistant.components.frontend
home-assistant-frontend==20190424.0 home-assistant-frontend==20190427.0
# homeassistant.components.homekit_controller # homeassistant.components.homekit_controller
homekit[IP]==0.13.0 homekit[IP]==0.13.0
@ -217,7 +217,7 @@ pydeconz==54
pydispatcher==2.0.5 pydispatcher==2.0.5
# homeassistant.components.heos # homeassistant.components.heos
pyheos==0.4.1 pyheos==0.5.1
# homeassistant.components.homematic # homeassistant.components.homematic
pyhomematic==0.1.58 pyhomematic==0.1.58

View File

@ -574,6 +574,32 @@ async def test_media_player(hass):
payload={'volumeSteps': -20}) payload={'volumeSteps': -20})
async def test_media_player_power(hass):
"""Test media player discovery with mapped on/off."""
device = (
'media_player.test',
'off', {
'friendly_name': "Test media player",
'supported_features': 0xfa3f,
'volume_level': 0.75
}
)
appliance = await discovery_test(device, hass)
assert appliance['endpointId'] == 'media_player#test'
assert appliance['displayCategories'][0] == "TV"
assert appliance['friendlyName'] == "Test media player"
assert_endpoint_capabilities(
appliance,
'Alexa.InputController',
'Alexa.Speaker',
'Alexa.StepSpeaker',
'Alexa.PlaybackController',
'Alexa.EndpointHealth',
)
async def test_alert(hass): async def test_alert(hass):
"""Test alert discovery.""" """Test alert discovery."""
device = ('alert.test', 'off', {'friendly_name': "Test alert"}) device = ('alert.test', 'off', {'friendly_name': "Test alert"})

View File

@ -161,7 +161,7 @@ async def test_update_sources_retry(hass, config_entry, config, controller,
source_manager.max_retry_attempts = 1 source_manager.max_retry_attempts = 1
controller.get_favorites.side_effect = CommandError("Test", "test", 0) controller.get_favorites.side_effect = CommandError("Test", "test", 0)
controller.dispatcher.send( controller.dispatcher.send(
const.SIGNAL_CONTROLLER_EVENT, const.EVENT_SOURCES_CHANGED) const.SIGNAL_CONTROLLER_EVENT, const.EVENT_SOURCES_CHANGED, {})
# Wait until it's finished # Wait until it's finished
while "Unable to update sources" not in caplog.text: while "Unable to update sources" not in caplog.text:
await asyncio.sleep(0.1) await asyncio.sleep(0.1)

View File

@ -103,7 +103,7 @@ async def test_updates_start_from_signals(
# Test controller player change updates # Test controller player change updates
player.available = False player.available = False
player.heos.dispatcher.send( player.heos.dispatcher.send(
const.SIGNAL_CONTROLLER_EVENT, const.EVENT_PLAYERS_CHANGED) const.SIGNAL_CONTROLLER_EVENT, const.EVENT_PLAYERS_CHANGED, {})
await hass.async_block_till_done() await hass.async_block_till_done()
state = hass.states.get('media_player.test_player') state = hass.states.get('media_player.test_player')
assert state.state == STATE_UNAVAILABLE assert state.state == STATE_UNAVAILABLE
@ -158,7 +158,7 @@ async def test_updates_from_sources_updated(
input_sources.clear() input_sources.clear()
player.heos.dispatcher.send( player.heos.dispatcher.send(
const.SIGNAL_CONTROLLER_EVENT, const.EVENT_SOURCES_CHANGED) const.SIGNAL_CONTROLLER_EVENT, const.EVENT_SOURCES_CHANGED, {})
await event.wait() await event.wait()
source_list = hass.data[DOMAIN][DATA_SOURCE_MANAGER].source_list source_list = hass.data[DOMAIN][DATA_SOURCE_MANAGER].source_list
assert len(source_list) == 2 assert len(source_list) == 2
@ -181,7 +181,7 @@ async def test_updates_from_user_changed(
controller.is_signed_in = False controller.is_signed_in = False
controller.signed_in_username = None controller.signed_in_username = None
player.heos.dispatcher.send( player.heos.dispatcher.send(
const.SIGNAL_CONTROLLER_EVENT, const.EVENT_USER_CHANGED) const.SIGNAL_CONTROLLER_EVENT, const.EVENT_USER_CHANGED, None)
await event.wait() await event.wait()
source_list = hass.data[DOMAIN][DATA_SOURCE_MANAGER].source_list source_list = hass.data[DOMAIN][DATA_SOURCE_MANAGER].source_list
assert len(source_list) == 1 assert len(source_list) == 1