No, but i’ll start working on it. I was hoping it would make the update before I made changes. Looks like that won’t happen. I’ll update over the next few days
Hi there’s, any news.
Plus - a question:
You said polling happens every 5 minutes, but in the DB I see an update saved every 10 minutes - any idea why?
How can I control the time between polls in the config / code?
Thanks!
Yes, polling happens about every 5 minutes. If you want to change the interval, update this line of code:
SCAN_INTERVAL = timedelta(minutes=5)
It’s towards the top of the file.
I’ll let you know when I have an update. Haven’t had a chance to look at it yet.
I’m wondering does this component supports other countries such as those in Asia?
It does not. I’ve been looking for a list of regions that the API supports but I cannot find one. I’m pretty sure that waze app on phones works in Asia. Once I find the region list, I can attempt to add support for the other area’s to the WazeRouteCalculator.py files.
Give this a whirl and let me know if it updates immediately. The interval is still 5 minutes, i’m trying to have it match the google component.
"""
Support for Waze travel time sensor.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.waze_travel_time/
"""
from datetime import timedelta
import logging
import voluptuous as vol
from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import (
ATTR_ATTRIBUTION, CONF_NAME, CONF_REGION, EVENT_HOMEASSISTANT_START,
ATTR_LATITUDE, ATTR_LONGITUDE)
import homeassistant.helpers.config_validation as cv
import homeassistant.helpers.location as location
from homeassistant.helpers.entity import Entity
from homeassistant.util import Throttle
REQUIREMENTS = ['WazeRouteCalculator==0.5']
_LOGGER = logging.getLogger(__name__)
ATTR_DURATION = 'duration'
ATTR_DISTANCE = 'distance'
ATTR_ROUTE = 'route'
CONF_ATTRIBUTION = "Data provided by the Waze.com"
CONF_DESTINATION = 'destination'
CONF_ORIGIN = 'origin'
DEFAULT_NAME = 'Waze Travel Time'
ICON = 'mdi:car'
REGIONS = ['US', 'NA', 'EU', 'IL']
SCAN_INTERVAL = timedelta(minutes=5)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_ORIGIN): cv.string,
vol.Required(CONF_DESTINATION): cv.string,
vol.Required(CONF_REGION): vol.In(REGIONS),
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
})
TRACKABLE_DOMAINS = ['device_tracker', 'sensor', 'zone']
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Waze travel time sensor platform."""
def run_setup(event):
destination = config.get(CONF_DESTINATION)
name = config.get(CONF_NAME)
origin = config.get(CONF_ORIGIN)
region = config.get(CONF_REGION)
sensor = WazeTravelTime(hass, name, origin, destination, region)
add_devices([sensor])
hass.bus.listen_once(EVENT_HOMEASSISTANT_START, run_setup)
class WazeTravelTime(Entity):
"""Representation of a Waze travel time sensor."""
def __init__(self, hass, name, origin, destination, region):
"""Initialize the Waze travel time sensor."""
self._hass = hass
self._name = name
self._region = region
self._state = None
if origin.split('.', 1)[0] in TRACKABLE_DOMAINS:
self._origin_entity_id = origin
else:
self._origin = origin
if destination.split('.', 1)[0] in TRACKABLE_DOMAINS:
self._destination_entity_id = destination
else:
self._destination = destination
self._update()
@property
def name(self):
"""Return the name of the sensor."""
return self._name
@property
def state(self):
"""Return the state of the sensor."""
if self._state is None:
return None
if 'duration' in self._state:
return round(self._state['duration'])
return None
@property
def unit_of_measurement(self):
"""Return the unit of measurement."""
return 'min'
@property
def icon(self):
"""Icon to use in the frontend, if any."""
return ICON
@property
def device_state_attributes(self):
"""Return the state attributes of the last update."""
if self._state is None:
return None
res = {ATTR_ATTRIBUTION: CONF_ATTRIBUTION}
if 'duration' in self._state:
res[ATTR_DURATION] = self._state['duration']
if 'distance' in self._state:
res[ATTR_DISTANCE] = self._state['distance']
if 'route' in self._state:
res[ATTR_ROUTE] = self._state['route']
return res
def _get_location_from_entity(self, entity_id):
"""Get the location from the entity state or attributes."""
entity = self._hass.states.get(entity_id)
if entity is None:
_LOGGER.error("Unable to find entity %s", entity_id)
return None
# Check if the entity has location attributes (zone)
if location.has_location(entity):
return self._get_location_from_attributes(entity)
# Check if device is in a zone (device_tracker)
zone_entity = self._hass.states.get("zone.%s" % entity.state)
if location.has_location(zone_entity):
_LOGGER.debug(
"%s is in %s, getting zone location",
entity_id, zone_entity.entity_id
)
return self._get_location_from_attributes(zone_entity)
# If zone was not found in state then use the state as the location
if entity_id.startswith("sensor."):
return entity.state
# When everything fails just return nothing
return None
@staticmethod
def _get_location_from_attributes(entity):
"""Get the lat/long string from an entities attributes."""
attr = entity.attributes
return "{},{}".format(attr.get(ATTR_LATITUDE), attr.get(ATTR_LONGITUDE))
def _resolve_zone(self, friendly_name):
entities = self._hass.states.all()
for entity in entities:
if entity.domain == 'zone' and entity.name == friendly_name:
return self._get_location_from_attributes(entity)
return friendly_name
@Throttle(SCAN_INTERVAL)
def update(self):
self._update()
def _update(self):
"""Fetch new state data for the sensor."""
import WazeRouteCalculator
if hasattr(self, '_origin_entity_id'):
self._origin = self._get_location_from_entity(
self._origin_entity_id
)
if hasattr(self, '_destination_entity_id'):
self._destination = self._get_location_from_entity(
self._destination_entity_id
)
self._destination = self._resolve_zone(self._destination)
self._origin = self._resolve_zone(self._origin)
if self._destination is not None and self._origin is not None:
try:
params = WazeRouteCalculator.WazeRouteCalculator(
self._origin, self._destination, self._region)
routes = params.calc_all_routes_info()
route = next(iter(routes))
duration, distance = routes[route]
route = bytes(route, 'ISO-8859-1').decode('UTF-8')
self._state = {
'duration': duration,
'distance': distance,
'route': route}
except WazeRouteCalculator.WRCError as exp:
_LOGGER.error("Error on retrieving data: %s", exp)
return
except KeyError:
_LOGGER.error("Error retrieving data from server")
return
I have 4 Waze sensors setup
with the new code I see one updating immediately after HA launch complete (random - not the same sensor on each load) ** for the rest I see 3 * the following error in the log** (one per sensor):
2018-05-04 09:15:57 ERROR (MainThread) [homeassistant.core] Error doing job: Future exception was never retrieved
Traceback (most recent call last):
File "/usr/lib/python3.6/concurrent/futures/thread.py", line 56, in run
result = self.fn(*self.args, **self.kwargs)
File "/config/custom_components/sensor/waze_travel_time.py", line 59, in run_setup
sensor = WazeTravelTime(hass, name, origin, destination, region)
File "/config/custom_components/sensor/waze_travel_time.py", line 85, in __init__
self._update()
File "/config/custom_components/sensor/waze_travel_time.py", line 194, in _update
routes = params.calc_all_routes_info()
File "/usr/lib/python3.6/site-packages/WazeRouteCalculator/WazeRouteCalculator.py", line 142, in calc_all_routes_info
routes = self.get_route(npaths, time_delta)
File "/usr/lib/python3.6/site-packages/WazeRouteCalculator/WazeRouteCalculator.py", line 92, in get_route
response_json = response.json()
File "/usr/lib/python3.6/site-packages/requests/models.py", line 892, in json
return complexjson.loads(self.text, **kwargs)
File "/usr/lib/python3.6/site-packages/simplejson/__init__.py", line 518, in loads
return _default_decoder.decode(s)
File "/usr/lib/python3.6/site-packages/simplejson/decoder.py", line 370, in decode
obj, end = self.raw_decode(s)
File "/usr/lib/python3.6/site-packages/simplejson/decoder.py", line 400, in raw_decode
return self.scan_once(s, idx=_w(s, idx).end())
simplejson.errors.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
Okay. That’s odd because I didn’t change anything related to the calculator. I’ll have to see what the initial update is doing because it seems like that is causing the problem. Maybe during startup the devices aren’t initialized yet. I’ll take a look tonight.
what 4 waze sensors do you have setup? I’ve been running this with a 1 minute update (and it loads on startup) without errors for 3 days using 4 sensors: lat/long, device_trackers, zones, and a sensor. Can you link your configuration so I can mimic it?
Nothing special really…
sensor:
- platform: waze_travel_time
name: "iphone to work"
origin: device_tracker.iphone
destination: zone.work
region: 'IL'
- platform: waze_travel_time
name: "iphone to home"
origin: device_tracker.iphone
destination: zone.home
region: 'IL'
#
- platform: waze_travel_time
name: "Home to work"
origin: zone.home
destination: zone.work
region: 'IL'
- platform: waze_travel_time
name: "Work to home"
origin: zone.work
destination: zone.home
region: 'IL'
I’m currently using this Waze component in Hass.io. Would it be possible to get the “avoid toll roads” option as a future configuration variable? Thanks!
I haven’t tried this specific version of the code, but for my purpose there is still an issue with this code. If i use this version it is not defined which route is selected, and for my routes specifically it’s neither the fastest nor the shortest.
I’m unable to make a full pull request but i suggest this change to make it the fastest route by default. This means replacing:
route = next(iter(routes))
by:
route=sorted(routes,key=(lambda key: routes[key][0]))[0]
I’ve tried this in a slightly different version of the code, it gives the fastest route by default. The reason I’ve put this in the forum is that it’s nowhere written the fastest route is selected.
The Waze API claims it’s orders them via the fastest route. I’ve never verified that. Either way, implementing that line won’t be hard. The PR still hasn’t gone through to get into the official build so for the time being, just use your own custom component. I’m going to try to see why the PR isn’t going through and I can ninja this fix in.
Thanx for the reply.
I cannot verify the statement regarding the ordering but the used python lib (WazeRouteCalculator) provides a standard dict, which to my knowledge is not sorted.
I’d happily use my own component until these changes make there way in the official build. Until then keep up the good work.
I see this is included now in 0.70 but the docs don’t mention that the origin and destination can be a zone or a device tracker. Does the included component work in exactly the same way as the custom one?
I don’t believe it’s included yet.
Actually, a different PR was included that included or excluded devices. I’ll probably have to make changes to the PR to handle the new additions.
Using 0.69.1 & 0.70, I get the following error on using the waze travel time component:
waze_travel_time: Error on device update!
Traceback (most recent call last):
File "/usr/lib/python3.6/site-packages/homeassistant/helpers/entity_platform.py", line 244, in _async_add_entity
await entity.async_device_update(warning=False)
File "/usr/lib/python3.6/site-packages/homeassistant/helpers/entity.py", line 327, in async_device_update
yield from self.hass.async_add_job(self.update)
File "/usr/lib/python3.6/concurrent/futures/thread.py", line 56, in run
result = self.fn(*self.args, **self.kwargs)
File "/usr/lib/python3.6/site-packages/homeassistant/components/sensor/waze_travel_time.py", line 110, in update
self.waze_data.update()
File "/usr/lib/python3.6/site-packages/homeassistant/util/__init__.py", line 319, in wrapper
result = method(*args, **kwargs)
File "/usr/lib/python3.6/site-packages/homeassistant/components/sensor/waze_travel_time.py", line 135, in update
self._origin, self._destination, self._region, None)
File "/usr/lib/python3.6/site-packages/WazeRouteCalculator/WazeRouteCalculator.py", line 35, in __init__
self.start_coords = self.address_to_coords(start_address)
File "/usr/lib/python3.6/site-packages/WazeRouteCalculator/WazeRouteCalculator.py", line 58, in address_to_coords
response_json = response.json()[0]
IndexError: list index out of range
My config looks like:
- platform: waze_travel_time
name: "Peter To Home (Waze)"
origin: device_tracker.phone_pixel
destination: zone.home
region: 'EU'
I tried the waze component with hard coded addresses and that worked just fine.
Any ideas how to fix?
Are you using the code pasted above or what is installed with HA? The normal installation doesn’t currently support entity_id’s. I’m not sure why the PR is taking so long to get into a normal build.
Ah ok, am using the standard install. Thanks for clarifying!