Black
This commit is contained in:
parent
da05dfe708
commit
4de97abc3a
2676 changed files with 163166 additions and 140084 deletions
|
@ -9,8 +9,14 @@ from aiohttp import web
|
|||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.device_tracker import (
|
||||
ATTR_ATTRIBUTES, ATTR_DEV_ID, ATTR_GPS, ATTR_GPS_ACCURACY, ATTR_HOST_NAME,
|
||||
ATTR_MAC, PLATFORM_SCHEMA)
|
||||
ATTR_ATTRIBUTES,
|
||||
ATTR_DEV_ID,
|
||||
ATTR_GPS,
|
||||
ATTR_GPS_ACCURACY,
|
||||
ATTR_HOST_NAME,
|
||||
ATTR_MAC,
|
||||
PLATFORM_SCHEMA,
|
||||
)
|
||||
from homeassistant.components.http import HomeAssistantView
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.core import callback
|
||||
|
@ -20,28 +26,30 @@ from homeassistant.helpers.event import async_track_time_interval
|
|||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ATTR_FUEL_LEVEL = 'fuel_level'
|
||||
AUTOMATIC_CONFIG_FILE = '.automatic/session-{}.json'
|
||||
ATTR_FUEL_LEVEL = "fuel_level"
|
||||
AUTOMATIC_CONFIG_FILE = ".automatic/session-{}.json"
|
||||
|
||||
CONF_CLIENT_ID = 'client_id'
|
||||
CONF_CURRENT_LOCATION = 'current_location'
|
||||
CONF_DEVICES = 'devices'
|
||||
CONF_SECRET = 'secret'
|
||||
CONF_CLIENT_ID = "client_id"
|
||||
CONF_CURRENT_LOCATION = "current_location"
|
||||
CONF_DEVICES = "devices"
|
||||
CONF_SECRET = "secret"
|
||||
|
||||
DATA_CONFIGURING = 'automatic_configurator_clients'
|
||||
DATA_REFRESH_TOKEN = 'refresh_token'
|
||||
DEFAULT_SCOPE = ['location', 'trip', 'vehicle:events', 'vehicle:profile']
|
||||
DATA_CONFIGURING = "automatic_configurator_clients"
|
||||
DATA_REFRESH_TOKEN = "refresh_token"
|
||||
DEFAULT_SCOPE = ["location", "trip", "vehicle:events", "vehicle:profile"]
|
||||
DEFAULT_TIMEOUT = 5
|
||||
EVENT_AUTOMATIC_UPDATE = 'automatic_update'
|
||||
EVENT_AUTOMATIC_UPDATE = "automatic_update"
|
||||
|
||||
FULL_SCOPE = DEFAULT_SCOPE + ['current_location']
|
||||
FULL_SCOPE = DEFAULT_SCOPE + ["current_location"]
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_CLIENT_ID): cv.string,
|
||||
vol.Required(CONF_SECRET): cv.string,
|
||||
vol.Optional(CONF_CURRENT_LOCATION, default=False): cv.boolean,
|
||||
vol.Optional(CONF_DEVICES): vol.All(cv.ensure_list, [cv.string]),
|
||||
})
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_CLIENT_ID): cv.string,
|
||||
vol.Required(CONF_SECRET): cv.string,
|
||||
vol.Optional(CONF_CURRENT_LOCATION, default=False): cv.boolean,
|
||||
vol.Optional(CONF_DEVICES): vol.All(cv.ensure_list, [cv.string]),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def _get_refresh_token_from_file(hass, filename):
|
||||
|
@ -67,10 +75,8 @@ def _write_refresh_token_to_file(hass, filename, refresh_token):
|
|||
path = hass.config.path(filename)
|
||||
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
with open(path, 'w+') as data_file:
|
||||
json.dump({
|
||||
DATA_REFRESH_TOKEN: refresh_token
|
||||
}, data_file)
|
||||
with open(path, "w+") as data_file:
|
||||
json.dump({DATA_REFRESH_TOKEN: refresh_token}, data_file)
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
|
@ -86,20 +92,21 @@ def async_setup_scanner(hass, config, async_see, discovery_info=None):
|
|||
client_id=config[CONF_CLIENT_ID],
|
||||
client_secret=config[CONF_SECRET],
|
||||
client_session=async_get_clientsession(hass),
|
||||
request_kwargs={'timeout': DEFAULT_TIMEOUT})
|
||||
request_kwargs={"timeout": DEFAULT_TIMEOUT},
|
||||
)
|
||||
|
||||
filename = AUTOMATIC_CONFIG_FILE.format(config[CONF_CLIENT_ID])
|
||||
refresh_token = yield from hass.async_add_job(
|
||||
_get_refresh_token_from_file, hass, filename)
|
||||
_get_refresh_token_from_file, hass, filename
|
||||
)
|
||||
|
||||
@asyncio.coroutine
|
||||
def initialize_data(session):
|
||||
"""Initialize the AutomaticData object from the created session."""
|
||||
hass.async_add_job(
|
||||
_write_refresh_token_to_file, hass, filename,
|
||||
session.refresh_token)
|
||||
data = AutomaticData(
|
||||
hass, client, session, config.get(CONF_DEVICES), async_see)
|
||||
_write_refresh_token_to_file, hass, filename, session.refresh_token
|
||||
)
|
||||
data = AutomaticData(hass, client, session, config.get(CONF_DEVICES), async_see)
|
||||
|
||||
# Load the initial vehicle data
|
||||
vehicles = yield from session.get_vehicles()
|
||||
|
@ -112,8 +119,7 @@ def async_setup_scanner(hass, config, async_see, discovery_info=None):
|
|||
|
||||
if refresh_token is not None:
|
||||
try:
|
||||
session = yield from client.create_session_from_refresh_token(
|
||||
refresh_token)
|
||||
session = yield from client.create_session_from_refresh_token(refresh_token)
|
||||
yield from initialize_data(session)
|
||||
return True
|
||||
except aioautomatic.exceptions.AutomaticError as err:
|
||||
|
@ -121,8 +127,8 @@ def async_setup_scanner(hass, config, async_see, discovery_info=None):
|
|||
|
||||
configurator = hass.components.configurator
|
||||
request_id = configurator.async_request_config(
|
||||
"Automatic", description=(
|
||||
"Authorization required for Automatic device tracker."),
|
||||
"Automatic",
|
||||
description=("Authorization required for Automatic device tracker."),
|
||||
link_name="Click here to authorize Home Assistant.",
|
||||
link_url=client.generate_oauth_url(scope),
|
||||
entity_picture="/static/images/logo_automatic.png",
|
||||
|
@ -132,8 +138,7 @@ def async_setup_scanner(hass, config, async_see, discovery_info=None):
|
|||
def initialize_callback(code, state):
|
||||
"""Call after OAuth2 response is returned."""
|
||||
try:
|
||||
session = yield from client.create_session_from_oauth_code(
|
||||
code, state)
|
||||
session = yield from client.create_session_from_oauth_code(code, state)
|
||||
yield from initialize_data(session)
|
||||
configurator.async_request_done(request_id)
|
||||
except aioautomatic.exceptions.AutomaticError as err:
|
||||
|
@ -152,32 +157,32 @@ class AutomaticAuthCallbackView(HomeAssistantView):
|
|||
"""Handle OAuth finish callback requests."""
|
||||
|
||||
requires_auth = False
|
||||
url = '/api/automatic/callback'
|
||||
name = 'api:automatic:callback'
|
||||
url = "/api/automatic/callback"
|
||||
name = "api:automatic:callback"
|
||||
|
||||
@callback
|
||||
def get(self, request): # pylint: disable=no-self-use
|
||||
"""Finish OAuth callback request."""
|
||||
hass = request.app['hass']
|
||||
hass = request.app["hass"]
|
||||
params = request.query
|
||||
response = web.HTTPFound('/states')
|
||||
response = web.HTTPFound("/states")
|
||||
|
||||
if 'state' not in params or 'code' not in params:
|
||||
if 'error' in params:
|
||||
_LOGGER.error(
|
||||
"Error authorizing Automatic: %s", params['error'])
|
||||
if "state" not in params or "code" not in params:
|
||||
if "error" in params:
|
||||
_LOGGER.error("Error authorizing Automatic: %s", params["error"])
|
||||
return response
|
||||
_LOGGER.error(
|
||||
"Error authorizing Automatic. Invalid response returned")
|
||||
_LOGGER.error("Error authorizing Automatic. Invalid response returned")
|
||||
return response
|
||||
|
||||
if DATA_CONFIGURING not in hass.data or \
|
||||
params['state'] not in hass.data[DATA_CONFIGURING]:
|
||||
if (
|
||||
DATA_CONFIGURING not in hass.data
|
||||
or params["state"] not in hass.data[DATA_CONFIGURING]
|
||||
):
|
||||
_LOGGER.error("Automatic configuration request not found")
|
||||
return response
|
||||
|
||||
code = params['code']
|
||||
state = params['state']
|
||||
code = params["code"]
|
||||
state = params["state"]
|
||||
initialize_callback = hass.data[DATA_CONFIGURING][state]
|
||||
hass.async_create_task(initialize_callback(code, state))
|
||||
|
||||
|
@ -201,7 +206,9 @@ class AutomaticData:
|
|||
|
||||
self.client.on_app_event(
|
||||
lambda name, event: self.hass.async_create_task(
|
||||
self.handle_event(name, event)))
|
||||
self.handle_event(name, event)
|
||||
)
|
||||
)
|
||||
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, self.ws_close())
|
||||
|
||||
|
@ -225,9 +232,11 @@ class AutomaticData:
|
|||
|
||||
if event.created_at < self.vehicle_seen[event.vehicle.id]:
|
||||
# Skip events received out of order
|
||||
_LOGGER.debug("Skipping out of order event. Event Created %s. "
|
||||
"Last seen event: %s", event.created_at,
|
||||
self.vehicle_seen[event.vehicle.id])
|
||||
_LOGGER.debug(
|
||||
"Skipping out of order event. Event Created %s. " "Last seen event: %s",
|
||||
event.created_at,
|
||||
self.vehicle_seen[event.vehicle.id],
|
||||
)
|
||||
return
|
||||
self.vehicle_seen[event.vehicle.id] = event.created_at
|
||||
|
||||
|
@ -253,6 +262,7 @@ class AutomaticData:
|
|||
def ws_connect(self, now=None):
|
||||
"""Open the websocket connection."""
|
||||
import aioautomatic
|
||||
|
||||
self.ws_close_requested = False
|
||||
|
||||
if self.ws_reconnect_handle is not None:
|
||||
|
@ -260,16 +270,19 @@ class AutomaticData:
|
|||
try:
|
||||
ws_loop_future = yield from self.client.ws_connect()
|
||||
except aioautomatic.exceptions.UnauthorizedClientError:
|
||||
_LOGGER.error("Client unauthorized for websocket connection. "
|
||||
"Ensure Websocket is selected in the Automatic "
|
||||
"developer application event delivery preferences")
|
||||
_LOGGER.error(
|
||||
"Client unauthorized for websocket connection. "
|
||||
"Ensure Websocket is selected in the Automatic "
|
||||
"developer application event delivery preferences"
|
||||
)
|
||||
return
|
||||
except aioautomatic.exceptions.AutomaticError as err:
|
||||
if self.ws_reconnect_handle is None:
|
||||
# Show log error and retry connection every 5 minutes
|
||||
_LOGGER.error("Error opening websocket connection: %s", err)
|
||||
self.ws_reconnect_handle = async_track_time_interval(
|
||||
self.hass, self.ws_connect, timedelta(minutes=5))
|
||||
self.hass, self.ws_connect, timedelta(minutes=5)
|
||||
)
|
||||
return
|
||||
|
||||
if self.ws_reconnect_handle is not None:
|
||||
|
@ -312,8 +325,9 @@ class AutomaticData:
|
|||
|
||||
name = vehicle.display_name
|
||||
if name is None:
|
||||
name = ' '.join(filter(None, (
|
||||
str(vehicle.year), vehicle.make, vehicle.model)))
|
||||
name = " ".join(
|
||||
filter(None, (str(vehicle.year), vehicle.make, vehicle.model))
|
||||
)
|
||||
|
||||
if self.devices is not None and name not in self.devices:
|
||||
self.vehicle_info[vehicle.id] = None
|
||||
|
@ -323,12 +337,9 @@ class AutomaticData:
|
|||
ATTR_DEV_ID: vehicle.id,
|
||||
ATTR_HOST_NAME: name,
|
||||
ATTR_MAC: vehicle.id,
|
||||
ATTR_ATTRIBUTES: {
|
||||
ATTR_FUEL_LEVEL: vehicle.fuel_level_percent,
|
||||
}
|
||||
ATTR_ATTRIBUTES: {ATTR_FUEL_LEVEL: vehicle.fuel_level_percent},
|
||||
}
|
||||
self.vehicle_seen[vehicle.id] = \
|
||||
vehicle.updated_at or vehicle.created_at
|
||||
self.vehicle_seen[vehicle.id] = vehicle.updated_at or vehicle.created_at
|
||||
|
||||
if vehicle.latest_location is not None:
|
||||
location = vehicle.latest_location
|
||||
|
@ -339,8 +350,7 @@ class AutomaticData:
|
|||
trips = []
|
||||
try:
|
||||
# Get the most recent trip for this vehicle
|
||||
trips = yield from self.session.get_trips(
|
||||
vehicle=vehicle.id, limit=1)
|
||||
trips = yield from self.session.get_trips(vehicle=vehicle.id, limit=1)
|
||||
except aioautomatic.exceptions.AutomaticError as err:
|
||||
_LOGGER.error(str(err))
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue