Nissanleaf (#21145)

* Remove unneeded returns from handle_update()

* Start __init__() params with hass.

* Remove excess logging and downgrade remaining logging.

* Remove period from end of comment

* Decorate callback with @callback

* Use more descriptive variables than key and value.

* Inherit from BinarySensorDevice and overwrite is_on rather than state.

* Removed uncheckedreturn values.

* Use super() rather than explicit object.

* Use add_entities instead of add_devices.

* Don't use listener when calling immediately.

* Remove some excess logging.

* Switch to sync since pycarwings2 is sync.

* Remove RuntimeError exception matching.

* Add temporary reviewer comments.

* Add UI help descriptions for update service.

* Fix hound errors.

* Replaced time.sleep() with await asyncio.sleep()

* Removed location_updateon_on attribute since on device_tracker.

* Use async_added_to_hass() and async_dispatcher_connect().

* Use dict[key] because schema key is required.

* Clarify variable names.

* Remove icon for charging switch.

* Convert LeafChargeSwitch into service and sensor.

* Use async_dispatcher_send().

* Add guard checks for discovery_info. Consistent logs.

* Use async_schedul_update_ha_state().

* Device tracker should return true.

* Remove icon for climate control.

* Really remove icon for climate control.

* Use register() instead of async_register().

* Add guard on device tracker if discovery_info is None.
This commit is contained in:
Phil Cole 2019-02-22 16:34:23 +00:00 committed by Martin Hjelmare
parent 4102e24481
commit caa3b123ae
6 changed files with 176 additions and 190 deletions

View file

@ -1,13 +1,13 @@
"""Support for the Nissan Leaf Carwings/Nissan Connect API.""" """Support for the Nissan Leaf Carwings/Nissan Connect API."""
import asyncio
from datetime import datetime, timedelta from datetime import datetime, timedelta
import asyncio
import logging import logging
import sys import sys
import urllib
import voluptuous as vol import voluptuous as vol
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.discovery import load_platform from homeassistant.helpers.discovery import load_platform
from homeassistant.helpers.dispatcher import ( from homeassistant.helpers.dispatcher import (
@ -78,32 +78,60 @@ LEAF_COMPONENTS = [
SIGNAL_UPDATE_LEAF = 'nissan_leaf_update' SIGNAL_UPDATE_LEAF = 'nissan_leaf_update'
SERVICE_UPDATE_LEAF = 'update' SERVICE_UPDATE_LEAF = 'update'
SERVICE_START_CHARGE_LEAF = 'start_charge'
ATTR_VIN = 'vin' ATTR_VIN = 'vin'
UPDATE_LEAF_SCHEMA = vol.Schema({ UPDATE_LEAF_SCHEMA = vol.Schema({
vol.Required(ATTR_VIN): cv.string, vol.Required(ATTR_VIN): cv.string,
}) })
START_CHARGE_LEAF_SCHEMA = vol.Schema({
vol.Required(ATTR_VIN): cv.string,
})
async def async_setup(hass, config): def setup(hass, config):
"""Set up the Nissan Leaf component.""" """Set up the Nissan Leaf component."""
import pycarwings2 import pycarwings2
async def handle_update(service): async def async_handle_update(service):
"""Handle service to update leaf data from Nissan servers."""
# It would be better if this was changed to use nickname, or # It would be better if this was changed to use nickname, or
# an entity name rather than a vin. # an entity name rather than a vin.
vin = service.data.get(ATTR_VIN, '') vin = service.data[ATTR_VIN]
if vin in hass.data[DATA_LEAF]: if vin in hass.data[DATA_LEAF]:
data_store = hass.data[DATA_LEAF][vin] data_store = hass.data[DATA_LEAF][vin]
async_track_point_in_utc_time( await data_store.async_update_data(utcnow())
hass, data_store.async_update_data, utcnow()) else:
return True
_LOGGER.debug("Vin %s not recognised for update", vin) _LOGGER.debug("Vin %s not recognised for update", vin)
return False
async def async_setup_leaf(car_config): async def async_handle_start_charge(service):
"""Handle service to start charging."""
# It would be better if this was changed to use nickname, or
# an entity name rather than a vin.
vin = service.data[ATTR_VIN]
if vin in hass.data[DATA_LEAF]:
data_store = hass.data[DATA_LEAF][vin]
# Send the command to request charging is started to Nissan
# servers. If that completes OK then trigger a fresh update to
# pull the charging status from the car after waiting a minute
# for the charging request to reach the car.
result = await hass.async_add_executor_job(
data_store.leaf.start_charging)
if result:
_LOGGER.debug("Start charging sent, "
"request updated data in 1 minute")
check_charge_at = utcnow() + timedelta(minutes=1)
data_store.next_update = check_charge_at
async_track_point_in_utc_time(
hass, data_store.async_update_data, check_charge_at)
else:
_LOGGER.debug("Vin %s not recognised for update", vin)
def setup_leaf(car_config):
"""Set up a car.""" """Set up a car."""
_LOGGER.debug("Logging into You+Nissan...") _LOGGER.debug("Logging into You+Nissan...")
@ -112,20 +140,11 @@ async def async_setup(hass, config):
region = car_config[CONF_REGION] region = car_config[CONF_REGION]
leaf = None leaf = None
async def leaf_login():
nonlocal leaf
sess = pycarwings2.Session(username, password, region)
leaf = sess.get_leaf()
try: try:
# This might need to be made async (somehow) causes # This might need to be made async (somehow) causes
# homeassistant to be slow to start # homeassistant to be slow to start
await hass.async_add_job(leaf_login) sess = pycarwings2.Session(username, password, region)
except(RuntimeError, urllib.error.HTTPError): leaf = sess.get_leaf()
_LOGGER.error(
"Unable to connect to Nissan Connect with "
"username and password")
return False
except KeyError: except KeyError:
_LOGGER.error( _LOGGER.error(
"Unable to fetch car details..." "Unable to fetch car details..."
@ -143,7 +162,7 @@ async def async_setup(hass, config):
" as the drive train battery won't connect." " as the drive train battery won't connect."
" Don't set the intervals too low.") " Don't set the intervals too low.")
data_store = LeafDataStore(leaf, hass, car_config) data_store = LeafDataStore(hass, leaf, car_config)
hass.data[DATA_LEAF][leaf.vin] = data_store hass.data[DATA_LEAF][leaf.vin] = data_store
for component in LEAF_COMPONENTS: for component in LEAF_COMPONENTS:
@ -154,12 +173,15 @@ async def async_setup(hass, config):
utcnow() + INITIAL_UPDATE) utcnow() + INITIAL_UPDATE)
hass.data[DATA_LEAF] = {} hass.data[DATA_LEAF] = {}
tasks = [async_setup_leaf(car) for car in config[DOMAIN]] for car in config[DOMAIN]:
if tasks: setup_leaf(car)
await asyncio.wait(tasks, loop=hass.loop)
hass.services.async_register(DOMAIN, SERVICE_UPDATE_LEAF, handle_update, hass.services.register(
schema=UPDATE_LEAF_SCHEMA) DOMAIN, SERVICE_UPDATE_LEAF,
async_handle_update, schema=UPDATE_LEAF_SCHEMA)
hass.services.register(
DOMAIN, SERVICE_START_CHARGE_LEAF,
async_handle_start_charge, schema=START_CHARGE_LEAF_SCHEMA)
return True return True
@ -167,13 +189,13 @@ async def async_setup(hass, config):
class LeafDataStore: class LeafDataStore:
"""Nissan Leaf Data Store.""" """Nissan Leaf Data Store."""
def __init__(self, leaf, hass, car_config): def __init__(self, hass, leaf, car_config):
"""Initialise the data store.""" """Initialise the data store."""
self.hass = hass
self.leaf = leaf self.leaf = leaf
self.car_config = car_config self.car_config = car_config
self.nissan_connect = car_config[CONF_NCONNECT] self.nissan_connect = car_config[CONF_NCONNECT]
self.force_miles = car_config[CONF_FORCE_MILES] self.force_miles = car_config[CONF_FORCE_MILES]
self.hass = hass
self.data = {} self.data = {}
self.data[DATA_CLIMATE] = False self.data[DATA_CLIMATE] = False
self.data[DATA_BATTERY] = 0 self.data[DATA_BATTERY] = 0
@ -223,25 +245,19 @@ class LeafDataStore:
if (self.last_battery_response is not None and if (self.last_battery_response is not None and
self.data[DATA_CHARGING] is False and self.data[DATA_CHARGING] is False and
self.data[DATA_BATTERY] <= RESTRICTED_BATTERY): self.data[DATA_BATTERY] <= RESTRICTED_BATTERY):
_LOGGER.info("Low battery so restricting refresh frequency (%s)", _LOGGER.debug("Low battery so restricting refresh frequency (%s)",
self.leaf.nickname) self.leaf.nickname)
interval = RESTRICTED_INTERVAL interval = RESTRICTED_INTERVAL
else: else:
intervals = [base_interval] intervals = [base_interval]
_LOGGER.debug("Could use base interval=%s", base_interval)
if self.data[DATA_CHARGING]: if self.data[DATA_CHARGING]:
intervals.append(charging_interval) intervals.append(charging_interval)
_LOGGER.debug("Could use charging interval=%s",
charging_interval)
if self.data[DATA_CLIMATE]: if self.data[DATA_CLIMATE]:
intervals.append(climate_interval) intervals.append(climate_interval)
_LOGGER.debug(
"Could use climate interval=%s", climate_interval)
interval = min(intervals) interval = min(intervals)
_LOGGER.debug("Resulting interval=%s", interval)
return utcnow() + interval return utcnow() + interval
@ -310,12 +326,10 @@ class LeafDataStore:
_LOGGER.debug("Empty Location Response Received") _LOGGER.debug("Empty Location Response Received")
self.data[DATA_LOCATION] = None self.data[DATA_LOCATION] = None
else: else:
_LOGGER.debug("Got location data for Leaf")
self.data[DATA_LOCATION] = location_response
self.last_location_response = utcnow()
_LOGGER.debug("Location Response: %s", _LOGGER.debug("Location Response: %s",
location_response.__dict__) location_response.__dict__)
self.data[DATA_LOCATION] = location_response
self.last_location_response = utcnow()
except CarwingsError: except CarwingsError:
_LOGGER.error("Error fetching location info") _LOGGER.error("Error fetching location info")
@ -336,9 +350,8 @@ class LeafDataStore:
from pycarwings2 import CarwingsError from pycarwings2 import CarwingsError
try: try:
# First, check nissan servers for the latest data # First, check nissan servers for the latest data
start_server_info = await self.hass.async_add_job( start_server_info = await self.hass.async_add_executor_job(
self.leaf.get_latest_battery_status self.leaf.get_latest_battery_status)
)
# Store the date from the nissan servers # Store the date from the nissan servers
start_date = self._extract_start_date(start_server_info) start_date = self._extract_start_date(start_server_info)
@ -346,32 +359,34 @@ class LeafDataStore:
_LOGGER.info("No start date from servers. Aborting") _LOGGER.info("No start date from servers. Aborting")
return None return None
_LOGGER.info("Start server date=%s", start_date) _LOGGER.debug("Start server date=%s", start_date)
# Request battery update from the car # Request battery update from the car
_LOGGER.info("Requesting battery update, %s", self.leaf.vin) _LOGGER.debug("Requesting battery update, %s", self.leaf.vin)
request = await self.hass.async_add_job(self.leaf.request_update) request = await self.hass.async_add_executor_job(
self.leaf.request_update)
if not request: if not request:
_LOGGER.error("Battery update request failed") _LOGGER.error("Battery update request failed")
return None return None
for attempt in range(MAX_RESPONSE_ATTEMPTS): for attempt in range(MAX_RESPONSE_ATTEMPTS):
_LOGGER.info("Waiting %s seconds for battery update (%s) (%s)", _LOGGER.debug(
"Waiting %s seconds for battery update (%s) (%s)",
PYCARWINGS2_SLEEP, self.leaf.vin, attempt) PYCARWINGS2_SLEEP, self.leaf.vin, attempt)
await asyncio.sleep(PYCARWINGS2_SLEEP) await asyncio.sleep(PYCARWINGS2_SLEEP)
# Note leaf.get_status_from_update is always returning 0, so # Note leaf.get_status_from_update is always returning 0, so
# don't try to use it anymore. # don't try to use it anymore.
server_info = await self.hass.async_add_job( server_info = await self.hass.async_add_executor_job(
self.leaf.get_latest_battery_status self.leaf.get_latest_battery_status)
)
latest_date = self._extract_start_date(server_info) latest_date = self._extract_start_date(server_info)
_LOGGER.info("Latest server date=%s", latest_date) _LOGGER.debug("Latest server date=%s", latest_date)
if latest_date is not None and latest_date != start_date: if latest_date is not None and latest_date != start_date:
return server_info return server_info
_LOGGER.info("%s attempts exceeded return latest data from server", _LOGGER.debug(
"%s attempts exceeded return latest data from server",
MAX_RESPONSE_ATTEMPTS) MAX_RESPONSE_ATTEMPTS)
return server_info return server_info
except CarwingsError: except CarwingsError:
@ -382,10 +397,8 @@ class LeafDataStore:
"""Request climate data from Nissan servers.""" """Request climate data from Nissan servers."""
from pycarwings2 import CarwingsError from pycarwings2 import CarwingsError
try: try:
request = await self.hass.async_add_job( return await self.hass.async_add_executor_job(
self.leaf.get_latest_hvac_status self.leaf.get_latest_hvac_status)
)
return request
except CarwingsError: except CarwingsError:
_LOGGER.error( _LOGGER.error(
"An error occurred communicating with the car %s", "An error occurred communicating with the car %s",
@ -396,40 +409,24 @@ class LeafDataStore:
"""Set climate control mode via Nissan servers.""" """Set climate control mode via Nissan servers."""
climate_result = None climate_result = None
if toggle: if toggle:
_LOGGER.info("Requesting climate turn on for %s", self.leaf.vin) _LOGGER.debug("Requesting climate turn on for %s", self.leaf.vin)
request = await self.hass.async_add_job( set_function = self.leaf.start_climate_control
self.leaf.start_climate_control result_function = self.leaf.get_start_climate_control_result
)
for attempt in range(MAX_RESPONSE_ATTEMPTS):
if attempt > 0:
_LOGGER.info("Climate data not in yet (%s) (%s). "
"Waiting (%s) seconds.", self.leaf.vin,
attempt, PYCARWINGS2_SLEEP)
await asyncio.sleep(PYCARWINGS2_SLEEP)
climate_result = await self.hass.async_add_job(
self.leaf.get_start_climate_control_result, request
)
if climate_result is not None:
break
else: else:
_LOGGER.info("Requesting climate turn off for %s", self.leaf.vin) _LOGGER.debug("Requesting climate turn off for %s", self.leaf.vin)
request = await self.hass.async_add_job( set_function = self.leaf.stop_climate_control
self.leaf.stop_climate_control result_function = self.leaf.get_stop_climate_control_result
)
request = await self.hass.async_add_executor_job(set_function)
for attempt in range(MAX_RESPONSE_ATTEMPTS): for attempt in range(MAX_RESPONSE_ATTEMPTS):
if attempt > 0: if attempt > 0:
_LOGGER.debug("Climate data not in yet. (%s) (%s). " _LOGGER.debug("Climate data not in yet (%s) (%s). "
"Waiting %s seconds", self.leaf.vin, "Waiting (%s) seconds", self.leaf.vin,
attempt, PYCARWINGS2_SLEEP) attempt, PYCARWINGS2_SLEEP)
await asyncio.sleep(PYCARWINGS2_SLEEP) await asyncio.sleep(PYCARWINGS2_SLEEP)
climate_result = await self.hass.async_add_job( climate_result = await self.hass.async_add_executor_job(
self.leaf.get_stop_climate_control_result, request result_function, request)
)
if climate_result is not None: if climate_result is not None:
break break
@ -444,7 +441,8 @@ class LeafDataStore:
async def async_get_location(self): async def async_get_location(self):
"""Get location from Nissan servers.""" """Get location from Nissan servers."""
request = await self.hass.async_add_job(self.leaf.request_location) request = await self.hass.async_add_executor_job(
self.leaf.request_location)
for attempt in range(MAX_RESPONSE_ATTEMPTS): for attempt in range(MAX_RESPONSE_ATTEMPTS):
if attempt > 0: if attempt > 0:
_LOGGER.debug("Location data not in yet. (%s) (%s). " _LOGGER.debug("Location data not in yet. (%s) (%s). "
@ -452,9 +450,8 @@ class LeafDataStore:
attempt, PYCARWINGS2_SLEEP) attempt, PYCARWINGS2_SLEEP)
await asyncio.sleep(PYCARWINGS2_SLEEP) await asyncio.sleep(PYCARWINGS2_SLEEP)
location_status = await self.hass.async_add_job( location_status = await self.hass.async_add_executor_job(
self.leaf.get_status_from_location, request self.leaf.get_status_from_location, request)
)
if location_status is not None: if location_status is not None:
_LOGGER.debug("Location_status=%s", location_status.__dict__) _LOGGER.debug("Location_status=%s", location_status.__dict__)
@ -462,21 +459,6 @@ class LeafDataStore:
return location_status return location_status
async def async_start_charging(self):
"""Request start charging via Nissan servers."""
# Send the command to request charging is started to Nissan servers.
# If that completes OK then trigger a fresh update to pull the
# charging status from the car after waiting a minute for the
# charging request to reach the car.
result = await self.hass.async_add_job(self.leaf.start_charging)
if result:
_LOGGER.debug("Start charging sent, "
"request updated data in 1 minute")
check_charge_at = utcnow() + timedelta(minutes=1)
self.next_update = check_charge_at
async_track_point_in_utc_time(
self.hass, self.async_update_data, check_charge_at)
class LeafEntity(Entity): class LeafEntity(Entity):
"""Base class for Nissan Leaf entity.""" """Base class for Nissan Leaf entity."""
@ -499,7 +481,6 @@ class LeafEntity(Entity):
'last_attempt': self.car.last_check, 'last_attempt': self.car.last_check,
'updated_on': self.car.last_battery_response, 'updated_on': self.car.last_battery_response,
'update_in_progress': self.car.request_in_progress, 'update_in_progress': self.car.request_in_progress,
'location_updated_on': self.car.last_location_response,
'vin': self.car.leaf.vin, 'vin': self.car.leaf.vin,
} }
@ -509,6 +490,7 @@ class LeafEntity(Entity):
async_dispatcher_connect( async_dispatcher_connect(
self.car.hass, SIGNAL_UPDATE_LEAF, self._update_callback) self.car.hass, SIGNAL_UPDATE_LEAF, self._update_callback)
@callback
def _update_callback(self): def _update_callback(self):
"""Update the state.""" """Update the state."""
self.schedule_update_ha_state(True) self.async_schedule_update_ha_state(True)

View file

@ -2,28 +2,29 @@
import logging import logging
from homeassistant.components.nissan_leaf import ( from homeassistant.components.nissan_leaf import (
DATA_LEAF, DATA_PLUGGED_IN, LeafEntity) DATA_CHARGING, DATA_LEAF, DATA_PLUGGED_IN, LeafEntity)
from homeassistant.components.binary_sensor import BinarySensorDevice
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['nissan_leaf'] DEPENDENCIES = ['nissan_leaf']
def setup_platform(hass, config, add_devices, discovery_info=None): def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up of a Nissan Leaf binary sensor.""" """Set up of a Nissan Leaf binary sensor."""
_LOGGER.debug( if discovery_info is None:
"binary_sensor setup_platform, discovery_info=%s", discovery_info) return
devices = [] devices = []
for key, value in hass.data[DATA_LEAF].items(): for vin, datastore in hass.data[DATA_LEAF].items():
_LOGGER.debug( _LOGGER.debug("Adding binary_sensors for vin=%s", vin)
"binary_sensor setup_platform, key=%s, value=%s", key, value) devices.append(LeafPluggedInSensor(datastore))
devices.append(LeafPluggedInSensor(value)) devices.append(LeafChargingSensor(datastore))
add_devices(devices, True) add_entities(devices, True)
class LeafPluggedInSensor(LeafEntity): class LeafPluggedInSensor(LeafEntity, BinarySensorDevice):
"""Plugged In Sensor class.""" """Plugged In Sensor class."""
@property @property
@ -32,7 +33,7 @@ class LeafPluggedInSensor(LeafEntity):
return "{} {}".format(self.car.leaf.nickname, "Plug Status") return "{} {}".format(self.car.leaf.nickname, "Plug Status")
@property @property
def state(self): def is_on(self):
"""Return true if plugged in.""" """Return true if plugged in."""
return self.car.data[DATA_PLUGGED_IN] return self.car.data[DATA_PLUGGED_IN]
@ -42,3 +43,24 @@ class LeafPluggedInSensor(LeafEntity):
if self.car.data[DATA_PLUGGED_IN]: if self.car.data[DATA_PLUGGED_IN]:
return 'mdi:power-plug' return 'mdi:power-plug'
return 'mdi:power-plug-off' return 'mdi:power-plug-off'
class LeafChargingSensor(LeafEntity, BinarySensorDevice):
"""Charging Sensor class."""
@property
def name(self):
"""Sensor name."""
return "{} {}".format(self.car.leaf.nickname, "Charging Status")
@property
def is_on(self):
"""Return true if charging."""
return self.car.data[DATA_CHARGING]
@property
def icon(self):
"""Icon handling."""
if self.car.data[DATA_CHARGING]:
return 'mdi:flash'
return 'mdi:flash-off'

View file

@ -15,28 +15,28 @@ ICON_CAR = "mdi:car"
def setup_scanner(hass, config, see, discovery_info=None): def setup_scanner(hass, config, see, discovery_info=None):
"""Set up the Nissan Leaf tracker.""" """Set up the Nissan Leaf tracker."""
_LOGGER.debug("Setting up Scanner (device_tracker) for Nissan Leaf, " if discovery_info is None:
"discovery_info=%s", discovery_info) return False
def see_vehicle(): def see_vehicle():
"""Handle the reporting of the vehicle position.""" """Handle the reporting of the vehicle position."""
for key, value in hass.data[DATA_LEAF].items(): for vin, datastore in hass.data[DATA_LEAF].items():
host_name = value.leaf.nickname host_name = datastore.leaf.nickname
dev_id = 'nissan_leaf_{}'.format(slugify(host_name)) dev_id = 'nissan_leaf_{}'.format(slugify(host_name))
if not value.data[DATA_LOCATION]: if not datastore.data[DATA_LOCATION]:
_LOGGER.debug("No position found for vehicle %s", key) _LOGGER.debug("No position found for vehicle %s", vin)
return False return
_LOGGER.debug("Updating device_tracker for %s with position %s", _LOGGER.debug("Updating device_tracker for %s with position %s",
value.leaf.nickname, datastore.leaf.nickname,
value.data[DATA_LOCATION].__dict__) datastore.data[DATA_LOCATION].__dict__)
attrs = { attrs = {
'updated_on': value.last_location_response, 'updated_on': datastore.last_location_response,
} }
see(dev_id=dev_id, see(dev_id=dev_id,
host_name=host_name, host_name=host_name,
gps=( gps=(
value.data[DATA_LOCATION].latitude, datastore.data[DATA_LOCATION].latitude,
value.data[DATA_LOCATION].longitude datastore.data[DATA_LOCATION].longitude
), ),
attributes=attrs, attributes=attrs,
icon=ICON_CAR) icon=ICON_CAR)

View file

@ -18,15 +18,15 @@ ICON_RANGE = 'mdi:speedometer'
def setup_platform(hass, config, add_devices, discovery_info=None): def setup_platform(hass, config, add_devices, discovery_info=None):
"""Sensors setup.""" """Sensors setup."""
_LOGGER.debug("setup_platform nissan_leaf sensors, discovery_info=%s", if discovery_info is None:
discovery_info) return
devices = [] devices = []
for key, value in hass.data[DATA_LEAF].items(): for vin, datastore in hass.data[DATA_LEAF].items():
_LOGGER.debug("adding sensor for item key=%s, value=%s", key, value) _LOGGER.debug("Adding sensors for vin=%s", vin)
devices.append(LeafBatterySensor(value)) devices.append(LeafBatterySensor(datastore))
devices.append(LeafRangeSensor(value, True)) devices.append(LeafRangeSensor(datastore, True))
devices.append(LeafRangeSensor(value, False)) devices.append(LeafRangeSensor(datastore, False))
add_devices(devices, True) add_devices(devices, True)

View file

@ -0,0 +1,21 @@
# Describes the format for available services for nissan_leaf
start_charge:
description: >
Start the vehicle charging. It must be plugged in first!
fields:
vin:
description: >
The vehicle identification number (VIN) of the vehicle, 17 characters
example: WBANXXXXXX1234567
update:
description: >
Fetch the last state of the vehicle of all your accounts, requesting
an update from of the state from the car if possible.
fields:
vin:
description: >
The vehicle identification number (VIN) of the vehicle, 17 characters
example: WBANXXXXXX1234567

View file

@ -2,7 +2,7 @@
import logging import logging
from homeassistant.components.nissan_leaf import ( from homeassistant.components.nissan_leaf import (
DATA_CHARGING, DATA_CLIMATE, DATA_LEAF, LeafEntity) DATA_CLIMATE, DATA_LEAF, LeafEntity)
from homeassistant.helpers.entity import ToggleEntity from homeassistant.helpers.entity import ToggleEntity
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -12,13 +12,13 @@ DEPENDENCIES = ['nissan_leaf']
def setup_platform(hass, config, add_devices, discovery_info=None): def setup_platform(hass, config, add_devices, discovery_info=None):
"""Nissan Leaf switch platform setup.""" """Nissan Leaf switch platform setup."""
_LOGGER.debug( if discovery_info is None:
"In switch setup platform, discovery_info=%s", discovery_info) return
devices = [] devices = []
for value in hass.data[DATA_LEAF].values(): for vin, datastore in hass.data[DATA_LEAF].items():
devices.append(LeafChargeSwitch(value)) _LOGGER.debug("Adding switch for vin=%s", vin)
devices.append(LeafClimateSwitch(value)) devices.append(LeafClimateSwitch(datastore))
add_devices(devices, True) add_devices(devices, True)
@ -40,7 +40,7 @@ class LeafClimateSwitch(LeafEntity, ToggleEntity):
@property @property
def device_state_attributes(self): def device_state_attributes(self):
"""Return climate control attributes.""" """Return climate control attributes."""
attrs = super(LeafClimateSwitch, self).device_state_attributes attrs = super().device_state_attributes
attrs["updated_on"] = self.car.last_climate_response attrs["updated_on"] = self.car.last_climate_response
return attrs return attrs
@ -58,42 +58,3 @@ class LeafClimateSwitch(LeafEntity, ToggleEntity):
"""Turn off climate control.""" """Turn off climate control."""
if await self.car.async_set_climate(False): if await self.car.async_set_climate(False):
self.car.data[DATA_CLIMATE] = False self.car.data[DATA_CLIMATE] = False
@property
def icon(self):
"""Climate control icon."""
if self.car.data[DATA_CLIMATE]:
return 'mdi:fan'
return 'mdi:fan-off'
class LeafChargeSwitch(LeafEntity, ToggleEntity):
"""Nissan Leaf Charging On switch."""
@property
def name(self):
"""Switch name."""
return "{} {}".format(self.car.leaf.nickname, "Charging Status")
@property
def icon(self):
"""Charging switch icon."""
if self.car.data[DATA_CHARGING]:
return 'mdi:flash'
return 'mdi:flash-off'
@property
def is_on(self):
"""Return true if charging."""
return self.car.data[DATA_CHARGING]
async def async_turn_on(self, **kwargs):
"""Start car charging."""
if await self.car.async_start_charging():
self.car.data[DATA_CHARGING] = True
def turn_off(self, **kwargs):
"""Nissan API doesn't allow stopping of charge remotely."""
_LOGGER.info(
"Cannot turn off Leaf charging."
" Nissan API does not support stopping charge remotely")