Skip to content

Commit 1469048

Browse files
authored
Home assistant (#1)
* Tentative homeassistant support.
1 parent bd92bc0 commit 1469048

File tree

9 files changed

+2245
-1288
lines changed

9 files changed

+2245
-1288
lines changed

kamereon/__init__.py

Lines changed: 141 additions & 1288 deletions
Large diffs are not rendered by default.

kamereon/binary_sensor.py

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
"""Support for Kamereon cars."""
2+
import logging
3+
4+
from homeassistant.components.binary_sensor import DEVICE_CLASSES, BinarySensorDevice
5+
from homeassistant.const import STATE_UNKNOWN
6+
7+
from . import KamereonEntity
8+
from .kamereon import ChargingStatus, Door, LockStatus, PluggedStatus
9+
10+
_LOGGER = logging.getLogger(__name__)
11+
12+
13+
async def async_setup_platform(hass, config, async_add_entities, vehicle=None):
14+
"""Set up the Kamereon sensors."""
15+
if vehicle is None:
16+
return
17+
async_add_entities([
18+
ChargingStatusEntity(vehicle),
19+
PluggedStatusEntity(vehicle),
20+
FuelLowWarningEntity(vehicle),
21+
DoorEntity(vehicle, Door.FRONT_LEFT),
22+
DoorEntity(vehicle, Door.FRONT_RIGHT),
23+
DoorEntity(vehicle, Door.REAR_LEFT),
24+
DoorEntity(vehicle, Door.REAR_RIGHT),
25+
DoorEntity(vehicle, Door.HATCH),
26+
])
27+
28+
29+
class ChargingStatusEntity(KamereonEntity, BinarySensorDevice):
30+
"""Representation of charging status."""
31+
32+
@property
33+
def _entity_name(self):
34+
return 'charging'
35+
36+
@property
37+
def icon(self):
38+
"""Return the icon."""
39+
return 'mdi:{}'.format('battery-charging' if self.is_on else 'battery-off')
40+
41+
@property
42+
def is_on(self):
43+
"""Return True if the binary sensor is on."""
44+
if self.vehicle.charging is None:
45+
return STATE_UNKNOWN
46+
return self.vehicle.charging is ChargingStatus.CHARGING
47+
48+
@property
49+
def device_class(self):
50+
"""Return the class of this sensor."""
51+
return 'power'
52+
53+
@property
54+
def device_state_attributes(self):
55+
a = KamereonEntity.device_state_attributes.fget(self)
56+
a.update({
57+
'charging_speed': self.vehicle.charging_speed.value,
58+
'last_updated': self.vehicle.battery_status_last_updated,
59+
})
60+
return a
61+
62+
63+
class PluggedStatusEntity(KamereonEntity, BinarySensorDevice):
64+
"""Representation of plugged status."""
65+
66+
@property
67+
def _entity_name(self):
68+
return 'plugged_in'
69+
70+
@property
71+
def icon(self):
72+
"""Return the icon."""
73+
return 'mdi:{}'.format('power-plug' if self.is_on else 'power-plug-off')
74+
75+
@property
76+
def is_on(self):
77+
"""Return True if the binary sensor is on."""
78+
if self.vehicle.plugged_in is None:
79+
return STATE_UNKNOWN
80+
return self.vehicle.plugged_in is PluggedStatus.PLUGGED
81+
82+
@property
83+
def device_class(self):
84+
"""Return the class of this sensor."""
85+
return 'plug'
86+
87+
@property
88+
def device_state_attributes(self):
89+
a = KamereonEntity.device_state_attributes.fget(self)
90+
a.update({
91+
'plugged_in_time': self.vehicle.plugged_in_time,
92+
'unplugged_time': self.vehicle.unplugged_time,
93+
'last_updated': self.vehicle.battery_status_last_updated,
94+
})
95+
return a
96+
97+
98+
class FuelLowWarningEntity(KamereonEntity, BinarySensorDevice):
99+
"""Representation of fuel low warning status."""
100+
101+
@property
102+
def _entity_name(self):
103+
return 'fuel_low'
104+
105+
@property
106+
def icon(self):
107+
"""Return the icon."""
108+
return 'mdi:fuel'
109+
110+
@property
111+
def is_on(self):
112+
"""Return True if the binary sensor is on."""
113+
if self.vehicle.fuel_low_warning is None:
114+
return STATE_UNKNOWN
115+
return self.vehicle.fuel_low_warning
116+
117+
@property
118+
def device_class(self):
119+
"""Return the class of this sensor."""
120+
return 'safety'
121+
122+
123+
class DoorEntity(KamereonEntity, BinarySensorDevice):
124+
"""Representation of a door (or hatch)."""
125+
126+
def __init__(self, vehicle, door):
127+
KamereonEntity.__init__(self, vehicle)
128+
self.door = door
129+
130+
@property
131+
def icon(self):
132+
"""Return the icon."""
133+
return 'mdi:car-door'
134+
135+
@property
136+
def _entity_name(self):
137+
return '{}_door'.format(self.door.value)
138+
139+
@property
140+
def is_on(self):
141+
"""Return True if the binary sensor is open."""
142+
if self.door not in self.vehicle.door_status or self.vehicle.door_status[self.door] is None:
143+
return STATE_UNKNOWN
144+
return self.vehicle.door_status[self.door] == LockStatus.OPEN
145+
146+
@property
147+
def device_class(self):
148+
"""Return the class of this sensor."""
149+
return 'door'

kamereon/climate.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
"""
2+
Support for Kamereon Platform
3+
"""
4+
import logging
5+
6+
from homeassistant.components.climate import ClimateDevice
7+
from homeassistant.components.climate.const import (HVAC_MODE_HEAT_COOL, HVAC_MODE_OFF, SUPPORT_TARGET_TEMPERATURE)
8+
from homeassistant.const import ATTR_TEMPERATURE, STATE_UNKNOWN, TEMP_CELSIUS
9+
10+
SUPPORT_HVAC = [HVAC_MODE_HEAT_COOL, HVAC_MODE_OFF]
11+
12+
from . import KamereonEntity
13+
from .kamereon import Feature, HVACAction, HVACStatus
14+
15+
_LOGGER = logging.getLogger(__name__)
16+
17+
18+
def setup_platform(hass, config, add_devices, vehicle=None):
19+
""" Setup the volkswagen climate."""
20+
if vehicle is None:
21+
return
22+
if Feature.TEMPERATURE in vehicle.features or Feature.INTERIOR_TEMP_SETTINGS in vehicle.features:
23+
add_devices([KamereonClimate(vehicle)])
24+
25+
26+
class KamereonClimate(KamereonEntity, ClimateDevice):
27+
"""Representation of a Kamereon Climate."""
28+
29+
@property
30+
def supported_features(self):
31+
"""Return the list of supported features."""
32+
return SUPPORT_TARGET_TEMPERATURE
33+
34+
@property
35+
def hvac_mode(self):
36+
"""Return hvac operation ie. heat, cool mode.
37+
Need to be one of HVAC_MODE_*.
38+
"""
39+
if self.vehicle.hvac_status is None:
40+
return STATE_UNKNOWN
41+
elif self.vehicle.hvac_status is HVACStatus.ON:
42+
return HVAC_MODE_HEAT_COOL
43+
return HVAC_MODE_OFF
44+
45+
46+
@property
47+
def hvac_modes(self):
48+
"""Return the list of available hvac operation modes.
49+
Need to be a subset of HVAC_MODES.
50+
"""
51+
return SUPPORT_HVAC
52+
53+
@property
54+
def temperature_unit(self):
55+
"""Return the unit of measurement."""
56+
return TEMP_CELSIUS
57+
58+
@property
59+
def current_temperature(self):
60+
"""Return the current temperature."""
61+
if self.vehicle.internal_temperature:
62+
return float(self.vehicle.internal_temperature)
63+
return None
64+
65+
@property
66+
def target_temperature(self):
67+
"""Return the temperature we try to reach."""
68+
if self.vehicle.next_target_temperature is not None:
69+
return float(self.vehicle.next_target_temperature)
70+
return None
71+
72+
def set_temperature(self, **kwargs):
73+
"""Set new target temperatures."""
74+
if Feature.TEMPERATURE not in self.vehicle.features:
75+
raise NotImplementedError()
76+
77+
_LOGGER.debug("Setting temperature for: %s", self.instrument.attr)
78+
temperature = kwargs.get(ATTR_TEMPERATURE)
79+
if temperature:
80+
self.vehicle.set_hvac_status(HVACAction.START, temperature)
81+
82+
def set_hvac_mode(self, hvac_mode):
83+
"""Set new target hvac mode."""
84+
if Feature.CLIMATE_ON_OFF not in self.vehicle.features:
85+
raise NotImplementedError()
86+
87+
_LOGGER.debug("Setting mode for: %s", self.instrument.attr)
88+
if hvac_mode == HVAC_MODE_OFF:
89+
self.vehicle.set_hvac_status(HVACAction.STOP)
90+
elif hvac_mode == HVAC_MODE_HEAT_COOL:
91+
self.vehicle.set_hvac_status(HVACAction.START)

kamereon/device_tracker.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""Support for tracking a Kamereon car."""
2+
import logging
3+
4+
from homeassistant.components.device_tracker import SOURCE_TYPE_GPS
5+
from homeassistant.helpers.dispatcher import async_dispatcher_connect
6+
from homeassistant.util import slugify
7+
8+
from . import SIGNAL_STATE_UPDATED
9+
10+
_LOGGER = logging.getLogger(__name__)
11+
12+
13+
async def async_setup_scanner(hass, config, async_see, vehicle=None):
14+
"""Set up the Kamereon tracker."""
15+
if vehicle is None:
16+
return
17+
18+
async def see_vehicle():
19+
"""Handle the reporting of the vehicle position."""
20+
host_name = slugify(vehicle.nickname or vehicle.model_name)
21+
await async_see(
22+
dev_id=host_name,
23+
host_name=host_name,
24+
source_type=SOURCE_TYPE_GPS,
25+
gps=vehicle.location,
26+
attributes={
27+
'last_updated': vehicle.location_last_updated.isoformat(),
28+
'manufacturer': vehicle.session.tenant,
29+
'vin': vehicle.vin,
30+
'name': vehicle.nickname or vehicle.model_name,
31+
'model': vehicle.model_name,
32+
'registration_number': vehicle.registration_number,
33+
},
34+
icon="mdi:car",
35+
)
36+
37+
async_dispatcher_connect(hass, SIGNAL_STATE_UPDATED, see_vehicle)
38+
39+
return True

0 commit comments

Comments
 (0)