Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
fa79aea
Bumped version to 0.74.0b0
balloob Jun 29, 2018
56bbadb
Version bump to 0.73.0b0
balloob Jun 29, 2018
5d6db9a
Bump frontend to 20180701.0
balloob Jul 1, 2018
c3ad30e
Rachio webhooks (#15111)
Klikini Jul 1, 2018
11ba7cc
Only create front-end client_id once (#15214)
awarecan Jul 1, 2018
4740173
Make LIFX color/temperature attributes mutually exclusive (#15234)
amelchio Jul 1, 2018
311a440
Fix an issue when user's nest developer account don't have permission…
awarecan Jul 1, 2018
c978281
Revert some changes to setup.py (#15248)
cdce8p Jul 1, 2018
279fd39
Bumped version to 0.73.0b1
balloob Jul 1, 2018
63b28aa
By default to use access_token if hass.auth.active (#15212)
awarecan Jul 1, 2018
3c3a53a
Update translations
balloob Jul 2, 2018
855cbc0
Update frontend to 20180702.0
balloob Jul 2, 2018
c39e6b9
Bumped version to 0.73.0b2
balloob Jul 2, 2018
0dc155c
Bump frontend to 20180702.1
balloob Jul 2, 2018
d7fd924
Bumped version to 0.73.0b3
balloob Jul 2, 2018
1c52596
Bump frontend to 20180703.0
balloob Jul 3, 2018
b82371f
Bumped version to 0.73.0b4
balloob Jul 3, 2018
cb458b7
Bump frontend to 20180703.1
balloob Jul 3, 2018
07dde62
Bumped version to 0.73.0b5
balloob Jul 3, 2018
852526e
Bump frontend to 20180704.0
balloob Jul 4, 2018
46de89e
Bumped version to 0.73.0b6
balloob Jul 4, 2018
1e7cfc0
Bumped version to 0.73.0
balloob Jul 6, 2018
a1d8b0e
Merge pull request #15330 from home-assistant/rc
balloob Jul 6, 2018
f8f8da9
Upgrade youtube_dl to 2018.07.04 (#15323)
fabaff Jul 7, 2018
b5c7afc
Upgrade keyring to 13.2.0 (#15322)
fabaff Jul 7, 2018
dabbd7b
Upgrade pytest to 3.6.3 (#15332)
scop Jul 7, 2018
bd62248
Add original message as dialogflow_query parameter (#15304)
quazzie Jul 7, 2018
02238b6
Add python 3.7 to travis and tox (#14523)
andrey-git Jul 7, 2018
b333dba
Bump frontend to 20180708.0
balloob Jul 8, 2018
b327ea2
Bump frontend to 20180708.0
balloob Jul 8, 2018
e6dd4f6
Bumped version to 0.73.1
balloob Jul 8, 2018
a2a4c63
Merge branch 'rc'
balloob Jul 8, 2018
703d71c
Frontend: Allow overriding default url when added to home screen (#15…
sjabby Jul 8, 2018
1ff329d
Add HomematicIP Cloud light power consumption and energie attributes …
worm-ee Jul 9, 2018
ec3d2e9
fix camera.push API overwrite (#15334)
dgomes Jul 9, 2018
287b1bc
Add support for multi-channel enocean switches (D2-01-12 profile) (#1…
NoUsername Jul 9, 2018
b9eb008
Add sound mode support (#14910)
starkillerOG Jul 9, 2018
1d1408b
Fixed issue 15340. alexa/smart_home module can now skip properties th…
iliketoprogram14 Jul 9, 2018
f7d7d82
Efergy (#15380)
fabaff Jul 9, 2018
0d4841c
Use IndieAuth for client ID (#15369)
balloob Jul 9, 2018
57977bc
Bump frontend to 20180709.0
balloob Jul 9, 2018
3b93fa8
Add httplib2 to h.c.google requirements (#15385)
scop Jul 9, 2018
14a34f8
Remove some unneeded pylint import-error disables (#15386)
scop Jul 9, 2018
6ee8d9b
Update ha-philipsjs to 0.0.5 (#15378)
danielperna84 Jul 9, 2018
e62bb29
Add new voices to Amazon Polly (#15320)
hanzoh Jul 9, 2018
c5a2ffb
Add Cloudflare DNS component. (#15388)
ludeeus Jul 9, 2018
df8c594
Add Facebox teach service (#14998)
robmarkcole Jul 10, 2018
dbdd0a1
Expire auth code after 10 minutes (#15381)
balloob Jul 10, 2018
2ee62b1
Bump frontend to 20180710.0
balloob Jul 10, 2018
9ea0c40
Improve NetAtmo sensors update logic (#14866)
glpatcern Jul 10, 2018
b65d7da
removed unused return (#15402)
ludeeus Jul 10, 2018
f32098a
Fix confused brightness of xiaomi_aqara gateway light (#15314)
amelchio Jul 10, 2018
fd568d7
Fix liveboxplaytv empty channel list (#15404)
pschmitt Jul 10, 2018
1f6331c
Fix credentials lookup (#15409)
balloob Jul 10, 2018
6197fe0
Change Ring binary_sensor frequency polling to avoid rate limit excee…
tchellomello Jul 11, 2018
43b31e8
Add Tuya component and switch support (#15399)
huangyupeng Jul 12, 2018
5342333
Merge branch 'master' into dev
balloob Jul 12, 2018
4c6394b
Fix HomeMatic variables (#15417)
danielperna84 Jul 12, 2018
c587536
Ignore some HomeKit devices (#15316)
mjg59 Jul 12, 2018
b557c17
Make LimitlessLED color/temperature attributes mutually exclusive (#1…
amelchio Jul 12, 2018
4a6afc5
Add HomematicIP alarm control panel (#15342)
worm-ee Jul 13, 2018
6e3ec97
Include request.path in legacy api password warning message (#15438)
awarecan Jul 13, 2018
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
5 changes: 5 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ omit =
homeassistant/components/cast/*
homeassistant/components/*/cast.py

homeassistant/components/cloudflare.py

homeassistant/components/comfoconnect.py
homeassistant/components/*/comfoconnect.py

Expand Down Expand Up @@ -341,6 +343,9 @@ omit =
homeassistant/components/zoneminder.py
homeassistant/components/*/zoneminder.py

homeassistant/components/tuya.py
homeassistant/components/*/tuya.py

homeassistant/components/alarm_control_panel/alarmdotcom.py
homeassistant/components/alarm_control_panel/canary.py
homeassistant/components/alarm_control_panel/concord232.py
Expand Down
5 changes: 3 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ matrix:
env: TOXENV=py35
- python: "3.6"
env: TOXENV=py36
# - python: "3.6-dev"
# env: TOXENV=py36
- python: "3.7"
env: TOXENV=py37
dist: xenial
# allow_failures:
# - python: "3.5"
# env: TOXENV=typing
Expand Down
96 changes: 6 additions & 90 deletions homeassistant/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,16 +186,6 @@ class Credentials:
is_new = attr.ib(type=bool, default=True)


@attr.s(slots=True)
class Client:
"""Client that interacts with Home Assistant on behalf of a user."""

name = attr.ib(type=str)
id = attr.ib(type=str, default=attr.Factory(lambda: uuid.uuid4().hex))
secret = attr.ib(type=str, default=attr.Factory(generate_secret))
redirect_uris = attr.ib(type=list, default=attr.Factory(list))


async def load_auth_provider_module(hass, provider):
"""Load an auth provider."""
try:
Expand Down Expand Up @@ -321,10 +311,7 @@ async def async_get_or_create_user(self, credentials):
if not credentials.is_new:
for user in await self._store.async_get_users():
for creds in user.credentials:
if (creds.auth_provider_type ==
credentials.auth_provider_type
and creds.auth_provider_id ==
credentials.auth_provider_id):
if creds.id == credentials.id:
return user

raise ValueError('Unable to find the user.')
Expand Down Expand Up @@ -356,20 +343,20 @@ async def async_remove_user(self, user):
"""Remove a user."""
await self._store.async_remove_user(user)

async def async_create_refresh_token(self, user, client=None):
async def async_create_refresh_token(self, user, client_id=None):
"""Create a new refresh token for a user."""
if not user.is_active:
raise ValueError('User is not active')

if user.system_generated and client is not None:
if user.system_generated and client_id is not None:
raise ValueError(
'System generated users cannot have refresh tokens connected '
'to a client.')

if not user.system_generated and client is None:
if not user.system_generated and client_id is None:
raise ValueError('Client is required to generate a refresh token.')

return await self._store.async_create_refresh_token(user, client)
return await self._store.async_create_refresh_token(user, client_id)

async def async_get_refresh_token(self, token):
"""Get refresh token by token."""
Expand All @@ -396,26 +383,6 @@ def async_get_access_token(self, token):

return tkn

async def async_create_client(self, name, *, redirect_uris=None,
no_secret=False):
"""Create a new client."""
return await self._store.async_create_client(
name, redirect_uris, no_secret)

async def async_get_or_create_client(self, name, *, redirect_uris=None,
no_secret=False):
"""Find a client, if not exists, create a new one."""
for client in await self._store.async_get_clients():
if client.name == name:
return client

return await self._store.async_create_client(
name, redirect_uris, no_secret)

async def async_get_client(self, client_id):
"""Get a client."""
return await self._store.async_get_client(client_id)

async def _async_create_login_flow(self, handler, *, source, data):
"""Create a login flow."""
auth_provider = self._providers[handler]
Expand Down Expand Up @@ -456,7 +423,6 @@ def __init__(self, hass):
"""Initialize the auth store."""
self.hass = hass
self._users = None
self._clients = None
self._store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY)

async def async_get_users(self):
Expand Down Expand Up @@ -515,9 +481,8 @@ async def async_remove_user(self, user):
self._users.pop(user.id)
await self.async_save()

async def async_create_refresh_token(self, user, client=None):
async def async_create_refresh_token(self, user, client_id=None):
"""Create a new token for a user."""
client_id = client.id if client is not None else None
refresh_token = RefreshToken(user=user, client_id=client_id)
user.refresh_tokens[refresh_token.token] = refresh_token
await self.async_save()
Expand All @@ -535,38 +500,6 @@ async def async_get_refresh_token(self, token):

return None

async def async_create_client(self, name, redirect_uris, no_secret):
"""Create a new client."""
if self._clients is None:
await self.async_load()

kwargs = {
'name': name,
'redirect_uris': redirect_uris
}

if no_secret:
kwargs['secret'] = None

client = Client(**kwargs)
self._clients[client.id] = client
await self.async_save()
return client

async def async_get_clients(self):
"""Return all clients."""
if self._clients is None:
await self.async_load()

return list(self._clients.values())

async def async_get_client(self, client_id):
"""Get a client."""
if self._clients is None:
await self.async_load()

return self._clients.get(client_id)

async def async_load(self):
"""Load the users."""
data = await self._store.async_load()
Expand All @@ -578,7 +511,6 @@ async def async_load(self):

if data is None:
self._users = {}
self._clients = {}
return

users = {
Expand Down Expand Up @@ -618,12 +550,7 @@ async def async_load(self):
)
refresh_token.access_tokens.append(token)

clients = {
cl_dict['id']: Client(**cl_dict) for cl_dict in data['clients']
}

self._users = users
self._clients = clients

async def async_save(self):
"""Save users."""
Expand Down Expand Up @@ -676,19 +603,8 @@ async def async_save(self):
for access_token in refresh_token.access_tokens
]

clients = [
{
'id': client.id,
'name': client.name,
'secret': client.secret,
'redirect_uris': client.redirect_uris,
}
for client in self._clients.values()
]

data = {
'users': users,
'clients': clients,
'credentials': credentials,
'access_tokens': access_tokens,
'refresh_tokens': refresh_tokens,
Expand Down
13 changes: 12 additions & 1 deletion homeassistant/components/alarm_control_panel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ def alarm_arm_custom_bypass(hass, code=None, entity_id=None):
@asyncio.coroutine
def async_setup(hass, config):
"""Track states and offer events for sensors."""
component = EntityComponent(
component = hass.data[DOMAIN] = EntityComponent(
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL)

yield from component.async_setup(config)
Expand Down Expand Up @@ -154,6 +154,17 @@ def async_alarm_service_handler(service):
return True


async def async_setup_entry(hass, entry):
"""Setup a config entry."""
return await hass.data[DOMAIN].async_setup_entry(entry)


async def async_unload_entry(hass, entry):
"""Unload a config entry."""
return await hass.data[DOMAIN].async_unload_entry(entry)


# pylint: disable=no-self-use
class AlarmControlPanel(Entity):
"""An abstract class for alarm control devices."""

Expand Down
88 changes: 88 additions & 0 deletions homeassistant/components/alarm_control_panel/homematicip_cloud.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
"""
Support for HomematicIP alarm control panel.

For more details about this component, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.homematicip_cloud/
"""

import logging

from homeassistant.const import (
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED,
STATE_ALARM_TRIGGERED)
from homeassistant.components.alarm_control_panel import AlarmControlPanel
from homeassistant.components.homematicip_cloud import (
HomematicipGenericDevice, DOMAIN as HMIPC_DOMAIN,
HMIPC_HAPID)


DEPENDENCIES = ['homematicip_cloud']

_LOGGER = logging.getLogger(__name__)

HMIP_OPEN = 'OPEN'
HMIP_ZONE_AWAY = 'EXTERNAL'
HMIP_ZONE_HOME = 'INTERNAL'


async def async_setup_platform(hass, config, async_add_devices,
discovery_info=None):
"""Set up the HomematicIP alarm control devices."""
pass


async def async_setup_entry(hass, config_entry, async_add_devices):
"""Set up the HomematicIP alarm control panel from a config entry."""
from homematicip.aio.group import AsyncSecurityZoneGroup

home = hass.data[HMIPC_DOMAIN][config_entry.data[HMIPC_HAPID]].home
devices = []
for group in home.groups:
if isinstance(group, AsyncSecurityZoneGroup):
devices.append(HomematicipSecurityZone(home, group))

if devices:
async_add_devices(devices)


class HomematicipSecurityZone(HomematicipGenericDevice, AlarmControlPanel):
"""Representation of an HomematicIP security zone group."""

def __init__(self, home, device):
"""Initialize the security zone group."""
device.modelType = 'Group-SecurityZone'
device.windowState = ''
super().__init__(home, device)

@property
def state(self):
"""Return the state of the device."""
if self._device.active:
if (self._device.sabotage or self._device.motionDetected or
self._device.windowState == HMIP_OPEN):
return STATE_ALARM_TRIGGERED

if self._device.label == HMIP_ZONE_HOME:
return STATE_ALARM_ARMED_HOME
return STATE_ALARM_ARMED_AWAY

return STATE_ALARM_DISARMED

async def async_alarm_disarm(self, code=None):
"""Send disarm command."""
await self._home.set_security_zones_activation(False, False)

async def async_alarm_arm_home(self, code=None):
"""Send arm home command."""
await self._home.set_security_zones_activation(True, False)

async def async_alarm_arm_away(self, code=None):
"""Send arm away command."""
await self._home.set_security_zones_activation(True, True)

@property
def device_state_attributes(self):
"""Return the state attributes of the alarm control device."""
# The base class is loading the battery property, but device doesn't
# have this property - base class needs clean-up.
return None
20 changes: 13 additions & 7 deletions homeassistant/components/alexa/smart_home.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,11 +270,14 @@ def serialize_properties(self):
"""Return properties serialized for an API response."""
for prop in self.properties_supported():
prop_name = prop['name']
yield {
'name': prop_name,
'namespace': self.name(),
'value': self.get_property(prop_name),
}
# pylint: disable=assignment-from-no-return
prop_value = self.get_property(prop_name)
if prop_value is not None:
yield {
'name': prop_name,
'namespace': self.name(),
'value': prop_value,
}


class _AlexaPowerController(_AlexaInterface):
Expand Down Expand Up @@ -438,14 +441,17 @@ def get_property(self, name):
unit = self.entity.attributes[CONF_UNIT_OF_MEASUREMENT]
temp = None
if name == 'targetSetpoint':
temp = self.entity.attributes.get(ATTR_TEMPERATURE)
temp = self.entity.attributes.get(climate.ATTR_TEMPERATURE)
elif name == 'lowerSetpoint':
temp = self.entity.attributes.get(climate.ATTR_TARGET_TEMP_LOW)
elif name == 'upperSetpoint':
temp = self.entity.attributes.get(climate.ATTR_TARGET_TEMP_HIGH)
if temp is None:
else:
raise _UnsupportedProperty(name)

if temp is None:
return None

return {
'value': float(temp),
'scale': API_TEMP_UNITS[unit],
Expand Down
Loading