Update mychevy to 0.4.0 (#14372)

After 2 months of being offline, the my.chevy website seems to be
working again. Some data structures changed in the mean time. The new
library will handle multiple cars. This involves a breaking change in
slug urls for devices where these now include the car make, model, and
year in them.

Discovery has to be delayed until after the initial site login to get
the car metadata.
This commit is contained in:
Sean Dague 2018-05-18 13:37:43 -04:00 committed by Paulus Schoutsen
parent d7640e6ec3
commit 25970027c6
4 changed files with 55 additions and 24 deletions

View File

@ -31,7 +31,8 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
sensors = [] sensors = []
hub = hass.data[MYCHEVY_DOMAIN] hub = hass.data[MYCHEVY_DOMAIN]
for sconfig in SENSORS: for sconfig in SENSORS:
sensors.append(EVBinarySensor(hub, sconfig)) for car in hub.cars:
sensors.append(EVBinarySensor(hub, sconfig, car.vid))
async_add_devices(sensors) async_add_devices(sensors)
@ -45,16 +46,18 @@ class EVBinarySensor(BinarySensorDevice):
""" """
def __init__(self, connection, config): def __init__(self, connection, config, car_vid):
"""Initialize sensor with car connection.""" """Initialize sensor with car connection."""
self._conn = connection self._conn = connection
self._name = config.name self._name = config.name
self._attr = config.attr self._attr = config.attr
self._type = config.device_class self._type = config.device_class
self._is_on = None self._is_on = None
self._car_vid = car_vid
self.entity_id = ENTITY_ID_FORMAT.format( self.entity_id = ENTITY_ID_FORMAT.format(
'{}_{}'.format(MYCHEVY_DOMAIN, slugify(self._name))) '{}_{}_{}'.format(MYCHEVY_DOMAIN,
slugify(self._car.name),
slugify(self._name)))
@property @property
def name(self): def name(self):
@ -66,6 +69,11 @@ class EVBinarySensor(BinarySensorDevice):
"""Return if on.""" """Return if on."""
return self._is_on return self._is_on
@property
def _car(self):
"""Return the car."""
return self._conn.get_car(self._car_vid)
@asyncio.coroutine @asyncio.coroutine
def async_added_to_hass(self): def async_added_to_hass(self):
"""Register callbacks.""" """Register callbacks."""
@ -75,8 +83,8 @@ class EVBinarySensor(BinarySensorDevice):
@callback @callback
def async_update_callback(self): def async_update_callback(self):
"""Update state.""" """Update state."""
if self._conn.car is not None: if self._car is not None:
self._is_on = getattr(self._conn.car, self._attr, None) self._is_on = getattr(self._car, self._attr, None)
self.async_schedule_update_ha_state() self.async_schedule_update_ha_state()
@property @property

View File

@ -16,7 +16,7 @@ from homeassistant.helpers import config_validation as cv
from homeassistant.helpers import discovery from homeassistant.helpers import discovery
from homeassistant.util import Throttle from homeassistant.util import Throttle
REQUIREMENTS = ["mychevy==0.1.1"] REQUIREMENTS = ["mychevy==0.4.0"]
DOMAIN = 'mychevy' DOMAIN = 'mychevy'
UPDATE_TOPIC = DOMAIN UPDATE_TOPIC = DOMAIN
@ -73,9 +73,6 @@ def setup(hass, base_config):
hass.data[DOMAIN] = MyChevyHub(mc.MyChevy(email, password), hass) hass.data[DOMAIN] = MyChevyHub(mc.MyChevy(email, password), hass)
hass.data[DOMAIN].start() hass.data[DOMAIN].start()
discovery.load_platform(hass, 'sensor', DOMAIN, {}, config)
discovery.load_platform(hass, 'binary_sensor', DOMAIN, {}, config)
return True return True
@ -98,8 +95,9 @@ class MyChevyHub(threading.Thread):
super().__init__() super().__init__()
self._client = client self._client = client
self.hass = hass self.hass = hass
self.car = None self.cars = []
self.status = None self.status = None
self.ready = False
@Throttle(MIN_TIME_BETWEEN_UPDATES) @Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self): def update(self):
@ -109,7 +107,22 @@ class MyChevyHub(threading.Thread):
(like 2 to 3 minutes long time) (like 2 to 3 minutes long time)
""" """
self.car = self._client.data() self._client.login()
self._client.get_cars()
self.cars = self._client.cars
if self.ready is not True:
discovery.load_platform(self.hass, 'sensor', DOMAIN, {}, {})
discovery.load_platform(self.hass, 'binary_sensor', DOMAIN, {}, {})
self.ready = True
self.cars = self._client.update_cars()
def get_car(self, vid):
"""Compatibility to work with one car."""
if self.cars:
for car in self.cars:
if car.vid == vid:
return car
return None
def run(self): def run(self):
"""Thread run loop.""" """Thread run loop."""

View File

@ -17,14 +17,15 @@ from homeassistant.helpers.entity import Entity
from homeassistant.helpers.icon import icon_for_battery_level from homeassistant.helpers.icon import icon_for_battery_level
from homeassistant.util import slugify from homeassistant.util import slugify
BATTERY_SENSOR = "percent" BATTERY_SENSOR = "batteryLevel"
SENSORS = [ SENSORS = [
EVSensorConfig("Mileage", "mileage", "miles", "mdi:speedometer"), EVSensorConfig("Mileage", "totalMiles", "miles", "mdi:speedometer"),
EVSensorConfig("Range", "range", "miles", "mdi:speedometer"), EVSensorConfig("Electric Range", "electricRange", "miles",
EVSensorConfig("Charging", "charging"), "mdi:speedometer"),
EVSensorConfig("Charge Mode", "charge_mode"), EVSensorConfig("Charged By", "estimatedFullChargeBy"),
EVSensorConfig("EVCharge", BATTERY_SENSOR, "%", "mdi:battery") EVSensorConfig("Charge Mode", "chargeMode"),
EVSensorConfig("Battery Level", BATTERY_SENSOR, "%", "mdi:battery")
] ]
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -38,7 +39,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
hub = hass.data[MYCHEVY_DOMAIN] hub = hass.data[MYCHEVY_DOMAIN]
sensors = [MyChevyStatus()] sensors = [MyChevyStatus()]
for sconfig in SENSORS: for sconfig in SENSORS:
sensors.append(EVSensor(hub, sconfig)) for car in hub.cars:
sensors.append(EVSensor(hub, sconfig, car.vid))
add_devices(sensors) add_devices(sensors)
@ -112,7 +114,7 @@ class EVSensor(Entity):
""" """
def __init__(self, connection, config): def __init__(self, connection, config, car_vid):
"""Initialize sensor with car connection.""" """Initialize sensor with car connection."""
self._conn = connection self._conn = connection
self._name = config.name self._name = config.name
@ -120,9 +122,12 @@ class EVSensor(Entity):
self._unit_of_measurement = config.unit_of_measurement self._unit_of_measurement = config.unit_of_measurement
self._icon = config.icon self._icon = config.icon
self._state = None self._state = None
self._car_vid = car_vid
self.entity_id = ENTITY_ID_FORMAT.format( self.entity_id = ENTITY_ID_FORMAT.format(
'{}_{}'.format(MYCHEVY_DOMAIN, slugify(self._name))) '{}_{}_{}'.format(MYCHEVY_DOMAIN,
slugify(self._car.name),
slugify(self._name)))
@asyncio.coroutine @asyncio.coroutine
def async_added_to_hass(self): def async_added_to_hass(self):
@ -130,6 +135,11 @@ class EVSensor(Entity):
self.hass.helpers.dispatcher.async_dispatcher_connect( self.hass.helpers.dispatcher.async_dispatcher_connect(
UPDATE_TOPIC, self.async_update_callback) UPDATE_TOPIC, self.async_update_callback)
@property
def _car(self):
"""Return the car."""
return self._conn.get_car(self._car_vid)
@property @property
def icon(self): def icon(self):
"""Return the icon.""" """Return the icon."""
@ -145,8 +155,8 @@ class EVSensor(Entity):
@callback @callback
def async_update_callback(self): def async_update_callback(self):
"""Update state.""" """Update state."""
if self._conn.car is not None: if self._car is not None:
self._state = getattr(self._conn.car, self._attr, None) self._state = getattr(self._car, self._attr, None)
self.async_schedule_update_ha_state() self.async_schedule_update_ha_state()
@property @property

View File

@ -543,7 +543,7 @@ motorparts==1.0.2
mutagen==1.40.0 mutagen==1.40.0
# homeassistant.components.mychevy # homeassistant.components.mychevy
mychevy==0.1.1 mychevy==0.4.0
# homeassistant.components.mycroft # homeassistant.components.mycroft
mycroftapi==2.0 mycroftapi==2.0