diff --git a/custom_components/tesla_custom/binary_sensor.py b/custom_components/tesla_custom/binary_sensor.py index 59f1b860..06a50cf5 100644 --- a/custom_components/tesla_custom/binary_sensor.py +++ b/custom_components/tesla_custom/binary_sensor.py @@ -32,6 +32,8 @@ async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entitie entities.append(TeslaCarChargerConnection(hass, car, coordinator)) entities.append(TeslaCarCharging(hass, car, coordinator)) entities.append(TeslaCarDoors(hass, car, coordinator)) + entities.append(TeslaCarScheduledCharging(hass, car, coordinator)) + entities.append(TeslaCarScheduledDeparture(hass, car, coordinator)) for energysite in energysites.values(): if energysite.resource_type == RESOURCE_TYPE_BATTERY: @@ -226,7 +228,12 @@ def __init__( @property def is_on(self): """Return True if a car door is open.""" - return self._car.door_df or self._car.door_dr or self._car.door_pf or self._car.door_pr + return ( + self._car.door_df + or self._car.door_dr + or self._car.door_pf + or self._car.door_pr + ) @property def extra_state_attributes(self): @@ -243,3 +250,68 @@ def _open_or_closed(self, door): if door: return "Open" return "Closed" + + +class TeslaCarScheduledCharging(TeslaCarEntity, BinarySensorEntity): + """Representation of a Tesla car scheduled charging binary sensor.""" + + def __init__( + self, + hass: HomeAssistant, + car: TeslaCar, + coordinator: TeslaDataUpdateCoordinator, + ) -> None: + """Initialize scheduled charging entity.""" + super().__init__(hass, car, coordinator) + self.type = "scheduled charging" + self._attr_icon = "mdi:calendar-plus" + self._attr_device_class = None + + @property + def is_on(self): + """Return True if scheduled charging enebaled.""" + if self._car.scheduled_charging_mode == "StartAt": + return True + return False + + @property + def extra_state_attributes(self): + """Return device state attributes.""" + return { + "Scheduled charging time": self._car.scheduled_charging_start_time_app, + } + + +class TeslaCarScheduledDeparture(TeslaCarEntity, BinarySensorEntity): + """Representation of a Tesla car scheduled departure binary sensor.""" + + def __init__( + self, + hass: HomeAssistant, + car: TeslaCar, + coordinator: TeslaDataUpdateCoordinator, + ) -> None: + """Initialize scheduled departure entity.""" + super().__init__(hass, car, coordinator) + self.type = "scheduled departure" + self._attr_icon = "mdi:calendar-plus" + self._attr_device_class = None + + @property + def is_on(self): + """Return True if scheduled departure enebaled.""" + if self._car.scheduled_charging_mode == "DepartBy": + return True + return False + + @property + def extra_state_attributes(self): + """Return device state attributes.""" + return { + "Departure time": self._car.scheduled_departure_time_minutes, + "Preconditioning enabled": self._car.is_preconditioning_enabled, + "Preconditioning weekdays only": self._car.is_preconditioning_weekday_only, + "Off peak charging enabled": self._car.is_off_peak_charging_enabled, + "Off peak charging weekdays only": self._car.is_off_peak_charging_weekday_only, + "End off peak time": self._car.off_peak_hours_end_time, + } diff --git a/tests/mock_data/car.py b/tests/mock_data/car.py index 6cec4c34..59eb9284 100644 --- a/tests/mock_data/car.py +++ b/tests/mock_data/car.py @@ -76,7 +76,7 @@ "scheduled_charging_mode": "DepartBy", "scheduled_charging_pending": False, "scheduled_charging_start_time": None, - "scheduled_charging_start_time_app": 0, + "scheduled_charging_start_time_app": 480, "scheduled_departure_time": 1661515200, "scheduled_departure_time_minutes": 300, "supercharger_session_trip_planner": False, diff --git a/tests/test_binary_sensor.py b/tests/test_binary_sensor.py index 802cdf13..5bf892c8 100644 --- a/tests/test_binary_sensor.py +++ b/tests/test_binary_sensor.py @@ -29,6 +29,12 @@ async def test_registry_entries(hass: HomeAssistant) -> None: entry = entity_registry.async_get("binary_sensor.my_model_s_online") assert entry.unique_id == f"{car_mock_data.VIN.lower()}_online" + entry = entity_registry.async_get("binary_sensor.my_model_s_scheduled_charging") + assert entry.unique_id == f"{car_mock_data.VIN.lower()}_scheduled_charging" + + entry = entity_registry.async_get("binary_sensor.my_model_s_scheduled_departure") + assert entry.unique_id == f"{car_mock_data.VIN.lower()}_scheduled_departure" + entry = entity_registry.async_get("binary_sensor.battery_home_battery_charging") assert entry.unique_id == "67890_battery_charging" @@ -138,6 +144,7 @@ async def test_grid_status(hass: HomeAssistant) -> None: assert state.attributes.get(ATTR_DEVICE_CLASS) == BinarySensorDeviceClass.POWER + async def test_car_doors(hass: HomeAssistant) -> None: """Tests car door is getting the correct value.""" await setup_platform(hass, BINARY_SENSOR_DOMAIN) @@ -145,11 +152,78 @@ async def test_car_doors(hass: HomeAssistant) -> None: state = hass.states.get("binary_sensor.my_model_s_doors") assert state.state == STATE_ON - assert ( - state.attributes.get(ATTR_DEVICE_CLASS) == BinarySensorDeviceClass.DOOR - ) + assert state.attributes.get(ATTR_DEVICE_CLASS) == BinarySensorDeviceClass.DOOR assert state.attributes.get("Driver Front") == "Open" assert state.attributes.get("Driver Rear") == "Closed" assert state.attributes.get("Passenger Front") == "Closed" assert state.attributes.get("Passenger Rear") == "Closed" + + +async def test_car_scheduled_charging(hass: HomeAssistant) -> None: + """Tests scheduled charging is getting the correct value.""" + await setup_platform(hass, BINARY_SENSOR_DOMAIN) + + state = hass.states.get("binary_sensor.my_model_s_scheduled_charging") + assert state.state == STATE_OFF + + assert ( + state.attributes.get("Scheduled charging time") + == car_mock_data.VEHICLE_DATA["charge_state"][ + "scheduled_charging_start_time_app" + ] + ) + + +async def test_car_scheduled_departure(hass: HomeAssistant) -> None: + """Tests scheduled departure is getting the correct value.""" + await setup_platform(hass, BINARY_SENSOR_DOMAIN) + + state = hass.states.get("binary_sensor.my_model_s_scheduled_departure") + assert state.state == STATE_ON + + assert ( + state.attributes.get("Departure time") + == car_mock_data.VEHICLE_DATA["charge_state"][ + "scheduled_departure_time_minutes" + ] + ) + + assert ( + state.attributes.get("Preconditioning enabled") + == car_mock_data.VEHICLE_DATA["charge_state"]["preconditioning_enabled"] + ) + + if ( + car_mock_data.VEHICLE_DATA["charge_state"]["preconditioning_times"] + == "weekdays" + ): + check_precondition_weekdays_only = True + else: + check_precondition_weekdays_only = False + assert ( + state.attributes.get("Preconditioning weekdays only") + == check_precondition_weekdays_only + ) + + assert ( + state.attributes.get("Off peak charging enabled") + == car_mock_data.VEHICLE_DATA["charge_state"]["off_peak_charging_enabled"] + ) + + if ( + car_mock_data.VEHICLE_DATA["charge_state"]["off_peak_charging_times"] + == "weekdays" + ): + check_off_peak_weekdays_only = True + else: + check_off_peak_weekdays_only = False + assert ( + state.attributes.get("Off peak charging weekdays only") + == check_off_peak_weekdays_only + ) + + assert ( + state.attributes.get("End off peak time") + == car_mock_data.VEHICLE_DATA["charge_state"]["off_peak_hours_end_time"] + )