Skip to content

Commit

Permalink
Merge branch 'master' into patch
Browse files Browse the repository at this point in the history
  • Loading branch information
Kolbi authored Apr 2, 2024
2 parents d0e1a8e + 87d68ca commit e5dd4b1
Show file tree
Hide file tree
Showing 12 changed files with 507 additions and 49 deletions.
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ ci:
autoupdate_commit_msg: "chore: pre-commit autoupdate"
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.3.3
rev: v0.3.5
hooks:
- id: ruff
args:
Expand All @@ -28,7 +28,7 @@ repos:
- id: check-json
exclude: (.vscode|.devcontainer)
- repo: https://github.com/asottile/pyupgrade
rev: v3.15.1
rev: v3.15.2
hooks:
- id: pyupgrade
- repo: https://github.com/adrienverge/yamllint.git
Expand Down
50 changes: 50 additions & 0 deletions custom_components/audiconnect/audi_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@
DOMAIN,
CONF_VIN,
CONF_ACTION,
CONF_CLIMATE_TEMP_F,
CONF_CLIMATE_TEMP_C,
CONF_CLIMATE_GLASS,
CONF_CLIMATE_SEAT_FL,
CONF_CLIMATE_SEAT_FR,
CONF_CLIMATE_SEAT_RL,
CONF_CLIMATE_SEAT_RR,
CONF_REGION,
CONF_SPIN,
SIGNAL_STATE_UPDATED,
Expand All @@ -44,6 +51,20 @@
{vol.Required(CONF_VIN): cv.string, vol.Required(CONF_ACTION): cv.string}
)

SERVICE_START_CLIMATE_CONTROL = "start_climate_control"
SERVICE_START_CLIMATE_CONTROL_SCHEMA = vol.Schema(
{
vol.Required(CONF_VIN): cv.string,
vol.Optional(CONF_CLIMATE_TEMP_F): cv.positive_int,
vol.Optional(CONF_CLIMATE_TEMP_C): cv.positive_int,
vol.Optional(CONF_CLIMATE_GLASS): cv.boolean,
vol.Optional(CONF_CLIMATE_SEAT_FL): cv.boolean,
vol.Optional(CONF_CLIMATE_SEAT_FR): cv.boolean,
vol.Optional(CONF_CLIMATE_SEAT_RL): cv.boolean,
vol.Optional(CONF_CLIMATE_SEAT_RR): cv.boolean,
}
)

_LOGGER = logging.getLogger(__name__)


Expand Down Expand Up @@ -79,6 +100,12 @@ def init_connection(self):
self.execute_vehicle_action,
schema=SERVICE_EXECUTE_VEHICLE_ACTION_SCHEMA,
)
self.hass.services.async_register(
DOMAIN,
SERVICE_START_CLIMATE_CONTROL,
self.start_climate_control,
schema=SERVICE_START_CLIMATE_CONTROL_SCHEMA,
)

self.connection.add_observer(self)

Expand Down Expand Up @@ -196,6 +223,29 @@ async def execute_vehicle_action(self, service):
if action == "stop_window_heating":
await self.connection.set_vehicle_window_heating(vin, False)

async def start_climate_control(self, service):
_LOGGER.info("Initiating Start Climate Control Service...")
vin = service.data.get(CONF_VIN).lower()
# Optional Parameters
temp_f = service.data.get(CONF_CLIMATE_TEMP_F, None)
temp_c = service.data.get(CONF_CLIMATE_TEMP_C, None)
glass_heating = service.data.get(CONF_CLIMATE_GLASS, False)
seat_fl = service.data.get(CONF_CLIMATE_SEAT_FL, False)
seat_fr = service.data.get(CONF_CLIMATE_SEAT_FR, False)
seat_rl = service.data.get(CONF_CLIMATE_SEAT_RL, False)
seat_rr = service.data.get(CONF_CLIMATE_SEAT_RR, False)

await self.connection.start_climate_control(
vin,
temp_f,
temp_c,
glass_heating,
seat_fl,
seat_fr,
seat_rl,
seat_rr,
)

async def handle_notification(self, vin: str, action: str) -> None:
await self._refresh_vehicle_data(vin)

Expand Down
46 changes: 46 additions & 0 deletions custom_components/audiconnect/audi_connect_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,52 @@ async def set_vehicle_climatisation(self, vin: str, activate: bool):
),
)

async def start_climate_control(
self,
vin: str,
temp_f: int,
temp_c: int,
glass_heating: bool,
seat_fl: bool,
seat_fr: bool,
seat_rl: bool,
seat_rr: bool,
):
if not self._loggedin:
await self.login()

if not self._loggedin:
return False

try:
_LOGGER.debug(
f"Sending command to start climate control for vehicle {vin} with settings - Temp(F): {temp_f}, Temp(C): {temp_c}, Glass Heating: {glass_heating}, Seat FL: {seat_fl}, Seat FR: {seat_fr}, Seat RL: {seat_rl}, Seat RR: {seat_rr}"
)

await self._audi_service.start_climate_control(
vin,
temp_f,
temp_c,
glass_heating,
seat_fl,
seat_fr,
seat_rl,
seat_rr,
)

_LOGGER.debug(f"Successfully started climate control of vehicle {vin}")

await self.notify(vin, ACTION_CLIMATISATION)

return True

except Exception as exception:
_LOGGER.error(
f"Unable to start climate control of vehicle {vin}. Error: {exception}",
exc_info=True,
)
return False

async def set_battery_charger(self, vin: str, activate: bool, timer: bool):
if not self._loggedin:
await self.login()
Expand Down
75 changes: 75 additions & 0 deletions custom_components/audiconnect/audi_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,81 @@ async def set_climatisation(self, vin: str, start: bool):
"action.actionState",
)

async def start_climate_control(
self,
vin: str,
temp_f: int,
temp_c: int,
glass_heating: bool,
seat_fl: bool,
seat_fr: bool,
seat_rl: bool,
seat_rr: bool,
):
target_temperature = None
if temp_f is not None:
target_temperature = int(((temp_f - 32) * (5 / 9)) * 10 + 2731)
elif temp_c is not None:
target_temperature = int(temp_c * 10 + 2731)

# Default Temp
target_temperature = target_temperature or 2941

# Construct Zone Settings
zone_settings = [
{"value": {"isEnabled": seat_fl, "position": "frontLeft"}},
{"value": {"isEnabled": seat_fr, "position": "frontRight"}},
{"value": {"isEnabled": seat_rl, "position": "rearLeft"}},
{"value": {"isEnabled": seat_rr, "position": "rearRight"}},
]

data = {
"action": {
"type": "startClimatisation",
"settings": {
"targetTemperature": target_temperature,
"climatisationWithoutHVpower": True,
"heaterSource": "electric",
"climaterElementSettings": {
"isClimatisationAtUnlock": False,
"isMirrorHeatingEnabled": glass_heating,
"zoneSettings": {"zoneSetting": zone_settings},
},
},
}
}

data = json.dumps(data)

headers = self._get_vehicle_action_header("application/json", None)
res = await self._api.request(
"POST",
"{homeRegion}/fs-car/bs/climatisation/v1/{type}/{country}/vehicles/{vin}/climater/actions".format(
homeRegion=await self._get_home_region(vin.upper()),
type=self._type,
country=self._country,
vin=vin.upper(),
),
headers=headers,
data=data,
)

checkUrl = "{homeRegion}/fs-car/bs/climatisation/v1/{type}/{country}/vehicles/{vin}/climater/actions/{actionid}".format(
homeRegion=await self._get_home_region(vin.upper()),
type=self._type,
country=self._country,
vin=vin.upper(),
actionid=res["action"]["actionId"],
)

await self.check_request_succeeded(
checkUrl,
"start climatisation",
SUCCEEDED,
FAILED,
"action.actionState",
)

async def set_window_heating(self, vin: str, start: bool):
data = '<?xml version="1.0" encoding= "UTF-8" ?><action><type>{action}</type></action>'.format(
action="startWindowHeating" if start else "stopWindowHeating"
Expand Down
10 changes: 5 additions & 5 deletions custom_components/audiconnect/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from homeassistant.core import callback

from .audi_connect_account import AudiConnectAccount
from .const import DOMAIN, CONF_SPIN, DEFAULT_UPDATE_INTERVAL
from .const import DOMAIN, CONF_SPIN, DEFAULT_UPDATE_INTERVAL, MIN_UPDATE_INTERVAL

_LOGGER = logging.getLogger(__name__)

Expand All @@ -35,7 +35,7 @@ def __init__(self):
self._password = vol.UNDEFINED
self._spin = vol.UNDEFINED
self._region = vol.UNDEFINED
self._scan_interval = 10
self._scan_interval = DEFAULT_UPDATE_INTERVAL

async def async_step_user(self, user_input=None):
"""Handle a user initiated config flow."""
Expand Down Expand Up @@ -111,13 +111,13 @@ async def async_step_import(self, user_input):
if user_input.get(CONF_REGION):
region = user_input.get(CONF_REGION)

scan_interval = 10
scan_interval = DEFAULT_UPDATE_INTERVAL

if user_input.get(CONF_SCAN_INTERVAL):
scan_interval = user_input[CONF_SCAN_INTERVAL]

if scan_interval < 5:
scan_interval = 5
if scan_interval < MIN_UPDATE_INTERVAL:
scan_interval = MIN_UPDATE_INTERVAL

try:
session = async_get_clientsession(self.hass)
Expand Down
11 changes: 9 additions & 2 deletions custom_components/audiconnect/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,16 @@
CONF_VIN = "vin"
CONF_CARNAME = "carname"
CONF_ACTION = "action"
CONF_CLIMATE_TEMP_F = "temp_f"
CONF_CLIMATE_TEMP_C = "temp_c"
CONF_CLIMATE_GLASS = "glass_heating"
CONF_CLIMATE_SEAT_FL = "seat_fl"
CONF_CLIMATE_SEAT_FR = "seat_fr"
CONF_CLIMATE_SEAT_RL = "seat_rl"
CONF_CLIMATE_SEAT_RR = "seat_rr"

MIN_UPDATE_INTERVAL = 5
DEFAULT_UPDATE_INTERVAL = 10
MIN_UPDATE_INTERVAL = 15
DEFAULT_UPDATE_INTERVAL = 15

CONF_SPIN = "spin"
CONF_REGION = "region"
Expand Down
2 changes: 1 addition & 1 deletion custom_components/audiconnect/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
"issue_tracker": "https://github.com/arjenvrh/audi_connect_ha/issues",
"loggers": ["audiconnect"],
"requirements": ["beautifulsoup4"],
"version": "1.0.1"
"version": "1.0.3"
}
36 changes: 35 additions & 1 deletion custom_components/audiconnect/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ execute_vehicle_action:
integration: audiconnect
action:
required: true
example: "lock"
selector:
select:
translation_key: select_mode
translation_key: vehicle_actions
options:
- lock
- unlock
Expand All @@ -32,3 +33,36 @@ execute_vehicle_action:
- stop_preheater
- start_window_heating
- stop_window_heating

start_climate_control:
fields:
vin:
required: true
selector:
device:
integration: audiconnect
temp_f:
selector:
number:
min: 59
max: 85
temp_c:
selector:
number:
min: 15
max: 30
glass_heating:
selector:
boolean:
seat_fl:
selector:
boolean:
seat_fr:
selector:
boolean:
seat_rl:
selector:
boolean:
seat_rr:
selector:
boolean:
Loading

0 comments on commit e5dd4b1

Please sign in to comment.