Google travel time improvements (#2047)
* Update google_travel_time.py * Update google_travel_time.py * pylint: disable=too-many-instance-attributes * Add the mode to the title of the sensor * Expose the travel mode on the sensor attributes * Big improvements to the Google Travel Time sensor. Allow passing any options that Google supports in the options dict of your configuration. Deprecate travel_mode. Change name format to show the mode * fu farcy * Dynamically convert departure and arrival times * Add a warning if user provides both departure and arrival times * Add deprecation warning for travel_mode outside options and other minor fixes * Use a copy of options dict to not overwrite the departure/arrival times constantly. * Remove default travel_mode, but set default options.mode to driving * Google doesnt let us query time in the past, so if the date we generate from a time string is in the past, add 1 day * spacing fix * Add config validation for all possible parameters * flake8 and pylint fixespull/2057/head
parent
8682e2def8
commit
d229cb46b1
|
@ -10,8 +10,10 @@ import logging
|
|||
import voluptuous as vol
|
||||
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.const import CONF_API_KEY, TEMP_CELSIUS
|
||||
from homeassistant.const import CONF_API_KEY, TEMP_CELSIUS, TEMP_FAHRENHEIT
|
||||
from homeassistant.util import Throttle
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -23,47 +25,102 @@ MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=5)
|
|||
CONF_ORIGIN = 'origin'
|
||||
CONF_DESTINATION = 'destination'
|
||||
CONF_TRAVEL_MODE = 'travel_mode'
|
||||
CONF_OPTIONS = 'options'
|
||||
CONF_MODE = 'mode'
|
||||
CONF_NAME = 'name'
|
||||
|
||||
ALL_LANGUAGES = ['ar', 'bg', 'bn', 'ca', 'cs', 'da', 'de', 'el', 'en', 'es',
|
||||
'eu', 'fa', 'fi', 'fr', 'gl', 'gu', 'hi', 'hr', 'hu', 'id',
|
||||
'it', 'iw', 'ja', 'kn', 'ko', 'lt', 'lv', 'ml', 'mr', 'nl',
|
||||
'no', 'pl', 'pt', 'pt-BR', 'pt-PT', 'ro', 'ru', 'sk', 'sl',
|
||||
'sr', 'sv', 'ta', 'te', 'th', 'tl', 'tr', 'uk', 'vi',
|
||||
'zh-CN', 'zh-TW']
|
||||
|
||||
TRANSIT_PREFS = ['less_walking', 'fewer_transfers']
|
||||
|
||||
PLATFORM_SCHEMA = vol.Schema({
|
||||
vol.Required('platform'): 'google_travel_time',
|
||||
vol.Optional(CONF_NAME): vol.Coerce(str),
|
||||
vol.Required(CONF_API_KEY): vol.Coerce(str),
|
||||
vol.Required(CONF_ORIGIN): vol.Coerce(str),
|
||||
vol.Required(CONF_DESTINATION): vol.Coerce(str),
|
||||
vol.Optional(CONF_TRAVEL_MODE, default='driving'):
|
||||
vol.In(["driving", "walking", "bicycling", "transit"])
|
||||
vol.Optional(CONF_TRAVEL_MODE):
|
||||
vol.In(["driving", "walking", "bicycling", "transit"]),
|
||||
vol.Optional(CONF_OPTIONS): vol.All(
|
||||
dict, vol.Schema({
|
||||
vol.Optional(CONF_MODE, default='driving'):
|
||||
vol.In(["driving", "walking", "bicycling", "transit"]),
|
||||
vol.Optional('language'): vol.In(ALL_LANGUAGES),
|
||||
vol.Optional('avoid'): vol.In(['tolls', 'highways',
|
||||
'ferries', 'indoor']),
|
||||
vol.Optional('units'): vol.In(['metric', 'imperial']),
|
||||
vol.Exclusive('arrival_time', 'time'): cv.string,
|
||||
vol.Exclusive('departure_time', 'time'): cv.string,
|
||||
vol.Optional('traffic_model'): vol.In(['best_guess',
|
||||
'pessimistic',
|
||||
'optimistic']),
|
||||
vol.Optional('transit_mode'): vol.In(['bus', 'subway', 'train',
|
||||
'tram', 'rail']),
|
||||
vol.Optional('transit_routing_preference'): vol.In(TRANSIT_PREFS)
|
||||
}))
|
||||
})
|
||||
|
||||
|
||||
def convert_time_to_utc(timestr):
|
||||
"""Take a string like 08:00:00 and convert it to a unix timestamp."""
|
||||
combined = datetime.combine(dt_util.start_of_local_day(),
|
||||
dt_util.parse_time(timestr))
|
||||
if combined < datetime.now():
|
||||
combined = combined + timedelta(days=1)
|
||||
return dt_util.as_timestamp(combined)
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||
"""Setup the travel time platform."""
|
||||
# pylint: disable=too-many-locals
|
||||
options = config.get(CONF_OPTIONS)
|
||||
|
||||
is_metric = (hass.config.temperature_unit == TEMP_CELSIUS)
|
||||
if options.get('units') is None:
|
||||
if hass.config.temperature_unit is TEMP_CELSIUS:
|
||||
options['units'] = 'metric'
|
||||
elif hass.config.temperature_unit is TEMP_FAHRENHEIT:
|
||||
options['units'] = 'imperial'
|
||||
|
||||
travel_mode = config.get(CONF_TRAVEL_MODE)
|
||||
mode = options.get(CONF_MODE)
|
||||
|
||||
if travel_mode is not None:
|
||||
wstr = ("Google Travel Time: travel_mode is deprecated, please add "
|
||||
"mode to the options dictionary instead!")
|
||||
_LOGGER.warning(wstr)
|
||||
if mode is None:
|
||||
options[CONF_MODE] = travel_mode
|
||||
|
||||
titled_mode = options.get(CONF_MODE, 'driving').title()
|
||||
formatted_name = "Google Travel Time - {}".format(titled_mode)
|
||||
name = config.get(CONF_NAME, formatted_name)
|
||||
api_key = config.get(CONF_API_KEY)
|
||||
origin = config.get(CONF_ORIGIN)
|
||||
destination = config.get(CONF_DESTINATION)
|
||||
travel_mode = config.get(CONF_TRAVEL_MODE)
|
||||
|
||||
sensor = GoogleTravelTimeSensor(api_key, origin, destination,
|
||||
travel_mode, is_metric)
|
||||
sensor = GoogleTravelTimeSensor(name, api_key, origin, destination,
|
||||
options)
|
||||
|
||||
if sensor.valid_api_connection:
|
||||
add_devices_callback([sensor])
|
||||
|
||||
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
class GoogleTravelTimeSensor(Entity):
|
||||
"""Representation of a tavel time sensor."""
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
def __init__(self, api_key, origin, destination, travel_mode, is_metric):
|
||||
def __init__(self, name, api_key, origin, destination, options):
|
||||
"""Initialize the sensor."""
|
||||
if is_metric:
|
||||
self._unit = 'metric'
|
||||
else:
|
||||
self._unit = 'imperial'
|
||||
self._name = name
|
||||
self._options = options
|
||||
self._origin = origin
|
||||
self._destination = destination
|
||||
self._travel_mode = travel_mode
|
||||
self._matrix = None
|
||||
self.valid_api_connection = True
|
||||
|
||||
|
@ -84,12 +141,13 @@ class GoogleTravelTimeSensor(Entity):
|
|||
@property
|
||||
def name(self):
|
||||
"""Get the name of the sensor."""
|
||||
return "Google Travel time"
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the state attributes."""
|
||||
res = self._matrix.copy()
|
||||
res.update(self._options)
|
||||
del res['rows']
|
||||
_data = self._matrix['rows'][0]['elements'][0]
|
||||
if 'duration_in_traffic' in _data:
|
||||
|
@ -108,10 +166,24 @@ class GoogleTravelTimeSensor(Entity):
|
|||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||
def update(self):
|
||||
"""Get the latest data from Google."""
|
||||
now = datetime.now()
|
||||
options_copy = self._options.copy()
|
||||
dtime = options_copy.get('departure_time')
|
||||
atime = options_copy.get('arrival_time')
|
||||
if dtime is not None and ':' in dtime:
|
||||
options_copy['departure_time'] = convert_time_to_utc(dtime)
|
||||
|
||||
if atime is not None and ':' in atime:
|
||||
options_copy['arrival_time'] = convert_time_to_utc(atime)
|
||||
|
||||
departure_time = options_copy.get('departure_time')
|
||||
arrival_time = options_copy.get('arrival_time')
|
||||
if departure_time is not None and arrival_time is not None:
|
||||
wstr = ("Google Travel Time: You can not provide both arrival "
|
||||
"and departure times! Deleting the arrival time...")
|
||||
_LOGGER.warning(wstr)
|
||||
del options_copy['arrival_time']
|
||||
del self._options['arrival_time']
|
||||
|
||||
self._matrix = self._client.distance_matrix(self._origin,
|
||||
self._destination,
|
||||
mode=self._travel_mode,
|
||||
units=self._unit,
|
||||
departure_time=now,
|
||||
traffic_model="optimistic")
|
||||
**options_copy)
|
||||
|
|
Loading…
Reference in New Issue