From 9f8acbec9595e7d9055a5762670de117bb432834 Mon Sep 17 00:00:00 2001 From: Erik Eriksson Date: Sun, 2 Oct 2016 08:00:01 +0200 Subject: [PATCH] Add support for tracking position and status for a Volvo car (#3617) --- .coveragerc | 1 + .../components/device_tracker/volvooncall.py | 105 ++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 homeassistant/components/device_tracker/volvooncall.py diff --git a/.coveragerc b/.coveragerc index e1652fb23b6..ee2551e4a20 100644 --- a/.coveragerc +++ b/.coveragerc @@ -141,6 +141,7 @@ omit = homeassistant/components/device_tracker/tomato.py homeassistant/components/device_tracker/tplink.py homeassistant/components/device_tracker/ubus.py + homeassistant/components/device_tracker/volvooncall.py homeassistant/components/discovery.py homeassistant/components/downloader.py homeassistant/components/fan/mqtt.py diff --git a/homeassistant/components/device_tracker/volvooncall.py b/homeassistant/components/device_tracker/volvooncall.py new file mode 100644 index 00000000000..6876d63c223 --- /dev/null +++ b/homeassistant/components/device_tracker/volvooncall.py @@ -0,0 +1,105 @@ +""" +Support for Volvo On Call. + +http://www.volvocars.com/intl/own/owner-info/volvo-on-call +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/device_tracker.volvooncall/ +""" +import logging +from datetime import timedelta +from urllib.parse import urljoin +import voluptuous as vol +import requests + +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.event import track_point_in_utc_time +from homeassistant.util.dt import utcnow +from homeassistant.const import ( + CONF_PASSWORD, + CONF_SCAN_INTERVAL, + CONF_USERNAME) +from homeassistant.components.device_tracker import ( + DEFAULT_SCAN_INTERVAL, + PLATFORM_SCHEMA) + +MIN_TIME_BETWEEN_SCANS = timedelta(minutes=1) + +_LOGGER = logging.getLogger(__name__) + +SERVICE_URL = 'https://vocapi.wirelesscar.net/customerapi/rest/v3.0/' +HEADERS = {"X-Device-Id": "Device", + "X-OS-Type": "Android", + "X-Originator-Type": "App"} + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_USERNAME): cv.string, + vol.Required(CONF_PASSWORD): cv.string, +}) + + +def setup_scanner(hass, config, see): + """Validate the configuration and return a scanner.""" + username = config.get(CONF_USERNAME) + password = config.get(CONF_PASSWORD) + + interval = max(MIN_TIME_BETWEEN_SCANS.seconds, + config.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)) + + session = requests.Session() + session.headers.update(HEADERS) + session.auth = (username, password) + + def query(ref, rel=SERVICE_URL): + """Perform a query to the online service.""" + url = urljoin(rel, ref) + try: + _LOGGER.debug("Request for %s", url) + res = session.get(url) + res.raise_for_status() + _LOGGER.debug("Received %s", res.json()) + return res.json() + except requests.exceptions.RequestException: + _LOGGER.exception("Could not make query to %s", url) + raise + + try: + _LOGGER.info('Logging in to service') + user = query("customeraccounts") + rel = query(user["accountVehicleRelations"][0]) + vehicle_url = rel["vehicle"] + '/' + except requests.exceptions.RequestException: + _LOGGER.error("Could not log in to service. " + "Please check configuration.") + return False + + def update(now): + """Update status from the online service.""" + _LOGGER.debug("Updating") + + status = query("status", vehicle_url) + position = query("position", vehicle_url) + attributes = query("attributes", vehicle_url) + + dev_id = "volvo_" + attributes["vin"] + host_name = "%s %s/%s" % (attributes["registrationNumber"], + attributes["vehicleType"], + attributes["modelYear"]) + + see(dev_id=dev_id, + host_name=host_name, + gps=(position["position"]["latitude"], + position["position"]["longitude"]), + attributes=dict( + tank_volume=attributes["fuelTankVolume"], + washer_fluid=status["washerFluidLevel"], + brake_fluid=status["brakeFluid"], + service_warning=status["serviceWarningStatus"], + fuel=status["fuelAmount"], + odometer=status["odometer"], + range=status["distanceToEmpty"])) + + track_point_in_utc_time(hass, update, + now + timedelta(seconds=interval)) + + update(utcnow()) + return True