Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

0.83.1 #18811

Merged
merged 14 commits into from
Nov 29, 2018
Merged

0.83.1 #18811

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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