Skip to content

Commit

Permalink
Add expire_after to options of integration
Browse files Browse the repository at this point in the history
  • Loading branch information
turbulator committed Dec 24, 2020
1 parent ba07af3 commit 0377c2e
Show file tree
Hide file tree
Showing 8 changed files with 59 additions and 49 deletions.
26 changes: 21 additions & 5 deletions custom_components/pandora_cas/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
OPTION_FUEL_UNITS,
OPTION_MILEAGE_SOURCE,
OPTION_MILEAGE_ADJUSTMENT,
OPTION_EXPIRE_AFTER,
FUEL_UNITS,
)

Expand Down Expand Up @@ -74,6 +75,12 @@ def devices(self) -> dict:

return self._devices

@property
def timestamp(self) -> int:
"""Get last update timestamp."""

return self._update_ts

async def _request(self, path, method="GET", data=None):
"""Request an information from server."""

Expand Down Expand Up @@ -169,9 +176,8 @@ async def _async_update(self, *_) -> bool:

stats = response.stats
if self._update_ts == 0:
self._force_update_ts = self._update_ts = response.timestamp
else:
self._update_ts = response.timestamp
self._force_update_ts = response.timestamp
self._update_ts = response.timestamp

# UCR means that device received the command and sent response (user command response?)
# Lot's of commands executes quick: like on/off tracking, ext. cannel and so on.
Expand Down Expand Up @@ -281,6 +287,16 @@ def is_online(self) -> bool:
"""Is device online now?"""
return bool(self.online)

@property
def expire_after(self) -> int:
"""Get expiring timeout."""
return int(self._info.get(OPTION_EXPIRE_AFTER, 0))

@property
def timestamp(self) -> int:
"""Get last update timestamp."""
return int(self.dtime_rec)

@property
def fuel_percentage(self) -> int:
"""Get fuel in percentage."""
Expand Down Expand Up @@ -425,7 +441,7 @@ def __init__(self, response):
class PandoraApiUpdateResponseParser:
"""
{
"ts":1599698262,
"ts":1599698262, <--- Current timestamp
"lenta": [{ <--- The list of events
"type": 0,
"time": 1600553265,
Expand Down Expand Up @@ -464,7 +480,7 @@ class PandoraApiUpdateResponseParser:
"online":0,
"move":0,
"dtime":1599721704,
"dtime_rec":1599696508,
"dtime_rec":1599696508, <--- timestamp of the data in stats section
"voltage":13.5,
"engine_temp":43,
"x":55.080632,
Expand Down
6 changes: 3 additions & 3 deletions custom_components/pandora_cas/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def __init__(
self._id = entity_id
self._config = entity_config
self._state = None
self._available = False
self._expired = True

@property
def unique_id(self) -> str:
Expand All @@ -45,7 +45,7 @@ def name(self) -> str:
@property
def is_connection_sensitive(self) -> bool:
"""Return the name of the binary sensor."""
return self._config[ATTR_IS_CONNECTION_SENSITIVE]
return self._config.get(ATTR_IS_CONNECTION_SENSITIVE, True)

@property
def device_attr(self) -> str:
Expand All @@ -60,7 +60,7 @@ def device_class(self) -> str:
@property
def available(self):
"""Return True if entity is available."""
return self._available
return self.is_connection_sensitive is False or not self._expired

@property
def should_poll(self) -> bool:
Expand Down
29 changes: 11 additions & 18 deletions custom_components/pandora_cas/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
ATTR_NAME: "engine",
ATTR_ICON: {True: "mdi:fan", False: "mdi:fan-off"},
ATTR_DEVICE_CLASS: "pandora_cas__engine",
ATTR_IS_CONNECTION_SENSITIVE: True,
ATTR_DEVICE_ATTR: "bit_state_1",
ATTR_SHIFT_BITS: 2,
ATTR_INVERSE: 0,
Expand All @@ -48,7 +47,6 @@
ATTR_NAME: "moving",
ATTR_ICON: None,
ATTR_DEVICE_CLASS: "pandora_cas__moving",
ATTR_IS_CONNECTION_SENSITIVE: True,
ATTR_DEVICE_ATTR: "move",
ATTR_SHIFT_BITS: 0,
ATTR_INVERSE: 0,
Expand All @@ -57,7 +55,6 @@
ATTR_NAME: "guard",
ATTR_ICON: {True: "mdi:shield-off", False: "mdi:shield-check"},
ATTR_DEVICE_CLASS: "pandora_cas__guard",
ATTR_IS_CONNECTION_SENSITIVE: True,
ATTR_DEVICE_ATTR: "bit_state_1",
ATTR_SHIFT_BITS: 0,
ATTR_INVERSE: 1,
Expand All @@ -66,7 +63,6 @@
ATTR_NAME: "front left door",
ATTR_ICON: "mdi:car-door",
ATTR_DEVICE_CLASS: DEVICE_CLASS_DOOR,
ATTR_IS_CONNECTION_SENSITIVE: True,
ATTR_DEVICE_ATTR: "bit_state_1",
ATTR_SHIFT_BITS: 21,
ATTR_INVERSE: 0,
Expand All @@ -75,7 +71,6 @@
ATTR_NAME: "front right door",
ATTR_ICON: "mdi:car-door",
ATTR_DEVICE_CLASS: DEVICE_CLASS_DOOR,
ATTR_IS_CONNECTION_SENSITIVE: True,
ATTR_DEVICE_ATTR: "bit_state_1",
ATTR_SHIFT_BITS: 22,
ATTR_INVERSE: 0,
Expand All @@ -84,7 +79,6 @@
ATTR_NAME: "back left door",
ATTR_ICON: "mdi:car-door",
ATTR_DEVICE_CLASS: DEVICE_CLASS_DOOR,
ATTR_IS_CONNECTION_SENSITIVE: True,
ATTR_DEVICE_ATTR: "bit_state_1",
ATTR_SHIFT_BITS: 23,
ATTR_INVERSE: 0,
Expand All @@ -93,7 +87,6 @@
ATTR_NAME: "back right door",
ATTR_ICON: "mdi:car-door",
ATTR_DEVICE_CLASS: DEVICE_CLASS_DOOR,
ATTR_IS_CONNECTION_SENSITIVE: True,
ATTR_DEVICE_ATTR: "bit_state_1",
ATTR_SHIFT_BITS: 24,
ATTR_INVERSE: 0,
Expand All @@ -102,7 +95,6 @@
ATTR_NAME: "trunk",
ATTR_ICON: "mdi:car-back",
ATTR_DEVICE_CLASS: "pandora_cas__door_male",
ATTR_IS_CONNECTION_SENSITIVE: True,
ATTR_DEVICE_ATTR: "bit_state_1",
ATTR_SHIFT_BITS: 25,
ATTR_INVERSE: 0,
Expand All @@ -111,7 +103,6 @@
ATTR_NAME: "hood",
ATTR_ICON: "mdi:car",
ATTR_DEVICE_CLASS: "pandora_cas__door_male",
ATTR_IS_CONNECTION_SENSITIVE: True,
ATTR_DEVICE_ATTR: "bit_state_1",
ATTR_SHIFT_BITS: 26,
ATTR_INVERSE: 0,
Expand All @@ -120,7 +111,6 @@
ATTR_NAME: "coolant heater",
ATTR_ICON: {True: "mdi:thermometer-plus", False: "mdi:thermometer"},
ATTR_DEVICE_CLASS: None,
ATTR_IS_CONNECTION_SENSITIVE: True,
ATTR_DEVICE_ATTR: "bit_state_1",
ATTR_SHIFT_BITS: 29,
ATTR_INVERSE: 0,
Expand Down Expand Up @@ -185,18 +175,21 @@ def is_on(self) -> bool:
@callback
def _update_callback(self, force=False):
""""""
api = self._hass.data[DOMAIN]
try:
state = False
if self._device.is_online or not self.is_connection_sensitive:
if (int(getattr(self._device, self.device_attr)) >> self.shift_bits) & 1 ^ self.inverse:
state = True
available = True
if (int(getattr(self._device, self.device_attr)) >> self.shift_bits) & 1 ^ self.inverse:
state = True
else:
available = False
state = False

if self._state != state or self._available != available:
if self.is_connection_sensitive:
expired = api.timestamp - self._device.timestamp > self._device.expire_after
else:
expired = False

if self._state != state or self._expired != expired:
self._state = state
self._available = available
self._expired = expired
self.async_write_ha_state()
except KeyError:
_LOGGER.warning("%s: can't get data from linked device", self.name)
Expand Down
12 changes: 9 additions & 3 deletions custom_components/pandora_cas/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
OPTION_FUEL_UNITS,
OPTION_MILEAGE_SOURCE,
OPTION_MILEAGE_ADJUSTMENT,
OPTION_EXPIRE_AFTER,
FUEL_UNITS,
)

Expand Down Expand Up @@ -143,18 +144,18 @@ async def async_step_init(self, user_input=None):
return await self.async_step_device()

async def async_step_device(self, user_input=None):
IDS = []
devices = []
api = self.hass.data[DOMAIN]

if user_input is not None:
self.pandora_id = user_input[PANDORA_ID]
return await self.async_step_options()

for pandora_id in api.devices.keys():
IDS.append(pandora_id)
devices.append(pandora_id)

fields = OrderedDict()
fields[vol.Required(PANDORA_ID, default=IDS[0])] = vol.In(IDS)
fields[vol.Required(PANDORA_ID, default=devices[0])] = vol.In(devices)

return self.async_show_form(step_id="device", data_schema=vol.Schema(fields))

Expand All @@ -170,6 +171,7 @@ async def async_step_options(self, user_input=None):
OPTION_MILEAGE_SOURCE, MILEAGE_SOURCES[0]
)
device_options[self.pandora_id][OPTION_MILEAGE_ADJUSTMENT] = user_input.get(OPTION_MILEAGE_ADJUSTMENT, 0)
device_options[self.pandora_id][OPTION_EXPIRE_AFTER] = user_input.get(OPTION_EXPIRE_AFTER, 0)
self.options.update(device_options)
self.pandora_id = None # invalidate pandora_id
return self.async_create_entry(title="", data=self.options)
Expand All @@ -180,6 +182,7 @@ async def async_step_options(self, user_input=None):
fields[vol.Optional(OPTION_FUEL_UNITS, default=FUEL_UNITS[0])] = vol.In(FUEL_UNITS)
fields[vol.Optional(OPTION_MILEAGE_SOURCE, default=MILEAGE_SOURCES[0])] = vol.In(MILEAGE_SOURCES)
fields[vol.Optional(OPTION_MILEAGE_ADJUSTMENT, default=0)] = vol.Coerce(int)
fields[vol.Optional(OPTION_EXPIRE_AFTER, default=0)] = vol.Coerce(int)
else:
fields[
vol.Optional(OPTION_FUEL_UNITS, default=device_options.get(OPTION_FUEL_UNITS, FUEL_UNITS[0]))
Expand All @@ -192,6 +195,9 @@ async def async_step_options(self, user_input=None):
fields[
vol.Optional(OPTION_MILEAGE_ADJUSTMENT, default=device_options.get(OPTION_MILEAGE_ADJUSTMENT, 0))
] = vol.Coerce(int)
fields[
vol.Optional(OPTION_EXPIRE_AFTER, default=device_options.get(OPTION_EXPIRE_AFTER, 0))
] = vol.Coerce(int)

return self.async_show_form(
step_id="options",
Expand Down
1 change: 1 addition & 0 deletions custom_components/pandora_cas/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@
OPTION_FUEL_UNITS = "fuel_units"
OPTION_MILEAGE_SOURCE = "mileage_source"
OPTION_MILEAGE_ADJUSTMENT = "mileage_adjustment"
OPTION_EXPIRE_AFTER = "expire_after"
28 changes: 10 additions & 18 deletions custom_components/pandora_cas/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
ATTR_ICON: "mdi:map-marker-distance",
ATTR_DEVICE_CLASS: None, # TODO: Make propper device class
ATTR_UNITS: LENGTH_KILOMETERS,
ATTR_IS_CONNECTION_SENSITIVE: True,
ATTR_DEVICE_ATTR: "mileage",
ATTR_FORMATTER: lambda v: round(float(v), 2),
},
Expand All @@ -35,31 +34,27 @@
ATTR_ICON: "mdi:gauge",
ATTR_DEVICE_CLASS: None,
ATTR_UNITS: PERCENTAGE,
ATTR_IS_CONNECTION_SENSITIVE: True,
ATTR_DEVICE_ATTR: "fuel",
},
"cabin_temperature": {
ATTR_NAME: "cabin temperature",
ATTR_ICON: "mdi:thermometer",
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
ATTR_UNITS: TEMP_CELSIUS,
ATTR_IS_CONNECTION_SENSITIVE: True,
ATTR_DEVICE_ATTR: "cabin_temp",
},
"engine_temperature": {
ATTR_NAME: "Engine temperature",
ATTR_ICON: "mdi:thermometer",
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
ATTR_UNITS: TEMP_CELSIUS,
ATTR_IS_CONNECTION_SENSITIVE: True,
ATTR_DEVICE_ATTR: "engine_temp",
},
"ambient_temperature": {
ATTR_NAME: "Ambient temperature",
ATTR_ICON: "mdi:thermometer",
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
ATTR_UNITS: TEMP_CELSIUS,
ATTR_IS_CONNECTION_SENSITIVE: True,
ATTR_DEVICE_ATTR: "out_temp",
},
"balance": {
Expand All @@ -76,7 +71,6 @@
ATTR_ICON: "mdi:gauge",
ATTR_DEVICE_CLASS: None,
ATTR_UNITS: "km/h",
ATTR_IS_CONNECTION_SENSITIVE: True,
ATTR_DEVICE_ATTR: "speed",
ATTR_FORMATTER: lambda v: round(float(v), 1),
},
Expand All @@ -85,23 +79,20 @@
ATTR_ICON: "mdi:gauge",
ATTR_DEVICE_CLASS: None,
ATTR_UNITS: None,
ATTR_IS_CONNECTION_SENSITIVE: True,
ATTR_DEVICE_ATTR: "engine_rpm",
},
"gsm_level": {
ATTR_NAME: "GSM level",
ATTR_ICON: "mdi:network-strength-2",
ATTR_DEVICE_CLASS: None,
ATTR_UNITS: None,
ATTR_IS_CONNECTION_SENSITIVE: True,
ATTR_DEVICE_ATTR: "gsm_level",
},
"battery_voltage": {
ATTR_NAME: "battery",
ATTR_ICON: "mdi:car-battery",
ATTR_DEVICE_CLASS: None,
ATTR_UNITS: "V",
ATTR_IS_CONNECTION_SENSITIVE: True,
ATTR_DEVICE_ATTR: "voltage",
},
}
Expand Down Expand Up @@ -161,19 +152,20 @@ def state(self):
@callback
def _update_callback(self, force=False):
""""""
api = self._hass.data[DOMAIN]
try:
state = None
if self._device.is_online or not self.is_connection_sensitive:
state = getattr(self._device, self.device_attr)
formatter = self._config.get(ATTR_FORMATTER)
state = formatter(state) if formatter else state
available = True
state = getattr(self._device, self.device_attr)
formatter = self._config.get(ATTR_FORMATTER)
state = formatter(state) if formatter else state

if self.is_connection_sensitive:
expired = api.timestamp - self._device.timestamp > self._device.expire_after
else:
available = False
expired = False

if self._state != state or self._available != available:
if self._state != state or self._expired != expired:
self._state = state
self._available = available
self._expired = expired
self.async_write_ha_state()
except KeyError:
_LOGGER.warning("%s: can't get data from linked device", self.name)
Expand Down
3 changes: 2 additions & 1 deletion custom_components/pandora_cas/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
"data": {
"fuel_units": "Fuel units",
"mileage_source": "Mileage source",
"mileage_adjustment": "Mileage adjustment"
"mileage_adjustment": "Mileage adjustment",
"expire_after": "Expire after"
},
"title": "Pandora CAS settings",
"description": "Options for {name}"
Expand Down
3 changes: 2 additions & 1 deletion custom_components/pandora_cas/translations/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
"data": {
"fuel_units": "Отображение топлива",
"mileage_source": "Источник пробега",
"mileage_adjustment": "Корректировка пробега"
"mileage_adjustment": "Корректировка пробега",
"expire_after": "Таймаут недоступности"
},
"title": "Настройка Pandora CAS",
"description": "Задайте параметры для {name}"
Expand Down

0 comments on commit 0377c2e

Please sign in to comment.