Skip to content

Commit

Permalink
Merge pull request #324 from alandtse/#260
Browse files Browse the repository at this point in the history
fix: add catch for HomeAssistantError when adding entities
  • Loading branch information
alandtse committed Sep 3, 2019
2 parents 6dd1bc3 + bcc0de7 commit ae11e52
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 112 deletions.
42 changes: 26 additions & 16 deletions custom_components/alexa_media/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,8 @@ async def test_login_status(hass, config, login,
"""Test the login status and spawn requests for info."""
_LOGGER.debug("Testing login status: %s", login.status)
if 'login_successful' in login.status and login.status['login_successful']:
_LOGGER.debug("Setting up Alexa devices")
_LOGGER.debug("Setting up Alexa devices for %s",
hide_email(login.email))
await hass.async_add_job(setup_alexa, hass, config,
login)
return
Expand Down Expand Up @@ -297,7 +298,7 @@ async def test_login_status(hass, config, login,
async def setup_alexa(hass, config, login_obj):
"""Set up a alexa api based on host parameter."""
@util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
async def update_devices():
async def update_devices(login_obj):
"""Ping Alexa API to identify all devices, bluetooth, and last called device.
This will add new devices and services when discovered. By default this
Expand All @@ -311,11 +312,16 @@ async def update_devices():
Each AlexaAPI call generally results in two webpage requests.
"""
from alexapy import AlexaAPI
email: Text = login_obj.email
existing_serials = (hass.data[DATA_ALEXAMEDIA]
['accounts']
[email]
['entities']
['media_player'].keys())
['media_player'].keys() if 'entities' in (
hass.data[DATA_ALEXAMEDIA]
['accounts']
[email])
else [])
existing_entities = (hass.data[DATA_ALEXAMEDIA]
['accounts']
[email]
Expand All @@ -338,18 +344,19 @@ async def update_devices():
if ((devices is None or bluetooth is None)
and not (hass.data[DATA_ALEXAMEDIA]
['accounts'][email]['config'])):
_LOGGER.debug("Alexa API disconnected; attempting to relogin")
_LOGGER.debug("%s: Alexa API disconnected; attempting to relogin",
hide_email(email))
await login_obj.login()
await test_login_status(hass,
config, login_obj, setup_platform_callback)
return

new_alexa_clients = [] # list of newly discovered device names
excluded = []
included = []
exclude_filter = []
include_filter = []
for device in devices:
if include and device['accountName'] not in include:
included.append(device['accountName'])
include_filter.append(device['accountName'])
if 'appDeviceList' in device:
for app in device['appDeviceList']:
(hass.data[DATA_ALEXAMEDIA]
Expand All @@ -364,7 +371,7 @@ async def update_devices():
[device['serialNumber']]) = device
continue
elif exclude and device['accountName'] in exclude:
excluded.append(device['accountName'])
exclude_filter.append(device['accountName'])
if 'appDeviceList' in device:
for app in device['appDeviceList']:
(hass.data[DATA_ALEXAMEDIA]
Expand Down Expand Up @@ -410,27 +417,30 @@ async def update_devices():
if device['serialNumber'] not in existing_serials:
new_alexa_clients.append(device['accountName'])
_LOGGER.debug("%s: Existing: %s New: %s;"
" Filtered by: include_devices: %s exclude_devices:%s",
" Filtered out by not being in include: %s "
"or in exclude: %s",
hide_email(email),
list(existing_entities),
new_alexa_clients,
included,
excluded)
include_filter,
exclude_filter)

if new_alexa_clients:
for component in ALEXA_COMPONENTS:
hass.async_create_task(
async_load_platform(hass,
component,
DOMAIN,
{CONF_NAME: DOMAIN},
{CONF_NAME: DOMAIN, "config": config},
config))

# Process last_called data to fire events
await update_last_called(login_obj)
scan_interval = config.get(CONF_SCAN_INTERVAL)
async_call_later(hass, scan_interval.total_seconds(), lambda _:
hass.async_create_task(update_devices()))
hass.async_create_task(
update_devices(login_obj,
no_throttle=True)))

async def update_last_called(login_obj, last_called=None):
"""Update the last called device for the login_obj.
Expand Down Expand Up @@ -643,7 +653,7 @@ async def ws_handler(message_obj):
_LOGGER.debug("Discovered new media_player %s", serial)
(hass.data[DATA_ALEXAMEDIA]
['accounts'][email]['new_devices']) = True
await update_devices(no_throttle=True)
await update_devices(login_obj, no_throttle=True)

async def ws_open_handler():
"""Handle websocket open."""
Expand Down Expand Up @@ -678,7 +688,7 @@ async def ws_close_handler():
hide_email(email))
(hass.data[DATA_ALEXAMEDIA]['accounts']
[email]['websocket']) = None
await update_devices()
await update_devices(login_obj, no_throttle=True)

async def ws_error_handler(message):
"""Handle websocket error.
Expand Down Expand Up @@ -714,7 +724,7 @@ async def ws_error_handler(message):
['accounts'][email]['new_devices']) = True # force initial update
(hass.data[DATA_ALEXAMEDIA]['accounts'][email]['websocket']) = \
await ws_connect()
await update_devices()
await update_devices(login_obj, no_throttle=True)
hass.services.async_register(DOMAIN, SERVICE_UPDATE_LAST_CALLED,
last_call_handler,
schema=LAST_CALL_UPDATE_SCHEMA)
Expand Down
59 changes: 31 additions & 28 deletions custom_components/alexa_media/alarm_control_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@
from homeassistant.components.alarm_control_panel import AlarmControlPanel
from homeassistant.const import (STATE_ALARM_ARMED_AWAY,
STATE_ALARM_DISARMED)
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.event import async_call_later

from . import DATA_ALEXAMEDIA
from . import DOMAIN as ALEXA_DOMAIN
from . import MIN_TIME_BETWEEN_FORCED_SCANS, MIN_TIME_BETWEEN_SCANS, hide_email
from . import (
CONF_EMAIL,
MIN_TIME_BETWEEN_FORCED_SCANS,
MIN_TIME_BETWEEN_SCANS, hide_email
)
from .helpers import add_devices

_LOGGER = logging.getLogger(__name__)

Expand All @@ -32,36 +36,36 @@ async def async_setup_platform(hass,
discovery_info=None) -> bool:
"""Set up the Alexa alarm control panel platform."""
devices = [] # type: List[AlexaAlarmControlPanel]
for account, account_dict in (hass.data[DATA_ALEXAMEDIA]
['accounts'].items()):
alexa_client: AlexaAlarmControlPanel = AlexaAlarmControlPanel(
account_dict['login_obj'])
await alexa_client.init()
if not (alexa_client and alexa_client.unique_id):
_LOGGER.debug("%s: Skipping creation of uninitialized device: %s",
hide_email(account),
alexa_client)
continue
config = discovery_info['config']
account = config[CONF_EMAIL]
account_dict = hass.data[DATA_ALEXAMEDIA]['accounts'][account]
if 'alarm_control_panel' not in (account_dict
['entities']):
(hass.data[DATA_ALEXAMEDIA]
['accounts']
[account]
['entities']['alarm_control_panel']) = {}
alexa_client: AlexaAlarmControlPanel = AlexaAlarmControlPanel(
account_dict['login_obj'])
await alexa_client.init()
if not (alexa_client and alexa_client.unique_id):
_LOGGER.debug("%s: Skipping creation of uninitialized device: %s",
hide_email(account),
alexa_client)
elif alexa_client.unique_id not in (account_dict
['entities']
['alarm_control_panel']):
devices.append(alexa_client)
(hass.data[DATA_ALEXAMEDIA]
['accounts']
[account]
['entities']
['alarm_control_panel']) = alexa_client
if devices:
_LOGGER.debug("Adding %s", devices)
try:
add_devices_callback(devices, True)
except HomeAssistantError as exception_:
message = exception_.message # type: str
if message.startswith("Entity id already exists"):
_LOGGER.debug("Device already added: %s",
message)
else:
_LOGGER.debug("Unable to add devices: %s : %s",
devices,
message)
return True
['alarm_control_panel'][alexa_client.unique_id]) = alexa_client
else:
_LOGGER.debug("%s: Skipping already added device: %s",
hide_email(account),
alexa_client)
return await add_devices(devices, add_devices_callback)


class AlexaAlarmControlPanel(AlarmControlPanel):
Expand Down Expand Up @@ -108,7 +112,6 @@ async def init(self):
self._guard_entity_id)
if not self._appliance_id:
_LOGGER.debug("%s: No Alexa Guard entity found", self.account)
return None

async def async_added_to_hass(self):
"""Store register state change callback."""
Expand Down
43 changes: 43 additions & 0 deletions custom_components/alexa_media/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# SPDX-License-Identifier: Apache-2.0
"""
Helper functions for Alexa Media Player.
For more details about this platform, please refer to the documentation at
https://community.home-assistant.io/t/echo-devices-alexa-as-media-player-testers-needed/58639
"""

import logging
from typing import List
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_component import EntityComponent

_LOGGER = logging.getLogger(__name__)


async def add_devices(devices: List[EntityComponent],
add_devices_callback: callable) -> bool:
"""Add devices using add_devices_callback."""
if devices:
_LOGGER.debug("Adding %s", devices)
try:
add_devices_callback(devices, True)
return True
except HomeAssistantError as exception_:
message = exception_.message # type: str
if message.startswith("Entity id already exists"):
_LOGGER.debug("Device already added: %s",
message)
else:
_LOGGER.debug("Unable to add devices: %s : %s",
devices,
message)
except BaseException as ex:
template = ("An exception of type {0} occurred."
" Arguments:\n{1!r}")
message = template.format(type(ex).__name__, ex.args)
_LOGGER.debug("Unable to add devices: %s",
message)

return False
50 changes: 23 additions & 27 deletions custom_components/alexa_media/media_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,17 @@
SUPPORT_VOLUME_SET)
from homeassistant.const import (STATE_IDLE, STATE_PAUSED, STATE_PLAYING,
STATE_STANDBY)
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.event import async_call_later
from homeassistant.helpers.service import extract_entity_ids
from homeassistant.helpers.discovery import async_load_platform

from .const import ATTR_MESSAGE, PLAY_SCAN_INTERVAL
from .helpers import add_devices

from . import (
DOMAIN as ALEXA_DOMAIN,
CONF_NAME,
CONF_NAME, CONF_EMAIL,
DATA_ALEXAMEDIA,
MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS,
hide_email, hide_serial)
Expand All @@ -59,32 +59,28 @@ async def async_setup_platform(hass, config, add_devices_callback,
discovery_info=None):
"""Set up the Alexa media player platform."""
devices = [] # type: List[AlexaClient]
for account, account_dict in (hass.data[DATA_ALEXAMEDIA]
['accounts'].items()):
for key, device in account_dict['devices']['media_player'].items():
if key not in account_dict['entities']['media_player']:
alexa_client = AlexaClient(device,
account_dict['login_obj']
)
await alexa_client.init(device)
devices.append(alexa_client)
(hass.data[DATA_ALEXAMEDIA]
['accounts']
[account]
['entities']
['media_player'][key]) = alexa_client
_LOGGER.debug("Adding %s", devices)
try:
add_devices_callback(devices, True)
except HomeAssistantError as exception_:
message = exception_.message # type: str
if message.startswith("Entity id already exists"):
_LOGGER.debug("Device already added: %s",
message)
config = discovery_info['config']
account = config[CONF_EMAIL]
account_dict = hass.data[DATA_ALEXAMEDIA]['accounts'][account]
for key, device in account_dict['devices']['media_player'].items():
if key not in account_dict['entities']['media_player']:
alexa_client = AlexaClient(device,
account_dict['login_obj']
)
await alexa_client.init(device)
devices.append(alexa_client)
(hass.data[DATA_ALEXAMEDIA]
['accounts']
[account]
['entities']
['media_player'][key]) = alexa_client
else:
_LOGGER.debug("Unable to add devices: %s : %s",
devices,
message)
_LOGGER.debug("%s: Skipping already added device: %s:%s",
hide_email(account),
hide_serial(key),
alexa_client
)
return await add_devices(devices, add_devices_callback)


class AlexaClient(MediaPlayerDevice):
Expand Down

0 comments on commit ae11e52

Please sign in to comment.