Skip to content

Commit

Permalink
Merge pull request #18811 from home-assistant/rc
Browse files Browse the repository at this point in the history
0.83.1
  • Loading branch information
balloob committed Nov 29, 2018
2 parents c6c55c4 + 440614d commit 3701c0f
Show file tree
Hide file tree
Showing 35 changed files with 490 additions and 178 deletions.
3 changes: 2 additions & 1 deletion homeassistant/auth/auth_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -462,10 +462,11 @@ def _data_to_save(self) -> Dict:
for group in self._groups.values():
g_dict = {
'id': group.id,
# Name not read for sys groups. Kept here for backwards compat
'name': group.name
} # type: Dict[str, Any]

if group.id not in (GROUP_ID_READ_ONLY, GROUP_ID_ADMIN):
g_dict['name'] = group.name
g_dict['policy'] = group.policy

groups.append(g_dict)
Expand Down
29 changes: 25 additions & 4 deletions homeassistant/auth/providers/legacy_api_password.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@
It will be removed when auth system production ready
"""
import hmac
from typing import Any, Dict, Optional, cast
from typing import Any, Dict, Optional, cast, TYPE_CHECKING

import voluptuous as vol

from homeassistant.components.http import HomeAssistantHTTP # noqa: F401
from homeassistant.core import callback
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError

from . import AuthProvider, AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS, LoginFlow
from ..models import Credentials, UserMeta
from .. import AuthManager
from ..models import Credentials, UserMeta, User

if TYPE_CHECKING:
from homeassistant.components.http import HomeAssistantHTTP # noqa: F401


USER_SCHEMA = vol.Schema({
Expand All @@ -31,6 +34,24 @@ class InvalidAuthError(HomeAssistantError):
"""Raised when submitting invalid authentication."""


async def async_get_user(hass: HomeAssistant) -> User:
"""Return the legacy API password user."""
auth = cast(AuthManager, hass.auth) # type: ignore
found = None

for prv in auth.auth_providers:
if prv.type == 'legacy_api_password':
found = prv
break

if found is None:
raise ValueError('Legacy API password provider not found')

return await auth.async_get_or_create_user(
await found.async_get_or_create_credentials({})
)


@AUTH_PROVIDERS.register('legacy_api_password')
class LegacyApiPasswordAuthProvider(AuthProvider):
"""Example auth provider based on hardcoded usernames and passwords."""
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/binary_sensor/rainmachine.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def unique_id(self) -> str:
async def async_added_to_hass(self):
"""Register callbacks."""
@callback
def update(self):
def update():
"""Update the state."""
self.async_schedule_update_ha_state(True)

Expand Down
27 changes: 13 additions & 14 deletions homeassistant/components/emulated_hue/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ async def async_setup(hass, yaml_config):
app._on_startup.freeze()
await app.startup()

handler = None
server = None
runner = None
site = None

DescriptionXmlView(config).register(app, app.router)
HueUsernameView().register(app, app.router)
Expand All @@ -115,25 +115,24 @@ async def async_setup(hass, yaml_config):
async def stop_emulated_hue_bridge(event):
"""Stop the emulated hue bridge."""
upnp_listener.stop()
if server:
server.close()
await server.wait_closed()
await app.shutdown()
if handler:
await handler.shutdown(10)
await app.cleanup()
if site:
await site.stop()
if runner:
await runner.cleanup()

async def start_emulated_hue_bridge(event):
"""Start the emulated hue bridge."""
upnp_listener.start()
nonlocal handler
nonlocal server
nonlocal site
nonlocal runner

handler = app.make_handler(loop=hass.loop)
runner = web.AppRunner(app)
await runner.setup()

site = web.TCPSite(runner, config.host_ip_addr, config.listen_port)

try:
server = await hass.loop.create_server(
handler, config.host_ip_addr, config.listen_port)
await site.start()
except OSError as error:
_LOGGER.error("Failed to create HTTP server at port %d: %s",
config.listen_port, error)
Expand Down
90 changes: 50 additions & 40 deletions homeassistant/components/fibaro.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,29 +103,31 @@ def _on_state_change(self, state):
"""Handle change report received from the HomeCenter."""
callback_set = set()
for change in state.get('changes', []):
dev_id = change.pop('id')
for property_name, value in change.items():
if property_name == 'log':
if value and value != "transfer OK":
_LOGGER.debug("LOG %s: %s",
self._device_map[dev_id].friendly_name,
value)
try:
dev_id = change.pop('id')
if dev_id not in self._device_map.keys():
continue
if property_name == 'logTemp':
continue
if property_name in self._device_map[dev_id].properties:
self._device_map[dev_id].properties[property_name] = \
value
_LOGGER.debug("<- %s.%s = %s",
self._device_map[dev_id].ha_id,
property_name,
str(value))
else:
_LOGGER.warning("Error updating %s data of %s, not found",
property_name,
self._device_map[dev_id].ha_id)
if dev_id in self._callbacks:
callback_set.add(dev_id)
device = self._device_map[dev_id]
for property_name, value in change.items():
if property_name == 'log':
if value and value != "transfer OK":
_LOGGER.debug("LOG %s: %s",
device.friendly_name, value)
continue
if property_name == 'logTemp':
continue
if property_name in device.properties:
device.properties[property_name] = \
value
_LOGGER.debug("<- %s.%s = %s", device.ha_id,
property_name, str(value))
else:
_LOGGER.warning("%s.%s not found", device.ha_id,
property_name)
if dev_id in self._callbacks:
callback_set.add(dev_id)
except (ValueError, KeyError):
pass
for item in callback_set:
self._callbacks[item]()

Expand All @@ -137,8 +139,12 @@ def register(self, device_id, callback):
def _map_device_to_type(device):
"""Map device to HA device type."""
# Use our lookup table to identify device type
device_type = FIBARO_TYPEMAP.get(
device.type, FIBARO_TYPEMAP.get(device.baseType))
if 'type' in device:
device_type = FIBARO_TYPEMAP.get(device.type)
elif 'baseType' in device:
device_type = FIBARO_TYPEMAP.get(device.baseType)
else:
device_type = None

# We can also identify device type by its capabilities
if device_type is None:
Expand All @@ -156,35 +162,39 @@ def _map_device_to_type(device):

# Switches that control lights should show up as lights
if device_type == 'switch' and \
'isLight' in device.properties and \
device.properties.isLight == 'true':
device.properties.get('isLight', 'false') == 'true':
device_type = 'light'
return device_type

def _read_devices(self):
"""Read and process the device list."""
devices = self._client.devices.list()
self._device_map = {}
for device in devices:
if device.roomID == 0:
room_name = 'Unknown'
else:
room_name = self._room_map[device.roomID].name
device.friendly_name = room_name + ' ' + device.name
device.ha_id = '{}_{}_{}'.format(
slugify(room_name), slugify(device.name), device.id)
self._device_map[device.id] = device
self.fibaro_devices = defaultdict(list)
for device in self._device_map.values():
if device.enabled and \
(not device.isPlugin or self._import_plugins):
device.mapped_type = self._map_device_to_type(device)
for device in devices:
try:
if device.roomID == 0:
room_name = 'Unknown'
else:
room_name = self._room_map[device.roomID].name
device.friendly_name = room_name + ' ' + device.name
device.ha_id = '{}_{}_{}'.format(
slugify(room_name), slugify(device.name), device.id)
if device.enabled and \
('isPlugin' not in device or
(not device.isPlugin or self._import_plugins)):
device.mapped_type = self._map_device_to_type(device)
else:
device.mapped_type = None
if device.mapped_type:
self._device_map[device.id] = device
self.fibaro_devices[device.mapped_type].append(device)
else:
_LOGGER.debug("%s (%s, %s) not mapped",
_LOGGER.debug("%s (%s, %s) not used",
device.ha_id, device.type,
device.baseType)
except (KeyError, ValueError):
pass


def setup(hass, config):
Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/google_assistant/trait.py
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,8 @@ def sync_attributes(self):
modes = self.state.attributes.get(fan.ATTR_SPEED_LIST, [])
speeds = []
for mode in modes:
if mode not in self.speed_synonyms:
continue
speed = {
"speed_name": mode,
"speed_values": [{
Expand Down
9 changes: 8 additions & 1 deletion homeassistant/components/group/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,13 @@ async def reload_service_handler(service):
DOMAIN, SERVICE_RELOAD, reload_service_handler,
schema=RELOAD_SERVICE_SCHEMA)

service_lock = asyncio.Lock()

async def locked_service_handler(service):
"""Handle a service with an async lock."""
async with service_lock:
await groups_service_handler(service)

async def groups_service_handler(service):
"""Handle dynamic group service functions."""
object_id = service.data[ATTR_OBJECT_ID]
Expand Down Expand Up @@ -284,7 +291,7 @@ async def groups_service_handler(service):
await component.async_remove_entity(entity_id)

hass.services.async_register(
DOMAIN, SERVICE_SET, groups_service_handler,
DOMAIN, SERVICE_SET, locked_service_handler,
schema=SET_SERVICE_SCHEMA)

hass.services.async_register(
Expand Down
7 changes: 1 addition & 6 deletions homeassistant/components/http/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,12 +302,6 @@ async def serve_file(request):

async def start(self):
"""Start the aiohttp server."""
# We misunderstood the startup signal. You're not allowed to change
# anything during startup. Temp workaround.
# pylint: disable=protected-access
self.app._on_startup.freeze()
await self.app.startup()

if self.ssl_certificate:
try:
if self.ssl_profile == SSL_INTERMEDIATE:
Expand Down Expand Up @@ -335,6 +329,7 @@ async def start(self):
# However in Home Assistant components can be discovered after boot.
# This will now raise a RunTimeError.
# To work around this we now prevent the router from getting frozen
# pylint: disable=protected-access
self.app._router.freeze = lambda: None

self.runner = web.AppRunner(self.app)
Expand Down
11 changes: 6 additions & 5 deletions homeassistant/components/http/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from homeassistant.core import callback
from homeassistant.const import HTTP_HEADER_HA_AUTH
from homeassistant.auth.providers import legacy_api_password
from homeassistant.auth.util import generate_secret
from homeassistant.util import dt as dt_util

Expand Down Expand Up @@ -78,12 +79,16 @@ async def auth_middleware(request, handler):
request.headers[HTTP_HEADER_HA_AUTH].encode('utf-8'))):
# A valid auth header has been set
authenticated = True
request['hass_user'] = await legacy_api_password.async_get_user(
app['hass'])

elif (legacy_auth and DATA_API_PASSWORD in request.query and
hmac.compare_digest(
api_password.encode('utf-8'),
request.query[DATA_API_PASSWORD].encode('utf-8'))):
authenticated = True
request['hass_user'] = await legacy_api_password.async_get_user(
app['hass'])

elif _is_trusted_ip(request, trusted_networks):
authenticated = True
Expand All @@ -96,11 +101,7 @@ async def auth_middleware(request, handler):
request[KEY_AUTHENTICATED] = authenticated
return await handler(request)

async def auth_startup(app):
"""Initialize auth middleware when app starts up."""
app.middlewares.append(auth_middleware)

app.on_startup.append(auth_startup)
app.middlewares.append(auth_middleware)


def _is_trusted_ip(request, trusted_networks):
Expand Down
17 changes: 9 additions & 8 deletions homeassistant/components/http/ban.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from aiohttp.web_exceptions import HTTPForbidden, HTTPUnauthorized
import voluptuous as vol

from homeassistant.core import callback
from homeassistant.core import callback, HomeAssistant
from homeassistant.config import load_yaml_config_file
from homeassistant.exceptions import HomeAssistantError
import homeassistant.helpers.config_validation as cv
Expand All @@ -36,13 +36,14 @@
@callback
def setup_bans(hass, app, login_threshold):
"""Create IP Ban middleware for the app."""
app.middlewares.append(ban_middleware)
app[KEY_FAILED_LOGIN_ATTEMPTS] = defaultdict(int)
app[KEY_LOGIN_THRESHOLD] = login_threshold

async def ban_startup(app):
"""Initialize bans when app starts up."""
app.middlewares.append(ban_middleware)
app[KEY_BANNED_IPS] = await hass.async_add_job(
load_ip_bans_config, hass.config.path(IP_BANS_FILE))
app[KEY_FAILED_LOGIN_ATTEMPTS] = defaultdict(int)
app[KEY_LOGIN_THRESHOLD] = login_threshold
app[KEY_BANNED_IPS] = await async_load_ip_bans_config(
hass, hass.config.path(IP_BANS_FILE))

app.on_startup.append(ban_startup)

Expand Down Expand Up @@ -149,15 +150,15 @@ def __init__(self, ip_ban: str, banned_at: datetime = None) -> None:
self.banned_at = banned_at or datetime.utcnow()


def load_ip_bans_config(path: str):
async def async_load_ip_bans_config(hass: HomeAssistant, path: str):
"""Load list of banned IPs from config file."""
ip_list = []

if not os.path.isfile(path):
return ip_list

try:
list_ = load_yaml_config_file(path)
list_ = await hass.async_add_executor_job(load_yaml_config_file, path)
except HomeAssistantError as err:
_LOGGER.error('Unable to load %s: %s', path, str(err))
return ip_list
Expand Down
6 changes: 1 addition & 5 deletions homeassistant/components/http/real_ip.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,4 @@ async def real_ip_middleware(request, handler):

return await handler(request)

async def app_startup(app):
"""Initialize bans when app starts up."""
app.middlewares.append(real_ip_middleware)

app.on_startup.append(app_startup)
app.middlewares.append(real_ip_middleware)
Loading

0 comments on commit 3701c0f

Please sign in to comment.