Updated volvooncall library + support sensors, heater and lock (#6052)

This commit is contained in:
Erik Eriksson 2017-02-19 02:09:25 +01:00 committed by Paulus Schoutsen
parent beb8b4b11f
commit 9e73115337
8 changed files with 375 additions and 80 deletions

View file

@ -88,6 +88,9 @@ omit =
homeassistant/components/verisure.py
homeassistant/components/*/verisure.py
homeassistant/components/volvooncall.py
homeassistant/components/*/volvooncall.py
homeassistant/components/*/webostv.py
homeassistant/components/wemo.py
@ -183,7 +186,6 @@ omit =
homeassistant/components/device_tracker/tplink.py
homeassistant/components/device_tracker/trackr.py
homeassistant/components/device_tracker/ubus.py
homeassistant/components/device_tracker/volvooncall.py
homeassistant/components/device_tracker/xiaomi.py
homeassistant/components/discovery.py
homeassistant/components/downloader.py

View file

@ -0,0 +1,59 @@
"""
Support for VOC.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.volvooncall/
"""
import logging
from homeassistant.components.volvooncall import VolvoEntity
from homeassistant.components.binary_sensor import BinarySensorDevice
_LOGGER = logging.getLogger(__name__)
SENSORS = [('washer_fluid_level', 'Washer fluid'),
('brake_fluid', 'Brake Fluid'),
('service_warning_status', 'Service'),
('bulb_failures', 'Bulbs'),
('doors', 'Doors'),
('windows', 'Windows')]
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup Volvo sensors."""
if discovery_info is None:
return
add_devices(VolvoSensor(hass, discovery_info, sensor)
for sensor in SENSORS)
class VolvoSensor(VolvoEntity, BinarySensorDevice):
"""Representation of a Volvo sensor."""
def __init__(self, hass, vehicle, sensor):
"""Initialize the sensor."""
super().__init__(hass, vehicle)
self._sensor = sensor
@property
def _name(self):
"""Name of sensor."""
return self._sensor[1]
@property
def is_on(self):
"""Return True if the binary sensor is on."""
attr = self._sensor[0]
val = getattr(self.vehicle, attr)
if attr == 'bulb_failures':
return len(val) > 0
elif attr in ['doors', 'windows']:
return any([val[key] for key in val if 'Open' in key])
else:
return val != 'Normal'
@property
def device_class(self):
"""Return the class of this sensor, from SENSOR_CLASSES."""
return 'safety'

View file

@ -1,97 +1,35 @@
"""
Support for Volvo On Call.
Support for tracking a Volvo.
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
import voluptuous as vol
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.util import slugify
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)
from homeassistant.components.volvooncall import DOMAIN
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['volvooncall==0.1.1']
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
})
def setup_scanner(hass, config, see, discovery_info=None):
"""Validate the configuration and return a scanner."""
from volvooncall import Connection
connection = Connection(
config.get(CONF_USERNAME),
config.get(CONF_PASSWORD))
"""Setup Volvo tracker."""
if discovery_info is None:
return
interval = max(MIN_TIME_BETWEEN_SCANS,
config.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL))
vin = discovery_info
vehicle = hass.data[DOMAIN].vehicles[vin]
def _see_vehicle(vehicle):
position = vehicle["position"]
dev_id = "volvo_" + slugify(vehicle["registrationNumber"])
host_name = "%s (%s/%s)" % (
vehicle["registrationNumber"],
vehicle["vehicleType"],
vehicle["modelYear"])
def any_opened(door):
"""True if any door/window is opened."""
return any([door[key] for key in door if "Open" in key])
attributes = dict(
unlocked=not vehicle["carLocked"],
tank_volume=vehicle["fuelTankVolume"],
average_fuel_consumption=round(
vehicle["averageFuelConsumption"] / 10, 1), # l/100km
washer_fluid_low=vehicle["washerFluidLevel"] != "Normal",
brake_fluid_low=vehicle["brakeFluid"] != "Normal",
service_warning=vehicle["serviceWarningStatus"] != "Normal",
bulb_failures=len(vehicle["bulbFailures"]) > 0,
doors_open=any_opened(vehicle["doors"]),
windows_open=any_opened(vehicle["windows"]),
fuel=vehicle["fuelAmount"],
odometer=round(vehicle["odometer"] / 1000), # km
range=vehicle["distanceToEmpty"])
if "heater" in vehicle and \
"status" in vehicle["heater"]:
attributes.update(heater_on=vehicle["heater"]["status"] != "off")
host_name = vehicle.registration_number
dev_id = 'volvo_' + slugify(host_name)
def see_vehicle(vehicle):
"""Callback for reporting vehicle position."""
see(dev_id=dev_id,
host_name=host_name,
gps=(position["latitude"],
position["longitude"]),
attributes=attributes)
gps=(vehicle.position['latitude'],
vehicle.position['longitude']))
def update(now):
"""Update status from the online service."""
_LOGGER.info("Updating")
try:
res, vehicles = connection.update()
if not res:
_LOGGER.error("Could not query server")
return False
hass.data[DOMAIN].entities[vin].append(see_vehicle)
for vehicle in vehicles:
_see_vehicle(vehicle)
return True
finally:
track_point_in_utc_time(hass, update, now + interval)
_LOGGER.info('Logging in to service')
return update(utcnow())
return True

View file

@ -0,0 +1,42 @@
"""
Support for Volvo locks.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/lock.volvooncall/
"""
import logging
from homeassistant.components.lock import LockDevice
from homeassistant.components.volvooncall import VolvoEntity
_LOGGER = logging.getLogger(__name__)
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the lock."""
if discovery_info is None:
return
add_devices([VolvoLock(hass, discovery_info)])
class VolvoLock(VolvoEntity, LockDevice):
"""Represents a car lock."""
@property
def is_locked(self):
"""Return true if lock is locked."""
return self.vehicle.is_locked
def lock(self, **kwargs):
"""Lock the car."""
self.vehicle.lock()
def unlock(self, **kwargs):
"""Unlock the car."""
self.vehicle.unlock()
@property
def _name(self):
"""Return name."""
return 'Lock'

View file

@ -0,0 +1,59 @@
"""
Support for VOC.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.volvooncall/
"""
import logging
from homeassistant.components.volvooncall import VolvoEntity
_LOGGER = logging.getLogger(__name__)
SENSORS = [('odometer', 'Odometer', 'km', 'mdi:speedometer'),
('fuel_amount', 'Fuel', 'L', 'mdi:gas-station'),
('fuel_amount_level', 'Fuel', '%', 'mdi:water-percent'),
('distance_to_empty', 'Range', 'km', 'mdi:ruler')]
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup Volvo sensors."""
if discovery_info is None:
return
add_devices(VolvoSensor(hass, discovery_info, sensor)
for sensor in SENSORS)
class VolvoSensor(VolvoEntity):
"""Representation of a Volvo sensor."""
def __init__(self, hass, vehicle, sensor):
"""Initialize sensor."""
super().__init__(hass, vehicle)
self._sensor = sensor
@property
def state(self):
"""Return the state of the sensor."""
attr = self._sensor[0]
val = getattr(self.vehicle, attr)
if attr == 'odometer':
return round(val / 1000) # km
else:
return val
@property
def _name(self):
"""Name of quantity."""
return self._sensor[1]
@property
def unit_of_measurement(self):
"""Return the unit of measurement."""
return self._sensor[2]
@property
def icon(self):
"""Return the icon."""
return self._sensor[3]

View file

@ -0,0 +1,47 @@
"""
Support for Volvo heater.
This platform uses the Telldus Live online service.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/switch.volvooncall/
"""
import logging
from homeassistant.components.volvooncall import VolvoEntity
from homeassistant.helpers.entity import ToggleEntity
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup Tellstick switches."""
if discovery_info is None:
return
add_devices([VolvoSwitch(hass, discovery_info)])
class VolvoSwitch(VolvoEntity, ToggleEntity):
"""Representation of a Volvo switch."""
@property
def is_on(self):
"""Return true if switch is on."""
return self.vehicle.is_heater_on
def turn_on(self, **kwargs):
"""Turn the switch on."""
self.vehicle.start_heater()
def turn_off(self, **kwargs):
"""Turn the switch off."""
self.vehicle.stop_heater()
@property
def _name(self):
"""Return the name of the switch."""
return 'Heater'
def icon(self):
"""Return the icon."""
return 'mdi:radiator'

View file

@ -0,0 +1,148 @@
"""
Support for Volvo On Call.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/volvooncall/
"""
from datetime import timedelta
import logging
from homeassistant.const import (CONF_USERNAME, CONF_PASSWORD)
from homeassistant.helpers import discovery
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.event import track_point_in_utc_time
from homeassistant.util.dt import utcnow
import voluptuous as vol
DOMAIN = 'volvooncall'
REQUIREMENTS = ['volvooncall==0.3.0']
_LOGGER = logging.getLogger(__name__)
CONF_UPDATE_INTERVAL = 'update_interval'
MIN_UPDATE_INTERVAL = timedelta(minutes=1)
DEFAULT_UPDATE_INTERVAL = timedelta(minutes=1)
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_UPDATE_INTERVAL, default=DEFAULT_UPDATE_INTERVAL): (
vol.All(cv.time_period, vol.Clamp(min=MIN_UPDATE_INTERVAL)))
}),
}, extra=vol.ALLOW_EXTRA)
def setup(hass, config):
"""Setup the VOC component."""
from volvooncall import Connection
connection = Connection(
config[DOMAIN].get(CONF_USERNAME),
config[DOMAIN].get(CONF_PASSWORD))
interval = config[DOMAIN].get(CONF_UPDATE_INTERVAL)
class state: # pylint:disable=invalid-name
"""Namespace to hold state for each vehicle."""
entities = {}
vehicles = {}
hass.data[DOMAIN] = state
def discover_vehicle(vehicle):
"""Load relevant platforms."""
state.entities[vehicle.vin] = []
components = ['sensor', 'binary_sensor']
if getattr(vehicle, 'position'):
components.append('device_tracker')
if vehicle.heater_supported:
components.append('switch')
if vehicle.lock_supported:
components.append('lock')
for component in components:
discovery.load_platform(hass,
component,
DOMAIN,
vehicle.vin,
config)
def update_vehicle(vehicle):
"""Updated information on vehicle received."""
state.vehicles[vehicle.vin] = vehicle
if vehicle.vin not in state.entities:
discover_vehicle(vehicle)
for entity in state.entities[vehicle.vin]:
if isinstance(entity, Entity):
entity.schedule_update_ha_state()
else:
entity(vehicle) # device tracker
def update(now):
"""Update status from the online service."""
try:
if not connection.update():
_LOGGER.warning('Could not query server')
return False
for vehicle in connection.vehicles:
update_vehicle(vehicle)
return True
finally:
track_point_in_utc_time(hass, update, now + interval)
_LOGGER.info('Logging in to service')
return update(utcnow())
class VolvoEntity(Entity):
"""Base class for all VOC entities."""
def __init__(self, hass, vin):
"""Initialize the entity."""
self._hass = hass
self._vin = vin
self._hass.data[DOMAIN].entities[self._vin].append(self)
@property
def vehicle(self):
"""Return vehicle."""
return self._hass.data[DOMAIN].vehicles[self._vin]
@property
def name(self):
"""Return the name of the sensor."""
return '%s %s' % (
self.vehicle.registration_number,
self._name)
@property
def _name(self):
"""Overridden by subclasses."""
return None
@property
def should_poll(self):
"""Polling is not needed."""
return False
@property
def assumed_state(self):
"""Return true if unable to access real state of entity."""
return True
@property
def device_state_attributes(self):
"""Return device specific state attributes."""
return dict(model='%s/%s' % (
self.vehicle.vehicle_type,
self.vehicle.model_year))

View file

@ -710,8 +710,8 @@ urllib3
# homeassistant.components.camera.uvc
uvcclient==0.10.0
# homeassistant.components.device_tracker.volvooncall
volvooncall==0.1.1
# homeassistant.components.volvooncall
volvooncall==0.3.0
# homeassistant.components.verisure
vsure==0.11.1