Skip to content

Commit

Permalink
Add options of integration
Browse files Browse the repository at this point in the history
  • Loading branch information
turbulator committed Dec 13, 2020
1 parent fb695ac commit 86d4e60
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 21 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
1. Нажмите кнопку "+", расположенную в нижней правой части экрана
1. Наберите "Pandora" для поиска компонента
1. Установите компонент Pandora Car Alarm System
1. (?) Перезапустите Home Assistant
1. Перезапустите Home Assistant

## Настройка

Expand All @@ -35,6 +35,7 @@
1. Введите логин, пароль, а также частоту обновления информации с сайта p-on.ru
1. При необходимотсти задайте помещение для автомобиля
1. Устройства и сенсоры добавятся в Home Assistant
1. При необходимости, в настройках интеграции выберите единицы измерения топлива, источник данных для одометра и его начальные значения. Для актуализации изменений сенсоров потребуется перезапуск Home Assistant.

## Device Tracker

Expand All @@ -47,7 +48,7 @@
| Объект | Назначение | Примечание |
|-|-|-|
| sensor.`PANDORA_ID`_mileage | Пробег | км |
| sensor.`PANDORA_ID`_fuel_level | | % |
| sensor.`PANDORA_ID`_fuel_level | Уровень топлива | % или L |
| sensor.`PANDORA_ID`_cabin_temperature | Температура салона | °C |
| sensor.`PANDORA_ID`_engine_temperature | Температура двигателя | °C |
| sensor.`PANDORA_ID`_ambient_temperature | Уличная температура | °C |
Expand Down
6 changes: 6 additions & 0 deletions custom_components/pandora_cas/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,12 @@ async def _execute_command(call) -> bool:
api = hass.data[DOMAIN] = PandoraApi(hass, username, password, polling_interval)
await api.load_devices()
await api.async_refresh()

# Save options which got from config_entry
for pandora_id, options in config_entry.options.items():
if pandora_id in api.devices.keys():
await api.devices[pandora_id].config_options(options)

except PandoraApiException as ex:
_LOGGER.error("Setting up entry %s failed: %s", username, str(ex))
return False
Expand Down
54 changes: 40 additions & 14 deletions custom_components/pandora_cas/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,21 @@

import aiohttp
from homeassistant.core import CALLBACK_TYPE, callback
from homeassistant.const import PERCENTAGE
from homeassistant.helpers.aiohttp_client import async_create_clientsession
from homeassistant.helpers.event import async_track_point_in_utc_time
from homeassistant.helpers.typing import HomeAssistantType
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from homeassistant.util.dt import utcnow

from .const import DOMAIN
from .const import (
DOMAIN,
MILEAGE_SOURCES,
OPTION_FUEL_UNITS,
OPTION_MILEAGE_SOURCE,
OPTION_MILEAGE_ADJUSTMENT,
FUEL_UNITS,
)


_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -158,10 +166,8 @@ async def _async_update(self, *_) -> bool:
if self._update_ts >= self._force_update_ts + FORCE_UPDATE_INTERVAL:
self._update_ts = 0

response = PandoraApiUpdateResponseParser(
await self._request_safe(UPDATE_PATH + str(self._update_ts - 1))
)

response = PandoraApiUpdateResponseParser(await self._request_safe(UPDATE_PATH + str(self._update_ts - 1)))

stats = response.stats
if self._update_ts == 0:
self._force_update_ts = self._update_ts = response.timestamp
Expand All @@ -183,7 +189,6 @@ async def _async_update(self, *_) -> bool:
except PandoraApiException as ex:
_LOGGER.info("Update failed: %s", str(ex))


# I made some experiments with my car. How long does it take between sending command
# and getting proper state of corresponding entity? Results is placed below:
# ----------------------------------------------------------------------------------
Expand Down Expand Up @@ -278,19 +283,32 @@ def is_online(self) -> bool:
return bool(self.online)

@property
def fuel_tank(self) -> int:
"""Get the capacity of fuel tank."""
return int(self._info["fuel_tank"])
def fuel_percentage(self) -> int:
"""Get fuel in percentage."""
return int(self._attributes["fuel"])

@property
def fuel_litres(self) -> int:
"""Get fuel in liters."""
return int(self._info["fuel_tank"]) * self.fuel_percentage / 100

@property
def fuel(self) -> int:
"""Get fuel in user-defined units."""
if self._info.get(OPTION_FUEL_UNITS, FUEL_UNITS[0]) == FUEL_UNITS[0]:
return self.fuel_percentage

return self.fuel_litres

@property
def mileage(self) -> float:
"""Get the mileage."""
"""Get mileage from user-defined source with user-defined adjustment."""
adjustment = float(self._info.get(OPTION_MILEAGE_ADJUSTMENT, 0))

# mileage_CAN will be used if supported
if self._attributes["mileage_CAN"] > 0:
return float(self._attributes["mileage_CAN"])
if self._info.get(OPTION_MILEAGE_SOURCE, MILEAGE_SOURCES[0]) == MILEAGE_SOURCES[0]:
return adjustment + float(self._attributes["mileage"])

return float(self._attributes["mileage"])
return adjustment + float(self._attributes["mileage_CAN"])

@property
def device_info(self) -> dict:
Expand All @@ -303,10 +321,18 @@ def device_info(self) -> dict:
"sw_version": self._info["firmware"],
}

def user_defined_units(self, item):
"""Get units of attribute."""
return self._info.get(item + "_units")

def __getattr__(self, item):
"""Generic get function for all backend attributes."""
return self._attributes[item]

async def config_options(self, options: dict) -> None:
"""Save options from config_entry."""
self._info.update(options)

async def update(self, attributes: dict) -> None:
"""Read new status data from the server."""

Expand Down
89 changes: 87 additions & 2 deletions custom_components/pandora_cas/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,32 @@
DETAILS
"""
from collections import OrderedDict
import logging
from typing import Optional

import voluptuous as vol
from homeassistant import config_entries
from homeassistant.config_entries import CONN_CLASS_CLOUD_POLL
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, PERCENTAGE
from homeassistant.helpers.typing import ConfigType

from .const import DOMAIN, CONF_POLLING_INTERVAL, DEFAULT_POLLING_INTERVAL, MIN_POLLING_INTERVAL
from .const import (
DOMAIN,
CONF_POLLING_INTERVAL,
DEFAULT_POLLING_INTERVAL,
MIN_POLLING_INTERVAL,
MILEAGE_SOURCES,
OPTION_FUEL_UNITS,
OPTION_MILEAGE_SOURCE,
OPTION_MILEAGE_ADJUSTMENT,
FUEL_UNITS,
)

_LOGGER = logging.getLogger(__name__)

PANDORA_ID = "pandora_id"

FLOW_SCHEMA = vol.Schema(
{vol.Required(CONF_USERNAME): str, vol.Required(CONF_PASSWORD): str, vol.Optional(CONF_POLLING_INTERVAL,): int,}
)
Expand Down Expand Up @@ -59,6 +72,7 @@ async def validate_input(user_input: Optional[ConfigType] = None):
raise ValueError


@config_entries.HANDLERS.register("pandora_cas")
class PandoraCasConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Pandora CAS"""

Expand All @@ -68,6 +82,10 @@ class PandoraCasConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
def __init__(self):
self.data_schema = {}

@staticmethod
def async_get_options_flow(config_entry):
return OptionsFlowHandler(config_entry)

async def async_step_user(self, user_input: Optional[ConfigType] = None):
errors = {}

Expand Down Expand Up @@ -113,3 +131,70 @@ async def async_step_discovery_confirm(self, user_input: Optional[ConfigType] =
return self.async_create_entry(title=username, data=user_input)

return self.async_show_form(step_id="discovery_confirm", data_schema=self.data_schema, errors=errors)


class OptionsFlowHandler(config_entries.OptionsFlow):
def __init__(self, config_entry):
self.config_entry = config_entry
self.options = dict(config_entry.options)
self.pandora_id = None

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 = []
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)

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

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

async def async_step_options(self, user_input=None):
"""Manage the options."""
api = self.hass.data[DOMAIN]
device_options = {}

if user_input is not None:
device_options[self.pandora_id] = {}
device_options[self.pandora_id][OPTION_FUEL_UNITS] = user_input.get(OPTION_FUEL_UNITS, FUEL_UNITS[0])
device_options[self.pandora_id][OPTION_MILEAGE_SOURCE] = user_input.get(
OPTION_MILEAGE_SOURCE, MILEAGE_SOURCES[0]
)
device_options[self.pandora_id][OPTION_MILEAGE_ADJUSTMENT] = user_input.get(OPTION_MILEAGE_ADJUSTMENT, 0)
self.options.update(device_options)
self.pandora_id = None # invalidate pandora_id
return self.async_create_entry(title="", data=self.options)

fields = OrderedDict()
device_options = self.options.get(self.pandora_id)
if device_options is 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)
else:
fields[
vol.Optional(OPTION_FUEL_UNITS, default=device_options.get(OPTION_FUEL_UNITS, FUEL_UNITS[0]))
] = vol.In(FUEL_UNITS)
fields[
vol.Optional(
OPTION_MILEAGE_SOURCE, default=device_options.get(OPTION_MILEAGE_SOURCE, MILEAGE_SOURCES[0])
)
] = vol.In(MILEAGE_SOURCES)
fields[
vol.Optional(OPTION_MILEAGE_ADJUSTMENT, default=device_options.get(OPTION_MILEAGE_ADJUSTMENT, 0))
] = vol.Coerce(int)

return self.async_show_form(
step_id="options",
data_schema=vol.Schema(fields),
description_placeholders={"name": api.devices[self.pandora_id].name},
)
8 changes: 8 additions & 0 deletions custom_components/pandora_cas/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

from datetime import timedelta

from homeassistant.const import PERCENTAGE, VOLUME_LITERS

DOMAIN = "pandora_cas"

ATTR_IS_CONNECTION_SENSITIVE = "is_connection_sensitive"
Expand All @@ -17,3 +19,9 @@
CONF_POLLING_INTERVAL = "polling_interval"
MIN_POLLING_INTERVAL = timedelta(seconds=10)
DEFAULT_POLLING_INTERVAL = timedelta(minutes=1)

FUEL_UNITS = [PERCENTAGE, VOLUME_LITERS]
MILEAGE_SOURCES = ["GPS", "CAN"]
OPTION_FUEL_UNITS = "fuel_units"
OPTION_MILEAGE_SOURCE = "mileage_source"
OPTION_MILEAGE_ADJUSTMENT = "mileage_adjustment"
10 changes: 7 additions & 3 deletions custom_components/pandora_cas/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from homeassistant.components.sensor import ENTITY_ID_FORMAT, DEVICE_CLASS_TEMPERATURE
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_DEVICE_CLASS, ATTR_ICON, ATTR_NAME, LENGTH_KILOMETERS, TEMP_CELSIUS
from homeassistant.const import ATTR_DEVICE_CLASS, ATTR_ICON, ATTR_NAME, LENGTH_KILOMETERS, PERCENTAGE, TEMP_CELSIUS
from homeassistant.core import callback
from homeassistant.helpers.typing import HomeAssistantType
from homeassistant.util import slugify
Expand All @@ -24,7 +24,7 @@
"mileage": {
ATTR_NAME: "mileage",
ATTR_ICON: "mdi:map-marker-distance",
ATTR_DEVICE_CLASS: None, # TODO: Make propper device class
ATTR_DEVICE_CLASS: None, # TODO: Make propper device class
ATTR_UNITS: LENGTH_KILOMETERS,
ATTR_IS_CONNECTION_SENSITIVE: True,
ATTR_DEVICE_ATTR: "mileage",
Expand All @@ -34,7 +34,7 @@
ATTR_NAME: "fuel",
ATTR_ICON: "mdi:gauge",
ATTR_DEVICE_CLASS: None,
ATTR_UNITS: "%",
ATTR_UNITS: PERCENTAGE,
ATTR_IS_CONNECTION_SENSITIVE: True,
ATTR_DEVICE_ATTR: "fuel",
},
Expand Down Expand Up @@ -134,6 +134,10 @@ def __init__(

self.entity_id = self.ENTITY_ID_FORMAT.format("{}_{}".format(slugify(device.pandora_id), entity_id))

user_defined_units = device.user_defined_units(self.device_attr)
if user_defined_units is not None:
self._config[ATTR_UNITS] = user_defined_units

@property
def icon(self) -> str:
"""Return the icon of the binary sensor."""
Expand Down
20 changes: 20 additions & 0 deletions custom_components/pandora_cas/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,25 @@
}
},
"title": "Pandora Car Alarm System"
},
"options": {
"step": {
"device": {
"data": {
"pandora_id": "Pandora ID"
},
"title": "Pandora CAS settings",
"description": "Select device for setup"
},
"options": {
"data": {
"fuel_units": "Fuel units",
"mileage_source": "Mileage source",
"mileage_adjustment": "Mileage adjustment"
},
"title": "Pandora CAS settings",
"description": "Options for {name}"
}
}
}
}
20 changes: 20 additions & 0 deletions custom_components/pandora_cas/translations/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,25 @@
}
},
"title": "Pandora Car Alarm System"
},
"options": {
"step": {
"device": {
"data": {
"pandora_id": "Pandora ID"
},
"title": "Настройка Pandora CAS",
"description": "Выберите устройство для настройки"
},
"options": {
"data": {
"fuel_units": "Отображение топлива",
"mileage_source": "Источник пробега",
"mileage_adjustment": "Корректировка пробега"
},
"title": "Настройка Pandora CAS",
"description": "Задайте параметры для {name}"
}
}
}
}

0 comments on commit 86d4e60

Please sign in to comment.