-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #15 from ad-ha/ad-ha-0.4.1
0.4.1
- Loading branch information
Showing
13 changed files
with
825 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
from homeassistant.components.button import ButtonEntity | ||
from homeassistant.helpers.update_coordinator import CoordinatorEntity | ||
from .const import DOMAIN, LOGGER | ||
import asyncio | ||
|
||
|
||
async def async_setup_entry(hass, entry, async_add_entities): | ||
"""Set up MG SAIC buttons.""" | ||
coordinator = hass.data[DOMAIN][f"{entry.entry_id}_coordinator"] | ||
client = hass.data[DOMAIN][entry.entry_id] | ||
vin_info = coordinator.data["info"][0] | ||
vin = vin_info.vin | ||
|
||
buttons = [ | ||
SAICMGOpenTailgateButton(coordinator, client, vin_info, vin), | ||
SAICMGTriggerAlarmButton(coordinator, client, vin_info, vin), | ||
SAICMGStartFrontDefrostButton(coordinator, client, vin_info, vin), | ||
SAICMGControlRearWindowHeatButton(coordinator, client, vin_info, vin), | ||
] | ||
|
||
async_add_entities(buttons) | ||
|
||
|
||
class SAICMGButton(CoordinatorEntity, ButtonEntity): | ||
"""Base class for MG SAIC buttons.""" | ||
|
||
def __init__(self, coordinator, client, vin_info, vin, name, icon): | ||
"""Initialize the button.""" | ||
super().__init__(coordinator) | ||
self._client = client | ||
self._vin = vin | ||
self._vin_info = vin_info | ||
self._attr_name = f"{vin_info.brandName} {vin_info.modelName} {name}" | ||
self._attr_unique_id = f"{vin}_{name.replace(' ', '_').lower()}_button" | ||
self._attr_icon = icon | ||
|
||
@property | ||
def device_info(self): | ||
"""Return device info.""" | ||
return { | ||
"identifiers": {(DOMAIN, self._vin)}, | ||
"name": f"{self._vin_info.brandName} {self._vin_info.modelName}", | ||
"manufacturer": self._vin_info.brandName, | ||
"model": self._vin_info.modelName, | ||
"serial_number": self._vin, | ||
} | ||
|
||
async def schedule_data_refresh(self): | ||
"""Schedule a data refresh for the coordinator associated with the VIN.""" | ||
coordinators_by_vin = self.hass.data[DOMAIN].get("coordinators_by_vin", {}) | ||
coordinator = coordinators_by_vin.get(self._vin) | ||
if coordinator: | ||
|
||
async def delayed_refresh(): | ||
await asyncio.sleep(15) # Wait for 15 seconds | ||
await coordinator.async_request_refresh() | ||
|
||
self.hass.async_create_task(delayed_refresh()) | ||
else: | ||
LOGGER.warning("Coordinator not found for VIN %s", self._vin) | ||
|
||
|
||
class SAICMGOpenTailgateButton(SAICMGButton): | ||
"""Button to open the tailgate.""" | ||
|
||
def __init__(self, coordinator, client, vin_info, vin): | ||
super().__init__( | ||
coordinator, client, vin_info, vin, "Open Tailgate", "mdi:car-back" | ||
) | ||
|
||
async def async_press(self): | ||
"""Handle the button press.""" | ||
try: | ||
await self._client.open_tailgate(self._vin) | ||
LOGGER.info("Tailgate opened for VIN: %s", self._vin) | ||
await self.schedule_data_refresh() | ||
except Exception as e: | ||
LOGGER.error("Error opening tailgate for VIN %s: %s", self._vin, e) | ||
|
||
|
||
class SAICMGTriggerAlarmButton(SAICMGButton): | ||
"""Button to trigger the vehicle alarm.""" | ||
|
||
def __init__(self, coordinator, client, vin_info, vin): | ||
super().__init__( | ||
coordinator, client, vin_info, vin, "Trigger Alarm", "mdi:alarm-light" | ||
) | ||
|
||
async def async_press(self): | ||
"""Handle the button press.""" | ||
try: | ||
await self._client.trigger_alarm(self._vin) | ||
LOGGER.info("Alarm triggered for VIN: %s", self._vin) | ||
await self.schedule_data_refresh() | ||
except Exception as e: | ||
LOGGER.error("Error triggering alarm for VIN %s: %s", self._vin, e) | ||
|
||
|
||
class SAICMGStartFrontDefrostButton(SAICMGButton): | ||
"""Button to start front defrost.""" | ||
|
||
def __init__(self, coordinator, client, vin_info, vin): | ||
super().__init__( | ||
coordinator, | ||
client, | ||
vin_info, | ||
vin, | ||
"Start Front Defrost", | ||
"mdi:car-defrost-front", | ||
) | ||
|
||
async def async_press(self): | ||
"""Handle the button press.""" | ||
try: | ||
await self._client.start_front_defrost(self._vin) | ||
LOGGER.info("Front defrost started for VIN: %s", self._vin) | ||
await self.schedule_data_refresh() | ||
except Exception as e: | ||
LOGGER.error("Error starting front defrost for VIN %s: %s", self._vin, e) | ||
|
||
|
||
class SAICMGControlRearWindowHeatButton(SAICMGButton): | ||
"""Button to activate rear window heat.""" | ||
|
||
def __init__(self, coordinator, client, vin_info, vin): | ||
super().__init__( | ||
coordinator, | ||
client, | ||
vin_info, | ||
vin, | ||
"Activate Rear Window Heat", | ||
"mdi:car-defrost-rear", | ||
) | ||
|
||
async def async_press(self): | ||
"""Handle the button press.""" | ||
try: | ||
await self._client.control_rear_window_heat(self._vin, True) | ||
LOGGER.info("Rear window heat activated for VIN: %s", self._vin) | ||
await self.schedule_data_refresh() | ||
except Exception as e: | ||
LOGGER.error( | ||
"Error activating rear window heat for VIN %s: %s", self._vin, e | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
from homeassistant.components.climate import ( | ||
ClimateEntity, | ||
ClimateEntityFeature, | ||
HVACMode, | ||
) | ||
from homeassistant.components.climate.const import ( | ||
FAN_LOW, | ||
FAN_MEDIUM, | ||
FAN_HIGH, | ||
) | ||
from homeassistant.const import ( | ||
UnitOfTemperature, | ||
ATTR_TEMPERATURE, | ||
) | ||
from homeassistant.helpers.update_coordinator import CoordinatorEntity | ||
from .const import DOMAIN, LOGGER | ||
|
||
|
||
async def async_setup_entry(hass, entry, async_add_entities): | ||
"""Set up the MG SAIC climate entity.""" | ||
coordinator = hass.data[DOMAIN][f"{entry.entry_id}_coordinator"] | ||
client = hass.data[DOMAIN][entry.entry_id] | ||
vin_info = coordinator.data["info"][0] | ||
vin = vin_info.vin | ||
|
||
climate_entity = SAICMGClimateEntity(coordinator, client, vin_info, vin) | ||
async_add_entities([climate_entity]) | ||
|
||
|
||
class SAICMGClimateEntity(CoordinatorEntity, ClimateEntity): | ||
"""Representation of the vehicle's climate control.""" | ||
|
||
def __init__(self, coordinator, client, vin_info, vin): | ||
"""Initialize the climate entity.""" | ||
super().__init__(coordinator) | ||
self._client = client | ||
self._vin = vin | ||
self._vin_info = vin_info | ||
|
||
self._attr_name = f"{vin_info.brandName} {vin_info.modelName} Climate" | ||
self._attr_unique_id = f"{vin}_climate" | ||
self._attr_temperature_unit = UnitOfTemperature.CELSIUS | ||
self._attr_supported_features = ( | ||
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE | ||
) | ||
self._attr_hvac_modes = [HVACMode.OFF, HVACMode.COOL] | ||
self._attr_fan_modes = [FAN_LOW, FAN_MEDIUM, FAN_HIGH] | ||
|
||
# Initialize with default values | ||
self._attr_current_temperature = None | ||
self._attr_target_temperature = 22.0 # Default target temperature | ||
self._attr_fan_mode = FAN_MEDIUM | ||
self._attr_hvac_mode = HVACMode.OFF | ||
|
||
@property | ||
def device_info(self): | ||
"""Return device info.""" | ||
return { | ||
"identifiers": {(DOMAIN, self._vin)}, | ||
"name": f"{self._vin_info.brandName} {self._vin_info.modelName}", | ||
"manufacturer": self._vin_info.brandName, | ||
"model": self._vin_info.modelName, | ||
"serial_number": self._vin, | ||
} | ||
|
||
@property | ||
def current_temperature(self): | ||
"""Return the current interior temperature.""" | ||
status = self.coordinator.data.get("status") | ||
if status: | ||
interior_temp = getattr( | ||
status.basicVehicleStatus, "interiorTemperature", None | ||
) | ||
if interior_temp is not None and interior_temp != -128: | ||
return interior_temp # Adjust if necessary | ||
return None | ||
|
||
@property | ||
def hvac_mode(self): | ||
"""Return the current HVAC mode.""" | ||
status = self.coordinator.data.get("status") | ||
if status: | ||
ac_status = getattr(status.basicVehicleStatus, "remoteClimateStatus", None) | ||
if ac_status == 1: | ||
return HVACMode.COOL | ||
return HVACMode.OFF | ||
|
||
async def async_set_hvac_mode(self, hvac_mode): | ||
"""Set the HVAC mode.""" | ||
if hvac_mode == HVACMode.OFF: | ||
await self._client.stop_ac(self._vin) | ||
self._attr_hvac_mode = HVACMode.OFF | ||
elif hvac_mode == HVACMode.COOL: | ||
await self._client.start_climate( | ||
self._vin, self._attr_target_temperature, self._fan_speed_to_int() | ||
) | ||
self._attr_hvac_mode = HVACMode.COOL | ||
else: | ||
LOGGER.warning("Unsupported HVAC mode: %s", hvac_mode) | ||
# Schedule data refresh | ||
await self.coordinator.async_request_refresh() | ||
|
||
async def async_set_temperature(self, **kwargs): | ||
"""Set new target temperature.""" | ||
temperature = kwargs.get(ATTR_TEMPERATURE) | ||
if temperature is not None: | ||
self._attr_target_temperature = temperature | ||
if self.hvac_mode != HVACMode.OFF: | ||
await self._client.start_climate( | ||
self._vin, temperature, self._fan_speed_to_int() | ||
) | ||
# Schedule data refresh | ||
await self.coordinator.async_request_refresh() | ||
|
||
@property | ||
def fan_mode(self): | ||
"""Return the current fan mode.""" | ||
return self._attr_fan_mode | ||
|
||
async def async_set_fan_mode(self, fan_mode): | ||
"""Set the fan mode.""" | ||
if fan_mode in self._attr_fan_modes: | ||
self._attr_fan_mode = fan_mode | ||
if self.hvac_mode != HVACMode.OFF: | ||
await self._client.start_climate( | ||
self._vin, self._attr_target_temperature, self._fan_speed_to_int() | ||
) | ||
# Schedule data refresh | ||
await self.coordinator.async_request_refresh() | ||
else: | ||
LOGGER.warning("Unsupported fan mode: %s", fan_mode) | ||
|
||
def _fan_speed_to_int(self): | ||
"""Convert fan mode to integer value expected by the API.""" | ||
mapping = { | ||
FAN_LOW: 1, | ||
FAN_MEDIUM: 3, | ||
FAN_HIGH: 5, | ||
} | ||
fan_speed = mapping.get(self._attr_fan_mode) | ||
if fan_speed is None: | ||
raise ValueError("Invalid fan mode.") | ||
return fan_speed | ||
|
||
@property | ||
def min_temp(self): | ||
"""Return the minimum temperature.""" | ||
return 16.0 | ||
|
||
@property | ||
def max_temp(self): | ||
"""Return the maximum temperature.""" | ||
return 30.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.