Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TPMS sensors [closes #41] #42

Merged
merged 2 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions custom_components/teslafi/alarm_control_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,9 @@ def _handle_coordinator_update(self) -> None:
key="sentry_mode",
name="Sentry Mode",
entity_registry_enabled_default=False,
convert=lambda v: STATE_ALARM_ARMED_AWAY
if _convert_to_bool(v)
else STATE_ALARM_DISARMED,
convert=lambda v: (
STATE_ALARM_ARMED_AWAY if _convert_to_bool(v) else STATE_ALARM_DISARMED
),
),
]

Expand Down
5 changes: 4 additions & 1 deletion custom_components/teslafi/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ class TeslaFiSensorEntityDescription(
icons: dict[str, str] = None
"""Dictionary of state -> icon"""

fix_unit: Callable[[TeslaFiVehicle, HomeAssistant], str] = lambda d, h: None
"""Convert the native unit of measurement. Return None to keep the original unit."""


@dataclass
class TeslaFiNumberEntityDescription(
Expand All @@ -156,7 +159,7 @@ class TeslaFiNumberEntityDescription(
):
"""TeslaFi Number EntityDescription"""

convert: Callable[[any], bool] = lambda v: int(v) if v else None
convert: Callable[[any], int] = lambda v: int(v) if v else None
cmd: Callable[[TeslaFiCoordinator, Number], dict] = None

max_value_key: str = None
Expand Down
1 change: 1 addition & 0 deletions custom_components/teslafi/config_flow.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Config flow for Bird Buddy integration."""

from __future__ import annotations

from typing import Any
Expand Down
7 changes: 2 additions & 5 deletions custom_components/teslafi/device_tracker.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Device tracker for MyBMW vehicles."""

from __future__ import annotations

from typing import Any
Expand Down Expand Up @@ -42,11 +43,7 @@ def __init__(self, coordinator: TeslaFiCoordinator) -> None:
@property
def extra_state_attributes(self) -> dict[str, Any]:
"""Return entity specific state attributes."""
heading = (
int(h)
if (h := self.coordinator.data.get("heading", None))
else None
)
heading = int(h) if (h := self.coordinator.data.get("heading", None)) else None
cardinal = _degrees_to_cardinal(heading) if heading is not None else None
return {
"heading": heading,
Expand Down
54 changes: 53 additions & 1 deletion custom_components/teslafi/model.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
"""TeslaFi Object Models"""

from collections import UserDict
from dataclasses import dataclass

from typing_extensions import deprecated

from homeassistant.const import UnitOfPressure

from .const import SHIFTER_STATES, VIN_YEARS
from .util import (
_convert_to_bool,
Expand All @@ -12,7 +16,6 @@
_lower_or_none,
)


NAN: float = float("NaN")

CHARGER_CONNECTED_STATES = [
Expand All @@ -24,6 +27,28 @@
]


@dataclass
class TeslaFiTirePressure:
"""TeslaFi Tire Pressure Data."""

front_left: float | None
front_right: float | None
rear_left: float | None
rear_right: float | None
unit: str | None = None

@staticmethod
def convert_unit(unit: str) -> str | None:
"""Convert units to Home Assistant's UnitOfPressure."""
unit_mapping = {
"kpa": UnitOfPressure.KPA,
"bar": UnitOfPressure.BAR,
"psi": UnitOfPressure.PSI,
"mmhg": UnitOfPressure.MMHG,
}
return unit_mapping.get(unit.lower(), None) if unit else None


class TeslaFiVehicle(UserDict):
"""TeslaFi Vehicle Data"""

Expand Down Expand Up @@ -196,3 +221,30 @@ def is_defrosting(self) -> bool | None:
or self.get("is_rear_defroster_on") == "1"
or self.get("defrost_mode", "0") != "0"
)

@property
def tpms(self) -> TeslaFiTirePressure:
"""TPMS state(s): (front-left, front-right, rear-left, rear-right)."""
return TeslaFiTirePressure(
front_left=(
float(tpms_fl)
if (tpms_fl := self.get("tpms_front_left", None))
else None
),
front_right=(
float(tpms_fr)
if (tpms_fr := self.get("tpms_front_right", None))
else None
),
rear_left=(
float(tpms_rl)
if (tpms_rl := self.get("tpms_rear_left", None))
else None
),
rear_right=(
float(tpms_rr)
if (tpms_rr := self.get("tpms_rear_right", None))
else None
),
unit=self.get("pressure", "psi"),
)
64 changes: 62 additions & 2 deletions custom_components/teslafi/sensor.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Sensors"""

from typing_extensions import override
from typing import override

from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
Expand All @@ -16,6 +17,7 @@
UnitOfEnergy,
UnitOfLength,
UnitOfPower,
UnitOfPressure,
UnitOfSpeed,
UnitOfTemperature,
UnitOfTime,
Expand All @@ -26,7 +28,7 @@
from .base import TeslaFiEntity, TeslaFiSensorEntityDescription
from .const import DOMAIN, SHIFTER_STATES
from .coordinator import TeslaFiCoordinator

from .model import TeslaFiTirePressure

SENSORS = [
# region Generic car info
Expand Down Expand Up @@ -187,6 +189,60 @@
),
# ... climate.py
# endregion
# region TPMS
TeslaFiSensorEntityDescription(
key="tpms_front_left",
name="TPMS Front Left",
icon="mdi:car-tire-alert",
device_class=SensorDeviceClass.PRESSURE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfPressure.PSI,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
fix_unit=lambda d, h: TeslaFiTirePressure.convert_unit(d.tpms.unit),
value=lambda d, h: d.tpms.front_left,
available=lambda u, d, h: u and d.tpms.front_left,
),
TeslaFiSensorEntityDescription(
key="tpms_front_right",
name="TPMS Front Right",
icon="mdi:car-tire-alert",
device_class=SensorDeviceClass.PRESSURE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfPressure.PSI,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
fix_unit=lambda d, h: TeslaFiTirePressure.convert_unit(d.tpms.unit),
value=lambda d, h: d.tpms.front_right,
available=lambda u, d, h: u and d.tpms.front_right,
),
TeslaFiSensorEntityDescription(
key="tpms_rear_left",
name="TPMS Rear Left",
icon="mdi:car-tire-alert",
device_class=SensorDeviceClass.PRESSURE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfPressure.PSI,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
fix_unit=lambda d, h: TeslaFiTirePressure.convert_unit(d.tpms.unit),
value=lambda d, h: d.tpms.rear_left,
available=lambda u, d, h: u and d.tpms.rear_left,
),
TeslaFiSensorEntityDescription(
key="tpms_rear_right",
name="TPMS Rear Right",
icon="mdi:car-tire-alert",
device_class=SensorDeviceClass.PRESSURE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfPressure.PSI,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
fix_unit=lambda d, h: TeslaFiTirePressure.convert_unit(d.tpms.unit),
value=lambda d, h: d.tpms.rear_right,
available=lambda u, d, h: u and d.tpms.rear_right,
),
# endregion
]


Expand All @@ -195,6 +251,10 @@ class TeslaFiSensor(TeslaFiEntity[TeslaFiSensorEntityDescription], SensorEntity)

def _handle_coordinator_update(self) -> None:
self._attr_native_value = self._get_value()
if self.entity_description.fix_unit and (
fixed := self.entity_description.fix_unit(self.coordinator.data, self.hass)
):
self._attr_native_unit_of_measurement = fixed
return super()._handle_coordinator_update()

@property
Expand Down
Loading