From 8015c337e81457ba8cd2c9e690d80054d1b551d6 Mon Sep 17 00:00:00 2001 From: Evan Peterson Date: Thu, 13 Apr 2023 13:12:37 -0700 Subject: [PATCH] feat(smartcar): get & set charge limit (#117) --- smartcar/types.py | 6 ++++++ smartcar/vehicle.py | 33 +++++++++++++++++++++++++++++++++ tests/conftest.py | 4 +++- tests/e2e/test_vehicle.py | 14 ++++++++++++++ 4 files changed, 56 insertions(+), 1 deletion(-) diff --git a/smartcar/types.py b/smartcar/types.py index a5849d30..9760fb91 100644 --- a/smartcar/types.py +++ b/smartcar/types.py @@ -182,6 +182,8 @@ def format_capabilities(capabilities_list: List[dict]) -> List[Capability]: [("is_plugged_in", bool), ("state", str), ("meta", namedtuple)], ) +ChargeLimit = NamedTuple("ChargeLimit", [("limit", float), ("meta", namedtuple)]) + Battery = NamedTuple( "Battery", [("percent_remaining", float), ("range", float), ("meta", namedtuple)], @@ -350,6 +352,9 @@ def select_named_tuple(path: str, response_or_dict) -> NamedTuple: elif path == "location": return Location(data["latitude"], data["longitude"], headers) + elif path == "charge/limit": + return ChargeLimit(data["limit"], headers) + elif path == "permissions": return Permissions( data["permissions"], @@ -365,6 +370,7 @@ def select_named_tuple(path: str, response_or_dict) -> NamedTuple: or path == "unlock" or path == "start_charge" or path == "stop_charge" + or path == "set_charge_limit" ): return Action(data["status"], data["message"], headers) diff --git a/smartcar/vehicle.py b/smartcar/vehicle.py index c40d4984..97acd89a 100644 --- a/smartcar/vehicle.py +++ b/smartcar/vehicle.py @@ -248,6 +248,22 @@ def attributes(self) -> types.Attributes: response = helpers.requester("GET", url, headers=headers) return types.select_named_tuple(path, response) + def get_charge_limit(self) -> types.ChargeLimit: + """ + GET Vehicle.get_charge_limit + + Returns: + ChargeLimit = NamedTuple("ChargeLimit", [("limit", float), ("meta", namedtuple)]) + + Raises: + SmartcarException + """ + path = "charge/limit" + url = self._format_url(path) + headers = self._get_headers() + response = helpers.requester("GET", url, headers=headers) + return types.select_named_tuple(path, response) + # =========================================== # Action (POST) Requests # =========================================== @@ -320,6 +336,23 @@ def stop_charge(self) -> types.Status: ) return types.select_named_tuple("stop_charge", response) + def set_charge_limit(self, limit) -> types.Status: + """ + POST Vehicle.set_charge_limit + + Returns: + Action: NamedTuple("Action", [("status", str), ("message", str), ("meta", rs.namedtuple)]) + + Raises: + SmartcarException + """ + url = self._format_url("charge/limit") + headers = self._get_headers(need_unit_system=False) + response = helpers.requester( + "POST", url, headers=headers, json={"limit": limit} + ) + return types.select_named_tuple("set_charge_limit", response) + def batch(self, paths: List[str]) -> namedtuple: """ POST Vehicle.batch diff --git a/tests/conftest.py b/tests/conftest.py index 5c25ca3c..440cc372 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -139,7 +139,9 @@ def access_ford(client): access_tesla namedtuple """ client = sc.AuthClient(*ah.get_auth_client_params()) - code = ah.run_auth_flow(client.get_auth_url(["required:control_charge"]), "FORD") + code = ah.run_auth_flow( + client.get_auth_url(["required:read_charge", "required:control_charge"]), "FORD" + ) access = client.exchange_code(code) yield access diff --git a/tests/e2e/test_vehicle.py b/tests/e2e/test_vehicle.py index cbd5dbcf..b9625669 100644 --- a/tests/e2e/test_vehicle.py +++ b/tests/e2e/test_vehicle.py @@ -82,6 +82,13 @@ def test_attributes(chevy_volt): assert attributes._fields == ("id", "make", "model", "year", "meta") +def test_get_charge_limit(ford_car): + charge_limit = ford_car.get_charge_limit() + assert charge_limit is not None + assert type(charge_limit) == types.ChargeLimit + assert charge_limit._fields == ("limit", "meta") + + def test_lock(chevy_volt): response = chevy_volt.lock() assert response.status == "success" @@ -110,6 +117,13 @@ def test_stop_charge(ford_car): assert response._fields == ("status", "message", "meta") +def test_set_charge_limit(ford_car): + response = ford_car.set_charge_limit(0.7) + assert response.status == "success" + assert type(response) == types.Action + assert response._fields == ("status", "message", "meta") + + def test_batch_success(chevy_volt): batch = chevy_volt.batch(["/odometer", "/location"]) assert batch is not None