Skip to content

Commit

Permalink
Switch to Pydantic v2 (#27)
Browse files Browse the repository at this point in the history
* Upgrade to v2

* Update example and deps

* Use json model validate

* Add validation error

* fix linting

* fix model validation
  • Loading branch information
drc38 authored Jun 26, 2024
1 parent 066a124 commit d84e373
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 80 deletions.
3 changes: 2 additions & 1 deletion examples/fronius_sw_example.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import asyncio
from pydantic import BaseSettings, SecretStr
from pydantic import SecretStr
from pydantic_settings import BaseSettings
from fronius_solarweb.api import Fronius_Solarweb


Expand Down
56 changes: 49 additions & 7 deletions fronius_solarweb/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ async def _check_api_response(self, response):
raise NotFoundException()

response.raise_for_status()

# returns dict type not string
return response.json()

@retry(
Expand All @@ -86,7 +86,14 @@ async def get_api_release_info(self) -> ReleaseInfo:
headers=self._common_headers,
)
json_data = await self._check_api_response(r)
return ReleaseInfo(**json_data)
try:
model_data = ReleaseInfo.model_validate(json_data)
except ValidationError as e:
_LOGGER.error(
f"Unable to validate data receieved from SolarWeb api: '{json_data}'"
)
_LOGGER.error(e)
return model_data

@retry(
wait=wait_random_exponential(multiplier=2, max=60),
Expand All @@ -102,7 +109,14 @@ async def get_pvsystems_meta_data(self) -> list[PvSystemMetaData]:
headers=self._common_headers,
)
json_data = await self._check_api_response(r)
return PvSystemsMetaData(**json_data).pvSystems
try:
model_data = PvSystemsMetaData.model_validate(json_data).pvSystems
except ValidationError as e:
_LOGGER.error(
f"Unable to validate data receieved from SolarWeb api: '{json_data}'"
)
_LOGGER.error(e)
return model_data

@retry(
wait=wait_random_exponential(multiplier=2, max=60),
Expand All @@ -118,7 +132,14 @@ async def get_pvsystem_meta_data(self) -> PvSystemMetaData:
headers=self._common_headers,
)
json_data = await self._check_api_response(r)
return PvSystemMetaData(**json_data)
try:
model_data = PvSystemMetaData.model_validate(json_data)
except ValidationError as e:
_LOGGER.error(
f"Unable to validate data receieved from SolarWeb api: '{json_data}'"
)
_LOGGER.error(e)
return model_data

@retry(
wait=wait_random_exponential(multiplier=2, max=60),
Expand All @@ -134,7 +155,14 @@ async def get_devices_meta_data(self) -> list[DeviceMetaData]:
headers=self._common_headers,
)
json_data = await self._check_api_response(r)
return DevicesMetaData(**json_data).devices
try:
model_data = DevicesMetaData.model_validate(json_data).devices
except ValidationError as e:
_LOGGER.error(
f"Unable to validate data receieved from SolarWeb api: '{json_data}'"
)
_LOGGER.error(e)
return model_data

@retry(
wait=wait_random_exponential(multiplier=2, max=60),
Expand All @@ -150,7 +178,14 @@ async def get_system_flow_data(self, tz: str = "zulu") -> PvSystemFlowData:
headers=self._common_headers,
)
json_data = await self._check_api_response(r)
return PvSystemFlowData(**json_data)
try:
model_data = PvSystemFlowData.model_validate(json_data)
except ValidationError as e:
_LOGGER.error(
f"Unable to validate data receieved from SolarWeb api: '{json_data}'"
)
_LOGGER.error(e)
return model_data

async def get_system_aggr_data_v2(
self, period: str = "total"
Expand All @@ -161,4 +196,11 @@ async def get_system_aggr_data_v2(
headers=self._common_headers,
)
json_data = await self._check_api_response(r)
return PvSystemAggrDataV2(**json_data)
try:
model_data = PvSystemAggrDataV2.model_validate(json_data)
except ValidationError as e:
_LOGGER.error(
f"Unable to validate data receieved from SolarWeb api: '{json_data}'"
)
_LOGGER.error(e)
return model_data
80 changes: 40 additions & 40 deletions fronius_solarweb/schema/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,68 +5,68 @@


class Firmware(BaseModel):
updateAvailable: Optional[bool]
installedVersion: Optional[str]
availableVersion: Optional[str]
updateAvailable: Optional[bool] = None
installedVersion: Optional[str] = None
availableVersion: Optional[str] = None


class Links(BaseModel):
first: Optional[str]
prev: Optional[str]
self: Optional[str]
next: Optional[str]
last: Optional[str]
first: Optional[str] = None
prev: Optional[str] = None
self: Optional[str] = None
next: Optional[str] = None
last: Optional[str] = None
totalItemsCount: int


class Power(BaseModel):
dc1: Optional[float]
dc2: Optional[float]
dc3: Optional[float]
dc4: Optional[float]
dc1: Optional[float] = None
dc2: Optional[float] = None
dc3: Optional[float] = None
dc4: Optional[float] = None


class Sensor(BaseModel):
sensorType: Optional[str]
sensorName: Optional[str]
isActive: Optional[bool]
activationDate: Optional[datetime]
deactivationDate: Optional[datetime]
sensorType: Optional[str] = None
sensorName: Optional[str] = None
isActive: Optional[bool] = None
activationDate: Optional[datetime] = None
deactivationDate: Optional[datetime] = None


class DeviceMetaData(BaseModel):
# inverter
deviceType: Optional[str]
deviceType: Optional[str] = None
deviceId: str
deviceName: Optional[str]
deviceManufacturer: Optional[str]
serialnumber: Optional[str]
deviceTypeDetails: Optional[str]
dataloggerId: Optional[str]
nodeType: Optional[str]
numberMPPTrackers: Optional[int]
numberPhases: Optional[int]
peakPower: Optional[Power]
nominalAcPower: Optional[float]
firmware: Optional[Firmware]
isActive: Optional[bool]
activationDate: Optional[datetime]
deactivationDate: Optional[datetime]
deviceName: Optional[str] = None
deviceManufacturer: Optional[str] = None
serialnumber: Optional[str] = None
deviceTypeDetails: Optional[str] = None
dataloggerId: Optional[str] = None
nodeType: Optional[str] = None
numberMPPTrackers: Optional[int] = None
numberPhases: Optional[int] = None
peakPower: Optional[Power] = None
nominalAcPower: Optional[float] = None
firmware: Optional[Firmware] = None
isActive: Optional[bool] = None
activationDate: Optional[datetime] = None
deactivationDate: Optional[datetime] = None
# battery extras
capacity: Optional[float]
capacity: Optional[float] = None
# sensor extras
sensors: Optional[Sensor]
sensors: Optional[Sensor] = None
# smart meter extras
deviceCategory: Optional[str]
deviceLocation: Optional[str]
deviceCategory: Optional[str] = None
deviceLocation: Optional[str] = None
# EV charger extras
isOnline: Optional[bool]
isOnline: Optional[bool] = None
# Data logger extras
# Swagger documentation extras
ipAddressV4: Optional[str]
ipAddressV4: Optional[str] = None


class DevicesMetaData(BaseModel):
devices: Optional[list[DeviceMetaData]]
devices: Optional[list[DeviceMetaData]] = None
# Swagger documentation extras
links: Optional[Links]
links: Optional[Links] = None
58 changes: 29 additions & 29 deletions fronius_solarweb/schema/pvsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,61 +7,61 @@


class Address(BaseModel):
street: Optional[str]
zipCode: Optional[str]
city: Optional[str]
state: Optional[str]
country: Optional[str]
street: Optional[str] = None
zipCode: Optional[str] = None
city: Optional[str] = None
state: Optional[str] = None
country: Optional[str] = None


class Channel(BaseModel):
channelName: Optional[str]
channelType: Optional[str]
unit: Optional[str]
value: Optional[float | str]
channelName: Optional[str] = None
channelType: Optional[str] = None
unit: Optional[str] = None
value: Optional[float | str] = None


class Data(BaseModel):
logDateTime: Optional[datetime]
channels: Optional[list[Channel]]
logDateTime: Optional[datetime] = None
channels: Optional[list[Channel]] = None


class AggrData(BaseModel):
logDateTime: Optional[str]
channels: Optional[list[Channel]]
logDateTime: Optional[str] = None
channels: Optional[list[Channel]] = None


class Status(BaseModel):
isOnline: bool
battMode: Optional[str]
battMode: Optional[str] = None


class PvSystemMetaData(BaseModel):
pvSystemId: str
name: Optional[str]
status: Optional[Status]
address: Optional[Address]
timezone: Optional[datetime]
pictureURL: Optional[str]
peakPower: Optional[float]
meteoData: Optional[str]
lastImport: Optional[datetime]
installationDate: Optional[datetime]
name: Optional[str] = None
status: Optional[Status] = None
address: Optional[Address] = None
timezone: Optional[datetime] = None
pictureURL: Optional[str] = None
peakPower: Optional[float] = None
meteoData: Optional[str] = None
lastImport: Optional[datetime] = None
installationDate: Optional[datetime] = None


class PvSystemsMetaData(BaseModel):
pvSystems: Optional[list[PvSystemMetaData]]
pvSystems: Optional[list[PvSystemMetaData]] = None
# Swagger documentation extras
links: Optional[Links]
links: Optional[Links] = None


class PvSystemFlowData(BaseModel):
pvSystemId: str
status: Optional[Status]
data: Optional[Data]
status: Optional[Status] = None
data: Optional[Data] = None


class PvSystemAggrDataV2(BaseModel):
pvSystemId: str
data: Optional[list[AggrData]]
links: Optional[Links]
data: Optional[list[AggrData]] = None
links: Optional[Links] = None
4 changes: 2 additions & 2 deletions fronius_solarweb/schema/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@


class ReleaseInfo(BaseModel):
releaseVersion: Optional[str]
releaseDate: Optional[str]
releaseVersion: Optional[str] = None
releaseDate: Optional[str] = None
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ issues = "https://github.com/drc38/python-fronius-web/issues"
[tool.poetry.dependencies]
python = ">=3.10"
httpx = ">=0.23"
pydantic = "^1.10"
pydantic = ">=2,<3.0"
tenacity = "^8.1"


Expand Down

0 comments on commit d84e373

Please sign in to comment.