Skip to content

Commit

Permalink
v0.5.0 (#16)
Browse files Browse the repository at this point in the history
- Change locks to use HA's built-in lock services
- Change "EV Time to Full Charge" units to ISO timestamp
- Update for HA 2021.8 compatibility
- Fix typo in binary sensor icons
- Add nightly CI tests to detect breaking changes from future HA releases
- Miscellaneous code quality changes from HA Core reviews
  • Loading branch information
G-Two authored Aug 8, 2021
1 parent f7aefb4 commit af7aab4
Show file tree
Hide file tree
Showing 22 changed files with 435 additions and 317 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ on:
branches: [ main ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 3 * * *'

jobs:
test:
Expand Down
14 changes: 1 addition & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,19 +119,7 @@ The options are:

## Services

### `subaru.lock`
Lock all doors of the vehicle. The vehicle is identified by the `vin`.

| Service Data Attribute | Required | Description |
| ---------------------- | -------- | -------------------------------------------------- |
| `vin` | yes | The vehicle identification number (VIN) of the vehicle, 17 characters |

### `subaru.unlock`
Unlock all doors of the vehicle. The vehicle is identified by the `vin`.

| Service Data Attribute | Required | Description |
| ---------------------- | -------- | -------------------------------------------------- |
| `vin` | yes | The vehicle identification number (VIN) of the vehicle, 17 characters |
**NOTE:** Subaru lock uses the services provided by the Home Assistant [Lock](https://www.home-assistant.io/integrations/lock/) integration

### `subaru.lights`
Flash the lights of the vehicle. The vehicle is identified by the `vin`.
Expand Down
136 changes: 29 additions & 107 deletions custom_components/subaru/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import asyncio
from datetime import timedelta
import logging
import time

from subarulink import Controller as SubaruAPI, InvalidCredentials, SubaruException
from subarulink.const import COUNTRY_USA
Expand All @@ -24,17 +23,7 @@
ENTRY_COORDINATOR,
ENTRY_VEHICLES,
FETCH_INTERVAL,
REMOTE_SERVICE_CHARGE_START,
REMOTE_SERVICE_FETCH,
REMOTE_SERVICE_HORN,
REMOTE_SERVICE_HORN_STOP,
REMOTE_SERVICE_LIGHTS,
REMOTE_SERVICE_LIGHTS_STOP,
REMOTE_SERVICE_LOCK,
REMOTE_SERVICE_REMOTE_START,
REMOTE_SERVICE_REMOTE_STOP,
REMOTE_SERVICE_UNLOCK,
REMOTE_SERVICE_UPDATE,
SUPPORTED_PLATFORMS,
UPDATE_INTERVAL,
VEHICLE_API_GEN,
Expand All @@ -46,17 +35,16 @@
VEHICLE_NAME,
VEHICLE_VIN,
)
from .remote_service import (
SERVICES_THAT_NEED_FETCH,
async_call_remote_service,
get_supported_services,
update_subaru,
)

_LOGGER = logging.getLogger(__name__)

REMOTE_SERVICE_SCHEMA = vol.Schema({vol.Required(VEHICLE_VIN): cv.string})
SERVICES_THAT_NEED_FETCH = [
REMOTE_SERVICE_FETCH,
REMOTE_SERVICE_REMOTE_START,
REMOTE_SERVICE_REMOTE_STOP,
REMOTE_SERVICE_UPDATE,
REMOTE_SERVICE_CHARGE_START,
]


async def async_setup(hass, base_config):
Expand Down Expand Up @@ -87,41 +75,22 @@ async def async_setup_entry(hass, entry):
update_interval=UPDATE_INTERVAL,
fetch_interval=FETCH_INTERVAL,
)
_LOGGER.debug(f"Using subarulink {controller.version}")
_LOGGER.debug("Using subarulink %s", controller.version)
await controller.connect()
except InvalidCredentials:
_LOGGER.error("Invalid account")
return False
except SubaruException as err:
raise ConfigEntryNotReady(err) from err

vehicle_info = {}
remote_services = []
vehicles = {}
for vin in controller.get_vehicles():
vehicle_info[vin] = get_vehicle_info(controller, vin)
if vehicle_info[vin][VEHICLE_HAS_SAFETY_SERVICE]:
remote_services.append(REMOTE_SERVICE_FETCH)
if vehicle_info[vin][VEHICLE_HAS_REMOTE_SERVICE]:
remote_services.append(REMOTE_SERVICE_HORN)
remote_services.append(REMOTE_SERVICE_HORN_STOP)
remote_services.append(REMOTE_SERVICE_LIGHTS)
remote_services.append(REMOTE_SERVICE_LIGHTS_STOP)
remote_services.append(REMOTE_SERVICE_LOCK)
remote_services.append(REMOTE_SERVICE_UNLOCK)
remote_services.append(REMOTE_SERVICE_UPDATE)
if (
vehicle_info[vin][VEHICLE_HAS_REMOTE_START]
or vehicle_info[vin][VEHICLE_HAS_EV]
):
remote_services.append(REMOTE_SERVICE_REMOTE_START)
remote_services.append(REMOTE_SERVICE_REMOTE_STOP)
if vehicle_info[vin][VEHICLE_HAS_EV]:
remote_services.append(REMOTE_SERVICE_CHARGE_START)
vehicles[vin] = get_vehicle_info(controller, vin)

async def async_update_data():
"""Fetch data from API endpoint."""
try:
return await refresh_subaru_data(entry, vehicle_info, controller)
return await refresh_subaru_data(entry, vehicles, controller)
except SubaruException as err:
raise UpdateFailed(err.message) from err

Expand All @@ -138,72 +107,37 @@ async def async_update_data():
hass.data.get(DOMAIN)[entry.entry_id] = {
ENTRY_CONTROLLER: controller,
ENTRY_COORDINATOR: coordinator,
ENTRY_VEHICLES: vehicle_info,
ENTRY_VEHICLES: vehicles,
}

for component in SUPPORTED_PLATFORMS:
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, component)
)

async def async_remote_service(call):
"""Execute remote services."""
async def async_call_service(call):
"""Execute subaru service."""
vin = call.data[VEHICLE_VIN].upper()
success = False
err_msg = ""
if vin not in vehicle_info.keys():
hass.components.persistent_notification.create(
f"ERROR - Invalid VIN provided while calling {call.service}", "Subaru"
)
raise HomeAssistantError(
f"Invalid VIN provided while calling {call.service}"
)
else:
name = vehicle_info[vin][VEHICLE_NAME]
if call.service == REMOTE_SERVICE_FETCH:
await coordinator.async_refresh()
return

try:
_LOGGER.debug("calling %s", call.service)
hass.components.persistent_notification.create(
f"Calling {call.service} for {name}\nThis may take 10-15 seconds",
"Subaru",
DOMAIN,

if vin in vehicles:
if call.service != REMOTE_SERVICE_FETCH:
await async_call_remote_service(
hass, controller, call.service, vehicles[vin]
)
if call.service == REMOTE_SERVICE_UPDATE:
vehicle = vehicle_info[vin]
success = await update_subaru(
vehicle, controller, override_interval=True
)
else:
success = await getattr(controller, call.service)(vin)
except SubaruException as err:
err_msg = err.message

if success and call.service in SERVICES_THAT_NEED_FETCH:
if call.service in SERVICES_THAT_NEED_FETCH:
await coordinator.async_refresh()
return

hass.components.persistent_notification.dismiss(DOMAIN)
if success:
hass.components.persistent_notification.create(
f"Service {call.service} successfully completed for {name}",
"Subaru",
)
_LOGGER.debug(
"Service %s successfully completed for %s", call.service, name
)
else:
hass.components.persistent_notification.create(
f"Service {call.service} failed for {name}: {err_msg}", "Subaru"
)
raise HomeAssistantError(
f"Service {call.service} failed for {name}: {err_msg}"
)
hass.components.persistent_notification.create(
f"ERROR - Invalid VIN provided while calling {call.service}", "Subaru"
)
raise HomeAssistantError(f"Invalid VIN provided while calling {call.service}")

supported_services = get_supported_services(vehicles)

for service in remote_services:
for service in supported_services:
hass.services.async_register(
DOMAIN, service, async_remote_service, schema=REMOTE_SERVICE_SCHEMA
DOMAIN, service, async_call_service, schema=REMOTE_SERVICE_SCHEMA
)

return True
Expand Down Expand Up @@ -241,7 +175,7 @@ async def refresh_subaru_data(config_entry, vehicle_info, controller):
if not vehicle[VEHICLE_HAS_SAFETY_SERVICE]:
continue

# Optionally send an "update" remote command to vehicle (throttled with update_interval)
# Send an "update" remote command to vehicle, if supported (throttled with update_interval)
if config_entry.options.get(CONF_UPDATE_ENABLED, False):
await update_subaru(vehicle, controller)

Expand All @@ -256,18 +190,6 @@ async def refresh_subaru_data(config_entry, vehicle_info, controller):
return data


async def update_subaru(vehicle, controller, override_interval=False):
"""Commands remote vehicle update (polls the vehicle to update subaru API cache)."""
cur_time = time.time()
last_update = vehicle[VEHICLE_LAST_UPDATE]

if (cur_time - last_update) > UPDATE_INTERVAL or override_interval:
await controller.update(vehicle[VEHICLE_VIN], force=True)
vehicle[VEHICLE_LAST_UPDATE] = cur_time

return True


def get_vehicle_info(controller, vin):
"""Obtain vehicle identifiers and capabilities."""
info = {
Expand Down
Loading

0 comments on commit af7aab4

Please sign in to comment.