Add charging and preconditioning actions to Teslemetry (#144184)

This commit is contained in:
Brett Adams
2025-08-11 19:59:39 +10:00
committed by GitHub
parent 23e6148d3b
commit 203c908730
5 changed files with 616 additions and 49 deletions

View File

@@ -773,6 +773,18 @@
}, },
"time_of_use": { "time_of_use": {
"service": "mdi:clock-time-eight-outline" "service": "mdi:clock-time-eight-outline"
},
"add_charge_schedule": {
"service": "mdi:calendar-plus"
},
"remove_charge_schedule": {
"service": "mdi:calendar-minus"
},
"add_precondition_schedule": {
"service": "mdi:hvac-outline"
},
"remove_precondition_schedule": {
"service": "mdi:hvac-off-outline"
} }
} }
} }

View File

@@ -22,6 +22,7 @@ ATTR_ID = "id"
ATTR_GPS = "gps" ATTR_GPS = "gps"
ATTR_TYPE = "type" ATTR_TYPE = "type"
ATTR_VALUE = "value" ATTR_VALUE = "value"
ATTR_LOCATION = "location"
ATTR_LOCALE = "locale" ATTR_LOCALE = "locale"
ATTR_ORDER = "order" ATTR_ORDER = "order"
ATTR_TIMESTAMP = "timestamp" ATTR_TIMESTAMP = "timestamp"
@@ -36,6 +37,12 @@ ATTR_DEPARTURE_TIME = "departure_time"
ATTR_OFF_PEAK_CHARGING_ENABLED = "off_peak_charging_enabled" ATTR_OFF_PEAK_CHARGING_ENABLED = "off_peak_charging_enabled"
ATTR_OFF_PEAK_CHARGING_WEEKDAYS = "off_peak_charging_weekdays_only" ATTR_OFF_PEAK_CHARGING_WEEKDAYS = "off_peak_charging_weekdays_only"
ATTR_END_OFF_PEAK_TIME = "end_off_peak_time" ATTR_END_OFF_PEAK_TIME = "end_off_peak_time"
ATTR_DAYS_OF_WEEK = "days_of_week"
ATTR_START_TIME = "start_time"
ATTR_END_TIME = "end_time"
ATTR_ONE_TIME = "one_time"
ATTR_NAME = "name"
ATTR_PRECONDITION_TIME = "precondition_time"
# Services # Services
SERVICE_NAVIGATE_ATTR_GPS_REQUEST = "navigation_gps_request" SERVICE_NAVIGATE_ATTR_GPS_REQUEST = "navigation_gps_request"
@@ -44,6 +51,10 @@ SERVICE_SET_SCHEDULED_DEPARTURE = "set_scheduled_departure"
SERVICE_VALET_MODE = "valet_mode" SERVICE_VALET_MODE = "valet_mode"
SERVICE_SPEED_LIMIT = "speed_limit" SERVICE_SPEED_LIMIT = "speed_limit"
SERVICE_TIME_OF_USE = "time_of_use" SERVICE_TIME_OF_USE = "time_of_use"
SERVICE_ADD_CHARGE_SCHEDULE = "add_charge_schedule"
SERVICE_REMOVE_CHARGE_SCHEDULE = "remove_charge_schedule"
SERVICE_ADD_PRECONDITION_SCHEDULE = "add_precondition_schedule"
SERVICE_REMOVE_PRECONDITION_SCHEDULE = "remove_precondition_schedule"
def async_get_device_for_service_call( def async_get_device_for_service_call(
@@ -315,3 +326,195 @@ def async_setup_services(hass: HomeAssistant) -> None:
} }
), ),
) )
async def add_charge_schedule(call: ServiceCall) -> None:
"""Configure charging schedule for a vehicle."""
device = async_get_device_for_service_call(hass, call)
config = async_get_config_for_device(hass, device)
vehicle = async_get_vehicle_for_entry(hass, device, config)
# Extract parameters from the service call
days_of_week = call.data[ATTR_DAYS_OF_WEEK]
# If days_of_week is a list (from select with multiple), convert to comma-separated string
if isinstance(days_of_week, list):
days_of_week = ",".join(days_of_week)
enabled = call.data[ATTR_ENABLE]
# Optional parameters
location = call.data.get(
ATTR_LOCATION,
{
CONF_LATITUDE: hass.config.latitude,
CONF_LONGITUDE: hass.config.longitude,
},
)
# Handle time inputs
start_time = None
if start_time_obj := call.data.get(ATTR_START_TIME):
# Convert time object to minutes since midnight
start_time = start_time_obj.hour * 60 + start_time_obj.minute
end_time = None
if end_time_obj := call.data.get(ATTR_END_TIME):
# Convert time object to minutes since midnight
end_time = end_time_obj.hour * 60 + end_time_obj.minute
one_time = call.data.get(ATTR_ONE_TIME)
schedule_id = call.data.get(ATTR_ID)
name = call.data.get(ATTR_NAME)
await handle_vehicle_command(
vehicle.api.add_charge_schedule(
days_of_week=days_of_week,
enabled=enabled,
lat=location[CONF_LATITUDE],
lon=location[CONF_LONGITUDE],
start_time=start_time,
end_time=end_time,
one_time=one_time,
id=schedule_id,
name=name,
)
)
hass.services.async_register(
DOMAIN,
SERVICE_ADD_CHARGE_SCHEDULE,
add_charge_schedule,
schema=vol.Schema(
{
vol.Required(CONF_DEVICE_ID): cv.string,
vol.Required(ATTR_DAYS_OF_WEEK): cv.ensure_list,
vol.Required(ATTR_ENABLE): cv.boolean,
vol.Optional(ATTR_LOCATION): {
vol.Required(CONF_LATITUDE): cv.latitude,
vol.Required(CONF_LONGITUDE): cv.longitude,
},
vol.Optional(ATTR_START_TIME): cv.time,
vol.Optional(ATTR_END_TIME): cv.time,
vol.Optional(ATTR_ONE_TIME): cv.boolean,
vol.Optional(ATTR_ID): cv.positive_int,
vol.Optional(ATTR_NAME): cv.string,
}
),
)
async def remove_charge_schedule(call: ServiceCall) -> None:
"""Remove a charging schedule for a vehicle."""
device = async_get_device_for_service_call(hass, call)
config = async_get_config_for_device(hass, device)
vehicle = async_get_vehicle_for_entry(hass, device, config)
# Extract parameters from the service call
schedule_id = call.data[ATTR_ID]
await handle_vehicle_command(
vehicle.api.remove_charge_schedule(
id=schedule_id,
)
)
hass.services.async_register(
DOMAIN,
SERVICE_REMOVE_CHARGE_SCHEDULE,
remove_charge_schedule,
schema=vol.Schema(
{
vol.Required(CONF_DEVICE_ID): cv.string,
vol.Required(ATTR_ID): cv.positive_int,
}
),
)
async def add_precondition_schedule(call: ServiceCall) -> None:
"""Add or modify a precondition schedule for a vehicle."""
device = async_get_device_for_service_call(hass, call)
config = async_get_config_for_device(hass, device)
vehicle = async_get_vehicle_for_entry(hass, device, config)
# Extract parameters from the service call
days_of_week = call.data[ATTR_DAYS_OF_WEEK]
# If days_of_week is a list (from select with multiple), convert to comma-separated string
if isinstance(days_of_week, list):
days_of_week = ",".join(days_of_week)
enabled = call.data[ATTR_ENABLE]
location = call.data.get(
ATTR_LOCATION,
{
CONF_LATITUDE: hass.config.latitude,
CONF_LONGITUDE: hass.config.longitude,
},
)
# Convert time object to minutes since midnight
precondition_time = (
call.data[ATTR_PRECONDITION_TIME].hour * 60
+ call.data[ATTR_PRECONDITION_TIME].minute
)
# Optional parameters
schedule_id = call.data.get(ATTR_ID)
one_time = call.data.get(ATTR_ONE_TIME)
name = call.data.get(ATTR_NAME)
await handle_vehicle_command(
vehicle.api.add_precondition_schedule(
days_of_week=days_of_week,
enabled=enabled,
lat=location[CONF_LATITUDE],
lon=location[CONF_LONGITUDE],
precondition_time=precondition_time,
id=schedule_id,
one_time=one_time,
name=name,
)
)
hass.services.async_register(
DOMAIN,
SERVICE_ADD_PRECONDITION_SCHEDULE,
add_precondition_schedule,
schema=vol.Schema(
{
vol.Required(CONF_DEVICE_ID): cv.string,
vol.Required(ATTR_DAYS_OF_WEEK): cv.ensure_list,
vol.Required(ATTR_ENABLE): cv.boolean,
vol.Optional(ATTR_LOCATION): {
vol.Required(CONF_LATITUDE): cv.latitude,
vol.Required(CONF_LONGITUDE): cv.longitude,
},
vol.Required(ATTR_PRECONDITION_TIME): cv.time,
vol.Optional(ATTR_ID): cv.positive_int,
vol.Optional(ATTR_ONE_TIME): cv.boolean,
vol.Optional(ATTR_NAME): cv.string,
}
),
)
async def remove_precondition_schedule(call: ServiceCall) -> None:
"""Remove a preconditioning schedule for a vehicle."""
device = async_get_device_for_service_call(hass, call)
config = async_get_config_for_device(hass, device)
vehicle = async_get_vehicle_for_entry(hass, device, config)
# Extract parameters from the service call
schedule_id = call.data[ATTR_ID]
await handle_vehicle_command(
vehicle.api.remove_precondition_schedule(
id=schedule_id,
)
)
hass.services.async_register(
DOMAIN,
SERVICE_REMOVE_PRECONDITION_SCHEDULE,
remove_precondition_schedule,
schema=vol.Schema(
{
vol.Required(CONF_DEVICE_ID): cv.string,
vol.Required(ATTR_ID): cv.positive_int,
}
),
)

View File

@@ -130,3 +130,139 @@ speed_limit:
min: 1000 min: 1000
max: 9999 max: 9999
mode: box mode: box
add_charge_schedule:
fields:
device_id:
required: true
selector:
device:
filter:
integration: teslemetry
days_of_week:
required: true
selector:
select:
options:
- monday
- tuesday
- wednesday
- thursday
- friday
- saturday
- sunday
multiple: true
translation_key: days_of_week
enable:
required: true
selector:
boolean:
location:
required: false
example: '{"latitude": -27.9699373, "longitude": 153.4081865}'
selector:
location:
radius: false
start_time:
required: false
selector:
time:
end_time:
required: false
selector:
time:
one_time:
required: false
selector:
boolean:
id:
required: false
selector:
number:
min: 1
mode: box
name:
required: false
selector:
text:
remove_charge_schedule:
fields:
device_id:
required: true
selector:
device:
filter:
integration: teslemetry
id:
required: true
selector:
number:
min: 1
mode: box
add_precondition_schedule:
fields:
device_id:
required: true
selector:
device:
filter:
integration: teslemetry
days_of_week:
required: true
selector:
select:
options:
- monday
- tuesday
- wednesday
- thursday
- friday
- saturday
- sunday
multiple: true
translation_key: days_of_week
enable:
required: true
selector:
boolean:
location:
required: false
example: '{"latitude": -27.9699373, "longitude": 153.4081865}'
selector:
location:
radius: false
precondition_time:
required: true
selector:
time:
id:
required: false
selector:
number:
min: 1
mode: box
one_time:
required: false
selector:
boolean:
name:
required: false
selector:
text:
remove_precondition_schedule:
fields:
device_id:
required: true
selector:
device:
filter:
integration: teslemetry
id:
required: true
selector:
number:
min: 1
mode: box

View File

@@ -3,6 +3,26 @@
"unavailable": "Unavailable", "unavailable": "Unavailable",
"abort": "Abort", "abort": "Abort",
"vehicle": "Vehicle", "vehicle": "Vehicle",
"wake_up_failed": "Failed to wake up vehicle: {message}",
"wake_up_timeout": "Timed out trying to wake up vehicle",
"schedule_id": "Schedule ID",
"schedule_id_description": "The ID of the schedule, use an existing ID to modify.",
"days_of_week": "Days of week",
"days_of_week_description": "Select which days this schedule should be enabled on. You can select multiple days.",
"one_time": "One-time",
"one_time_description": "If this is a one-time schedule.",
"location_description": "The approximate location the vehicle must be at to use this schedule. Defaults to Home Assistant's configured location.",
"start_time": "Start time",
"start_time_description": "The time this schedule begins, e.g. 01:05 for 1:05 AM.",
"end_time": "End time",
"end_time_description": "The time this schedule ends, e.g. 01:05 for 1:05 AM.",
"precondition_time": "Precondition time",
"precondition_time_description": "The time the vehicle should complete preconditioning, e.g. 01:05 for 1:05 AM.",
"schedule_name_description": "The name of the schedule.",
"vehicle_to_schedule": "Vehicle to schedule.",
"vehicle_to_remove_schedule": "Vehicle to remove schedule from.",
"schedule_enable_description": "If this schedule should be considered for execution.",
"schedule_id_remove_description": "The ID of the schedule to remove.",
"descr_pin": "4-digit code to enable or disable the setting" "descr_pin": "4-digit code to enable or disable the setting"
}, },
"config": { "config": {
@@ -1079,15 +1099,6 @@
"invalid_cop_temp": { "invalid_cop_temp": {
"message": "Cabin overheat protection does not support that temperature" "message": "Cabin overheat protection does not support that temperature"
}, },
"set_scheduled_charging_time": {
"message": "Time required to complete the operation"
},
"set_scheduled_departure_preconditioning": {
"message": "Departure time required to enable preconditioning"
},
"set_scheduled_departure_off_peak": {
"message": "To enable scheduled departure, 'End off-peak time' is required."
},
"invalid_device": { "invalid_device": {
"message": "Invalid device ID: {device_id}" "message": "Invalid device ID: {device_id}"
}, },
@@ -1145,7 +1156,7 @@
"description": "Sets a time at which charging should be started.", "description": "Sets a time at which charging should be started.",
"fields": { "fields": {
"device_id": { "device_id": {
"description": "Vehicle to schedule.", "description": "[%key:component::teslemetry::common::vehicle_to_schedule%]",
"name": "[%key:component::teslemetry::common::vehicle%]" "name": "[%key:component::teslemetry::common::vehicle%]"
}, },
"enable": { "enable": {
@@ -1167,7 +1178,7 @@
"name": "Departure time" "name": "Departure time"
}, },
"device_id": { "device_id": {
"description": "Vehicle to schedule.", "description": "[%key:component::teslemetry::common::vehicle_to_schedule%]",
"name": "[%key:component::teslemetry::common::vehicle%]" "name": "[%key:component::teslemetry::common::vehicle%]"
}, },
"enable": { "enable": {
@@ -1246,6 +1257,127 @@
} }
}, },
"name": "Set valet mode" "name": "Set valet mode"
},
"add_charge_schedule": {
"description": "Adds or modifies a charging schedule for a vehicle.",
"fields": {
"device_id": {
"description": "[%key:component::teslemetry::common::vehicle_to_schedule%]",
"name": "[%key:component::teslemetry::common::vehicle%]"
},
"days_of_week": {
"description": "[%key:component::teslemetry::common::days_of_week_description%]",
"name": "[%key:component::teslemetry::common::days_of_week%]"
},
"enable": {
"description": "[%key:component::teslemetry::common::schedule_enable_description%]",
"name": "[%key:common::action::enable%]"
},
"location": {
"description": "[%key:component::teslemetry::common::location_description%]",
"name": "Location"
},
"start_time": {
"description": "[%key:component::teslemetry::common::start_time_description%]",
"name": "[%key:component::teslemetry::common::start_time%]"
},
"end_time": {
"description": "[%key:component::teslemetry::common::end_time_description%]",
"name": "[%key:component::teslemetry::common::end_time%]"
},
"one_time": {
"description": "[%key:component::teslemetry::common::one_time_description%]",
"name": "[%key:component::teslemetry::common::one_time%]"
},
"id": {
"description": "[%key:component::teslemetry::common::schedule_id_description%]",
"name": "[%key:component::teslemetry::common::schedule_id%]"
},
"name": {
"description": "[%key:component::teslemetry::common::schedule_name_description%]",
"name": "[%key:common::config_flow::data::name%]"
}
},
"name": "Add charge schedule"
},
"remove_charge_schedule": {
"description": "Removes a charging schedule for a vehicle.",
"fields": {
"device_id": {
"description": "[%key:component::teslemetry::common::vehicle_to_remove_schedule%]",
"name": "[%key:component::teslemetry::common::vehicle%]"
},
"id": {
"description": "[%key:component::teslemetry::common::schedule_id_remove_description%]",
"name": "[%key:component::teslemetry::common::schedule_id%]"
}
},
"name": "Remove charge schedule"
},
"add_precondition_schedule": {
"description": "Adds or modifies a preconditioning schedule for a vehicle.",
"fields": {
"device_id": {
"description": "[%key:component::teslemetry::common::vehicle_to_schedule%]",
"name": "[%key:component::teslemetry::common::vehicle%]"
},
"days_of_week": {
"description": "[%key:component::teslemetry::common::days_of_week_description%]",
"name": "[%key:component::teslemetry::common::days_of_week%]"
},
"enable": {
"description": "[%key:component::teslemetry::common::schedule_enable_description%]",
"name": "[%key:common::action::enable%]"
},
"location": {
"description": "[%key:component::teslemetry::common::location_description%]",
"name": "Location"
},
"precondition_time": {
"description": "[%key:component::teslemetry::common::precondition_time_description%]",
"name": "[%key:component::teslemetry::common::precondition_time%]"
},
"id": {
"description": "[%key:component::teslemetry::common::schedule_id_description%]",
"name": "[%key:component::teslemetry::common::schedule_id%]"
},
"one_time": {
"description": "[%key:component::teslemetry::common::one_time_description%]",
"name": "[%key:component::teslemetry::common::one_time%]"
},
"name": {
"description": "[%key:component::teslemetry::common::schedule_name_description%]",
"name": "[%key:common::config_flow::data::name%]"
}
},
"name": "Add precondition schedule"
},
"remove_precondition_schedule": {
"description": "Removes a preconditioning schedule for a vehicle.",
"fields": {
"device_id": {
"description": "[%key:component::teslemetry::common::vehicle_to_remove_schedule%]",
"name": "[%key:component::teslemetry::common::vehicle%]"
},
"id": {
"description": "[%key:component::teslemetry::common::schedule_id_remove_description%]",
"name": "[%key:component::teslemetry::common::schedule_id%]"
}
},
"name": "Remove precondition schedule"
}
},
"selector": {
"days_of_week": {
"options": {
"monday": "[%key:common::time::monday%]",
"tuesday": "[%key:common::time::tuesday%]",
"wednesday": "[%key:common::time::wednesday%]",
"thursday": "[%key:common::time::thursday%]",
"friday": "[%key:common::time::friday%]",
"saturday": "[%key:common::time::saturday%]",
"sunday": "[%key:common::time::sunday%]"
}
} }
} }
} }

View File

@@ -1,23 +1,36 @@
"""Test the Teslemetry services.""" """Test the Teslemetry services."""
from datetime import time
from unittest.mock import patch from unittest.mock import patch
import pytest import pytest
from homeassistant.components.teslemetry.const import DOMAIN from homeassistant.components.teslemetry.const import DOMAIN
from homeassistant.components.teslemetry.services import ( from homeassistant.components.teslemetry.services import (
ATTR_DAYS_OF_WEEK,
ATTR_DEPARTURE_TIME, ATTR_DEPARTURE_TIME,
ATTR_ENABLE, ATTR_ENABLE,
ATTR_END_OFF_PEAK_TIME, ATTR_END_OFF_PEAK_TIME,
ATTR_END_TIME,
ATTR_GPS, ATTR_GPS,
ATTR_ID,
ATTR_LOCATION,
ATTR_NAME,
ATTR_OFF_PEAK_CHARGING_ENABLED, ATTR_OFF_PEAK_CHARGING_ENABLED,
ATTR_OFF_PEAK_CHARGING_WEEKDAYS, ATTR_OFF_PEAK_CHARGING_WEEKDAYS,
ATTR_ONE_TIME,
ATTR_PIN, ATTR_PIN,
ATTR_PRECONDITION_TIME,
ATTR_PRECONDITIONING_ENABLED, ATTR_PRECONDITIONING_ENABLED,
ATTR_PRECONDITIONING_WEEKDAYS, ATTR_PRECONDITIONING_WEEKDAYS,
ATTR_START_TIME,
ATTR_TIME, ATTR_TIME,
ATTR_TOU_SETTINGS, ATTR_TOU_SETTINGS,
SERVICE_ADD_CHARGE_SCHEDULE,
SERVICE_ADD_PRECONDITION_SCHEDULE,
SERVICE_NAVIGATE_ATTR_GPS_REQUEST, SERVICE_NAVIGATE_ATTR_GPS_REQUEST,
SERVICE_REMOVE_CHARGE_SCHEDULE,
SERVICE_REMOVE_PRECONDITION_SCHEDULE,
SERVICE_SET_SCHEDULED_CHARGING, SERVICE_SET_SCHEDULED_CHARGING,
SERVICE_SET_SCHEDULED_DEPARTURE, SERVICE_SET_SCHEDULED_DEPARTURE,
SERVICE_SPEED_LIMIT, SERVICE_SPEED_LIMIT,
@@ -75,23 +88,12 @@ async def test_services(
{ {
CONF_DEVICE_ID: vehicle_device, CONF_DEVICE_ID: vehicle_device,
ATTR_ENABLE: True, ATTR_ENABLE: True,
ATTR_TIME: "6:00", ATTR_TIME: "06:00", # 6:00 AM
}, },
blocking=True, blocking=True,
) )
set_scheduled_charging.assert_called_once() set_scheduled_charging.assert_called_once()
with pytest.raises(ServiceValidationError):
await hass.services.async_call(
DOMAIN,
SERVICE_SET_SCHEDULED_CHARGING,
{
CONF_DEVICE_ID: vehicle_device,
ATTR_ENABLE: True,
},
blocking=True,
)
with patch( with patch(
"tesla_fleet_api.teslemetry.Vehicle.set_scheduled_departure", "tesla_fleet_api.teslemetry.Vehicle.set_scheduled_departure",
return_value=COMMAND_OK, return_value=COMMAND_OK,
@@ -104,39 +106,15 @@ async def test_services(
ATTR_ENABLE: True, ATTR_ENABLE: True,
ATTR_PRECONDITIONING_ENABLED: True, ATTR_PRECONDITIONING_ENABLED: True,
ATTR_PRECONDITIONING_WEEKDAYS: False, ATTR_PRECONDITIONING_WEEKDAYS: False,
ATTR_DEPARTURE_TIME: "6:00", ATTR_DEPARTURE_TIME: "06:00", # 6:00 AM
ATTR_OFF_PEAK_CHARGING_ENABLED: True, ATTR_OFF_PEAK_CHARGING_ENABLED: True,
ATTR_OFF_PEAK_CHARGING_WEEKDAYS: False, ATTR_OFF_PEAK_CHARGING_WEEKDAYS: False,
ATTR_END_OFF_PEAK_TIME: "5:00", ATTR_END_OFF_PEAK_TIME: "05:00", # 5:00 AM
}, },
blocking=True, blocking=True,
) )
set_scheduled_departure.assert_called_once() set_scheduled_departure.assert_called_once()
with pytest.raises(ServiceValidationError):
await hass.services.async_call(
DOMAIN,
SERVICE_SET_SCHEDULED_DEPARTURE,
{
CONF_DEVICE_ID: vehicle_device,
ATTR_ENABLE: True,
ATTR_PRECONDITIONING_ENABLED: True,
},
blocking=True,
)
with pytest.raises(ServiceValidationError):
await hass.services.async_call(
DOMAIN,
SERVICE_SET_SCHEDULED_DEPARTURE,
{
CONF_DEVICE_ID: vehicle_device,
ATTR_ENABLE: True,
ATTR_OFF_PEAK_CHARGING_ENABLED: True,
},
blocking=True,
)
with patch( with patch(
"tesla_fleet_api.teslemetry.Vehicle.set_valet_mode", "tesla_fleet_api.teslemetry.Vehicle.set_valet_mode",
return_value=COMMAND_OK, return_value=COMMAND_OK,
@@ -200,6 +178,112 @@ async def test_services(
) )
set_time_of_use.assert_called_once() set_time_of_use.assert_called_once()
with patch(
"tesla_fleet_api.teslemetry.Vehicle.add_charge_schedule",
return_value=COMMAND_OK,
) as add_charge_schedule:
await hass.services.async_call(
DOMAIN,
SERVICE_ADD_CHARGE_SCHEDULE,
{
CONF_DEVICE_ID: vehicle_device,
ATTR_DAYS_OF_WEEK: ["Monday", "Tuesday"],
ATTR_ENABLE: True,
ATTR_LOCATION: {CONF_LATITUDE: lat, CONF_LONGITUDE: lon},
ATTR_START_TIME: time(7, 0, 0), # 7:00 AM
ATTR_END_TIME: time(18, 0, 0), # 6:00 PM
ATTR_ONE_TIME: False,
ATTR_NAME: "Test Schedule",
},
blocking=True,
)
add_charge_schedule.assert_called_once()
# Test add_charge_schedule with minimal required parameters
with patch(
"tesla_fleet_api.teslemetry.Vehicle.add_charge_schedule",
return_value=COMMAND_OK,
) as add_charge_schedule_minimal:
await hass.services.async_call(
DOMAIN,
SERVICE_ADD_CHARGE_SCHEDULE,
{
CONF_DEVICE_ID: vehicle_device,
ATTR_DAYS_OF_WEEK: ["Monday", "Tuesday"],
ATTR_ENABLE: True,
},
blocking=True,
)
add_charge_schedule_minimal.assert_called_once()
with patch(
"tesla_fleet_api.teslemetry.Vehicle.remove_charge_schedule",
return_value=COMMAND_OK,
) as remove_charge_schedule:
await hass.services.async_call(
DOMAIN,
SERVICE_REMOVE_CHARGE_SCHEDULE,
{
CONF_DEVICE_ID: vehicle_device,
ATTR_ID: 123,
},
blocking=True,
)
remove_charge_schedule.assert_called_once()
with patch(
"tesla_fleet_api.teslemetry.Vehicle.add_precondition_schedule",
return_value=COMMAND_OK,
) as add_precondition_schedule:
await hass.services.async_call(
DOMAIN,
SERVICE_ADD_PRECONDITION_SCHEDULE,
{
CONF_DEVICE_ID: vehicle_device,
ATTR_DAYS_OF_WEEK: ["Monday", "Tuesday"],
ATTR_ENABLE: True,
ATTR_LOCATION: {CONF_LATITUDE: lat, CONF_LONGITUDE: lon},
ATTR_PRECONDITION_TIME: time(7, 0, 0), # 7:00 AM
ATTR_ONE_TIME: False,
ATTR_NAME: "Test Precondition Schedule",
},
blocking=True,
)
add_precondition_schedule.assert_called_once()
# Test add_precondition_schedule with minimal required parameters
with patch(
"tesla_fleet_api.teslemetry.Vehicle.add_precondition_schedule",
return_value=COMMAND_OK,
) as add_precondition_schedule_minimal:
await hass.services.async_call(
DOMAIN,
SERVICE_ADD_PRECONDITION_SCHEDULE,
{
CONF_DEVICE_ID: vehicle_device,
ATTR_DAYS_OF_WEEK: ["Monday", "Tuesday"],
ATTR_ENABLE: True,
ATTR_PRECONDITION_TIME: time(8, 0, 0), # 8:00 AM
},
blocking=True,
)
add_precondition_schedule_minimal.assert_called_once()
with patch(
"tesla_fleet_api.teslemetry.Vehicle.remove_precondition_schedule",
return_value=COMMAND_OK,
) as remove_precondition_schedule:
await hass.services.async_call(
DOMAIN,
SERVICE_REMOVE_PRECONDITION_SCHEDULE,
{
CONF_DEVICE_ID: vehicle_device,
ATTR_ID: 123,
},
blocking=True,
)
remove_precondition_schedule.assert_called_once()
with ( with (
patch( patch(
"tesla_fleet_api.teslemetry.EnergySite.time_of_use_settings", "tesla_fleet_api.teslemetry.EnergySite.time_of_use_settings",