Skip to content

Commit

Permalink
Home assistant (#1)
Browse files Browse the repository at this point in the history
* Tentative homeassistant support.
  • Loading branch information
mitchellrj authored Apr 5, 2020
1 parent bd92bc0 commit 1469048
Show file tree
Hide file tree
Showing 9 changed files with 2,245 additions and 1,288 deletions.
1,429 changes: 141 additions & 1,288 deletions kamereon/__init__.py

Large diffs are not rendered by default.

149 changes: 149 additions & 0 deletions kamereon/binary_sensor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
"""Support for Kamereon cars."""
import logging

from homeassistant.components.binary_sensor import DEVICE_CLASSES, BinarySensorDevice
from homeassistant.const import STATE_UNKNOWN

from . import KamereonEntity
from .kamereon import ChargingStatus, Door, LockStatus, PluggedStatus

_LOGGER = logging.getLogger(__name__)


async def async_setup_platform(hass, config, async_add_entities, vehicle=None):
"""Set up the Kamereon sensors."""
if vehicle is None:
return
async_add_entities([
ChargingStatusEntity(vehicle),
PluggedStatusEntity(vehicle),
FuelLowWarningEntity(vehicle),
DoorEntity(vehicle, Door.FRONT_LEFT),
DoorEntity(vehicle, Door.FRONT_RIGHT),
DoorEntity(vehicle, Door.REAR_LEFT),
DoorEntity(vehicle, Door.REAR_RIGHT),
DoorEntity(vehicle, Door.HATCH),
])


class ChargingStatusEntity(KamereonEntity, BinarySensorDevice):
"""Representation of charging status."""

@property
def _entity_name(self):
return 'charging'

@property
def icon(self):
"""Return the icon."""
return 'mdi:{}'.format('battery-charging' if self.is_on else 'battery-off')

@property
def is_on(self):
"""Return True if the binary sensor is on."""
if self.vehicle.charging is None:
return STATE_UNKNOWN
return self.vehicle.charging is ChargingStatus.CHARGING

@property
def device_class(self):
"""Return the class of this sensor."""
return 'power'

@property
def device_state_attributes(self):
a = KamereonEntity.device_state_attributes.fget(self)
a.update({
'charging_speed': self.vehicle.charging_speed.value,
'last_updated': self.vehicle.battery_status_last_updated,
})
return a


class PluggedStatusEntity(KamereonEntity, BinarySensorDevice):
"""Representation of plugged status."""

@property
def _entity_name(self):
return 'plugged_in'

@property
def icon(self):
"""Return the icon."""
return 'mdi:{}'.format('power-plug' if self.is_on else 'power-plug-off')

@property
def is_on(self):
"""Return True if the binary sensor is on."""
if self.vehicle.plugged_in is None:
return STATE_UNKNOWN
return self.vehicle.plugged_in is PluggedStatus.PLUGGED

@property
def device_class(self):
"""Return the class of this sensor."""
return 'plug'

@property
def device_state_attributes(self):
a = KamereonEntity.device_state_attributes.fget(self)
a.update({
'plugged_in_time': self.vehicle.plugged_in_time,
'unplugged_time': self.vehicle.unplugged_time,
'last_updated': self.vehicle.battery_status_last_updated,
})
return a


class FuelLowWarningEntity(KamereonEntity, BinarySensorDevice):
"""Representation of fuel low warning status."""

@property
def _entity_name(self):
return 'fuel_low'

@property
def icon(self):
"""Return the icon."""
return 'mdi:fuel'

@property
def is_on(self):
"""Return True if the binary sensor is on."""
if self.vehicle.fuel_low_warning is None:
return STATE_UNKNOWN
return self.vehicle.fuel_low_warning

@property
def device_class(self):
"""Return the class of this sensor."""
return 'safety'


class DoorEntity(KamereonEntity, BinarySensorDevice):
"""Representation of a door (or hatch)."""

def __init__(self, vehicle, door):
KamereonEntity.__init__(self, vehicle)
self.door = door

@property
def icon(self):
"""Return the icon."""
return 'mdi:car-door'

@property
def _entity_name(self):
return '{}_door'.format(self.door.value)

@property
def is_on(self):
"""Return True if the binary sensor is open."""
if self.door not in self.vehicle.door_status or self.vehicle.door_status[self.door] is None:
return STATE_UNKNOWN
return self.vehicle.door_status[self.door] == LockStatus.OPEN

@property
def device_class(self):
"""Return the class of this sensor."""
return 'door'
91 changes: 91 additions & 0 deletions kamereon/climate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
"""
Support for Kamereon Platform
"""
import logging

from homeassistant.components.climate import ClimateDevice
from homeassistant.components.climate.const import (HVAC_MODE_HEAT_COOL, HVAC_MODE_OFF, SUPPORT_TARGET_TEMPERATURE)
from homeassistant.const import ATTR_TEMPERATURE, STATE_UNKNOWN, TEMP_CELSIUS

SUPPORT_HVAC = [HVAC_MODE_HEAT_COOL, HVAC_MODE_OFF]

from . import KamereonEntity
from .kamereon import Feature, HVACAction, HVACStatus

_LOGGER = logging.getLogger(__name__)


def setup_platform(hass, config, add_devices, vehicle=None):
""" Setup the volkswagen climate."""
if vehicle is None:
return
if Feature.TEMPERATURE in vehicle.features or Feature.INTERIOR_TEMP_SETTINGS in vehicle.features:
add_devices([KamereonClimate(vehicle)])


class KamereonClimate(KamereonEntity, ClimateDevice):
"""Representation of a Kamereon Climate."""

@property
def supported_features(self):
"""Return the list of supported features."""
return SUPPORT_TARGET_TEMPERATURE

@property
def hvac_mode(self):
"""Return hvac operation ie. heat, cool mode.
Need to be one of HVAC_MODE_*.
"""
if self.vehicle.hvac_status is None:
return STATE_UNKNOWN
elif self.vehicle.hvac_status is HVACStatus.ON:
return HVAC_MODE_HEAT_COOL
return HVAC_MODE_OFF


@property
def hvac_modes(self):
"""Return the list of available hvac operation modes.
Need to be a subset of HVAC_MODES.
"""
return SUPPORT_HVAC

@property
def temperature_unit(self):
"""Return the unit of measurement."""
return TEMP_CELSIUS

@property
def current_temperature(self):
"""Return the current temperature."""
if self.vehicle.internal_temperature:
return float(self.vehicle.internal_temperature)
return None

@property
def target_temperature(self):
"""Return the temperature we try to reach."""
if self.vehicle.next_target_temperature is not None:
return float(self.vehicle.next_target_temperature)
return None

def set_temperature(self, **kwargs):
"""Set new target temperatures."""
if Feature.TEMPERATURE not in self.vehicle.features:
raise NotImplementedError()

_LOGGER.debug("Setting temperature for: %s", self.instrument.attr)
temperature = kwargs.get(ATTR_TEMPERATURE)
if temperature:
self.vehicle.set_hvac_status(HVACAction.START, temperature)

def set_hvac_mode(self, hvac_mode):
"""Set new target hvac mode."""
if Feature.CLIMATE_ON_OFF not in self.vehicle.features:
raise NotImplementedError()

_LOGGER.debug("Setting mode for: %s", self.instrument.attr)
if hvac_mode == HVAC_MODE_OFF:
self.vehicle.set_hvac_status(HVACAction.STOP)
elif hvac_mode == HVAC_MODE_HEAT_COOL:
self.vehicle.set_hvac_status(HVACAction.START)
39 changes: 39 additions & 0 deletions kamereon/device_tracker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"""Support for tracking a Kamereon car."""
import logging

from homeassistant.components.device_tracker import SOURCE_TYPE_GPS
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.util import slugify

from . import SIGNAL_STATE_UPDATED

_LOGGER = logging.getLogger(__name__)


async def async_setup_scanner(hass, config, async_see, vehicle=None):
"""Set up the Kamereon tracker."""
if vehicle is None:
return

async def see_vehicle():
"""Handle the reporting of the vehicle position."""
host_name = slugify(vehicle.nickname or vehicle.model_name)
await async_see(
dev_id=host_name,
host_name=host_name,
source_type=SOURCE_TYPE_GPS,
gps=vehicle.location,
attributes={
'last_updated': vehicle.location_last_updated.isoformat(),
'manufacturer': vehicle.session.tenant,
'vin': vehicle.vin,
'name': vehicle.nickname or vehicle.model_name,
'model': vehicle.model_name,
'registration_number': vehicle.registration_number,
},
icon="mdi:car",
)

async_dispatcher_connect(hass, SIGNAL_STATE_UPDATED, see_vehicle)

return True
Loading

0 comments on commit 1469048

Please sign in to comment.