Skip to content

Commit

Permalink
Merge pull request #99 from zabuldon/dev
Browse files Browse the repository at this point in the history
2020-07-18
  • Loading branch information
alandtse authored Jul 19, 2020
2 parents b6d5279 + d2df26d commit bd8d1f5
Show file tree
Hide file tree
Showing 7 changed files with 215 additions and 86 deletions.
136 changes: 103 additions & 33 deletions teslajsonpy/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
For more details about this api, please refer to the documentation at
https://github.com/zabuldon/teslajsonpy
"""
from typing import Dict, Text
from typing import Dict, Optional, Text

from teslajsonpy.vehicle import VehicleDevice
from teslajsonpy.const import RELEASE_NOTES_URL


class ParkingSensor(VehicleDevice):
"""Home-assistant parking brake class for Tesla vehicles.
class BinarySensor(VehicleDevice):
"""Home-assistant binary sensor class for Tesla vehicles.
This is intended to be partially inherited by a Home-Assitant entity.
"""
Expand All @@ -35,14 +36,60 @@ def __init__(self, data: Dict, controller):
super().__init__(data, controller)
self.__state = None

self.type = "parking brake sensor"
self.type = "binary sensor"
self.hass_type = "binary_sensor"
self.sensor_type = "power"

self._sensor_type = "connectivity"
self.name = self._name()
self.uniq_name = self._uniq_name()

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

def get_value(self):
"""Return whether binary sensor is true."""
return self.__state

@property
def sensor_type(self):
"""Return the sensor_type for use by HA as a device_class."""
return self._sensor_type

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


class ParkingSensor(BinarySensor):
"""Home-assistant parking brake class for Tesla vehicles.
This is intended to be partially inherited by a Home-Assitant entity.
"""

def __init__(self, data: Dict, controller):
"""Initialize the parking brake sensor.
Parameters
----------
data : dict
The base state for a Tesla vehicle.
https://tesla-api.timdorr.com/vehicle/state/data
controller : teslajsonpy.Controller
The controller that controls updates to the Tesla API.
Returns
-------
None
"""
super().__init__(data, controller)
self.__state = None
self.type = "parking brake sensor"
self.hass_type = "binary_sensor"
self._sensor_type = "power"
self.name = self._name()
self.uniq_name = self._uniq_name()
self.bin_type = 0x1

async def async_update(self, wake_if_asleep=False, force=False) -> None:
"""Update the parking brake sensor."""
Expand All @@ -61,13 +108,8 @@ def get_value(self):
"""Return whether parking brake engaged."""
return self.__state

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


class ChargerConnectionSensor(VehicleDevice):
class ChargerConnectionSensor(BinarySensor):
"""Home-assistant charger connection class for Tesla vehicles.
This is intended to be partially inherited by a Home-Assitant entity.
Expand All @@ -91,14 +133,11 @@ def __init__(self, data, controller):
"""
super().__init__(data, controller)
self.__state = None

self.type = "charger sensor"
self.hass_type = "binary_sensor"
self._sensor_type = "connectivity"
self.name = self._name()
self.sensor_type = "connectivity"

self.uniq_name = self._uniq_name()
self.bin_type = 0x2

async def async_update(self, wake_if_asleep=False, force=False) -> None:
"""Update the charger connection sensor."""
Expand All @@ -119,13 +158,8 @@ def get_value(self):
"""Return whether the charger cable is connected."""
return self.__state

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


class OnlineSensor(VehicleDevice):
class OnlineSensor(BinarySensor):
"""Home-Assistant Online sensor class for a Tesla VehicleDevice."""

def __init__(self, data: Dict, controller) -> None:
Expand All @@ -138,24 +172,60 @@ def __init__(self, data: Dict, controller) -> None:
"""
super().__init__(data, controller)
self.__online_state: bool = None
self.__online_state: Optional[bool] = None
self.type: Text = "online sensor"
self.hass_type = "binary_sensor"
self.sensor_type = "connectivity"
self.name: Text = self._name()
self.uniq_name: Text = self._uniq_name()
self._sensor_type = "connectivity"
self.name = self._name()
self.uniq_name = self._uniq_name()

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.__online_state = self._controller.car_online[self._vin]
self.attrs["state"] = self._controller.car_state[self._vin].get("state")

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

def get_value(self) -> bool:
def get_value(self) -> Optional[bool]:
"""Return the car is online."""
return self.__online_state


class UpdateSensor(BinarySensor):
"""Home-Assistant update sensor class for a Tesla VehicleDevice."""

def __init__(self, data: Dict, controller) -> None:
"""Initialize the Update sensor.
Args:
data (Dict): Thes base state for a Tesla vehicle.
https://tesla-api.timdorr.com/vehicle/state/data
controller (Controller): The controller that controls updates to the Tesla API.
"""
super().__init__(data, controller)
self.type: Text = "update available sensor"
self._sensor_type = "problem"
self.name = self._name()
self.uniq_name = self._uniq_name()

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.attrs = (
self.device_state_attributes.copy() if self.device_state_attributes else {}
)

def get_value(self) -> Optional[bool]:
"""Return the car is online."""
return self.update_available

@property
def device_state_attributes(self) -> Optional[dict]:
"""Return the optional state attributes."""
if not self.update_available:
return None
data = {}
data["release_notes"] = f"{RELEASE_NOTES_URL}{self.update_version}"
data["update_version"] = self.update_version
data["installed_version"] = self.car_version
return data
1 change: 1 addition & 0 deletions teslajsonpy/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
SLEEP_INTERVAL = 660 # interval required to let vehicle sleep; based on testing
DRIVING_INTERVAL = 60 # interval when driving detected
WEBSOCKET_TIMEOUT = 11 # time for websocket to timeout
RELEASE_NOTES_URL = "https://teslascope.com/teslapedia/software/"
2 changes: 2 additions & 0 deletions teslajsonpy/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
ChargerConnectionSensor,
OnlineSensor,
ParkingSensor,
UpdateSensor,
)
from teslajsonpy.charger import ChargerSwitch, ChargingSensor, RangeSwitch
from teslajsonpy.climate import Climate, TempSensor
Expand Down Expand Up @@ -501,6 +502,7 @@ def _add_components(self, car):
self.__components.append(SentryModeSwitch(car, self))
self.__components.append(TrunkLock(car, self))
self.__components.append(FrunkLock(car, self))
self.__components.append(UpdateSensor(car, self))

async def _wake_up(self, car_id):
car_vin = self._id_to_vin(car_id)
Expand Down
67 changes: 45 additions & 22 deletions teslajsonpy/vehicle.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
https://github.com/zabuldon/teslajsonpy
"""
import logging
from typing import Dict, Text
from typing import Dict, Optional, Text

_LOGGER = logging.getLogger(__name__)

Expand All @@ -33,45 +33,51 @@ def __init__(self, data, controller):
None
"""
self._id = data["id"]
self._vehicle_id = data["vehicle_id"]
self._display_name = data["display_name"]
self._vin = data["vin"]
self._id: int = data["id"]
self._vehicle_id: int = data["vehicle_id"]
self._display_name: Text = data["display_name"]
self._vin: Text = data["vin"]
self._state = data["state"]
self._car_type = f"Model {str(self._vin[3]).upper()}"
self._car_version = ""
self._sentry_mode_available = (
self._car_type: Text = f"Model {str(self._vin[3]).upper()}"
self._car_version: Text = ""
self._sentry_mode_available: bool = (
"vehicle_state" in data
and "sentry_mode_available" in data["vehicle_state"]
and data["vehicle_state"]["sentry_mode_available"]
)
self._controller = controller
self.should_poll = True
self.type = "device"
self.should_poll: bool = True
self.type: Text = "device"
self.attrs: Dict[Text, Text] = {}
self._update_available: bool = (
data.get("software_update", {}).get("status") == "available"
)
self._update_version: Optional[Text] = data.get("software_update", {}).get(
"version"
)

def _name(self):
def _name(self) -> Text:
return (
"{} {}".format(self._display_name, self.type)
if self._display_name is not None and self._display_name != self._vin[-6:]
else "Tesla Model {} {}".format(str(self._vin[3]).upper(), self.type)
)

def _uniq_name(self):
def _uniq_name(self) -> Text:
return "Tesla Model {} {} {}".format(
str(self._vin[3]).upper(), self._vin[-6:], self.type
)

def id(self):
def id(self) -> int:
# pylint: disable=invalid-name
"""Return the id of this Vehicle."""
return self._id

def vehicle_id(self):
def vehicle_id(self) -> int:
"""Return the vehicle_id of this Vehicle."""
return self._vehicle_id

def car_name(self):
def car_name(self) -> Text:
"""Return the car name of this Vehicle."""
return (
self._display_name
Expand All @@ -80,21 +86,31 @@ def car_name(self):
)

@property
def car_version(self):
def car_version(self) -> Text:
"""Return the software version of this Vehicle."""
return self._car_version

@property
def car_type(self):
def update_available(self) -> bool:
"""Return whether an update is available for this Vehicle."""
return self._update_available

@property
def update_version(self) -> Optional[Text]:
"""Return the update version of this Vehicle."""
return self._update_version

@property
def car_type(self) -> Text:
"""Return the type of this Vehicle."""
return self._car_type

@property
def sentry_mode_available(self):
def sentry_mode_available(self) -> bool:
"""Return True if sentry mode is available on this Vehicle."""
return self._sentry_mode_available

def assumed_state(self):
def assumed_state(self) -> bool:
# pylint: disable=protected-access
"""Return whether the data is from an online vehicle."""
return not self._controller.car_online[self.id()] and (
Expand All @@ -103,7 +119,9 @@ def assumed_state(self):
> self._controller.update_interval
)

async def async_update(self, wake_if_asleep=False, force=False):
async def async_update(
self, wake_if_asleep: bool = False, force: bool = False
) -> None:
"""Update the vehicle data."""
await self._controller.update(
self.id(), wake_if_asleep=wake_if_asleep, force=force
Expand All @@ -113,13 +131,18 @@ async def async_update(self, wake_if_asleep=False, force=False):
self._car_version = state["car_version"]
if state and "sentry_mode_available" in state:
self._sentry_mode_available = state["sentry_mode_available"]
self._update_available = state.get("software_update", {}).get("status") in {
"available",
"scheduled",
}
self._update_version = state.get("software_update", {}).get("version")

@staticmethod
def is_armable():
def is_armable() -> bool:
"""Return whether the data is from an online vehicle."""
return False

@staticmethod
def is_armed():
def is_armed() -> bool:
"""Return whether the vehicle is armed."""
return False
Loading

0 comments on commit bd8d1f5

Please sign in to comment.