Skip to content

Commit

Permalink
feat: SDK support for new diagnostics endpoints
Browse files Browse the repository at this point in the history
feat: SDK support for new diagnostics endpoints
  • Loading branch information
hhovsepi authored Nov 1, 2024
2 parents fa8dc9c + 9535000 commit 668bdd0
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 0 deletions.
36 changes: 36 additions & 0 deletions REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,42 @@ Returns a list of all the service records performed on the vehicle, filtered by

---

### `diagnostic_system_status(self)`

Retrieve the status of various diagnostic systems in the vehicle.

#### Return

| Value | Type | Description |
| :---------------------------- | :--------------------- | :------------------------------------------------------------------- |
| `DiagnosticSystemStatus` | typing.NamedTuple | The returned object with diagnostic system statuses data |
| `DiagnosticSystemStatus.systems` | List[Dict] | List of system statuses, each with `system_id`, `status`, and `description` |
| `DiagnosticSystemStatus.meta` | collections.namedtuple | Smartcar response headers (`request_id`, `data_age`, and/or `unit_system`) |

Each system entry contains:
- `system_id` (String): Unique identifier for the system.
- `status` (String): Status of the system, either "OK" or "ALERT".
- `description` (String, optional): Additional context or description for the status, if any.

---

### `diagnostic_trouble_codes(self)`

Retrieve active diagnostic trouble codes (DTCs) for the vehicle.

#### Return

| Value | Type | Description |
| :------------------------- | :--------------------- | :------------------------------------------------------- |
| `DiagnosticTroubleCodes` | typing.NamedTuple | The returned object with active diagnostic trouble codes |
| `DiagnosticTroubleCodes.active_codes` | List[Dict] | List of active DTCs, each with `code` and `timestamp` |
| `DiagnosticTroubleCodes.meta` | collections.namedtuple | Smartcar response headers (`request_id`, `data_age`, and/or `unit_system`) |

Each trouble code entry contains:
- `code` (String): The DTC code representing the issue.
- `timestamp` (String, optional): ISO 8601 timestamp when the code was triggered. May be `null` if unavailable.

---
### `attributes(self)`

Returns a single vehicle object, containing identifying information.
Expand Down
2 changes: 2 additions & 0 deletions smartcar/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ def format_path_and_attribute_for_batch(raw_path: str) -> tuple:
"tires/pressure": "tire_pressure",
"": "attributes",
"security": "lock_status",
"diagnostics/system_status": "diagnostic_system_status",
"diagnostics/dtcs": "diagnostic_trouble_codes",
}
formatted_path = raw_path[1:] if raw_path[0] == "/" else raw_path
formatted_attribute = mapper.get(formatted_path, formatted_path)
Expand Down
37 changes: 37 additions & 0 deletions smartcar/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,25 @@ def format_capabilities(capabilities_list: List[dict]) -> List[Capability]:

ChargeLimit = NamedTuple("ChargeLimit", [("limit", float), ("meta", namedtuple)])

DiagnosticSystem = NamedTuple(
"DiagnosticSystem",
[("system_id", str), ("status", str), ("description", Optional[str])],
)

DiagnosticSystemStatus = NamedTuple(
"DiagnosticSystemStatus",
[("systems", List[DiagnosticSystem]), ("meta", namedtuple)],
)

DiagnosticTroubleCode = NamedTuple(
"DiagnosticTroubleCode", [("code", str), ("timestamp", Optional[datetime.datetime])]
)

DiagnosticTroubleCodes = NamedTuple(
"DiagnosticTroubleCodes",
[("active_codes", List[DiagnosticTroubleCode]), ("meta", namedtuple)],
)


class ServiceCost:
total_cost: Optional[float] = None
Expand Down Expand Up @@ -431,6 +450,24 @@ def select_named_tuple(path: str, response_or_dict) -> NamedTuple:
elif path == "service/history":
return ServiceHistory(data, headers)

elif path == "diagnostics/system_status":
systems = [
DiagnosticSystem(
system_id=item["systemId"],
status=item["status"],
description=item.get("description"),
)
for item in data["systems"]
]
return DiagnosticSystemStatus(systems=systems, meta=headers)

elif path == "diagnostics/dtcs":
active_codes = [
DiagnosticTroubleCode(code=item["code"], timestamp=item.get("timestamp"))
for item in data["activeCodes"]
]
return DiagnosticTroubleCodes(active_codes=active_codes, meta=headers)

elif path == "permissions":
return Permissions(
data["permissions"],
Expand Down
38 changes: 38 additions & 0 deletions smartcar/vehicle.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,44 @@ def service_history(
response = helpers.requester("GET", url, headers=headers, params=params)
return types.select_named_tuple(path, response)

def diagnostic_system_status(self) -> types.DiagnosticSystemStatus:
"""
GET Vehicle.diagnostic_system_status
Returns:
DiagnosticSystemStatus: NamedTuple("DiagnosticSystemStatus", [
("systems", List[DiagnosticSystem]),
("meta", namedtuple)
])
Raises:
SmartcarException
"""
path = "diagnostics/system_status"
url = self._format_url(path)
headers = self._get_headers()
response = helpers.requester("GET", url, headers=headers)
return types.select_named_tuple(path, response)

def diagnostic_trouble_codes(self) -> types.DiagnosticTroubleCodes:
"""
GET Vehicle.diagnostic_trouble_codes
Returns:
DiagnosticTroubleCodes: NamedTuple("DiagnosticTroubleCodes", [
("active_codes", List[DiagnosticTroubleCode]),
("meta", namedtuple)
])
Raises:
SmartcarException
"""
path = "diagnostics/dtcs"
url = self._format_url(path)
headers = self._get_headers()
response = helpers.requester("GET", url, headers=headers)
return types.select_named_tuple(path, response)

def location(self) -> types.Location:
"""
GET Vehicle.location
Expand Down
1 change: 1 addition & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ def access_ford(client):
"required:control_charge",
"control_navigation",
"read_service_history",
"read_diagnostics",
]
),
"FORD",
Expand Down
40 changes: 40 additions & 0 deletions tests/e2e/test_vehicle.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,46 @@ def test_service_history(ford_car):
assert response._fields == ("items", "meta")


def test_diagnostic_system_status(ford_car):
diagnostic_status = ford_car.diagnostic_system_status()
assert diagnostic_status is not None
assert isinstance(diagnostic_status, types.DiagnosticSystemStatus)
assert diagnostic_status._fields == ("systems", "meta")

for system in diagnostic_status.systems:
assert isinstance(system, types.DiagnosticSystem)


def test_diagnostic_trouble_codes(ford_car):
dtc_response = ford_car.diagnostic_trouble_codes()
assert dtc_response is not None
assert isinstance(dtc_response, types.DiagnosticTroubleCodes)
assert dtc_response._fields == ("active_codes", "meta")

for code in dtc_response.active_codes:
assert isinstance(code, types.DiagnosticTroubleCode)


def test_batch_diagnostics(ford_car):
batch_response = ford_car.batch(["/diagnostics/system_status", "/diagnostics/dtcs"])
assert batch_response is not None
assert batch_response._fields == (
"diagnostic_system_status",
"diagnostic_trouble_codes",
"meta",
)

diagnostic_status = batch_response.diagnostic_system_status()
assert diagnostic_status is not None
for system in diagnostic_status.systems:
assert isinstance(system, types.DiagnosticSystem)

dtc_response = batch_response.diagnostic_trouble_codes()
assert dtc_response is not None
for code in dtc_response.active_codes:
assert isinstance(code, types.DiagnosticTroubleCode)


def test_batch_success(chevy_volt):
batch = chevy_volt.batch(
[
Expand Down

0 comments on commit 668bdd0

Please sign in to comment.