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

fix: add catch for HomeAssistantError when adding entities #324

Merged
merged 8 commits into from
Sep 3, 2019
42 changes: 26 additions & 16 deletions custom_components/alexa_media/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,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 @@ -284,7 +285,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 @@ -298,11 +299,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 @@ -325,18 +331,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 @@ -351,7 +358,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 @@ -397,27 +404,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 @@ -614,7 +624,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 @@ -649,7 +659,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 @@ -685,7 +695,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
Loading