Skip to content

Commit

Permalink
Merge pull request #150 from keatontaylor/dev
Browse files Browse the repository at this point in the history
Update master to 1.2.4
  • Loading branch information
alandtse authored Apr 8, 2019
2 parents 31c2cb9 + af1b4c5 commit a462ed7
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 77 deletions.
176 changes: 138 additions & 38 deletions alexa_media/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

from homeassistant import util
from homeassistant.const import (
CONF_EMAIL, CONF_PASSWORD, CONF_SCAN_INTERVAL, CONF_URL)
CONF_EMAIL, CONF_NAME, CONF_PASSWORD, CONF_SCAN_INTERVAL, CONF_URL)
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.event import track_time_interval
from homeassistant.helpers.discovery import load_platform
Expand All @@ -26,9 +26,9 @@

# from .config_flow import configured_instances

REQUIREMENTS = ['alexapy==0.4.2']
REQUIREMENTS = ['alexapy==0.4.3']

__version__ = '1.2.3'
__version__ = '1.2.4'

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -98,7 +98,7 @@ def setup(hass, config, discovery_info=None):
email = account.get(CONF_EMAIL)
password = account.get(CONF_PASSWORD)
url = account.get(CONF_URL)
hass.data[DOMAIN]['accounts'][email] = {"config": []}
hass.data[DATA_ALEXAMEDIA]['accounts'][email] = {"config": []}
login = AlexaLogin(url, email, password, hass.config.path,
account.get(CONF_DEBUG))

Expand Down Expand Up @@ -171,7 +171,7 @@ async def configuration_callback(callback_data):
description=('Please select the verification method. '
'(e.g., sms or email).<br />{}').format(
options
),
),
submit_caption="Confirm",
fields=[{'id': 'claimsoption', 'name': 'Option'}]
)
Expand All @@ -194,13 +194,13 @@ async def configuration_callback(callback_data):
submit_caption="Confirm",
fields=[]
)
hass.data[DOMAIN]['accounts'][email]['config'].append(config_id)
hass.data[DATA_ALEXAMEDIA]['accounts'][email]['config'].append(config_id)
if 'error_message' in status and status['error_message']:
configurator.notify_errors( # use sync to delay next pop
config_id,
status['error_message'])
if len(hass.data[DOMAIN]['accounts'][email]['config']) > 1:
configurator.async_request_done((hass.data[DOMAIN]
if len(hass.data[DATA_ALEXAMEDIA]['accounts'][email]['config']) > 1:
configurator.async_request_done((hass.data[DATA_ALEXAMEDIA]
['accounts'][email]['config']).pop(0))


Expand Down Expand Up @@ -239,12 +239,14 @@ def update_devices():
"""Ping Alexa API to identify all devices, bluetooth, and last called device.
This will add new devices and services when discovered. By default this
runs every SCAN_INTERVAL seconds unless another method calls it. While
throttled at MIN_TIME_BETWEEN_SCANS, care should be taken to reduce the
number of runs to avoid flooding. Slow changing states should be
checked here instead of in spawned components like media_player since
this object is one per account.
Each AlexaAPI call generally results in one webpage request.
runs every SCAN_INTERVAL seconds unless another method calls it. if
websockets is connected, it will return immediately unless
'new_devices' has been set to True.
While throttled at MIN_TIME_BETWEEN_SCANS, care should be taken to
reduce the number of runs to avoid flooding. Slow changing states
should be checked here instead of in spawned components like
media_player since this object is one per account.
Each AlexaAPI call generally results in two webpage requests.
"""
from alexapy import AlexaAPI
existing_serials = (hass.data[DATA_ALEXAMEDIA]
Expand All @@ -257,14 +259,20 @@ def update_devices():
[email]
['entities']
['media_player'].values())
if (hass.data[DATA_ALEXAMEDIA]['accounts'][email]['websocket']
and not (hass.data[DATA_ALEXAMEDIA]
['accounts'][email]['new_devices'])):
return
hass.data[DATA_ALEXAMEDIA]['accounts'][email]['new_devices'] = False
devices = AlexaAPI.get_devices(login_obj)
bluetooth = AlexaAPI.get_bluetooth(login_obj)
_LOGGER.debug("%s: Found %s devices, %s bluetooth",
hide_email(email),
len(devices) if devices is not None else '',
len(bluetooth) if bluetooth is not None else '')
if ((devices is None or bluetooth is None)
and not hass.data[DOMAIN]['accounts'][email]['config']):
and not hass.data[DATA_ALEXAMEDIA]
['accounts'][email]['config']):
_LOGGER.debug("Alexa API disconnected; attempting to relogin")
login_obj.login_with_cookie()
test_login_status(hass, config, login_obj, setup_platform_callback)
Expand All @@ -276,9 +284,33 @@ def update_devices():
for device in devices:
if include and device['accountName'] not in include:
included.append(device['accountName'])
if 'appDeviceList' in device:
for app in device['appDeviceList']:
(hass.data[DATA_ALEXAMEDIA]
['accounts']
[email]
['excluded']
[app['serialNumber']]) = device
(hass.data[DATA_ALEXAMEDIA]
['accounts']
[email]
['excluded']
[device['serialNumber']]) = device
continue
elif exclude and device['accountName'] in exclude:
excluded.append(device['accountName'])
if 'appDeviceList' in device:
for app in device['appDeviceList']:
(hass.data[DATA_ALEXAMEDIA]
['accounts']
[email]
['excluded']
[app['serialNumber']]) = device
(hass.data[DATA_ALEXAMEDIA]
['accounts']
[email]
['excluded']
[device['serialNumber']]) = device
continue

for b_state in bluetooth['bluetoothStates']:
Expand All @@ -304,7 +336,8 @@ def update_devices():

if new_alexa_clients:
for component in ALEXA_COMPONENTS:
load_platform(hass, component, DOMAIN, {}, config)
load_platform(hass, component, DOMAIN, {CONF_NAME: DOMAIN},
config)

# Process last_called data to fire events
update_last_called(login_obj)
Expand Down Expand Up @@ -340,6 +373,22 @@ def update_last_called(login_obj, last_called=None):
[email]
['last_called']) = last_called

def update_bluetooth_state(login_obj, device_serial):
"""Update the bluetooth state on ws bluetooth event."""
from alexapy import AlexaAPI
bluetooth = AlexaAPI.get_bluetooth(login_obj)
device = (hass.data[DATA_ALEXAMEDIA]
['accounts']
[email]
['devices']
['media_player']
[device_serial])

for b_state in bluetooth['bluetoothStates']:
if device_serial == b_state['deviceSerialNumber']:
device['bluetooth_state'] = b_state
return device['bluetooth_state']

def last_call_handler(call):
"""Handle last call service request.
Expand Down Expand Up @@ -390,31 +439,72 @@ def ws_handler(message_obj):
if isinstance(message_obj.json_payload, dict) and
'payload' in message_obj.json_payload
else None)
existing_serials = (hass.data[DATA_ALEXAMEDIA]
['accounts']
[email]
['entities']
['media_player'].keys())
if command and json_payload:
_LOGGER.debug("%s: Received websocket command: %s : %s",
hide_email(email),
command, json_payload)
serial = None
if command == 'PUSH_ACTIVITY':
# Last_Alexa Updated
serial = (json_payload
['key']
['entryId']).split('#')[2]
last_called = {
'serialNumber': (json_payload
['key']
['entryId']).split('#')[2],
'serialNumber': serial,
'timestamp': json_payload['timestamp']
}
update_last_called(login_obj, last_called)
}
if (serial and serial in existing_serials):
update_last_called(login_obj, last_called)
elif command == 'PUSH_AUDIO_PLAYER_STATE':
# Player update
_LOGGER.debug("Updating media_player: %s", json_payload)
hass.bus.fire(('{}_{}'.format(DOMAIN,
hide_email(email)))[0:32],
{'player_state': json_payload})
serial = (json_payload['dopplerId']['deviceSerialNumber'])
if (serial and serial in existing_serials):
_LOGGER.debug("Updating media_player: %s", json_payload)
hass.bus.fire(('{}_{}'.format(DOMAIN,
hide_email(email)))[0:32],
{'player_state': json_payload})
elif command == 'PUSH_VOLUME_CHANGE':
# Player volume update
_LOGGER.debug("Updating media_player volume: %s", json_payload)
hass.bus.fire(('{}_{}'.format(DOMAIN,
hide_email(email)))[0:32],
{'player_state': json_payload})
serial = (json_payload['dopplerId']['deviceSerialNumber'])
if (serial and serial in existing_serials):
_LOGGER.debug("Updating media_player volume: %s",
json_payload)
hass.bus.fire(('{}_{}'.format(DOMAIN,
hide_email(email)))[0:32],
{'player_state': json_payload})
elif command == 'PUSH_DOPPLER_CONNECTION_CHANGE':
# Player availability update
serial = (json_payload['dopplerId']['deviceSerialNumber'])
if (serial and serial in existing_serials):
_LOGGER.debug("Updating media_player availability %s",
json_payload)
hass.bus.fire(('{}_{}'.format(DOMAIN,
hide_email(email)))[0:32],
{'player_state': json_payload})
elif command == 'PUSH_BLUETOOTH_STATE_CHANGE':
# Player bluetooth update
serial = (json_payload['dopplerId']['deviceSerialNumber'])
if (serial and serial in existing_serials):
_LOGGER.debug("Updating media_player bluetooth %s",
json_payload)
bluetooth_state = update_bluetooth_state(login_obj, serial)
hass.bus.fire(('{}_{}'.format(DOMAIN,
hide_email(email)))[0:32],
{'bluetooth_change': bluetooth_state})
if (serial and serial not in existing_serials
and serial not in (hass.data[DATA_ALEXAMEDIA]
['accounts']
[email]
['excluded'].keys())):
_LOGGER.debug("Discovered new media_player %s", serial)
(hass.data[DATA_ALEXAMEDIA]
['accounts'][email]['new_devices']) = True
update_devices(no_throttle=True)

def ws_close_handler():
"""Handle websocket close.
Expand All @@ -424,7 +514,8 @@ def ws_close_handler():
email = login_obj.email
_LOGGER.debug("%s: Received websocket close; attempting reconnect",
hide_email(email))
(hass.data[DOMAIN]['accounts'][email]['websocket']) = ws_connect()
(hass.data[DATA_ALEXAMEDIA]['accounts']
[email]['websocket']) = ws_connect()

def ws_error_handler(message):
"""Handle websocket error.
Expand All @@ -437,23 +528,32 @@ def ws_error_handler(message):
_LOGGER.debug("%s: Received websocket error %s",
hide_email(email),
message)
(hass.data[DOMAIN]['accounts'][email]['websocket']) = None
(hass.data[DATA_ALEXAMEDIA]['accounts'][email]['websocket']) = None
include = config.get(CONF_INCLUDE_DEVICES)
exclude = config.get(CONF_EXCLUDE_DEVICES)
scan_interval = config.get(CONF_SCAN_INTERVAL)
email = login_obj.email
(hass.data[DOMAIN]['accounts'][email]['websocket']) = ws_connect()
(hass.data[DOMAIN]['accounts'][email]['login_obj']) = login_obj
(hass.data[DOMAIN]['accounts'][email]['devices']) = {'media_player': {}}
(hass.data[DOMAIN]['accounts'][email]['entities']) = {'media_player': {}}
(hass.data[DATA_ALEXAMEDIA]['accounts'][email]['websocket']) = ws_connect()
(hass.data[DATA_ALEXAMEDIA]['accounts'][email]['login_obj']) = login_obj
if 'devices' not in hass.data[DATA_ALEXAMEDIA]['accounts'][email]:
(hass.data[DATA_ALEXAMEDIA]['accounts'][email]
['devices']) = {'media_player': {}}
if 'excluded' not in hass.data[DATA_ALEXAMEDIA]['accounts'][email]:
(hass.data[DATA_ALEXAMEDIA]['accounts'][email]
['excluded']) = {}
if 'entities' not in hass.data[DATA_ALEXAMEDIA]['accounts'][email]:
(hass.data[DATA_ALEXAMEDIA]['accounts'][email]
['entities']) = {'media_player': {}}
(hass.data[DATA_ALEXAMEDIA]
['accounts'][email]['new_devices']) = True # force initial update
track_time_interval(hass, lambda now: update_devices(), scan_interval)
update_devices()
track_time_interval(hass, lambda now: update_devices(), scan_interval)
hass.services.register(DOMAIN, SERVICE_UPDATE_LAST_CALLED,
last_call_handler, schema=LAST_CALL_UPDATE_SCHEMA)

# Clear configurator. We delay till here to avoid leaving a modal orphan
for config_id in hass.data[DOMAIN]['accounts'][email]['config']:
for config_id in hass.data[DATA_ALEXAMEDIA]['accounts'][email]['config']:
configurator = hass.components.configurator
configurator.async_request_done(config_id)
hass.data[DOMAIN]['accounts'][email]['config'] = []
hass.data[DATA_ALEXAMEDIA]['accounts'][email]['config'] = []
return True
3 changes: 2 additions & 1 deletion alexa_media/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(seconds=1)

ALEXA_COMPONENTS = [
'media_player'
'media_player',
'notify'
]

CONF_ACCOUNTS = 'accounts'
Expand Down
Loading

0 comments on commit a462ed7

Please sign in to comment.