diff --git a/custom_components/smarthashtag/__init__.py b/custom_components/smarthashtag/__init__.py index 1d98e21..755b9d6 100644 --- a/custom_components/smarthashtag/__init__.py +++ b/custom_components/smarthashtag/__init__.py @@ -20,6 +20,7 @@ Platform.DEVICE_TRACKER, # Platform.BINARY_SENSOR, # Platform.SWITCH, + Platform.CLIMATE, ] diff --git a/custom_components/smarthashtag/climate.py b/custom_components/smarthashtag/climate.py new file mode 100644 index 0000000..a484a07 --- /dev/null +++ b/custom_components/smarthashtag/climate.py @@ -0,0 +1,147 @@ +"""Support for Smart #1 / #3 switches.""" + +from functools import cached_property +from typing import Any +from homeassistant.components.climate import ClimateEntity, ClimateEntityFeature +from homeassistant.components.climate.const import HVACMode +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import EntityCategory +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature + +from .coordinator import SmartHashtagDataUpdateCoordinator + +from .const import ( + CONF_CONDITIONING_TEMP, + CONF_VEHICLE, + DEFAULT_CONDITIONING_TEMP, + DOMAIN, + LOGGER, +) + + +async def async_setup_entry(hass: HomeAssistant, entry, async_add_entities): + """Set up the Smart switches by config_entry.""" + coordinator = hass.data[DOMAIN][entry.entry_id] + vehicle = hass.data[DOMAIN][CONF_VEHICLE] + entities = [] + + entities.append(SmartConditioningMode(coordinator, vehicle)) + + async_add_entities(entities, update_before_add=True) + + +class SmartConditioningMode(ClimateEntity): + """Representation of a Smart vehicle location device tracker.""" + + type = "conditioning mode" + _attr_max_temp = 30 + _attr_min_temp = 16 + _attr_hvac_modes = [HVACMode.HEAT, HVACMode.OFF] + _attr_hvac_mode = HVACMode.OFF + _attr_temperature_unit = UnitOfTemperature.CELSIUS + _attr_supported_features = ( + ClimateEntityFeature.TARGET_TEMPERATURE + | ClimateEntityFeature.TURN_OFF + | ClimateEntityFeature.TURN_ON + ) + _attr_entity_category = EntityCategory.CONFIG + _attr_has_entity_name = True + _attr_should_poll = False + _attr_icon = "mdi:thermostat-auto" + _enable_turn_on_off_backwards_compatibility = False + + def __init__( + self, + coordinator: SmartHashtagDataUpdateCoordinator, + vehicle: str, + ) -> None: + """Initialize the Contitioner class.""" + super().__init__() + self.coordinator = coordinator + self._vehicle = self.coordinator.account.vehicles[vehicle] + self._attr_name = f"Smart {vehicle} Conditioning" + self._attr_unique_id = f"{self._attr_unique_id}_{vehicle}" + self._temperature = self.coordinator.config_entry.options.get( + CONF_CONDITIONING_TEMP, DEFAULT_CONDITIONING_TEMP + ) + self._attr_target_temperature = self._temperature + + async def async_turn_on(self) -> None: + """Turn on the climate system.""" + await self._vehicle.climate_control.set_climate_conditioning( + self._temperature, True + ) + await self.async_set_hvac_mode(HVACMode.HEAT) + + async def async_turn_off(self) -> None: + """Turn off the climate system.""" + await self._vehicle.climate_control.set_climate_conditioning( + self._temperature, False + ) + await self.async_set_hvac_mode(HVACMode.OFF) + + async def async_set_temperature(self, **kwargs: Any) -> None: + """Set new target temperature for the vehicle.""" + temperature = kwargs[ATTR_TEMPERATURE] + if temperature is not None: + self._temperature = temperature + self.async_write_ha_state() + + async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: + """Set new target hvac mode.""" + if hvac_mode is not None: + self._attr_hvac_mode = hvac_mode + if hvac_mode == "heat": + await self.async_turn_on() + else: + await self.async_turn_off() + self.async_write_ha_state() + + @cached_property + def current_temperature(self) -> float | None: + """Return the current temperature.""" + LOGGER.warning(self._vehicle.climate.interior_temperature.value) + if self._vehicle.climate.interior_temperature.value is not None: + return self._vehicle.climate.interior_temperature.value + else: + return self._attr_current_temperature.value + + def set_fan_mode(self, fan_mode: str) -> None: + """Set the fan mode.""" + raise NotImplementedError + + def set_humidity(self, humidity: float) -> None: + """Set the humidity level.""" + raise NotImplementedError + + def set_hvac_mode(self, hvac_mode: str) -> None: + """Set the HVAC mode.""" + raise NotImplementedError + + def set_preset_mode(self, preset_mode: str) -> None: + """Set the preset mode.""" + raise NotImplementedError + + def set_swing_mode(self, swing_mode: str) -> None: + """Set the swing mode.""" + raise NotImplementedError + + def set_temperature(self, **kwargs: Any) -> None: + """Set the target temperature.""" + raise NotImplementedError + + def turn_aux_heat_off(self) -> None: + """Turn off the auxiliary heat.""" + raise NotImplementedError + + def turn_aux_heat_on(self) -> None: + """Turn on the auxiliary heat.""" + raise NotImplementedError + + def turn_off(self) -> None: + """Turn off the climate system.""" + raise NotImplementedError + + def turn_on(self) -> None: + """Turn on the climate system.""" + raise NotImplementedError diff --git a/custom_components/smarthashtag/const.py b/custom_components/smarthashtag/const.py index a2827d0..56ab023 100644 --- a/custom_components/smarthashtag/const.py +++ b/custom_components/smarthashtag/const.py @@ -20,7 +20,9 @@ # Platforms SENSOR = "sensor" DEVICE_TRACKER = "device_tracker" -PLATFORMS = [SENSOR, DEVICE_TRACKER] +SWITCH = "switch" +CLIMATE = "climate" +PLATFORMS = [SENSOR, DEVICE_TRACKER, CLIMATE, SWITCH] # Configuration and options @@ -29,6 +31,8 @@ CONF_PASSWORD = "password" CONF_CHARGING_INTERVAL = "charging_interval" CONF_DRIVING_INTERVAL = "driving_interval" +CONF_CONDITIONING_TEMP = "conditioning_temp" +CONF_SEATHEATING_LEVEL = "seatheating_level" # Defaults DEFAULT_NAME = DOMAIN @@ -36,6 +40,8 @@ DEFAULT_CHARGING_INTERVAL = 30 DEFAULT_DRIVING_INTERVAL = 60 MIN_SCAN_INTERVAL = 10 +DEFAULT_CONDITIONING_TEMP = 21 +DEFAULT_SEATHEATING_LEVEL = 3 STARTUP_MESSAGE = f""" diff --git a/custom_components/smarthashtag/manifest.json b/custom_components/smarthashtag/manifest.json index fc955ad..2244c0a 100644 --- a/custom_components/smarthashtag/manifest.json +++ b/custom_components/smarthashtag/manifest.json @@ -7,6 +7,6 @@ "integration_type": "device", "iot_class": "cloud_polling", "issue_tracker": "https://github.com/DasBasti/SmartHashtag/issues", - "requirements": ["pysmarthashtag==0.2.8"], + "requirements": ["pysmarthashtag==0.2.9"], "version": "0.2.5" } diff --git a/custom_components/smarthashtag/sensor.py b/custom_components/smarthashtag/sensor.py index fe42436..34fc602 100644 --- a/custom_components/smarthashtag/sensor.py +++ b/custom_components/smarthashtag/sensor.py @@ -478,6 +478,237 @@ ), ) +ENTITY_CLIMATE_DESCRIPTIONS = ( + SensorEntityDescription( + key="air_blower_active", + translation_key="air_blower_active", + name="Air blower active", + icon="mdi:fan", + ), + SensorEntityDescription( + key="cds_climate_active", + translation_key="cds_climate_active", + name="CDS climate active", + icon="mdi:snowflake", + ), + SensorEntityDescription( + key="climate_over_heat_protection_active", + translation_key="climate_over_heat_protection_active", + name="Climate over heat protection active", + icon="mdi:thermostat", + ), + SensorEntityDescription( + key="curtain_open_status", + translation_key="curtain_open_status", + name="Curtain open status", + icon="mdi:curtains", + ), + SensorEntityDescription( + key="defrosting_active", + translation_key="defrosting_active", + name="Defrosting active", + icon="mdi:snowflake", + ), + SensorEntityDescription( + key="driver_heating_detail", + translation_key="driver_heating_detail", + name="Driver heating detail", + icon="mdi:snowflake", + ), + SensorEntityDescription( + key="driver_heating_status", + translation_key="driver_heating_status", + name="Driver heating status", + icon="mdi:fan", + ), + SensorEntityDescription( + key="driver_ventilation_detail", + translation_key="driver_ventilation_detail", + name="Driver ventilation detail", + icon="mdi:fan", + ), + SensorEntityDescription( + key="driver_ventilation_status", + translation_key="driver_ventilation_status", + name="Driver ventilation status", + icon="mdi:fan", + ), + SensorEntityDescription( + key="exterior_temperature", + translation_key="exterior_temperature", + name="Exterior temperature", + icon="mdi:home-thermometer-outline", + ), + SensorEntityDescription( + key="frag_active", + translation_key="frag_active", + name="FRAG active", + icon="mdi:chat-question-outline", + ), + SensorEntityDescription( + key="interior_temperature", + translation_key="interior_temperature", + name="Interior temperature", + icon="mdi:thermometer", + ), + SensorEntityDescription( + key="passenger_heating_detail", + translation_key="passenger_heating_detail", + name="Passenger heating detail", + icon="mdi:fan", + ), + SensorEntityDescription( + key="passenger_heating_status", + translation_key="passenger_heating_status", + name="Passenger heating status", + icon="mdi:fan", + ), + SensorEntityDescription( + key="passenger_ventilation_detail", + translation_key="passenger_ventilation_detail", + name="Passenger ventilation detail", + icon="mdi:fan", + ), + SensorEntityDescription( + key="passenger_ventilation_status", + translation_key="passenger_ventilation_status", + name="Passenger ventilation status", + icon="mdi:fan", + ), + SensorEntityDescription( + key="pre_climate_active", + translation_key="pre_climate_active", + name="Pre climate active", + icon="mdi:heat-pump-outline", + ), + SensorEntityDescription( + key="rear_left_heating_detail", + translation_key="rear_left_heating_detail", + name="Rear left heating detail", + icon="mdi:heat-pump-outline", + ), + SensorEntityDescription( + key="rear_left_heating_status", + translation_key="rear_left_heating_status", + name="Rear left heating status", + icon="mdi:heat-pump-outline", + ), + SensorEntityDescription( + key="rear_left_ventilation_detail", + translation_key="rear_left_ventilation_detail", + name="Rear left ventilation detail", + icon="mdi:fan", + ), + SensorEntityDescription( + key="rear_left_ventilation_status", + translation_key="rear_left_ventilation_status", + name="Rear left ventilation status", + icon="mdi:fan", + ), + SensorEntityDescription( + key="rear_right_heating_detail", + translation_key="rear_right_heating_detail", + name="Rear right heating detail", + icon="mdi:heat-pump-outline", + ), + SensorEntityDescription( + key="rear_right_heating_status", + translation_key="rear_right_heating_status", + name="Rear right heating status", + icon="mdi:heat-pump-outline", + ), + SensorEntityDescription( + key="rear_right_ventilation_detail", + translation_key="rear_right_ventilation_detail", + name="Rear right ventilation detail", + icon="mdi:fan", + ), + SensorEntityDescription( + key="rear_right_ventilation_status", + translation_key="rear_right_ventilation_status", + name="Rear right ventilation status", + icon="mdi:fan", + ), + SensorEntityDescription( + key="steering_wheel_heating_status", + translation_key="steering_wheel_heating_status", + name="Steering wheel heating status", + icon="mdi:steering", + ), + SensorEntityDescription( + key="sun_curtain_rear_open_status", + translation_key="sun_curtain_rear_open_status", + name="Sun curtain rear open status", + icon="mdi:curtains", + ), + SensorEntityDescription( + key="sun_curtain_rear_position", + translation_key="sun_curtain_rear_position", + name="Sun curtain rear position", + icon="mdi:curtains", + ), + SensorEntityDescription( + key="sunroof_open_status", + translation_key="sunroof_open_status", + name="Sunroof open status", + icon="mdi:window-shutter", + ), + SensorEntityDescription( + key="sunroof_position", + translation_key="sunroof_position", + name="Sunroof position", + icon="mdi:window-shutter", + ), + SensorEntityDescription( + key="window_driver_position", + translation_key="window_driver_position", + name="Window driver position", + icon="mdi:window-open", + ), + SensorEntityDescription( + key="window_driver_rear_position", + translation_key="window_driver_rear_position", + name="Window driver rear position", + icon="mdi:window-open", + ), + SensorEntityDescription( + key="window_passenger_position", + translation_key="window_passenger_position", + name="Window passenger position", + icon="mdi:window-open", + ), + SensorEntityDescription( + key="window_passenger_rear_position", + translation_key="window_passenger_rear_position", + name="Window passenger rear position", + icon="mdi:window-open", + ), + SensorEntityDescription( + key="window_driver_status", + translation_key="window_driver_status", + name="Window driver status", + icon="mdi:window-open", + ), + SensorEntityDescription( + key="window_driver_rear_status", + translation_key="window_driver_rear_status", + name="Window driver rear status", + icon="mdi:window-open", + ), + SensorEntityDescription( + key="window_passenger_status", + translation_key="window_passenger_status", + name="Window passenger status", + icon="mdi:window-open", + ), + SensorEntityDescription( + key="window_passenger_rear_status", + translation_key="window_passenger_rear_status", + name="Window passenger rear status", + icon="mdi:window-open", + ), +) + def remove_vin_from_key(key: str) -> str: """Remove the vin from the key.""" @@ -544,6 +775,16 @@ async def async_setup_entry(hass, entry, async_add_devices): for entity_description in ENTITY_RUNNING_DESCRIPTIONS ) + async_add_devices( + SmartHashtagClimateSensor( + coordinator=coordinator, + entity_description=dataclasses.replace( + entity_description, key=f"{vehicle}_{entity_description.key}" + ), + ) + for entity_description in ENTITY_CLIMATE_DESCRIPTIONS + ) + class SmartHashtagBatteryRangeSensor(SmartHashtagEntity, SensorEntity): """Battery Sensor class.""" @@ -703,7 +944,7 @@ def native_unit_of_measurement(self) -> str: key = remove_vin_from_key(self.entity_description.key) vin = vin_from_key(self.entity_description.key) if key.startswith("service"): - key = key.split("_")[-1] + key = key.rsplit("_", maxsplit=1)[-1] data = self.coordinator.account.vehicles.get(vin).service[key] else: data = getattr( @@ -793,3 +1034,43 @@ def native_unit_of_measurement(self) -> str: if isinstance(data, ValueWithUnit): return data.unit return self.entity_description.native_unit_of_measurement + + +class SmartHashtagClimateSensor(SmartHashtagEntity, SensorEntity): + """Tire Status class.""" + + def __init__( + self, + coordinator: SmartHashtagDataUpdateCoordinator, + entity_description: SensorEntityDescription, + ) -> None: + """Initialize the sensor class.""" + super().__init__(coordinator) + self._attr_unique_id = f"{self._attr_unique_id}_{entity_description.key}" + self.entity_description = entity_description + + @property + def native_value(self) -> float | int | str | None: + """Return the native value of the sensor.""" + key = remove_vin_from_key(self.entity_description.key) + vin = vin_from_key(self.entity_description.key) + data = getattr( + self.coordinator.account.vehicles.get(vin).climate, + key, + ) + if isinstance(data, ValueWithUnit): + return data.value + return data + + @property + def native_unit_of_measurement(self) -> str: + """Return the unit of measurement of the sensor.""" + key = remove_vin_from_key(self.entity_description.key) + vin = vin_from_key(self.entity_description.key) + data = getattr( + self.coordinator.account.vehicles.get(vin).climate, + key, + ) + if isinstance(data, ValueWithUnit): + return data.unit + return self.entity_description.native_unit_of_measurement diff --git a/custom_components/smarthashtag/translations/de.json b/custom_components/smarthashtag/translations/de.json index 60f4eb3..d0a86cb 100644 --- a/custom_components/smarthashtag/translations/de.json +++ b/custom_components/smarthashtag/translations/de.json @@ -322,6 +322,120 @@ "engine_off": "Aus", "engine_running": "An" } + }, + "air_blower_active": { + "name": "Lüfter aktiv" + }, + "cds_climate_active": { + "name": "CDS Klima aktiv" + }, + "climate_over_heat_protection_active": { + "name": "Überhitzungsschutz aktiv" + }, + "curtain_open_status": { + "name": "Rollo offen" + }, + "defrosting_active": { + "name": "Enteisung aktiv" + }, + "driver_heating_detail": { + "name": "Heizung Fahrerseite Detail" + }, + "driver_heating_status": { + "name": "Heizung Fahrerseite Status" + }, + "driver_ventilation_detail": { + "name": "Lüftung Fahrerseite Detail" + }, + "driver_ventilation_status": { + "name": "Lüftung Fahrerseite Status" + }, + "exterior_temperature": { + "name": "Außentemperatur" + }, + "frag_active": { + "name": "FRAG aktiv" + }, + "interior_temperature": { + "name": "Innentemperatur" + }, + "passenger_heating_detail": { + "name": "Heizung Beifahrerseite Detail" + }, + "passenger_heating_status": { + "name": "Heizung Beifahrerseite Status" + }, + "passenger_ventilation_detail": { + "name": "Lüftung Beifahrerseite Detail" + }, + "passenger_ventilation_status": { + "name": "Lüftung Beifahrerseite Status" + }, + "pre_climate_active": { + "name": "Vorklimatisierung aktiv" + }, + "rear_left_heating_detail": { + "name": "Heizung hinten links Detail" + }, + "rear_left_heating_status": { + "name": "Heizung hinten links Status" + }, + "rear_left_ventilation_detail": { + "name": "Lüftung hinten links Detail" + }, + "rear_left_ventilation_status": { + "name": "Lüftung hinten links Status" + }, + "rear_right_heating_detail": { + "name": "Heizung hinten rechts Detail" + }, + "rear_right_heating_status": { + "name": "Heizung hinten rechts Status" + }, + "rear_right_ventilation_detail": { + "name": "Lüftung hinten rechts Detail" + }, + "rear_right_ventilation_status": { + "name": "Lüftung hinten rechts Status" + }, + "steering_wheel_heating_status": { + "name": "Heizung Lenkrad Status" + }, + "sun_curtain_rear_open_status": { + "name": "Sonnenrollo hinten offen" + }, + "sun_curtain_rear_position": { + "name": "Sonnenrollo hinten Position" + }, + "sunroof_open_status": { + "name": "Schiebedach offen" + }, + "sunroof_position": { + "name": "Schiebedach Position" + }, + "window_driver_position": { + "name": "Fenster Fahrerseite Position" + }, + "window_driver_rear_position": { + "name": "Fenster Fahrerseite hinten Position" + }, + "window_passenger_position": { + "name": "Fenster Beifahrerseite Position" + }, + "window_passenger_rear_position": { + "name": "Fenster Beifahrerseite hinten Position" + }, + "window_driver_status": { + "name": "Fenster Fahrerseite Status" + }, + "window_driver_rear_status": { + "name": "Fenster Fahrerseite hinten Status" + }, + "window_passenger_status": { + "name": "Fenster Beifahrerseite Status" + }, + "window_passenger_rear_status": { + "name": "Fenster Beifahrerseite hinten Status" } } } diff --git a/custom_components/smarthashtag/translations/en.json b/custom_components/smarthashtag/translations/en.json index 3e16c38..491724a 100644 --- a/custom_components/smarthashtag/translations/en.json +++ b/custom_components/smarthashtag/translations/en.json @@ -322,6 +322,120 @@ "engine_off": "Off", "engine_running": "On" } + }, + "air_blower_active": { + "name": "Air blower active" + }, + "cds_climate_active": { + "name": "CDS climate active" + }, + "climate_over_heat_protection_active": { + "name": "Climate over heat protection active" + }, + "curtain_open_status": { + "name": "Curtain open status" + }, + "defrosting_active": { + "name": "Defrosting active" + }, + "driver_heating_detail": { + "name": "Driver heating detail" + }, + "driver_heating_status": { + "name": "Driver heating status" + }, + "driver_ventilation_detail": { + "name": "Driver ventilation detail" + }, + "driver_ventilation_status": { + "name": "Driver ventilation status" + }, + "exterior_temperature": { + "name": "Exterior temperature" + }, + "frag_active": { + "name": "FRAG active" + }, + "interior_temperature": { + "name": "Interior temperature" + }, + "passenger_heating_detail": { + "name": "Passenger heating detail" + }, + "passenger_heating_status": { + "name": "Passenger heating status" + }, + "passenger_ventilation_detail": { + "name": "Passenger ventilation detail" + }, + "passenger_ventilation_status": { + "name": "Passenger ventilation status" + }, + "pre_climate_active": { + "name": "Pre climate active" + }, + "rear_left_heating_detail": { + "name": "Rear left heating detail" + }, + "rear_left_heating_status": { + "name": "Rear left heating status" + }, + "rear_left_ventilation_detail": { + "name": "Rear left ventilation detail" + }, + "rear_left_ventilation_status": { + "name": "Rear left ventilation status" + }, + "rear_right_heating_detail": { + "name": "Rear right heating detail" + }, + "rear_right_heating_status": { + "name": "Rear right heating status" + }, + "rear_right_ventilation_detail": { + "name": "Rear right ventilation detail" + }, + "rear_right_ventilation_status": { + "name": "Rear right ventilation status" + }, + "steering_wheel_heating_status": { + "name": "Steering wheel heating status" + }, + "sun_curtain_rear_open_status": { + "name": "Sun curtain rear open status" + }, + "sun_curtain_rear_position": { + "name": "Sun curtain rear position" + }, + "sunroof_open_status": { + "name": "Sunroof open status" + }, + "sunroof_position": { + "name": "Sunroof position" + }, + "window_driver_position": { + "name": "Window driver position" + }, + "window_driver_rear_position": { + "name": "Window driver rear position" + }, + "window_passenger_position": { + "name": "Window passenger position" + }, + "window_passenger_rear_position": { + "name": "Window passenger rear position" + }, + "window_driver_status": { + "name": "Window driver status" + }, + "window_driver_rear_status": { + "name": "Window driver rear status" + }, + "window_passenger_status": { + "name": "Window passenger status" + }, + "window_passenger_rear_status": { + "name": "Window passenger rear status" } } }