diff --git a/.coveragerc b/.coveragerc index a34e137f61c6..95bf05eb8b05 100644 --- a/.coveragerc +++ b/.coveragerc @@ -668,7 +668,6 @@ omit = homeassistant/components/mutesync/binary_sensor.py homeassistant/components/nest/const.py homeassistant/components/mvglive/sensor.py - homeassistant/components/mychevy/* homeassistant/components/mycroft/* homeassistant/components/mysensors/__init__.py homeassistant/components/mysensors/binary_sensor.py diff --git a/homeassistant/components/mychevy/__init__.py b/homeassistant/components/mychevy/__init__.py deleted file mode 100644 index 5ea5b142657b..000000000000 --- a/homeassistant/components/mychevy/__init__.py +++ /dev/null @@ -1,155 +0,0 @@ -"""Support for MyChevy.""" -from datetime import timedelta -import logging -import threading -import time - -import mychevy.mychevy as mc -import voluptuous as vol - -from homeassistant.const import CONF_PASSWORD, CONF_USERNAME -from homeassistant.helpers import config_validation as cv, discovery -from homeassistant.util import Throttle - -DOMAIN = "mychevy" -UPDATE_TOPIC = DOMAIN -ERROR_TOPIC = f"{DOMAIN}_error" - -MYCHEVY_SUCCESS = "success" -MYCHEVY_ERROR = "error" - -NOTIFICATION_ID = "mychevy_website_notification" -NOTIFICATION_TITLE = "MyChevy website status" - -_LOGGER = logging.getLogger(__name__) - -MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=30) -ERROR_SLEEP_TIME = timedelta(minutes=30) - -CONF_COUNTRY = "country" -DEFAULT_COUNTRY = "us" - -CONFIG_SCHEMA = vol.Schema( - { - DOMAIN: vol.Schema( - { - vol.Required(CONF_USERNAME): cv.string, - vol.Required(CONF_PASSWORD): cv.string, - vol.Optional(CONF_COUNTRY, default=DEFAULT_COUNTRY): vol.All( - cv.string, vol.In(["us", "ca"]) - ), - } - ) - }, - extra=vol.ALLOW_EXTRA, -) - - -class EVSensorConfig: - """The EV sensor configuration.""" - - def __init__( - self, name, attr, unit_of_measurement=None, icon=None, extra_attrs=None - ): - """Create new sensor configuration.""" - self.name = name - self.attr = attr - self.extra_attrs = extra_attrs or [] - self.unit_of_measurement = unit_of_measurement - self.icon = icon - - -class EVBinarySensorConfig: - """The EV binary sensor configuration.""" - - def __init__(self, name, attr, device_class=None): - """Create new binary sensor configuration.""" - self.name = name - self.attr = attr - self.device_class = device_class - - -def setup(hass, base_config): - """Set up the mychevy component.""" - config = base_config.get(DOMAIN) - - email = config.get(CONF_USERNAME) - password = config.get(CONF_PASSWORD) - country = config.get(CONF_COUNTRY) - hass.data[DOMAIN] = MyChevyHub( - mc.MyChevy(email, password, country), hass, base_config - ) - hass.data[DOMAIN].start() - - return True - - -class MyChevyHub(threading.Thread): - """MyChevy Hub. - - Connecting to the mychevy website is done through a selenium - webscraping process. That can only run synchronously. In order to - prevent blocking of other parts of Home Assistant the architecture - launches a polling loop in a thread. - - When new data is received, sensors are updated, and hass is - signaled that there are updates. Sensors are not created until the - first update, which will be 60 - 120 seconds after the platform - starts. - """ - - def __init__(self, client, hass, hass_config): - """Initialize MyChevy Hub.""" - super().__init__() - self._client = client - self.hass = hass - self.hass_config = hass_config - self.cars = [] - self.status = None - self.ready = False - - @Throttle(MIN_TIME_BETWEEN_UPDATES) - def update(self): - """Update sensors from mychevy website. - - This is a synchronous polling call that takes a very long time - (like 2 to 3 minutes long time) - - """ - 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, {}, self.hass_config) - discovery.load_platform( - self.hass, "binary_sensor", DOMAIN, {}, self.hass_config - ) - 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): - """Thread run loop.""" - # We add the status device first outside of the loop - - # And then busy wait on threads - while True: - try: - _LOGGER.info("Starting mychevy loop") - self.update() - self.hass.helpers.dispatcher.dispatcher_send(UPDATE_TOPIC) - time.sleep(MIN_TIME_BETWEEN_UPDATES.total_seconds()) - except Exception: # pylint: disable=broad-except - _LOGGER.exception( - "Error updating mychevy data. " - "This probably means the OnStar link is down again" - ) - self.hass.helpers.dispatcher.dispatcher_send(ERROR_TOPIC) - time.sleep(ERROR_SLEEP_TIME.total_seconds()) diff --git a/homeassistant/components/mychevy/binary_sensor.py b/homeassistant/components/mychevy/binary_sensor.py deleted file mode 100644 index eb28dd26b06a..000000000000 --- a/homeassistant/components/mychevy/binary_sensor.py +++ /dev/null @@ -1,79 +0,0 @@ -"""Support for MyChevy binary sensors.""" -from homeassistant.components.binary_sensor import ( - DOMAIN as BINARY_SENSOR_DOMAIN, - BinarySensorEntity, -) -from homeassistant.core import callback -from homeassistant.util import slugify - -from . import DOMAIN as MYCHEVY_DOMAIN, UPDATE_TOPIC, EVBinarySensorConfig - -SENSORS = [EVBinarySensorConfig("Plugged In", "plugged_in", "plug")] - - -async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): - """Set up the MyChevy sensors.""" - if discovery_info is None: - return - - sensors = [] - hub = hass.data[MYCHEVY_DOMAIN] - for sconfig in SENSORS: - for car in hub.cars: - sensors.append(EVBinarySensor(hub, sconfig, car.vid)) - - async_add_entities(sensors) - - -class EVBinarySensor(BinarySensorEntity): - """Base EVSensor class. - - The only real difference between sensors is which units and what - attribute from the car object they are returning. All logic can be - built with just setting subclass attributes. - """ - - def __init__(self, connection, config, car_vid): - """Initialize sensor with car connection.""" - self._conn = connection - self._name = config.name - self._attr = config.attr - self._type = config.device_class - self._is_on = None - self._car_vid = car_vid - self.entity_id = f"{BINARY_SENSOR_DOMAIN}.{MYCHEVY_DOMAIN}_{slugify(self._car.name)}_{slugify(self._name)}" - - @property - def name(self): - """Return the name.""" - return self._name - - @property - def is_on(self): - """Return if on.""" - return self._is_on - - @property - def _car(self): - """Return the car.""" - return self._conn.get_car(self._car_vid) - - async def async_added_to_hass(self): - """Register callbacks.""" - self.async_on_remove( - self.hass.helpers.dispatcher.async_dispatcher_connect( - UPDATE_TOPIC, self.async_update_callback - ) - ) - - @callback - def async_update_callback(self): - """Update state.""" - if self._car is not None: - self._is_on = getattr(self._car, self._attr, None) - self.async_write_ha_state() - - @property - def should_poll(self): - """Return the polling state.""" - return False diff --git a/homeassistant/components/mychevy/manifest.json b/homeassistant/components/mychevy/manifest.json deleted file mode 100644 index e726d49bb648..000000000000 --- a/homeassistant/components/mychevy/manifest.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "domain": "mychevy", - "name": "myChevrolet", - "documentation": "https://www.home-assistant.io/integrations/mychevy", - "requirements": ["mychevy==2.1.1"], - "codeowners": [], - "iot_class": "cloud_polling" -} diff --git a/homeassistant/components/mychevy/sensor.py b/homeassistant/components/mychevy/sensor.py deleted file mode 100644 index 1a5613d88649..000000000000 --- a/homeassistant/components/mychevy/sensor.py +++ /dev/null @@ -1,186 +0,0 @@ -"""Support for MyChevy sensors.""" -import logging - -from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN, SensorEntity -from homeassistant.const import PERCENTAGE -from homeassistant.core import callback -from homeassistant.helpers.icon import icon_for_battery_level -from homeassistant.util import slugify - -from . import ( - DOMAIN as MYCHEVY_DOMAIN, - ERROR_TOPIC, - MYCHEVY_ERROR, - MYCHEVY_SUCCESS, - UPDATE_TOPIC, - EVSensorConfig, -) - -_LOGGER = logging.getLogger(__name__) - -BATTERY_SENSOR = "batteryLevel" - -SENSORS = [ - EVSensorConfig("Mileage", "totalMiles", "miles", "mdi:speedometer"), - EVSensorConfig("Electric Range", "electricRange", "miles", "mdi:speedometer"), - EVSensorConfig("Charged By", "estimatedFullChargeBy"), - EVSensorConfig("Charge Mode", "chargeMode"), - EVSensorConfig( - "Battery Level", BATTERY_SENSOR, PERCENTAGE, "mdi:battery", ["charging"] - ), -] - - -def setup_platform(hass, config, add_entities, discovery_info=None): - """Set up the MyChevy sensors.""" - if discovery_info is None: - return - - hub = hass.data[MYCHEVY_DOMAIN] - sensors = [MyChevyStatus()] - for sconfig in SENSORS: - for car in hub.cars: - sensors.append(EVSensor(hub, sconfig, car.vid)) - - add_entities(sensors) - - -class MyChevyStatus(SensorEntity): - """A string representing the charge mode.""" - - _name = "MyChevy Status" - _icon = "mdi:car-connected" - - def __init__(self): - """Initialize sensor with car connection.""" - self._state = None - - async def async_added_to_hass(self): - """Register callbacks.""" - self.async_on_remove( - self.hass.helpers.dispatcher.async_dispatcher_connect( - UPDATE_TOPIC, self.success - ) - ) - - self.async_on_remove( - self.hass.helpers.dispatcher.async_dispatcher_connect( - ERROR_TOPIC, self.error - ) - ) - - @callback - def success(self): - """Update state, trigger updates.""" - if self._state != MYCHEVY_SUCCESS: - _LOGGER.debug("Successfully connected to mychevy website") - self._state = MYCHEVY_SUCCESS - self.async_write_ha_state() - - @callback - def error(self): - """Update state, trigger updates.""" - _LOGGER.error( - "Connection to mychevy website failed. " - "This probably means the mychevy to OnStar link is down" - ) - self._state = MYCHEVY_ERROR - self.async_write_ha_state() - - @property - def icon(self): - """Return the icon.""" - return self._icon - - @property - def name(self): - """Return the name.""" - return self._name - - @property - def native_value(self): - """Return the state.""" - return self._state - - @property - def should_poll(self): - """Return the polling state.""" - return False - - -class EVSensor(SensorEntity): - """Base EVSensor class. - - The only real difference between sensors is which units and what - attribute from the car object they are returning. All logic can be - built with just setting subclass attributes. - """ - - def __init__(self, connection, config, car_vid): - """Initialize sensor with car connection.""" - self._conn = connection - self._name = config.name - self._attr = config.attr - self._extra_attrs = config.extra_attrs - self._unit_of_measurement = config.unit_of_measurement - self._icon = config.icon - self._state = None - self._state_attributes = {} - self._car_vid = car_vid - - self.entity_id = f"{SENSOR_DOMAIN}.{MYCHEVY_DOMAIN}_{slugify(self._car.name)}_{slugify(self._name)}" - - async def async_added_to_hass(self): - """Register callbacks.""" - self.hass.helpers.dispatcher.async_dispatcher_connect( - UPDATE_TOPIC, self.async_update_callback - ) - - @property - def _car(self): - """Return the car.""" - return self._conn.get_car(self._car_vid) - - @property - def icon(self): - """Return the icon.""" - if self._attr == BATTERY_SENSOR: - charging = self._state_attributes.get("charging", False) - return icon_for_battery_level(self.state, charging) - return self._icon - - @property - def name(self): - """Return the name.""" - return self._name - - @callback - def async_update_callback(self): - """Update state.""" - if self._car is not None: - self._state = getattr(self._car, self._attr, None) - if self._unit_of_measurement == "miles": - self._state = round(self._state) - for attr in self._extra_attrs: - self._state_attributes[attr] = getattr(self._car, attr) - self.async_write_ha_state() - - @property - def native_value(self): - """Return the state.""" - return self._state - - @property - def extra_state_attributes(self): - """Return all the state attributes.""" - return self._state_attributes - - @property - def native_unit_of_measurement(self): - """Return the unit of measurement the state is expressed in.""" - return self._unit_of_measurement - - @property - def should_poll(self): - """Return the polling state.""" - return False diff --git a/requirements_all.txt b/requirements_all.txt index f9fd2ce34a73..e58f0b1409fc 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1034,9 +1034,6 @@ mutagen==1.45.1 # homeassistant.components.mutesync mutesync==0.0.1 -# homeassistant.components.mychevy -mychevy==2.1.1 - # homeassistant.components.mycroft mycroftapi==2.0