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 13 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
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
5 changes: 5 additions & 0 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 Down
6 changes: 6 additions & 0 deletions homeassistant/components/logbook.py
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,12 @@ def _exclude_events(events, entities_filter):
domain = event.data.get(ATTR_DOMAIN)
entity_id = event.data.get(ATTR_ENTITY_ID)

elif event.event_type == EVENT_ALEXA_SMART_HOME:
domain = 'alexa'

elif event.event_type == EVENT_HOMEKIT_CHANGED:
domain = DOMAIN_HOMEKIT

if not entity_id and domain:
entity_id = "%s." % (domain, )

Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/owntracks/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ async def async_step_user(self, user_input=None):

if supports_encryption():
secret_desc = (
"The encryption key is {secret} "
"(on Android under preferences -> advanced)")
"The encryption key is {} "
"(on Android under preferences -> advanced)".format(secret))
else:
secret_desc = (
"Encryption is not supported because libsodium is not "
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/sensor/rainmachine.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def unit_of_measurement(self):
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
2 changes: 1 addition & 1 deletion homeassistant/components/sensor/seventeentrack.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from homeassistant.helpers.entity import Entity
from homeassistant.util import Throttle, slugify

REQUIREMENTS = ['py17track==2.0.2']
REQUIREMENTS = ['py17track==2.1.0']
_LOGGER = logging.getLogger(__name__)

ATTR_DESTINATION_COUNTRY = 'destination_country'
Expand Down
51 changes: 51 additions & 0 deletions homeassistant/components/shopping_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,28 @@
})

WS_TYPE_SHOPPING_LIST_ITEMS = 'shopping_list/items'
WS_TYPE_SHOPPING_LIST_ADD_ITEM = 'shopping_list/items/add'
WS_TYPE_SHOPPING_LIST_UPDATE_ITEM = 'shopping_list/items/update'

SCHEMA_WEBSOCKET_ITEMS = \
websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({
vol.Required('type'): WS_TYPE_SHOPPING_LIST_ITEMS
})

SCHEMA_WEBSOCKET_ADD_ITEM = \
websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({
vol.Required('type'): WS_TYPE_SHOPPING_LIST_ADD_ITEM,
vol.Required('name'): str
})

SCHEMA_WEBSOCKET_UPDATE_ITEM = \
websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({
vol.Required('type'): WS_TYPE_SHOPPING_LIST_UPDATE_ITEM,
vol.Required('item_id'): str,
vol.Optional('name'): str,
vol.Optional('complete'): bool
})


@asyncio.coroutine
def async_setup(hass, config):
Expand Down Expand Up @@ -103,6 +119,14 @@ def complete_item_service(call):
WS_TYPE_SHOPPING_LIST_ITEMS,
websocket_handle_items,
SCHEMA_WEBSOCKET_ITEMS)
hass.components.websocket_api.async_register_command(
WS_TYPE_SHOPPING_LIST_ADD_ITEM,
websocket_handle_add,
SCHEMA_WEBSOCKET_ADD_ITEM)
hass.components.websocket_api.async_register_command(
WS_TYPE_SHOPPING_LIST_UPDATE_ITEM,
websocket_handle_update,
SCHEMA_WEBSOCKET_UPDATE_ITEM)

return True

Expand Down Expand Up @@ -276,3 +300,30 @@ def websocket_handle_items(hass, connection, msg):
"""Handle get shopping_list items."""
connection.send_message(websocket_api.result_message(
msg['id'], hass.data[DOMAIN].items))


@callback
def websocket_handle_add(hass, connection, msg):
"""Handle add item to shopping_list."""
item = hass.data[DOMAIN].async_add(msg['name'])
hass.bus.async_fire(EVENT)
connection.send_message(websocket_api.result_message(
msg['id'], item))


@websocket_api.async_response
async def websocket_handle_update(hass, connection, msg):
"""Handle update shopping_list item."""
msg_id = msg.pop('id')
item_id = msg.pop('item_id')
msg.pop('type')
data = msg

try:
item = hass.data[DOMAIN].async_update(item_id, data)
hass.bus.async_fire(EVENT)
connection.send_message(websocket_api.result_message(
msg_id, item))
except KeyError:
connection.send_message(websocket_api.error_message(
msg_id, 'item_not_found', 'Item not found'))
2 changes: 1 addition & 1 deletion homeassistant/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"""Constants used by Home Assistant components."""
MAJOR_VERSION = 0
MINOR_VERSION = 83
PATCH_VERSION = '0'
PATCH_VERSION = '1'
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
REQUIRED_PYTHON_VER = (3, 5, 3)
Expand Down
2 changes: 1 addition & 1 deletion requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -804,7 +804,7 @@ py-melissa-climate==2.0.0
py-synology==0.2.0

# homeassistant.components.sensor.seventeentrack
py17track==2.0.2
py17track==2.1.0

# homeassistant.components.hdmi_cec
pyCEC==0.4.13
Expand Down
Loading