Add Renewable Component Library (RCL) integration - #37
Conversation
|
|
||
|
|
||
| @dataclass | ||
| class RCLCatalogItem: |
There was a problem hiding this comment.
a pydantic model subclassing from solarfarmerbasemodel would keep things consistent and remove the need for the manual snake case handling
There was a problem hiding this comment.
like this
class RCLCatalogItemBase(SolarFarmerBaseModel):
file_uuid: str
component_id: str
filename: str
manufacturer: str
model: str
class RCLModuleItem(RCLCatalogItemBase):
p_nom: float | None = None
bifaciality_factor: float | None = None
technol: str | None = None
class RCLInverterItem(RCLCatalogItemBase):
p_nom_conv: float | None = None
effic_max: float | None = None| return self.raw.items() | ||
|
|
||
|
|
||
| class RCLCatalogResponse(TypedDict): |
There was a problem hiding this comment.
same pydantic model suggestion
| Properties (inverters, if requested) | ||
| ------------------------------------ | ||
| p_nom_conv : float | None | ||
| Rated AC power in kW. Aliases: ``pNomConv``. |
There was a problem hiding this comment.
this was noted as units of watts elsewhere. If there's a conversion I missed it
There was a problem hiding this comment.
good catch. Yes, the PVSystem property had in Watts. I will update it to be all consistent (in kW).
| @property | ||
| def is_low(self) -> bool: |
There was a problem hiding this comment.
I think this is a strange feature in a python sdk. I recommend we delete it. Leave it to the user to implement with their own judgement instead of imposing ours upon them
There was a problem hiding this comment.
Ok, I will remove the is_low() method.
| "pandas is required for this function. Install it with: pip install 'dnv-solarfarmer[weather]'" | ||
| ) | ||
|
|
||
| # RCL (Renewable Component Library) configuration |
There was a problem hiding this comment.
why put this in a generic config.py instead of where it's actually used in rcl.py?
There was a problem hiding this comment.
RCL_BASE_URL would need to be stay to avoid a circular import of api.py's RCLClient and rcl.py, which import api.py. However, the rest of the variables can be simplified.
| return base | ||
|
|
||
|
|
||
| def _build_auth_headers(api_key: str | None = None) -> dict[str, str]: |
There was a problem hiding this comment.
refactor Client._check_params to use this so they don't drift apart
There was a problem hiding this comment.
Good point. I will make this a shared validation logic.
| from pathlib import Path | ||
| from typing import TypedDict | ||
|
|
||
| import requests |
There was a problem hiding this comment.
Don't import requests here. The other modules rightly don't import requests. Keep that concern specific to api.py
There was a problem hiding this comment.
It's needed for the RCLRateLimitInfo in _extract_rate_limit so that it can parse the headers that indicate remaining, limit and reset details. I can move it to a Type_Checking import and add a note for the import reason.
| dest = None | ||
|
|
||
| # Check local cache | ||
| if use_cache and dest is not None and dest.exists(): |
There was a problem hiding this comment.
I think we should remove the cache feature at this time. There's a tension here between caching against file name and against uuid. Today it probably doesn't matter, but it will matter in the future as the library evolves. In any case, it's also easy enough for a user to roll their own cache in a way that makes more sense to them (e.g. maybe they want to put it in ~/.rcl/cache, maybe it should be coordinated with SF Desktop cache). Don't want to download twice? Don't call download_file unless you need to! If users really want a the sdk to provide a cache later then we can put more thought into how to do it.
There was a problem hiding this comment.
ok, I will remove the cache feature from download_file()
…ngs and disambiguation labels corrected). Remove RCLRateLimitInfo.is_low property. Remove RCL_RATE_LIMIT_WARNING_THRESHOLD and the low-quota warning; always log quota info instead. Remove local file caching from download_file (drop use_cache parameter). Fix _build_query_params mangling raw dot-notation filter keys (filter.filter.x). Drop module-level requests import; only import it under TYPE_CHECKING. Add User-Agent header to RCLClient.get() requests. Add _raise_for_status helper so RCL errors surface the API's own message/problem details to align with the rest of the SDK. Fix synthetic/misleading 500 status code when rate-limit headers can't be parsed. Make _extract_rate_limit raise on missing/invalid headers instead of silently returning None; catch and log at call sites where quota info is secondary (catalog search, download), propagate where it's the primary result (get_rate_limit_status).
Adds support for searching and downloading PV module (PAN) and inverter (OND) files from DNV's API curated component database.
New functions:
Catalog search
sf.rcl.list_modules()- Search modules by manufacturer, model, power, bifaciality, technologysf.rcl.list_inverters()- Search inverters by manufacturer, model, power, efficiency, MPPT specsoutput_parameterFile downloads
sf.rcl.download_file()- Download with automatic local cachingRate limit management
sf.rcl.get_rate_limit_status()- Zero-cost endpoint to check remaining downloadsPVSystem integration
plant.set_module_from_rcl()- Search + download + assign moduleplant.set_inverter_from_rcl()- Search + download + assign inverterstrictparameter controls error handling (raise vs return None) when multiple components match requested filtered query.Includes documentation, example notebook, and full test coverage.