Skip to content
Open
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
122 changes: 57 additions & 65 deletions src/integrations/home_assistant/discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,9 @@ def __publish_location_sensors(self) -> None:
)

def __publish_windows_sensors(self) -> None:
# Window sensors are always published. The WINDOW model configuration code
# cannot be used to gate them: e.g. the MG4 reports WINDOW=0000 while sending
# state for all 4 windows.
self.__unpublish_ha_discovery_message("switch", "Window driver")
self.__unpublish_ha_discovery_message("switch", "Window passenger")
self.__unpublish_ha_discovery_message("switch", "Window rear left")
Expand Down Expand Up @@ -604,34 +607,23 @@ def __publish_lights_sensors(self) -> None:
)

def __publish_tyres_sensors(self) -> None:
self._publish_sensor(
mqtt_topics.TYRES_FRONT_LEFT_PRESSURE,
"Tyres front left pressure",
device_class="pressure",
unit_of_measurement="bar",
icon="mdi:tire",
)
self._publish_sensor(
mqtt_topics.TYRES_FRONT_RIGHT_PRESSURE,
"Tyres front right pressure",
device_class="pressure",
unit_of_measurement="bar",
icon="mdi:tire",
)
self._publish_sensor(
mqtt_topics.TYRES_REAR_LEFT_PRESSURE,
"Tyres rear left pressure",
device_class="pressure",
unit_of_measurement="bar",
icon="mdi:tire",
)
self._publish_sensor(
mqtt_topics.TYRES_REAR_RIGHT_PRESSURE,
"Tyres rear right pressure",
device_class="pressure",
unit_of_measurement="bar",
icon="mdi:tire",
)
tyres = [
(mqtt_topics.TYRES_FRONT_LEFT_PRESSURE, "Tyres front left pressure"),
(mqtt_topics.TYRES_FRONT_RIGHT_PRESSURE, "Tyres front right pressure"),
(mqtt_topics.TYRES_REAR_LEFT_PRESSURE, "Tyres rear left pressure"),
(mqtt_topics.TYRES_REAR_RIGHT_PRESSURE, "Tyres rear right pressure"),
]
for topic, name in tyres:
if self.__vin_info.has_tyre_pressure:
self._publish_sensor(
topic,
name,
device_class="pressure",
unit_of_measurement="bar",
icon="mdi:tire",
)
else:
self.__unpublish_ha_discovery_message("sensor", name)

def __publish_doors_sensors(self) -> None:
self._publish_lock(
Expand All @@ -644,36 +636,33 @@ def __publish_doors_sensors(self) -> None:
state_locked="False",
state_unlocked="True",
)
self._publish_binary_sensor(
mqtt_topics.DOORS_DRIVER,
"Door driver",
device_class="door",
icon="mdi:car-door",
)
self._publish_binary_sensor(
mqtt_topics.DOORS_PASSENGER,
"Door passenger",
device_class="door",
icon="mdi:car-door",
)
self._publish_binary_sensor(
mqtt_topics.DOORS_REAR_LEFT,
"Door rear left",
device_class="door",
icon="mdi:car-door",
)
self._publish_binary_sensor(
mqtt_topics.DOORS_REAR_RIGHT,
"Door rear right",
device_class="door",
icon="mdi:car-door",
)
self._publish_binary_sensor(
mqtt_topics.DOORS_BONNET, "Bonnet", device_class="door", icon="mdi:car-door"
)
self._publish_binary_sensor(
mqtt_topics.DOORS_BOOT, "Boot", device_class="door", icon="mdi:car-door"
)
doors = [
(mqtt_topics.DOORS_DRIVER, "Door driver", self.__vin_info.has_driver_door),
(
mqtt_topics.DOORS_PASSENGER,
"Door passenger",
self.__vin_info.has_passenger_door,
),
(
mqtt_topics.DOORS_REAR_LEFT,
"Door rear left",
self.__vin_info.has_rear_left_door,
),
(
mqtt_topics.DOORS_REAR_RIGHT,
"Door rear right",
self.__vin_info.has_rear_right_door,
),
(mqtt_topics.DOORS_BONNET, "Bonnet", True),
(mqtt_topics.DOORS_BOOT, "Boot", self.__vin_info.has_boot_status),
]
for topic, name, supported in doors:
if supported:
self._publish_binary_sensor(
topic, name, device_class="door", icon="mdi:car-door"
)
else:
self.__unpublish_ha_discovery_message("binary_sensor", name)

def __publish_gateway_sensors(self) -> None:
options = [m.value for m in RefreshMode]
Expand Down Expand Up @@ -793,13 +782,16 @@ def __publish_climate_sensors(self) -> None:
self.__publish_remote_ac()
self.__publish_heated_seats()
self.__publish_back_window_heat()
self._publish_sensor(
mqtt_topics.CLIMATE_INTERIOR_TEMPERATURE,
"Interior temperature",
device_class="temperature",
state_class="measurement",
unit_of_measurement="°C",
)
if self.__vin_info.has_interior_temperature:
self._publish_sensor(
mqtt_topics.CLIMATE_INTERIOR_TEMPERATURE,
"Interior temperature",
device_class="temperature",
state_class="measurement",
unit_of_measurement="°C",
)
else:
self.__unpublish_ha_discovery_message("sensor", "Interior temperature")
self._publish_sensor(
mqtt_topics.CLIMATE_EXTERIOR_TEMPERATURE,
"Exterior temperature",
Expand Down
49 changes: 48 additions & 1 deletion src/vehicle_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,21 @@ def __get_property_by_code(self, property_name: str) -> str | None:
normalized_property_name = str(property_name).strip().upper()
return self.__properties_by_code.get(normalized_property_name)

def __get_int_property_by_code(self, property_name: str) -> int:
# Absent or non-numeric codes read as 0, i.e. "feature not available"
try:
return int(self.__get_property_by_code(property_name) or 0)
except ValueError:
return 0

def __is_feature_enabled(self, property_name: str) -> bool:
return self.__get_int_property_by_code(property_name) != 0

def __get_mask_by_code(self, property_name: str, index: int) -> bool:
# 4-char positional mask: driver, passenger, rear left, rear right
mask = self.__get_property_by_code(property_name) or ""
return len(mask) == 4 and mask[index] == "1"

@property
def is_ev(self) -> bool:
return not self.series.startswith("ZP22")
Expand All @@ -88,7 +103,39 @@ def has_fossil_fuel(self) -> bool:

@property
def has_sunroof(self) -> bool:
return self.__get_property_by_code("S35") != "0"
# The value 2 alone does not denote a sunroof
return self.__get_int_property_by_code("S35") & ~2 != 0

@property
def has_driver_door(self) -> bool:
return self.__get_mask_by_code("DOOR", 0)

@property
def has_passenger_door(self) -> bool:
return self.__get_mask_by_code("DOOR", 1)

@property
def has_rear_left_door(self) -> bool:
return self.__get_mask_by_code("DOOR", 2)

@property
def has_rear_right_door(self) -> bool:
return self.__get_mask_by_code("DOOR", 3)

# WINDOW, BONNUT, BATTERY and EXTEMP are deliberately not used: real vehicles
# report 0 for them while still sending the data (e.g. WINDOW=0000 on the MG4)

@property
def has_boot_status(self) -> bool:
return self.__is_feature_enabled("BOOT")

@property
def has_tyre_pressure(self) -> bool:
return self.__is_feature_enabled("J17")

@property
def has_interior_temperature(self) -> bool:
return self.__is_feature_enabled("INTEMP")

@property
def has_on_off_heated_seats(self) -> bool:
Expand Down
Loading
Loading