Skip to content

Commit

Permalink
feat: add ChargingEnergySensor
Browse files Browse the repository at this point in the history
  • Loading branch information
alandtse committed Aug 13, 2021
1 parent a5f8858 commit 3e3d1a3
Show file tree
Hide file tree
Showing 3 changed files with 201 additions and 13 deletions.
8 changes: 7 additions & 1 deletion teslajsonpy/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@
ParkingSensor,
UpdateSensor,
)
from teslajsonpy.homeassistant.charger import ChargerSwitch, ChargingSensor, RangeSwitch
from teslajsonpy.homeassistant.charger import (
ChargerSwitch,
ChargingEnergySensor,
ChargingSensor,
RangeSwitch,
)
from teslajsonpy.homeassistant.climate import Climate, TempSensor
from teslajsonpy.homeassistant.gps import GPS, Odometer
from teslajsonpy.homeassistant.heated_seats import HeatedSeatSwitch
Expand Down Expand Up @@ -548,6 +553,7 @@ def _add_components(self, car):
self.__components.append(ChargerLock(car, self))
self.__components.append(ChargerConnectionSensor(car, self))
self.__components.append(ChargingSensor(car, self))
self.__components.append(ChargingEnergySensor(car, self))
self.__components.append(ChargerSwitch(car, self))
self.__components.append(RangeSwitch(car, self))
self.__components.append(ParkingSensor(car, self))
Expand Down
145 changes: 145 additions & 0 deletions teslajsonpy/homeassistant/charger.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"""
import time
from typing import Dict, Optional, Text
import datetime

from teslajsonpy.homeassistant.vehicle import VehicleDevice

Expand Down Expand Up @@ -270,3 +271,147 @@ def charger_power(self) -> float:
def device_class(self) -> Text:
"""Return the HA device class."""
return self._device_class


class ChargingEnergySensor(VehicleDevice):
"""Home-Assistant energy sensor class for a Tesla VehicleDevice."""

def __init__(self, data: Dict, controller) -> None:
"""Initialize the Charger sensor.
Args:
data (Dict): The charging parameters for a Tesla vehicle.
https://tesla-api.timdorr.com/vehicle/state/chargestate
controller (Controller): The controller that controls updates to the Tesla API.
"""
super().__init__(data, controller)
self.type: Text = "energy added sensor"
self.__rated: bool = True
self.__miles: bool = True
self.measurement: Text = "kWh"
self.hass_type: Text = "sensor"
self._device_class: Optional[Text] = "energy"
self.name: Text = self._name()
self.uniq_name: Text = self._uniq_name()
self.__added_range = None
self.__charge_energy_added = None
self.__charging_rate = None
self.__time_to_full = None
self.__charge_current_request = None
self.__charger_actual_current = None
self.__charger_voltage = None
self.__charge_limit_soc = None
self.__charger_power = None
self.__last_reset: datetime.datetime = datetime.time.min

async def async_update(self, wake_if_asleep=False, force=False) -> None:
"""Update the battery state."""
await super().async_update(wake_if_asleep=wake_if_asleep)
self.refresh()

def refresh(self) -> None:
"""Refresh data.
This assumes the controller has already been updated
"""
super().refresh()
data = self._controller.get_gui_params(self._id)
if data:
self.__miles = data["gui_distance_units"] == "mi/hr"
self.__rated = data["gui_range_display"] == "Rated"
data = self._controller.get_charging_params(self._id)
if data:
self.attrs["charger_phases"] = data["charger_phases"]
self.__added_range = (
data["charge_miles_added_rated"]
if self.__rated
else data["charge_miles_added_ideal"]
)
if (
self.__charge_energy_added
and self.__charge_energy_added >= data["charge_energy_added"]
):
self.__last_reset = datetime.datetime.utcnow()
self.__charge_energy_added = data["charge_energy_added"]
self.__charging_rate = data["charge_rate"]
self.__time_to_full = data["time_to_full_charge"]
self.__charge_current_request = data["charge_current_request"]
self.__charger_actual_current = data["charger_actual_current"]
self.__charger_voltage = data["charger_voltage"]
self.__charge_limit_soc = data["charge_limit_soc"]
self.__charger_power = data["charger_power"]
self.attrs["charge_limit_soc"] = self.charge_limit_soc
self.attrs["last_reset"] = str(self.last_reset)
if self.__miles:
self.__added_range = round(self.__added_range / 0.621371, 2)
self.__charging_rate = round(self.__charging_rate / 0.621371, 2)

@staticmethod
def has_battery() -> bool:
"""Return whether the device has a battery."""
return False

@property
def charging_rate(self) -> float:
"""Return the charging rate."""
return self.__charging_rate

@property
def time_left(self) -> float:
"""Return the time left to full in hours."""
return self.__time_to_full

@property
def added_range(self) -> float:
"""Return the added range."""
return self.__added_range

@property
def charge_current_request(self) -> float:
"""Return the requested current."""
return self.__charge_current_request

@property
def charger_actual_current(self) -> float:
"""Return the actual current."""
return self.__charger_actual_current

@property
def charger_voltage(self) -> float:
"""Return the voltage."""
return self.__charger_voltage

@property
def charge_energy_added(self) -> float:
"""Return the energy added."""
return self.__charge_energy_added

@property
def charge_limit_soc(self) -> int:
"""Return the state of charge limit."""
return self.__charge_limit_soc

@property
def charger_power(self) -> float:
"""Return the state of charger power."""
return self.__charger_power

@property
def device_class(self) -> Text:
"""Return the HA device class."""
return self._device_class

@property
def last_reset(self) -> datetime.datetime:
"""Return the last reset time."""
return self.__last_reset

@property
def state_class(self) -> Text:
"""Return the state class."""
return "measurement"

def get_value(self) -> float:
"""Return charge energy added."""
return self.charge_energy_added
61 changes: 49 additions & 12 deletions tests/unit_tests/homeassistant/test_charging_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import pytest

from teslajsonpy.controller import Controller
from teslajsonpy.homeassistant.charger import ChargingSensor
from teslajsonpy.homeassistant.charger import ChargingSensor, ChargingEnergySensor

from tests.tesla_mock import TeslaMock

Expand Down Expand Up @@ -32,24 +32,45 @@ def test_device_class(monkeypatch):
assert _sensor.device_class is None


def test_state_class(monkeypatch):
"""Test device_class()."""

_mock = TeslaMock(monkeypatch)
_controller = Controller(None)

_data = _mock.data_request_vehicle()
_sensor = ChargingSensor(_data, _controller)

assert _sensor.device_class is None

_sensor2 = ChargingEnergySensor(_data, _controller)
assert _sensor2.device_class == "energy"


def test_get_value_on_init(monkeypatch):
"""Test get_value() after initialization."""

_mock = TeslaMock(monkeypatch)
_controller = Controller(None)

_data = _mock.data_request_vehicle()
_sensor = ChargingSensor(_data, _controller)
sensors = [
ChargingSensor(_data, _controller),
ChargingEnergySensor(_data, _controller),
]

for _sensor in sensors:

assert not _sensor is None
assert _sensor.charging_rate is None
assert _sensor.time_left is None
assert _sensor.added_range is None
assert _sensor.charge_current_request is None
assert _sensor.charger_actual_current is None
assert _sensor.charger_voltage is None
assert _sensor.charge_energy_added is None
assert _sensor.charge_limit_soc is None
assert _sensor is not None
assert _sensor.charging_rate is None
assert _sensor.time_left is None
assert _sensor.added_range is None
assert _sensor.charge_current_request is None
assert _sensor.charger_actual_current is None
assert _sensor.charger_voltage is None
assert _sensor.charger_power is None
assert _sensor.charge_energy_added is None
assert _sensor.charge_limit_soc is None


@pytest.mark.asyncio
Expand All @@ -61,7 +82,7 @@ async def test_get_value_after_update(monkeypatch):

_data = _mock.data_request_vehicle()
_sensor = ChargingSensor(_data, _controller)

_sensor2 = ChargingEnergySensor(_data, _controller)
await _sensor.async_update()

assert _sensor is not None
Expand All @@ -74,7 +95,23 @@ async def test_get_value_after_update(monkeypatch):
assert _sensor.charger_power == 0
assert _sensor.charge_energy_added == 12.41
assert _sensor.charge_limit_soc == 90
assert _sensor.device_class is None

_sensor2 = ChargingEnergySensor(_data, _controller)
await _sensor2.async_update()

assert _sensor2 is not None
assert _sensor2.charging_rate == 0
assert _sensor2.time_left == 0
assert _sensor2.charge_current_request == 48
assert _sensor2.charger_actual_current == 0
assert _sensor2.charger_voltage == 0
assert _sensor2.charger_power == 0
assert _sensor2.charge_energy_added == 12.41
assert _sensor2.charge_limit_soc == 90
assert _sensor2.last_reset != 0
assert _sensor2.state_class == "measurement"
assert _sensor2.device_class == "energy"

@pytest.mark.asyncio
async def test_async_update(monkeypatch):
Expand Down

0 comments on commit 3e3d1a3

Please sign in to comment.