From 79d8954c1aaa2098b84124b91a4c1997f5f67816 Mon Sep 17 00:00:00 2001 From: Sarah Sayeed Qureshi Date: Wed, 4 Feb 2026 13:24:16 +0100 Subject: [PATCH 1/3] feat(python): add support for get latest icing endpoint --- python/examples/print_latest_icing.py | 42 +++ ...grid_insights_v1_lines_get_latest_icing.py | 252 ++++++++++++++++++ ..._v1_lines_get_latest_icing_response_200.py | 65 +++++ ...ghts_v1_lines_get_latest_icing_x_region.py | 9 + .../grid_insights_api_client/models/icing.py | 82 ++++++ .../models/latest_icing.py | 81 ++++++ .../models/max_icing.py | 84 ++++++ .../models/measurement_result.py | 68 +++++ .../models/span_icing.py | 82 ++++++ .../models/span_phase_icing.py | 102 +++++++ .../models/span_phase_measurement_result.py | 88 ++++++ 11 files changed, 955 insertions(+) create mode 100644 python/examples/print_latest_icing.py create mode 100644 python/heimdall_api_client/grid_insights_api_client/api/line/grid_insights_v1_lines_get_latest_icing.py create mode 100644 python/heimdall_api_client/grid_insights_api_client/models/grid_insights_v1_lines_get_latest_icing_response_200.py create mode 100644 python/heimdall_api_client/grid_insights_api_client/models/grid_insights_v1_lines_get_latest_icing_x_region.py create mode 100644 python/heimdall_api_client/grid_insights_api_client/models/icing.py create mode 100644 python/heimdall_api_client/grid_insights_api_client/models/latest_icing.py create mode 100644 python/heimdall_api_client/grid_insights_api_client/models/max_icing.py create mode 100644 python/heimdall_api_client/grid_insights_api_client/models/measurement_result.py create mode 100644 python/heimdall_api_client/grid_insights_api_client/models/span_icing.py create mode 100644 python/heimdall_api_client/grid_insights_api_client/models/span_phase_icing.py create mode 100644 python/heimdall_api_client/grid_insights_api_client/models/span_phase_measurement_result.py diff --git a/python/examples/print_latest_icing.py b/python/examples/print_latest_icing.py new file mode 100644 index 0000000..96813a8 --- /dev/null +++ b/python/examples/print_latest_icing.py @@ -0,0 +1,42 @@ +import logging +from datetime import datetime, timedelta, timezone + +from heimdall_api_client.client import HeimdallApiClient + +logging.basicConfig(level=logging.WARN) + +client = HeimdallApiClient( + client_id="your_client_id", + client_secret="your_client_secret", +) + +assets = client.get_assets() +grid_owner = assets.data.grid_owners[0] + +print(f"\nGrid Owner: {grid_owner.name}\n") + +for facility in grid_owner.facilities: + line = facility.line + if not line: + print(f"Facility: {facility.name} has no lines.\n") + continue + + line_id = line.id + print(f"Line: {line.name} (ID: {line_id})") + + since = datetime.now(timezone.utc) - timedelta(minutes=30) + latest_icing_response = client.get_latest_icing(line_id=line_id, since=since) + latest_icing = latest_icing_response.data.icing + + max_icing = latest_icing.max_ + print( + f" Max Ice Weight: {max_icing.ice_weight.value} {max_icing.ice_weight.unit} (span phase {max_icing.ice_weight.span_phase_id}, {max_icing.ice_weight.timestamp})" + ) + print( + f" Max Tension: {max_icing.tension.value} {max_icing.tension.unit} (span phase {max_icing.tension.span_phase_id}, {max_icing.tension.timestamp})" + ) + print( + " Max Tension %: " + f"{max_icing.tension_percentage_of_break_strength.value} {max_icing.tension_percentage_of_break_strength.unit} " + f"(span phase {max_icing.tension_percentage_of_break_strength.span_phase_id}, {max_icing.tension_percentage_of_break_strength.timestamp})" + ) diff --git a/python/heimdall_api_client/grid_insights_api_client/api/line/grid_insights_v1_lines_get_latest_icing.py b/python/heimdall_api_client/grid_insights_api_client/api/line/grid_insights_v1_lines_get_latest_icing.py new file mode 100644 index 0000000..fcde73b --- /dev/null +++ b/python/heimdall_api_client/grid_insights_api_client/api/line/grid_insights_v1_lines_get_latest_icing.py @@ -0,0 +1,252 @@ +from http import HTTPStatus +from typing import Any, Optional, Union, cast + +import httpx + +from ...client import AuthenticatedClient, Client +from ...types import Response, UNSET +from ... import errors + +from ...models.grid_insights_v1_lines_get_latest_icing_response_200 import ( + GridInsightsV1LinesGetLatestIcingResponse200, +) +from ...models.grid_insights_v1_lines_get_latest_icing_x_region import GridInsightsV1LinesGetLatestIcingXRegion +from ...models.problem_details import ProblemDetails +from ...models.unit_system import UnitSystem +from ...types import Unset +from uuid import UUID +import datetime + + +def _get_kwargs( + line_id: UUID, + *, + unit_system: Union[Unset, UnitSystem] = UNSET, + since: Union[Unset, datetime.datetime] = UNSET, + x_region: Union[Unset, GridInsightsV1LinesGetLatestIcingXRegion] = GridInsightsV1LinesGetLatestIcingXRegion.EU, +) -> dict[str, Any]: + headers: dict[str, Any] = {} + if not isinstance(x_region, Unset): + headers["x-region"] = str(x_region) + + params: dict[str, Any] = {} + + json_unit_system: Union[Unset, str] = UNSET + if not isinstance(unit_system, Unset): + json_unit_system = unit_system.value + + params["unit_system"] = json_unit_system + + json_since: Union[Unset, str] = UNSET + if not isinstance(since, Unset): + if since.tzinfo is None: + json_since = since.isoformat() + else: + json_since = since.astimezone(datetime.timezone.utc).isoformat() + + params["since"] = json_since + + params = {k: v for k, v in params.items() if v is not UNSET and v is not None} + + _kwargs: dict[str, Any] = { + "method": "get", + "url": "/grid_insights/v1/lines/{line_id}/icing/latest".format( + line_id=line_id, + ), + "params": params, + } + + _kwargs["headers"] = headers + return _kwargs + + +def _parse_response( + *, client: Union[AuthenticatedClient, Client], response: httpx.Response +) -> Optional[Union[Any, GridInsightsV1LinesGetLatestIcingResponse200, ProblemDetails]]: + if response.status_code == 200: + response_200 = GridInsightsV1LinesGetLatestIcingResponse200.from_dict(response.json()) + + return response_200 + if response.status_code == 401: + response_401 = cast(Any, None) + return response_401 + if response.status_code == 403: + response_403 = ProblemDetails.from_dict(response.json()) + + return response_403 + if response.status_code == 404: + response_404 = cast(Any, None) + return response_404 + if response.status_code == 500: + response_500 = ProblemDetails.from_dict(response.json()) + + return response_500 + if client.raise_on_unexpected_status: + raise errors.UnexpectedStatus(response.status_code, response.content) + else: + return None + + +def _build_response( + *, client: Union[AuthenticatedClient, Client], response: httpx.Response +) -> Response[Union[Any, GridInsightsV1LinesGetLatestIcingResponse200, ProblemDetails]]: + return Response( + status_code=HTTPStatus(response.status_code), + content=response.content, + headers=response.headers, + parsed=_parse_response(client=client, response=response), + ) + + +def sync_detailed( + line_id: UUID, + *, + client: Union[AuthenticatedClient, Client], + unit_system: Union[Unset, UnitSystem] = UNSET, + since: Union[Unset, datetime.datetime] = UNSET, + x_region: Union[Unset, GridInsightsV1LinesGetLatestIcingXRegion] = GridInsightsV1LinesGetLatestIcingXRegion.EU, +) -> Response[Union[Any, GridInsightsV1LinesGetLatestIcingResponse200, ProblemDetails]]: + """Get latest icing + + Get the most recent icing measurements for the line, including maximum values and per-span/phase metrics. + + Args: + line_id (UUID): + unit_system (Union[Unset, UnitSystem]): + since (Union[Unset, datetime.datetime]): + x_region (Union[Unset, GridInsightsV1LinesGetLatestIcingXRegion]): Default: + GridInsightsV1LinesGetLatestIcingXRegion.EU. + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Response[Union[Any, GridInsightsV1LinesGetLatestIcingResponse200, ProblemDetails]] + """ + + kwargs = _get_kwargs( + line_id=line_id, + unit_system=unit_system, + since=since, + x_region=x_region, + ) + + response = client.get_httpx_client().request( + **kwargs, + ) + + return _build_response(client=client, response=response) + + +def sync( + line_id: UUID, + *, + client: Union[AuthenticatedClient, Client], + unit_system: Union[Unset, UnitSystem] = UNSET, + since: Union[Unset, datetime.datetime] = UNSET, + x_region: Union[Unset, GridInsightsV1LinesGetLatestIcingXRegion] = GridInsightsV1LinesGetLatestIcingXRegion.EU, +) -> Optional[Union[Any, GridInsightsV1LinesGetLatestIcingResponse200, ProblemDetails]]: + """Get latest icing + + Get the most recent icing measurements for the line, including maximum values and per-span/phase metrics. + + Args: + line_id (UUID): + unit_system (Union[Unset, UnitSystem]): + since (Union[Unset, datetime.datetime]): + x_region (Union[Unset, GridInsightsV1LinesGetLatestIcingXRegion]): Default: + GridInsightsV1LinesGetLatestIcingXRegion.EU. + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Union[Any, GridInsightsV1LinesGetLatestIcingResponse200, ProblemDetails] + """ + + return sync_detailed( + line_id=line_id, + client=client, + unit_system=unit_system, + since=since, + x_region=x_region, + ).parsed + + +async def asyncio_detailed( + line_id: UUID, + *, + client: Union[AuthenticatedClient, Client], + unit_system: Union[Unset, UnitSystem] = UNSET, + since: Union[Unset, datetime.datetime] = UNSET, + x_region: Union[Unset, GridInsightsV1LinesGetLatestIcingXRegion] = GridInsightsV1LinesGetLatestIcingXRegion.EU, +) -> Response[Union[Any, GridInsightsV1LinesGetLatestIcingResponse200, ProblemDetails]]: + """Get latest icing + + Get the most recent icing measurements for the line, including maximum values and per-span/phase metrics. + + Args: + line_id (UUID): + unit_system (Union[Unset, UnitSystem]): + since (Union[Unset, datetime.datetime]): + x_region (Union[Unset, GridInsightsV1LinesGetLatestIcingXRegion]): Default: + GridInsightsV1LinesGetLatestIcingXRegion.EU. + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Response[Union[Any, GridInsightsV1LinesGetLatestIcingResponse200, ProblemDetails]] + """ + + kwargs = _get_kwargs( + line_id=line_id, + unit_system=unit_system, + since=since, + x_region=x_region, + ) + + response = await client.get_async_httpx_client().request(**kwargs) + + return _build_response(client=client, response=response) + + +async def asyncio( + line_id: UUID, + *, + client: Union[AuthenticatedClient, Client], + unit_system: Union[Unset, UnitSystem] = UNSET, + since: Union[Unset, datetime.datetime] = UNSET, + x_region: Union[Unset, GridInsightsV1LinesGetLatestIcingXRegion] = GridInsightsV1LinesGetLatestIcingXRegion.EU, +) -> Optional[Union[Any, GridInsightsV1LinesGetLatestIcingResponse200, ProblemDetails]]: + """Get latest icing + + Get the most recent icing measurements for the line, including maximum values and per-span/phase metrics. + + Args: + line_id (UUID): + unit_system (Union[Unset, UnitSystem]): + since (Union[Unset, datetime.datetime]): + x_region (Union[Unset, GridInsightsV1LinesGetLatestIcingXRegion]): Default: + GridInsightsV1LinesGetLatestIcingXRegion.EU. + + Raises: + errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. + httpx.TimeoutException: If the request takes longer than Client.timeout. + + Returns: + Union[Any, GridInsightsV1LinesGetLatestIcingResponse200, ProblemDetails] + """ + + return ( + await asyncio_detailed( + line_id=line_id, + client=client, + unit_system=unit_system, + since=since, + x_region=x_region, + ) + ).parsed diff --git a/python/heimdall_api_client/grid_insights_api_client/models/grid_insights_v1_lines_get_latest_icing_response_200.py b/python/heimdall_api_client/grid_insights_api_client/models/grid_insights_v1_lines_get_latest_icing_response_200.py new file mode 100644 index 0000000..bda4294 --- /dev/null +++ b/python/heimdall_api_client/grid_insights_api_client/models/grid_insights_v1_lines_get_latest_icing_response_200.py @@ -0,0 +1,65 @@ +from collections.abc import Mapping +from typing import Any, TypeVar, TYPE_CHECKING + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +if TYPE_CHECKING: + from ..models.latest_icing import LatestIcing + + +T = TypeVar("T", bound="GridInsightsV1LinesGetLatestIcingResponse200") + + +@_attrs_define +class GridInsightsV1LinesGetLatestIcingResponse200: + """ + Attributes: + data (LatestIcing): + """ + + data: "LatestIcing" + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + data = self.data.to_dict() + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "data": data, + } + ) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.latest_icing import LatestIcing + + d = dict(src_dict) + data = LatestIcing.from_dict(d.pop("data")) + + grid_insights_v1_lines_get_latest_icing_response_200 = cls( + data=data, + ) + + grid_insights_v1_lines_get_latest_icing_response_200.additional_properties = d + return grid_insights_v1_lines_get_latest_icing_response_200 + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/python/heimdall_api_client/grid_insights_api_client/models/grid_insights_v1_lines_get_latest_icing_x_region.py b/python/heimdall_api_client/grid_insights_api_client/models/grid_insights_v1_lines_get_latest_icing_x_region.py new file mode 100644 index 0000000..500532b --- /dev/null +++ b/python/heimdall_api_client/grid_insights_api_client/models/grid_insights_v1_lines_get_latest_icing_x_region.py @@ -0,0 +1,9 @@ +from enum import Enum + + +class GridInsightsV1LinesGetLatestIcingXRegion(str, Enum): + EU = "eu" + US = "us" + + def __str__(self) -> str: + return str(self.value) diff --git a/python/heimdall_api_client/grid_insights_api_client/models/icing.py b/python/heimdall_api_client/grid_insights_api_client/models/icing.py new file mode 100644 index 0000000..6912ef5 --- /dev/null +++ b/python/heimdall_api_client/grid_insights_api_client/models/icing.py @@ -0,0 +1,82 @@ +from collections.abc import Mapping +from typing import Any, TypeVar, TYPE_CHECKING + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +if TYPE_CHECKING: + from ..models.max_icing import MaxIcing + from ..models.span_icing import SpanIcing + + +T = TypeVar("T", bound="Icing") + + +@_attrs_define +class Icing: + """ + Attributes: + max_ (MaxIcing): The maximum icing data across all span phases on the line. + spans (list[SpanIcing]): List of spans on the line with their icing data. + """ + + max_: "MaxIcing" + spans: list["SpanIcing"] + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + max_ = self.max_.to_dict() + + spans = [] + for spans_item_data in self.spans: + spans_item = spans_item_data.to_dict() + spans.append(spans_item) + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "max": max_, + "spans": spans, + } + ) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.max_icing import MaxIcing + from ..models.span_icing import SpanIcing + + d = dict(src_dict) + max_ = MaxIcing.from_dict(d.pop("max")) + + spans = [] + _spans = d.pop("spans") + for spans_item_data in _spans: + spans_item = SpanIcing.from_dict(spans_item_data) + spans.append(spans_item) + + icing = cls( + max_=max_, + spans=spans, + ) + + icing.additional_properties = d + return icing + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/python/heimdall_api_client/grid_insights_api_client/models/latest_icing.py b/python/heimdall_api_client/grid_insights_api_client/models/latest_icing.py new file mode 100644 index 0000000..0831dcc --- /dev/null +++ b/python/heimdall_api_client/grid_insights_api_client/models/latest_icing.py @@ -0,0 +1,81 @@ +from collections.abc import Mapping +from typing import Any, TypeVar, TYPE_CHECKING + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +if TYPE_CHECKING: + from ..models.icing import Icing + + +T = TypeVar("T", bound="LatestIcing") + + +@_attrs_define +class LatestIcing: + """ + Attributes: + metric (str): What kind of data does this response contain. Example: Icing. + unit (str): The unit description for the response (multiple units across measurements). + icing (Icing): The icing measurements including max and per-span/phase values. + """ + + metric: str + unit: str + icing: "Icing" + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + metric = self.metric + + unit = self.unit + + icing = self.icing.to_dict() + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "metric": metric, + "unit": unit, + "icing": icing, + } + ) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.icing import Icing + + d = dict(src_dict) + metric = d.pop("metric") + + unit = d.pop("unit") + + icing = Icing.from_dict(d.pop("icing")) + + latest_icing = cls( + metric=metric, + unit=unit, + icing=icing, + ) + + latest_icing.additional_properties = d + return latest_icing + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/python/heimdall_api_client/grid_insights_api_client/models/max_icing.py b/python/heimdall_api_client/grid_insights_api_client/models/max_icing.py new file mode 100644 index 0000000..047cf7b --- /dev/null +++ b/python/heimdall_api_client/grid_insights_api_client/models/max_icing.py @@ -0,0 +1,84 @@ +from collections.abc import Mapping +from typing import Any, TypeVar, TYPE_CHECKING + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +if TYPE_CHECKING: + from ..models.span_phase_measurement_result import SpanPhaseMeasurementResult + + +T = TypeVar("T", bound="MaxIcing") + + +@_attrs_define +class MaxIcing: + """ + Attributes: + ice_weight (SpanPhaseMeasurementResult): The maximum mass of ice accumulated on the conductor. + tension (SpanPhaseMeasurementResult): The maximum mechanical tension force in the conductor. + tension_percentage_of_break_strength (SpanPhaseMeasurementResult): Maximum safety-critical metric showing how + close the conductor is to its breaking point. + """ + + ice_weight: "SpanPhaseMeasurementResult" + tension: "SpanPhaseMeasurementResult" + tension_percentage_of_break_strength: "SpanPhaseMeasurementResult" + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + ice_weight = self.ice_weight.to_dict() + + tension = self.tension.to_dict() + + tension_percentage_of_break_strength = self.tension_percentage_of_break_strength.to_dict() + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "ice_weight": ice_weight, + "tension": tension, + "tension_percentage_of_break_strength": tension_percentage_of_break_strength, + } + ) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.span_phase_measurement_result import SpanPhaseMeasurementResult + + d = dict(src_dict) + ice_weight = SpanPhaseMeasurementResult.from_dict(d.pop("ice_weight")) + + tension = SpanPhaseMeasurementResult.from_dict(d.pop("tension")) + + tension_percentage_of_break_strength = SpanPhaseMeasurementResult.from_dict( + d.pop("tension_percentage_of_break_strength") + ) + + max_icing = cls( + ice_weight=ice_weight, + tension=tension, + tension_percentage_of_break_strength=tension_percentage_of_break_strength, + ) + + max_icing.additional_properties = d + return max_icing + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/python/heimdall_api_client/grid_insights_api_client/models/measurement_result.py b/python/heimdall_api_client/grid_insights_api_client/models/measurement_result.py new file mode 100644 index 0000000..1a5ca7d --- /dev/null +++ b/python/heimdall_api_client/grid_insights_api_client/models/measurement_result.py @@ -0,0 +1,68 @@ +from collections.abc import Mapping +from typing import Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + + +T = TypeVar("T", bound="MeasurementResult") + + +@_attrs_define +class MeasurementResult: + """ + Attributes: + value (float): The numerical value of the measurement. + unit (str): The unit of the measurement. + """ + + value: float + unit: str + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + value = self.value + + unit = self.unit + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "value": value, + "unit": unit, + } + ) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + value = d.pop("value") + + unit = d.pop("unit") + + measurement_result = cls( + value=value, + unit=unit, + ) + + measurement_result.additional_properties = d + return measurement_result + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/python/heimdall_api_client/grid_insights_api_client/models/span_icing.py b/python/heimdall_api_client/grid_insights_api_client/models/span_icing.py new file mode 100644 index 0000000..ee7e103 --- /dev/null +++ b/python/heimdall_api_client/grid_insights_api_client/models/span_icing.py @@ -0,0 +1,82 @@ +from collections.abc import Mapping +from typing import Any, TypeVar, TYPE_CHECKING + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from uuid import UUID + +if TYPE_CHECKING: + from ..models.span_phase_icing import SpanPhaseIcing + + +T = TypeVar("T", bound="SpanIcing") + + +@_attrs_define +class SpanIcing: + """ + Attributes: + span_id (UUID): The id of the span. + span_phases (list[SpanPhaseIcing]): List of span phases (conductors) within this span. + """ + + span_id: UUID + span_phases: list["SpanPhaseIcing"] + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + span_id = str(self.span_id) + + span_phases = [] + for span_phases_item_data in self.span_phases: + span_phases_item = span_phases_item_data.to_dict() + span_phases.append(span_phases_item) + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "span_id": span_id, + "span_phases": span_phases, + } + ) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.span_phase_icing import SpanPhaseIcing + + d = dict(src_dict) + span_id = UUID(d.pop("span_id")) + + span_phases = [] + _span_phases = d.pop("span_phases") + for span_phases_item_data in _span_phases: + span_phases_item = SpanPhaseIcing.from_dict(span_phases_item_data) + span_phases.append(span_phases_item) + + span_icing = cls( + span_id=span_id, + span_phases=span_phases, + ) + + span_icing.additional_properties = d + return span_icing + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/python/heimdall_api_client/grid_insights_api_client/models/span_phase_icing.py b/python/heimdall_api_client/grid_insights_api_client/models/span_phase_icing.py new file mode 100644 index 0000000..a935ce4 --- /dev/null +++ b/python/heimdall_api_client/grid_insights_api_client/models/span_phase_icing.py @@ -0,0 +1,102 @@ +from collections.abc import Mapping +from typing import Any, TypeVar, TYPE_CHECKING + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from dateutil.parser import isoparse +import datetime +from uuid import UUID + +if TYPE_CHECKING: + from ..models.measurement_result import MeasurementResult + + +T = TypeVar("T", bound="SpanPhaseIcing") + + +@_attrs_define +class SpanPhaseIcing: + """ + Attributes: + span_phase_id (UUID): The id of the span phase the measurement belongs to. + timestamp (datetime.datetime): Time (UTC) when the icing measurements were calculated for the span phase. + ice_weight (MeasurementResult): The mass of ice accumulated on the conductor. + tension (MeasurementResult): The mechanical tension force in the conductor. + tension_percentage_of_break_strength (MeasurementResult): Safety-critical metric showing how close the conductor + is to its breaking point. + """ + + span_phase_id: UUID + timestamp: datetime.datetime + ice_weight: "MeasurementResult" + tension: "MeasurementResult" + tension_percentage_of_break_strength: "MeasurementResult" + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + span_phase_id = str(self.span_phase_id) + + timestamp = self.timestamp.isoformat() + + ice_weight = self.ice_weight.to_dict() + + tension = self.tension.to_dict() + + tension_percentage_of_break_strength = self.tension_percentage_of_break_strength.to_dict() + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "span_phase_id": span_phase_id, + "timestamp": timestamp, + "ice_weight": ice_weight, + "tension": tension, + "tension_percentage_of_break_strength": tension_percentage_of_break_strength, + } + ) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.measurement_result import MeasurementResult + + d = dict(src_dict) + span_phase_id = UUID(d.pop("span_phase_id")) + + timestamp = isoparse(d.pop("timestamp")) + + ice_weight = MeasurementResult.from_dict(d.pop("ice_weight")) + + tension = MeasurementResult.from_dict(d.pop("tension")) + + tension_percentage_of_break_strength = MeasurementResult.from_dict(d.pop("tension_percentage_of_break_strength")) + + span_phase_icing = cls( + span_phase_id=span_phase_id, + timestamp=timestamp, + ice_weight=ice_weight, + tension=tension, + tension_percentage_of_break_strength=tension_percentage_of_break_strength, + ) + + span_phase_icing.additional_properties = d + return span_phase_icing + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/python/heimdall_api_client/grid_insights_api_client/models/span_phase_measurement_result.py b/python/heimdall_api_client/grid_insights_api_client/models/span_phase_measurement_result.py new file mode 100644 index 0000000..ad52124 --- /dev/null +++ b/python/heimdall_api_client/grid_insights_api_client/models/span_phase_measurement_result.py @@ -0,0 +1,88 @@ +from collections.abc import Mapping +from typing import Any, TypeVar + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from dateutil.parser import isoparse +import datetime +from uuid import UUID + + +T = TypeVar("T", bound="SpanPhaseMeasurementResult") + + +@_attrs_define +class SpanPhaseMeasurementResult: + """ + Attributes: + timestamp (datetime.datetime): Time (UTC) when the icing measurements were calculated for the span phase. + span_phase_id (UUID): The id of the span phase the measurement belongs to. + value (float): The numerical value of the measurement. + unit (str): The unit of the measurement value. + """ + + timestamp: datetime.datetime + span_phase_id: UUID + value: float + unit: str + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + timestamp = self.timestamp.isoformat() + + span_phase_id = str(self.span_phase_id) + + value = self.value + + unit = self.unit + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update( + { + "timestamp": timestamp, + "span_phase_id": span_phase_id, + "value": value, + "unit": unit, + } + ) + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + timestamp = isoparse(d.pop("timestamp")) + + span_phase_id = UUID(d.pop("span_phase_id")) + + value = d.pop("value") + + unit = d.pop("unit") + + span_phase_measurement_result = cls( + timestamp=timestamp, + span_phase_id=span_phase_id, + value=value, + unit=unit, + ) + + span_phase_measurement_result.additional_properties = d + return span_phase_measurement_result + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties From b108360292525fb45e31e3d816127cee12acd991 Mon Sep 17 00:00:00 2001 From: Sarah Sayeed Qureshi Date: Wed, 4 Feb 2026 13:27:43 +0100 Subject: [PATCH 2/3] feat: add missing files --- .../assets_api_client/models/assets.py | 46 +++++++++++++------ python/heimdall_api_client/client.py | 21 +++++++++ python/heimdall_api_client/grid_insights.py | 34 +++++++++++++- .../models/__init__.py | 18 ++++++++ 4 files changed, 104 insertions(+), 15 deletions(-) diff --git a/python/heimdall_api_client/assets_api_client/models/assets.py b/python/heimdall_api_client/assets_api_client/models/assets.py index 5e4b73a..9f24c1e 100644 --- a/python/heimdall_api_client/assets_api_client/models/assets.py +++ b/python/heimdall_api_client/assets_api_client/models/assets.py @@ -1,59 +1,77 @@ from collections.abc import Mapping -from typing import Any, TypeVar, TYPE_CHECKING +from typing import Any, TypeVar, Optional, BinaryIO, TextIO, TYPE_CHECKING, Generator from attrs import define as _attrs_define from attrs import field as _attrs_field +from ..types import UNSET, Unset + +from typing import cast if TYPE_CHECKING: - from ..models.grid_owner import GridOwner + from ..models.grid_owner import GridOwner + + + T = TypeVar("T", bound="Assets") + @_attrs_define class Assets: - """ - Attributes: - grid_owners (list['GridOwner']): List of grid owners the API consumer has access to. - """ + """ + Attributes: + grid_owners (list['GridOwner']): List of grid owners the API consumer has access to. + """ - grid_owners: list["GridOwner"] + grid_owners: list['GridOwner'] additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + + + def to_dict(self) -> dict[str, Any]: + from ..models.grid_owner import GridOwner grid_owners = [] for grid_owners_item_data in self.grid_owners: grid_owners_item = grid_owners_item_data.to_dict() grid_owners.append(grid_owners_item) + + + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update( - { - "grid_owners": grid_owners, - } - ) + field_dict.update({ + "grid_owners": grid_owners, + }) return field_dict + + @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: from ..models.grid_owner import GridOwner - d = dict(src_dict) grid_owners = [] _grid_owners = d.pop("grid_owners") - for grid_owners_item_data in _grid_owners: + for grid_owners_item_data in (_grid_owners): grid_owners_item = GridOwner.from_dict(grid_owners_item_data) + + grid_owners.append(grid_owners_item) + assets = cls( grid_owners=grid_owners, ) + assets.additional_properties = d return assets diff --git a/python/heimdall_api_client/client.py b/python/heimdall_api_client/client.py index fa931c3..5c04eac 100644 --- a/python/heimdall_api_client/client.py +++ b/python/heimdall_api_client/client.py @@ -1,6 +1,8 @@ import logging from typing import List, Optional from uuid import UUID +import datetime +from heimdall_api_client.grid_insights_api_client.models.unit_system import UnitSystem from heimdall_api_client.auth import AuthService from heimdall_api_client.assets import get_assets from heimdall_api_client.capacity_monitoring import ( @@ -132,3 +134,22 @@ def get_latest_current(self, line_id: UUID): from heimdall_api_client.grid_insights import get_latest_current return get_latest_current(client=self._get_authenticated_client(), line_id=line_id, region=self._get_region()) + + def get_latest_icing( + self, + line_id: UUID, + unit_system: UnitSystem | str | None = None, + since: datetime.datetime | None = None, + ): + """ + Returns the latest icing measurements for a given line. + """ + from heimdall_api_client.grid_insights import get_latest_icing + + return get_latest_icing( + client=self._get_authenticated_client(), + line_id=line_id, + region=self._get_region(), + unit_system=unit_system, + since=since, + ) diff --git a/python/heimdall_api_client/grid_insights.py b/python/heimdall_api_client/grid_insights.py index 304e138..46e9639 100644 --- a/python/heimdall_api_client/grid_insights.py +++ b/python/heimdall_api_client/grid_insights.py @@ -1,5 +1,8 @@ from uuid import UUID +import datetime from heimdall_api_client.assets_api_client.client import AuthenticatedClient +from heimdall_api_client.grid_insights_api_client.models.unit_system import UnitSystem +from heimdall_api_client.grid_insights_api_client.types import UNSET def get_latest_conductor_temperature(client: AuthenticatedClient, line_id: UUID, region: str): @@ -9,7 +12,7 @@ def get_latest_conductor_temperature(client: AuthenticatedClient, line_id: UUID, response = get_latest_conductor_temperature.sync_detailed(client=client, line_id=line_id, x_region=region) if response.status_code != 200: - raise Exception(f"Error fetching latest conductor temperature: {response.status_code} {response.text}") + raise Exception(f"Error fetching latest conductor temperature: {response.status_code} {response.content}") return response.parsed @@ -22,3 +25,32 @@ def get_latest_current(client: AuthenticatedClient, line_id: UUID, region: str): if response.status_code != 200: raise Exception(f"Error fetching latest current: {response.status_code} {response.text}") return response.parsed + + +def get_latest_icing( + client: AuthenticatedClient, + line_id: UUID, + region: str, + unit_system: UnitSystem | str | None = None, + since: datetime.datetime | None = None, +): + from heimdall_api_client.grid_insights_api_client.api.line import ( + grid_insights_v1_lines_get_latest_icing as get_latest_icing, + ) + + unit_system_value = UNSET + if unit_system is not None: + unit_system_value = unit_system if isinstance(unit_system, UnitSystem) else UnitSystem(unit_system) + + since_value = UNSET if since is None else since + + response = get_latest_icing.sync_detailed( + client=client, + line_id=line_id, + x_region=region, + unit_system=unit_system_value, + since=since_value, + ) + if response.status_code != 200: + raise Exception(f"Error fetching latest icing: {response.status_code} {response.text}") + return response.parsed diff --git a/python/heimdall_api_client/grid_insights_api_client/models/__init__.py b/python/heimdall_api_client/grid_insights_api_client/models/__init__.py index ee7626f..89a9671 100644 --- a/python/heimdall_api_client/grid_insights_api_client/models/__init__.py +++ b/python/heimdall_api_client/grid_insights_api_client/models/__init__.py @@ -10,10 +10,19 @@ ) from .grid_insights_v1_lines_get_latest_current_response_200 import GridInsightsV1LinesGetLatestCurrentResponse200 from .grid_insights_v1_lines_get_latest_current_x_region import GridInsightsV1LinesGetLatestCurrentXRegion +from .grid_insights_v1_lines_get_latest_icing_response_200 import GridInsightsV1LinesGetLatestIcingResponse200 +from .grid_insights_v1_lines_get_latest_icing_x_region import GridInsightsV1LinesGetLatestIcingXRegion +from .icing import Icing from .latest_conductor_temperature import LatestConductorTemperature +from .latest_icing import LatestIcing from .latest_line_current import LatestLineCurrent from .line_current import LineCurrent +from .max_icing import MaxIcing +from .measurement_result import MeasurementResult from .problem_details import ProblemDetails +from .span_icing import SpanIcing +from .span_phase_icing import SpanPhaseIcing +from .span_phase_measurement_result import SpanPhaseMeasurementResult from .unit_system import UnitSystem __all__ = ( @@ -23,9 +32,18 @@ "GridInsightsV1LinesGetLatestConductorTemperatureXRegion", "GridInsightsV1LinesGetLatestCurrentResponse200", "GridInsightsV1LinesGetLatestCurrentXRegion", + "GridInsightsV1LinesGetLatestIcingResponse200", + "GridInsightsV1LinesGetLatestIcingXRegion", + "Icing", "LatestConductorTemperature", + "LatestIcing", "LatestLineCurrent", "LineCurrent", + "MaxIcing", + "MeasurementResult", "ProblemDetails", + "SpanIcing", + "SpanPhaseIcing", + "SpanPhaseMeasurementResult", "UnitSystem", ) From c957d5cf9a9f52a9ce6869b7e74c9032ea133892 Mon Sep 17 00:00:00 2001 From: Sarah Sayeed Qureshi Date: Thu, 5 Feb 2026 23:14:32 +0100 Subject: [PATCH 3/3] simplify imports and cleanup --- .../assets_api_client/models/assets.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/python/heimdall_api_client/assets_api_client/models/assets.py b/python/heimdall_api_client/assets_api_client/models/assets.py index 9f24c1e..e40077c 100644 --- a/python/heimdall_api_client/assets_api_client/models/assets.py +++ b/python/heimdall_api_client/assets_api_client/models/assets.py @@ -1,13 +1,9 @@ from collections.abc import Mapping -from typing import Any, TypeVar, Optional, BinaryIO, TextIO, TYPE_CHECKING, Generator +from typing import Any, TypeVar, TYPE_CHECKING from attrs import define as _attrs_define from attrs import field as _attrs_field -from ..types import UNSET, Unset - -from typing import cast - if TYPE_CHECKING: from ..models.grid_owner import GridOwner @@ -21,7 +17,7 @@ @_attrs_define class Assets: - """ + """ Attributes: grid_owners (list['GridOwner']): List of grid owners the API consumer has access to. """ @@ -34,7 +30,6 @@ class Assets: def to_dict(self) -> dict[str, Any]: - from ..models.grid_owner import GridOwner grid_owners = [] for grid_owners_item_data in self.grid_owners: grid_owners_item = grid_owners_item_data.to_dict()