Skip to content

Commit e197611

Browse files
Development (#211)
* Add Victron inverter (#202) * wip * wip * solarman * update token * update inverters * undo some changes * add docstring * mock inverter docstring * remove dotenv * update givenergy * enphase * try to fix tests running twice * delete event * pr comments * revert workflow changes * split inverters into separate modules * import * Revert "import" This reverts commit f9f3c5f. * Revert "split inverters into separate modules" This reverts commit 94a9e70. * add pydantic_settings * set config within settings classes * add dependency * add victron * use named argument * start and end times * add a line to the documentation * dependencies * add test * add test * update script * add username and password to example env variable file * move vrmapi dependency to optional-dependencies * comment * update to use ocf_vrmapi * use issue/pip-all branch * add all to project.optional-dep * update import --------- Co-authored-by: Matthew Duffin <mduffin95@users.noreply.github.com>
1 parent 9528869 commit e197611

File tree

7 files changed

+1518
-2
lines changed

7 files changed

+1518
-2
lines changed

.env.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ SOLIS_CLOUD_API_PORT = '13333'
1515
# User needs to add their GivEnergy API details
1616
GIVENERGY_API_KEY = 'user_givenergy_api_key'
1717

18+
# To connect to a Victron system use the environment variables below to set the username and password
19+
#VICTRON_USER=username
20+
#VICTRON_PASS=password
21+
1822
# User needs to add their GivEnergy API details
1923
SOLARMAN_API_URL = 'https://home.solarmanpv.com/maintain-s/history/power'
2024
SOLARMAN_TOKEN = 'user_solarman_token'

.github/workflows/pytest.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ on:
1010
types: [opened, synchronize, reopened, ready_for_review]
1111
jobs:
1212
call-run-python-tests:
13-
uses: openclimatefix/.github/.github/workflows/python-test.yml@main
13+
uses: openclimatefix/.github/.github/workflows/python-test.yml@issue/pip-all
1414
with:
1515
# pytest-cov looks at this folder
1616
pytest_cov_dir: "quartz_solar_forecast"

examples/inverter_example.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def main(save_outputs: bool = False):
1414
ts = pd.to_datetime(timestamp_str)
1515

1616
# make input data with live enphase, solis, givenergy, or solarman data
17-
site_live = PVSite(latitude=51.75, longitude=-1.25, capacity_kwp=1.25, inverter_type="enphase") # inverter_type="enphase", "solis", "givenergy", or "solarman"
17+
site_live = PVSite(latitude=51.75, longitude=-1.25, capacity_kwp=1.25, inverter_type="enphase") # inverter_type="enphase", "solis", "givenergy", "solarman" or "victron"
1818

1919
# make input data with nan data
2020
site_no_live = PVSite(latitude=51.75, longitude=-1.25, capacity_kwp=1.25)

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ package-data = { "quartz_solar_forecast" = ["*"] }
4343

4444
[project.optional-dependencies]
4545
dev = []
46+
# additional vendor-specific dependencies for connecting to inverter APIs
47+
inverters = ["ocf_vrmapi"] # victron
48+
all = ["ocf_vrmapi"]
4649

4750
[tool.mypy]
4851

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
from typing import Callable
2+
3+
import pandas as pd
4+
from datetime import datetime, timedelta
5+
from quartz_solar_forecast.inverters.inverter import AbstractInverter
6+
from pydantic import Field
7+
from pydantic_settings import BaseSettings, SettingsConfigDict
8+
from ocf_vrmapi.vrm import VRM_API
9+
10+
11+
class VictronSettings(BaseSettings):
12+
model_config = SettingsConfigDict(env_file='.env', extra='ignore')
13+
14+
username: str = Field(alias="VICTRON_USER")
15+
password: str = Field(alias="VICTRON_PASS")
16+
17+
18+
class VictronInverter(AbstractInverter):
19+
20+
def __init__(self, get_sites: Callable, get_kwh_stats: Callable):
21+
self.__get_sites = get_sites
22+
self.__get_kwh_stats = get_kwh_stats
23+
24+
@classmethod
25+
def from_settings(cls, settings: VictronSettings):
26+
api = VRM_API(username=settings.username, password=settings.password)
27+
get_sites = lambda: api.get_user_sites(api.user_id)
28+
end = datetime.now()
29+
start = end - timedelta(weeks=1)
30+
get_kwh_stats = lambda site_id: api.get_kwh_stats(site_id, start=start, end=end)
31+
return cls(get_sites, get_kwh_stats)
32+
33+
def get_data(self, ts: pd.Timestamp) -> pd.DataFrame:
34+
sites = self.__get_sites()
35+
# get first site (bit of a guess)
36+
first_site_id = sites["records"][0]["idSite"]
37+
38+
stats = self.__get_kwh_stats(first_site_id)
39+
40+
kwh = stats["records"]["kwh"]
41+
42+
df = pd.DataFrame(kwh)
43+
44+
df[0] = pd.to_datetime(df[0], unit='ms')
45+
df.columns = ["timestamp", "power_kw"]
46+
return df

quartz_solar_forecast/pydantic_models.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from quartz_solar_forecast.inverters.mock import MockInverter
77
from quartz_solar_forecast.inverters.solarman import SolarmanSettings, SolarmanInverter
88
from quartz_solar_forecast.inverters.solis import SolisSettings, SolisInverter
9+
from quartz_solar_forecast.inverters.victron import VictronSettings, VictronInverter
910

1011

1112
class PVSite(BaseModel):
@@ -41,6 +42,8 @@ def get_inverter(self):
4142
return GivEnergyInverter(GivEnergySettings())
4243
elif self.inverter_type == 'solarman':
4344
return SolarmanInverter(SolarmanSettings())
45+
elif self.inverter_type == 'victron':
46+
return VictronInverter.from_settings(VictronSettings())
4447
else:
4548
return MockInverter()
4649

0 commit comments

Comments
 (0)