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.92.1 #23448

Merged
merged 14 commits into from
Apr 26, 2019
Merged

0.92.1 #23448

2 changes: 1 addition & 1 deletion homeassistant/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ async def _async_set_up_integrations(
if stage_1_domains:
await asyncio.gather(*[
async_setup_component(hass, domain, config)
for domain in logging_domains
for domain in stage_1_domains
])

# Load all integrations
Expand Down
19 changes: 7 additions & 12 deletions homeassistant/components/broadlink/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import asyncio
from base64 import b64decode, b64encode
import logging
import re
import socket

from datetime import timedelta
Expand All @@ -19,26 +18,22 @@
DEFAULT_RETRY = 3


def ipv4_address(value):
"""Validate an ipv4 address."""
regex = re.compile(r'^\d+\.\d+\.\d+\.\d+$')
if not regex.match(value):
raise vol.Invalid('Invalid Ipv4 address, expected a.b.c.d')
return value


def data_packet(value):
"""Decode a data packet given for broadlink."""
return b64decode(cv.string(value))
value = cv.string(value)
extra = len(value) % 4
if extra > 0:
value = value + ('=' * (4 - extra))
return b64decode(value)


SERVICE_SEND_SCHEMA = vol.Schema({
vol.Required(CONF_HOST): ipv4_address,
vol.Required(CONF_HOST): cv.string,
vol.Required(CONF_PACKET): vol.All(cv.ensure_list, [data_packet])
})

SERVICE_LEARN_SCHEMA = vol.Schema({
vol.Required(CONF_HOST): ipv4_address,
vol.Required(CONF_HOST): cv.string,
})


Expand Down
8 changes: 4 additions & 4 deletions homeassistant/components/daikin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,10 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry):
if not daikin_api:
return False
hass.data.setdefault(DOMAIN, {}).update({entry.entry_id: daikin_api})
await asyncio.wait([
hass.config_entries.async_forward_entry_setup(entry, component)
for component in COMPONENT_TYPES
])
for component in COMPONENT_TYPES:
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(
entry, component))
return True


Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/ecovacs/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"name": "Ecovacs",
"documentation": "https://www.home-assistant.io/components/ecovacs",
"requirements": [
"sucks==0.9.3"
"sucks==0.9.4"
],
"dependencies": [],
"codeowners": [
Expand Down
5 changes: 2 additions & 3 deletions homeassistant/components/flux/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
"name": "Flux",
"documentation": "https://www.home-assistant.io/components/flux",
"requirements": [],
"dependencies": [
"light"
],
"dependencies": [],
"after_dependencies": ["light"],
"codeowners": []
}
2 changes: 1 addition & 1 deletion homeassistant/components/heos/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"name": "Heos",
"documentation": "https://www.home-assistant.io/components/heos",
"requirements": [
"pyheos==0.4.0"
"pyheos==0.4.1"
],
"dependencies": [],
"codeowners": [
Expand Down
10 changes: 9 additions & 1 deletion homeassistant/components/heos/media_player.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Denon HEOS Media Player."""
import asyncio
from functools import reduce, wraps
import logging
from operator import ior
Expand Down Expand Up @@ -47,7 +48,7 @@ async def wrapper(*args, **kwargs):
from pyheos import CommandError
try:
await func(*args, **kwargs)
except CommandError as ex:
except (CommandError, asyncio.TimeoutError, ConnectionError) as ex:
_LOGGER.error("Unable to %s: %s", command, ex)
return wrapper
return decorator
Expand Down Expand Up @@ -85,6 +86,13 @@ async def _controller_event(self, event):

async def _heos_event(self, event):
"""Handle connection event."""
from pyheos import CommandError, const
if event == const.EVENT_CONNECTED:
try:
await self._player.refresh()
except (CommandError, asyncio.TimeoutError, ConnectionError) as ex:
_LOGGER.error("Unable to refresh player %s: %s",
self._player, ex)
await self.async_update_ha_state(True)

async def _player_update(self, player_id, event):
Expand Down
47 changes: 25 additions & 22 deletions homeassistant/components/media_player/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@
SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PLAY, SUPPORT_PLAY_MEDIA,
SUPPORT_PREVIOUS_TRACK, SUPPORT_SEEK, SUPPORT_SELECT_SOUND_MODE,
SUPPORT_SELECT_SOURCE, SUPPORT_SHUFFLE_SET, SUPPORT_STOP, SUPPORT_TURN_OFF,
SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET)
SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET,
SUPPORT_VOLUME_STEP)
from .reproduce_state import async_reproduce_states # noqa

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -164,89 +165,89 @@ async def async_setup(hass, config):

component.async_register_entity_service(
SERVICE_TURN_ON, MEDIA_PLAYER_SCHEMA,
'async_turn_on', SUPPORT_TURN_ON
'async_turn_on', [SUPPORT_TURN_ON]
)
component.async_register_entity_service(
SERVICE_TURN_OFF, MEDIA_PLAYER_SCHEMA,
'async_turn_off', SUPPORT_TURN_OFF
'async_turn_off', [SUPPORT_TURN_OFF]
)
component.async_register_entity_service(
SERVICE_TOGGLE, MEDIA_PLAYER_SCHEMA,
'async_toggle', SUPPORT_TURN_OFF | SUPPORT_TURN_ON
'async_toggle', [SUPPORT_TURN_OFF | SUPPORT_TURN_ON]
)
component.async_register_entity_service(
SERVICE_VOLUME_UP, MEDIA_PLAYER_SCHEMA,
'async_volume_up', SUPPORT_VOLUME_SET
'async_volume_up', [SUPPORT_VOLUME_SET, SUPPORT_VOLUME_STEP]
)
component.async_register_entity_service(
SERVICE_VOLUME_DOWN, MEDIA_PLAYER_SCHEMA,
'async_volume_down', SUPPORT_VOLUME_SET
'async_volume_down', [SUPPORT_VOLUME_SET, SUPPORT_VOLUME_STEP]
)
component.async_register_entity_service(
SERVICE_MEDIA_PLAY_PAUSE, MEDIA_PLAYER_SCHEMA,
'async_media_play_pause', SUPPORT_PLAY | SUPPORT_PAUSE
'async_media_play_pause', [SUPPORT_PLAY | SUPPORT_PAUSE]
)
component.async_register_entity_service(
SERVICE_MEDIA_PLAY, MEDIA_PLAYER_SCHEMA,
'async_media_play', SUPPORT_PLAY
'async_media_play', [SUPPORT_PLAY]
)
component.async_register_entity_service(
SERVICE_MEDIA_PAUSE, MEDIA_PLAYER_SCHEMA,
'async_media_pause', SUPPORT_PAUSE
'async_media_pause', [SUPPORT_PAUSE]
)
component.async_register_entity_service(
SERVICE_MEDIA_STOP, MEDIA_PLAYER_SCHEMA,
'async_media_stop', SUPPORT_STOP
'async_media_stop', [SUPPORT_STOP]
)
component.async_register_entity_service(
SERVICE_MEDIA_NEXT_TRACK, MEDIA_PLAYER_SCHEMA,
'async_media_next_track', SUPPORT_NEXT_TRACK
'async_media_next_track', [SUPPORT_NEXT_TRACK]
)
component.async_register_entity_service(
SERVICE_MEDIA_PREVIOUS_TRACK, MEDIA_PLAYER_SCHEMA,
'async_media_previous_track', SUPPORT_PREVIOUS_TRACK
'async_media_previous_track', [SUPPORT_PREVIOUS_TRACK]
)
component.async_register_entity_service(
SERVICE_CLEAR_PLAYLIST, MEDIA_PLAYER_SCHEMA,
'async_clear_playlist', SUPPORT_CLEAR_PLAYLIST
'async_clear_playlist', [SUPPORT_CLEAR_PLAYLIST]
)
component.async_register_entity_service(
SERVICE_VOLUME_SET, MEDIA_PLAYER_SET_VOLUME_SCHEMA,
lambda entity, call: entity.async_set_volume_level(
volume=call.data[ATTR_MEDIA_VOLUME_LEVEL]),
SUPPORT_VOLUME_SET
[SUPPORT_VOLUME_SET]
)
component.async_register_entity_service(
SERVICE_VOLUME_MUTE, MEDIA_PLAYER_MUTE_VOLUME_SCHEMA,
lambda entity, call: entity.async_mute_volume(
mute=call.data[ATTR_MEDIA_VOLUME_MUTED]),
SUPPORT_VOLUME_MUTE
[SUPPORT_VOLUME_MUTE]
)
component.async_register_entity_service(
SERVICE_MEDIA_SEEK, MEDIA_PLAYER_MEDIA_SEEK_SCHEMA,
lambda entity, call: entity.async_media_seek(
position=call.data[ATTR_MEDIA_SEEK_POSITION]),
SUPPORT_SEEK
[SUPPORT_SEEK]
)
component.async_register_entity_service(
SERVICE_SELECT_SOURCE, MEDIA_PLAYER_SELECT_SOURCE_SCHEMA,
'async_select_source', SUPPORT_SELECT_SOURCE
'async_select_source', [SUPPORT_SELECT_SOURCE]
)
component.async_register_entity_service(
SERVICE_SELECT_SOUND_MODE, MEDIA_PLAYER_SELECT_SOUND_MODE_SCHEMA,
'async_select_sound_mode', SUPPORT_SELECT_SOUND_MODE
'async_select_sound_mode', [SUPPORT_SELECT_SOUND_MODE]
)
component.async_register_entity_service(
SERVICE_PLAY_MEDIA, MEDIA_PLAYER_PLAY_MEDIA_SCHEMA,
lambda entity, call: entity.async_play_media(
media_type=call.data[ATTR_MEDIA_CONTENT_TYPE],
media_id=call.data[ATTR_MEDIA_CONTENT_ID],
enqueue=call.data.get(ATTR_MEDIA_ENQUEUE)
), SUPPORT_PLAY_MEDIA
), [SUPPORT_PLAY_MEDIA]
)
component.async_register_entity_service(
SERVICE_SHUFFLE_SET, MEDIA_PLAYER_SET_SHUFFLE_SCHEMA,
'async_set_shuffle', SUPPORT_SHUFFLE_SET
'async_set_shuffle', [SUPPORT_SHUFFLE_SET]
)

return True
Expand Down Expand Up @@ -686,7 +687,8 @@ async def async_volume_up(self):
await self.hass.async_add_job(self.volume_up)
return

if self.volume_level < 1:
if self.volume_level < 1 \
and self.supported_features & SUPPORT_VOLUME_SET:
await self.async_set_volume_level(min(1, self.volume_level + .1))

async def async_volume_down(self):
Expand All @@ -699,7 +701,8 @@ async def async_volume_down(self):
await self.hass.async_add_job(self.volume_down)
return

if self.volume_level > 0:
if self.volume_level > 0 \
and self.supported_features & SUPPORT_VOLUME_SET:
await self.async_set_volume_level(
max(0, self.volume_level - .1))

Expand Down
19 changes: 11 additions & 8 deletions homeassistant/components/netatmo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import homeassistant.helpers.config_validation as cv
from homeassistant.util import Throttle

from .const import DOMAIN, DATA_NETATMO_AUTH

_LOGGER = logging.getLogger(__name__)

DATA_PERSONS = 'netatmo_persons'
Expand All @@ -20,8 +22,6 @@
CONF_SECRET_KEY = 'secret_key'
CONF_WEBHOOKS = 'webhooks'

DOMAIN = 'netatmo'

SERVICE_ADDWEBHOOK = 'addwebhook'
SERVICE_DROPWEBHOOK = 'dropwebhook'

Expand Down Expand Up @@ -83,10 +83,9 @@ def setup(hass, config):
"""Set up the Netatmo devices."""
import pyatmo

global NETATMO_AUTH
hass.data[DATA_PERSONS] = {}
try:
NETATMO_AUTH = pyatmo.ClientAuth(
auth = pyatmo.ClientAuth(
config[DOMAIN][CONF_API_KEY], config[DOMAIN][CONF_SECRET_KEY],
config[DOMAIN][CONF_USERNAME], config[DOMAIN][CONF_PASSWORD],
'read_station read_camera access_camera '
Expand All @@ -96,6 +95,9 @@ def setup(hass, config):
_LOGGER.error("Unable to connect to Netatmo API")
return False

# Store config to be used during entry setup
hass.data[DATA_NETATMO_AUTH] = auth

if config[DOMAIN][CONF_DISCOVERY]:
for component in 'camera', 'sensor', 'binary_sensor', 'climate':
discovery.load_platform(hass, component, DOMAIN, {}, config)
Expand All @@ -107,7 +109,7 @@ def setup(hass, config):
webhook_id)
hass.components.webhook.async_register(
DOMAIN, 'Netatmo', webhook_id, handle_webhook)
NETATMO_AUTH.addwebhook(hass.data[DATA_WEBHOOK_URL])
auth.addwebhook(hass.data[DATA_WEBHOOK_URL])
hass.bus.listen_once(
EVENT_HOMEASSISTANT_STOP, dropwebhook)

Expand All @@ -117,7 +119,7 @@ def _service_addwebhook(service):
if url is None:
url = hass.data[DATA_WEBHOOK_URL]
_LOGGER.info("Adding webhook for URL: %s", url)
NETATMO_AUTH.addwebhook(url)
auth.addwebhook(url)

hass.services.register(
DOMAIN, SERVICE_ADDWEBHOOK, _service_addwebhook,
Expand All @@ -126,7 +128,7 @@ def _service_addwebhook(service):
def _service_dropwebhook(service):
"""Service to drop webhooks during runtime."""
_LOGGER.info("Dropping webhook")
NETATMO_AUTH.dropwebhook()
auth.dropwebhook()

hass.services.register(
DOMAIN, SERVICE_DROPWEBHOOK, _service_dropwebhook,
Expand All @@ -137,7 +139,8 @@ def _service_dropwebhook(service):

def dropwebhook(hass):
"""Drop the webhook subscription."""
NETATMO_AUTH.dropwebhook()
auth = hass.data[DATA_NETATMO_AUTH]
auth.dropwebhook()


async def handle_webhook(hass, webhook_id, request):
Expand Down
8 changes: 6 additions & 2 deletions homeassistant/components/netatmo/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
from homeassistant.const import CONF_TIMEOUT
from homeassistant.helpers import config_validation as cv

from . import CameraData, NETATMO_AUTH
from .const import DATA_NETATMO_AUTH
from . import CameraData

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -59,8 +60,11 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
module_name = None

import pyatmo

auth = hass.data[DATA_NETATMO_AUTH]

try:
data = CameraData(hass, NETATMO_AUTH, home)
data = CameraData(hass, auth, home)
if not data.get_camera_names():
return None
except pyatmo.NoDevice:
Expand Down
8 changes: 6 additions & 2 deletions homeassistant/components/netatmo/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
from homeassistant.const import CONF_VERIFY_SSL
from homeassistant.helpers import config_validation as cv

from . import CameraData, NETATMO_AUTH
from .const import DATA_NETATMO_AUTH
from . import CameraData

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -37,8 +38,11 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
verify_ssl = config.get(CONF_VERIFY_SSL, True)
quality = config.get(CONF_QUALITY, DEFAULT_QUALITY)
import pyatmo

auth = hass.data[DATA_NETATMO_AUTH]

try:
data = CameraData(hass, NETATMO_AUTH, home)
data = CameraData(hass, auth, home)
for camera_name in data.get_camera_names():
camera_type = data.get_camera_type(camera=camera_name, home=home)
if CONF_CAMERAS in config:
Expand Down