Skip to content

Commit

Permalink
Mopar split (#21526)
Browse files Browse the repository at this point in the history
* Split out mopar into a component and sensor platform

* Add the mopar switch platform

* Add the mopar lock platform

* Clean up and bump version

* Update per review

* Re-add service to trigger horn

* Clean up again

* Don't call async from sync context

* Lint

* Implement changes from review

* Lint

* A little more clean up
  • Loading branch information
rohankapoorcom authored and balloob committed Mar 28, 2019
1 parent f11f525 commit c4eab21
Show file tree
Hide file tree
Showing 7 changed files with 317 additions and 115 deletions.
3 changes: 2 additions & 1 deletion .coveragerc
Expand Up @@ -328,6 +328,7 @@ omit =
homeassistant/components/mobile_app/*
homeassistant/components/mochad/*
homeassistant/components/modbus/*
homeassistant/components/mopar/*
homeassistant/components/mychevy/*
homeassistant/components/mycroft/*
homeassistant/components/mysensors/*
Expand Down Expand Up @@ -499,7 +500,7 @@ omit =
homeassistant/components/miflora/sensor.py
homeassistant/components/mitemp_bt/sensor.py
homeassistant/components/modem_callerid/sensor.py
homeassistant/components/mopar/sensor.py
homeassistant/components/mopar/*
homeassistant/components/mqtt_room/sensor.py
homeassistant/components/mvglive/sensor.py
homeassistant/components/nederlandse_spoorwegen/sensor.py
Expand Down
158 changes: 157 additions & 1 deletion homeassistant/components/mopar/__init__.py
@@ -1 +1,157 @@
"""The mopar component."""
"""Support for Mopar vehicles."""
import logging
from datetime import timedelta

import voluptuous as vol

from homeassistant.components.lock import DOMAIN as LOCK
from homeassistant.components.sensor import DOMAIN as SENSOR
from homeassistant.components.switch import DOMAIN as SWITCH
from homeassistant.const import (
CONF_USERNAME,
CONF_PASSWORD,
CONF_PIN,
CONF_SCAN_INTERVAL
)
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.discovery import load_platform
from homeassistant.helpers.dispatcher import dispatcher_send
from homeassistant.helpers.event import track_time_interval

REQUIREMENTS = ['motorparts==1.1.0']

DOMAIN = 'mopar'
DATA_UPDATED = '{}_data_updated'.format(DOMAIN)

_LOGGER = logging.getLogger(__name__)

COOKIE_FILE = 'mopar_cookies.pickle'
SUCCESS_RESPONSE = 'completed'

SUPPORTED_PLATFORMS = [LOCK, SENSOR, SWITCH]

DEFAULT_INTERVAL = timedelta(days=7)

CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_PIN): cv.positive_int,
vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_INTERVAL):
vol.All(cv.time_period, cv.positive_timedelta),
})
}, extra=vol.ALLOW_EXTRA)

SERVICE_HORN = 'sound_horn'
ATTR_VEHICLE_INDEX = 'vehicle_index'
SERVICE_HORN_SCHEMA = vol.Schema({
vol.Required(ATTR_VEHICLE_INDEX): cv.positive_int
})


def setup(hass, config):
"""Set up the Mopar component."""
import motorparts

cookie = hass.config.path(COOKIE_FILE)
try:
session = motorparts.get_session(
config[CONF_USERNAME],
config[CONF_PASSWORD],
config[CONF_PIN],
cookie_path=cookie
)
except motorparts.MoparError:
_LOGGER.error("Failed to login")
return False

data = hass.data[DOMAIN] = MoparData(hass, session)
data.update(now=None)

track_time_interval(
hass, data.update, config[CONF_SCAN_INTERVAL]
)

def handle_horn(call):
"""Enable the horn on the Mopar vehicle."""
data.actuate('horn', call.data[ATTR_VEHICLE_INDEX])

hass.services.register(
DOMAIN,
SERVICE_HORN,
handle_horn,
schema=SERVICE_HORN_SCHEMA
)

for platform in SUPPORTED_PLATFORMS:
load_platform(hass, platform, DOMAIN, {}, config)

return True


class MoparData:
"""
Container for Mopar vehicle data.
Prevents session expiry re-login race condition.
"""

def __init__(self, hass, session):
"""Initialize data."""
self._hass = hass
self._session = session
self.vehicles = []
self.vhrs = {}
self.tow_guides = {}

def update(self, now, **kwargs):
"""Update data."""
import motorparts

_LOGGER.debug("Updating vehicle data")
try:
self.vehicles = motorparts.get_summary(self._session)['vehicles']
except motorparts.MoparError:
_LOGGER.exception("Failed to get summary")
return

for index, _ in enumerate(self.vehicles):
try:
self.vhrs[index] = motorparts.get_report(self._session, index)
self.tow_guides[index] = motorparts.get_tow_guide(
self._session, index)
except motorparts.MoparError:
_LOGGER.warning("Failed to update for vehicle index %s", index)
return

dispatcher_send(self._hass, DATA_UPDATED)

@property
def attribution(self):
"""Get the attribution string from Mopar."""
import motorparts

return motorparts.ATTRIBUTION

def get_vehicle_name(self, index):
"""Get the name corresponding with this vehicle."""
vehicle = self.vehicles[index]
if not vehicle:
return None
return '{} {} {}'.format(
vehicle['year'],
vehicle['make'],
vehicle['model']
)

def actuate(self, command, index):
"""Run a command on the specified Mopar vehicle."""
import motorparts

try:
response = getattr(motorparts, command)(self._session, index)
except motorparts.MoparError as error:
_LOGGER.error(error)
return False

return response == SUCCESS_RESPONSE
55 changes: 55 additions & 0 deletions homeassistant/components/mopar/lock.py
@@ -0,0 +1,55 @@
"""Support for the Mopar vehicle lock."""
import logging

from homeassistant.components.lock import LockDevice
from homeassistant.components.mopar import (
DOMAIN as MOPAR_DOMAIN
)
from homeassistant.const import STATE_LOCKED, STATE_UNLOCKED

DEPENDENCIES = ['mopar']

_LOGGER = logging.getLogger(__name__)


def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the Mopar lock platform."""
data = hass.data[MOPAR_DOMAIN]
add_entities([MoparLock(data, index)
for index, _ in enumerate(data.vehicles)], True)


class MoparLock(LockDevice):
"""Representation of a Mopar vehicle lock."""

def __init__(self, data, index):
"""Initialize the Mopar lock."""
self._index = index
self._name = '{} Lock'.format(data.get_vehicle_name(self._index))
self._actuate = data.actuate
self._state = None

@property
def name(self):
"""Return the name of the lock."""
return self._name

@property
def is_locked(self):
"""Return true if vehicle is locked."""
return self._state == STATE_LOCKED

@property
def should_poll(self):
"""Return the polling requirement for this lock."""
return False

def lock(self, **kwargs):
"""Lock the vehicle."""
if self._actuate('lock', self._index):
self._state = STATE_LOCKED

def unlock(self, **kwargs):
"""Unlock the vehicle."""
if self._actuate('unlock', self._index):
self._state = STATE_UNLOCKED

0 comments on commit c4eab21

Please sign in to comment.