pax_global_header 0000666 0000000 0000000 00000000064 14625713532 0014522 g ustar 00root root 0000000 0000000 52 comment=cb8c67cbc4ce1c80df9b3e62b2d18da9ce1c23a7
rokam-sunweg-cb8c67c/ 0000775 0000000 0000000 00000000000 14625713532 0014642 5 ustar 00root root 0000000 0000000 rokam-sunweg-cb8c67c/.github/ 0000775 0000000 0000000 00000000000 14625713532 0016202 5 ustar 00root root 0000000 0000000 rokam-sunweg-cb8c67c/.github/workflows/ 0000775 0000000 0000000 00000000000 14625713532 0020237 5 ustar 00root root 0000000 0000000 rokam-sunweg-cb8c67c/.github/workflows/python-build.yml 0000664 0000000 0000000 00000002516 14625713532 0023404 0 ustar 00root root 0000000 0000000 name: Python build
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11", "3.10"]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest pytest-cov genbadge[all]
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics --output-file ./reports/flake8/flake8stats.txt
- name: Test with pytest
run: |
pytest --cov=sunweg --cov-report html --cov-report xml --junitxml=reports/junit/junit.xml
mv htmlcov reports/coverage
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
rokam-sunweg-cb8c67c/.github/workflows/python-publish.yml 0000664 0000000 0000000 00000005462 14625713532 0023756 0 ustar 00root root 0000000 0000000 # This workflow will upload a Python Package using Twine when a release is created
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
name: Upload Python Package
on:
release:
types: [published]
permissions:
contents: read
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest pytest-cov genbadge[all]
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics --output-file ./reports/flake8/flake8stats.txt
- name: Test with pytest
run: |
pytest --cov=sunweg --cov-report html --cov-report xml --junitxml=reports/junit/junit.xml
mv htmlcov reports/coverage
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
- name: Generate badges
if: github.event_name != 'pull_request'
run: |
genbadge tests -o reports/tests.svg
genbadge coverage -i coverage.xml -o reports/coverage.svg
genbadge flake8 -i reports/flake8/flake8stats.txt -o reports/flake8.svg
- name: Publish badges report to badges branch
uses: JamesIves/github-pages-deploy-action@v4
if: github.event_name != 'pull_request'
with:
branch: badges
folder: reports
token: ${{ secrets.SUNWEG_GITHUB_PAT }}
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install build
- name: Build package
run: python -m build
- name: Publish package
uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
rokam-sunweg-cb8c67c/.gitignore 0000664 0000000 0000000 00000003420 14625713532 0016631 0 ustar 00root root 0000000 0000000 # Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
reports/
rokam-sunweg-cb8c67c/.vscode/ 0000775 0000000 0000000 00000000000 14625713532 0016203 5 ustar 00root root 0000000 0000000 rokam-sunweg-cb8c67c/.vscode/extensions.json 0000664 0000000 0000000 00000000227 14625713532 0021276 0 ustar 00root root 0000000 0000000 {
"recommendations": [
"esbenp.prettier-vscode",
"ms-python.python",
"github.vscode-pull-request-github",
"charliermarsh.ruff"
]
}
rokam-sunweg-cb8c67c/.vscode/launch.json 0000664 0000000 0000000 00000000731 14625713532 0020351 0 ustar 00root root 0000000 0000000 {
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python Debugger: Current File",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal"
}
]
} rokam-sunweg-cb8c67c/.vscode/settings.json 0000664 0000000 0000000 00000002424 14625713532 0020740 0 ustar 00root root 0000000 0000000 {
"editor.formatOnPaste": false,
"editor.formatOnSave": true,
"editor.formatOnType": true,
"python.formatting.provider": "black",
"python.testing.pytestArgs": [
"tests"
],
"python.testing.pytestEnabled": true,
"python.linting.mypyEnabled": true,
"cSpell.ignoreWords": [
"Acumulada",
"Acumulado",
"Atualizacao",
"Corrente",
"Gerada",
"Hoje",
"Inversor",
"Potencia",
"SUNWEG",
"Tensao",
"alarme",
"autenticacao",
"carbono",
"descricao",
"economia",
"energia",
"energiaacumuladanumber",
"fatorpotencia",
"franqueado",
"frequencia",
"getpaineloperacao",
"idusina",
"integrador",
"inversores",
"mppt",
"mppts",
"nomemppt",
"planos",
"potenciaativa",
"procurar",
"reduz",
"rtype",
"senha",
"situacao",
"stringmppt",
"temperatura",
"tensaoca",
"usinas",
"usuario",
"rokam"
],
"python.linting.mypyArgs": [
"--follow-imports=silent",
"--ignore-missing-imports",
"--show-column-numbers",
"--no-pretty",
"--install-types"
],
"python.testing.unittestEnabled": false,
"python.linting.flake8Enabled": true,
"python.linting.flake8Args": [
"--max-complexity=10",
"--max-line-length=127"
],
}
rokam-sunweg-cb8c67c/.vscode/tasks.json 0000664 0000000 0000000 00000001043 14625713532 0020221 0 ustar 00root root 0000000 0000000 {
"version": "2.0.0",
"tasks": [
{
"label": "Code Coverage",
"detail": "Generate code coverage report.",
"type": "shell",
"command": "pytest ./tests/ --cov=sunweg --cov-report term-missing --durations-min=1 --durations=0",
"group": {
"kind": "test",
"isDefault": true
},
"presentation": {
"reveal": "always",
"panel": "new"
},
"problemMatcher": []
}
]
} rokam-sunweg-cb8c67c/LICENSE 0000664 0000000 0000000 00000002073 14625713532 0015651 0 ustar 00root root 0000000 0000000 MIT License
Copyright (c) 2023 Lucas MindĂȘllo de Andrade
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
rokam-sunweg-cb8c67c/README.md 0000664 0000000 0000000 00000002106 14625713532 0016120 0 ustar 00root root 0000000 0000000 # SunWeg
[](https://github.com/rokam/sunweg/actions/workflows/python-build.yml)



Python lib for WEG solar energy platform, [SunWEG.net](https://sunweg.net/)
## Usage
``` python
from sunweg.api import APIHelper
api = APIHelper('username','password')
plants = api.listPlants()
for plant in plants:
print(plant)
for inverter in plant.inverters:
print(inverter)
for phase in inverter.phases:
print(phase)
for mppt in inverter.mppts:
print(mppt)
for string in mppt.strings:
print(string)
```
## Documentation
Check the [DOCs](https://github.com/rokam/sunweg/blob/main/docs/index.md) for API documentation.
## Contribute
Feel free to send issues and pull requests.
rokam-sunweg-cb8c67c/docs/ 0000775 0000000 0000000 00000000000 14625713532 0015572 5 ustar 00root root 0000000 0000000 rokam-sunweg-cb8c67c/docs/index.md 0000664 0000000 0000000 00000061174 14625713532 0017234 0 ustar 00root root 0000000 0000000 # Table of Contents
* [sunweg](#sunweg)
* [sunweg.device](#sunweg.device)
* [Phase](#sunweg.device.Phase)
* [\_\_init\_\_](#sunweg.device.Phase.__init__)
* [name](#sunweg.device.Phase.name)
* [voltage](#sunweg.device.Phase.voltage)
* [amperage](#sunweg.device.Phase.amperage)
* [status\_voltage](#sunweg.device.Phase.status_voltage)
* [status\_amperage](#sunweg.device.Phase.status_amperage)
* [\_\_str\_\_](#sunweg.device.Phase.__str__)
* [String](#sunweg.device.String)
* [\_\_init\_\_](#sunweg.device.String.__init__)
* [name](#sunweg.device.String.name)
* [voltage](#sunweg.device.String.voltage)
* [amperage](#sunweg.device.String.amperage)
* [status](#sunweg.device.String.status)
* [\_\_str\_\_](#sunweg.device.String.__str__)
* [MPPT](#sunweg.device.MPPT)
* [\_\_init\_\_](#sunweg.device.MPPT.__init__)
* [name](#sunweg.device.MPPT.name)
* [strings](#sunweg.device.MPPT.strings)
* [\_\_str\_\_](#sunweg.device.MPPT.__str__)
* [Inverter](#sunweg.device.Inverter)
* [\_\_init\_\_](#sunweg.device.Inverter.__init__)
* [id](#sunweg.device.Inverter.id)
* [name](#sunweg.device.Inverter.name)
* [sn](#sunweg.device.Inverter.sn)
* [status](#sunweg.device.Inverter.status)
* [temperature](#sunweg.device.Inverter.temperature)
* [today\_energy](#sunweg.device.Inverter.today_energy)
* [today\_energy](#sunweg.device.Inverter.today_energy)
* [today\_energy\_metric](#sunweg.device.Inverter.today_energy_metric)
* [today\_energy\_metric](#sunweg.device.Inverter.today_energy_metric)
* [total\_energy](#sunweg.device.Inverter.total_energy)
* [total\_energy](#sunweg.device.Inverter.total_energy)
* [total\_energy\_metric](#sunweg.device.Inverter.total_energy_metric)
* [total\_energy\_metric](#sunweg.device.Inverter.total_energy_metric)
* [power\_factor](#sunweg.device.Inverter.power_factor)
* [power\_factor](#sunweg.device.Inverter.power_factor)
* [frequency](#sunweg.device.Inverter.frequency)
* [frequency](#sunweg.device.Inverter.frequency)
* [power](#sunweg.device.Inverter.power)
* [power](#sunweg.device.Inverter.power)
* [power\_metric](#sunweg.device.Inverter.power_metric)
* [power\_metric](#sunweg.device.Inverter.power_metric)
* [is\_complete](#sunweg.device.Inverter.is_complete)
* [phases](#sunweg.device.Inverter.phases)
* [mppts](#sunweg.device.Inverter.mppts)
* [\_\_str\_\_](#sunweg.device.Inverter.__str__)
* [sunweg.const](#sunweg.const)
* [SUNWEG\_URL](#sunweg.const.SUNWEG_URL)
* [SUNWEG\_LOGIN\_PATH](#sunweg.const.SUNWEG_LOGIN_PATH)
* [SUNWEG\_PLANT\_LIST\_PATH](#sunweg.const.SUNWEG_PLANT_LIST_PATH)
* [SUNWEG\_PLANT\_DETAIL\_PATH](#sunweg.const.SUNWEG_PLANT_DETAIL_PATH)
* [SUNWEG\_INVERTER\_DETAIL\_PATH](#sunweg.const.SUNWEG_INVERTER_DETAIL_PATH)
* [SUNWEG\_MONTH\_STATS\_PATH](#sunweg.const.SUNWEG_MONTH_STATS_PATH)
* [sunweg.api](#sunweg.api)
* [SunWegApiError](#sunweg.api.SunWegApiError)
* [LoginError](#sunweg.api.LoginError)
* [convert\_situation\_status](#sunweg.api.convert_situation_status)
* [separate\_value\_metric](#sunweg.api.separate_value_metric)
* [APIHelper](#sunweg.api.APIHelper)
* [\_\_init\_\_](#sunweg.api.APIHelper.__init__)
* [authenticate](#sunweg.api.APIHelper.authenticate)
* [listPlants](#sunweg.api.APIHelper.listPlants)
* [plant](#sunweg.api.APIHelper.plant)
* [inverter](#sunweg.api.APIHelper.inverter)
* [complete\_inverter](#sunweg.api.APIHelper.complete_inverter)
* [month\_stats\_production](#sunweg.api.APIHelper.month_stats_production)
* [month\_stats\_production\_by\_id](#sunweg.api.APIHelper.month_stats_production_by_id)
* [sunweg.plant](#sunweg.plant)
* [Plant](#sunweg.plant.Plant)
* [\_\_init\_\_](#sunweg.plant.Plant.__init__)
* [id](#sunweg.plant.Plant.id)
* [name](#sunweg.plant.Plant.name)
* [total\_power](#sunweg.plant.Plant.total_power)
* [kwh\_per\_kwp](#sunweg.plant.Plant.kwh_per_kwp)
* [performance\_rate](#sunweg.plant.Plant.performance_rate)
* [saving](#sunweg.plant.Plant.saving)
* [today\_energy](#sunweg.plant.Plant.today_energy)
* [today\_energy\_metric](#sunweg.plant.Plant.today_energy_metric)
* [total\_energy](#sunweg.plant.Plant.total_energy)
* [total\_carbon\_saving](#sunweg.plant.Plant.total_carbon_saving)
* [last\_update](#sunweg.plant.Plant.last_update)
* [inverters](#sunweg.plant.Plant.inverters)
* [\_\_str\_\_](#sunweg.plant.Plant.__str__)
* [sunweg.util](#sunweg.util)
* [Status](#sunweg.util.Status)
* [ProductionStats](#sunweg.util.ProductionStats)
* [\_\_init\_\_](#sunweg.util.ProductionStats.__init__)
* [date](#sunweg.util.ProductionStats.date)
* [production](#sunweg.util.ProductionStats.production)
* [prognostic](#sunweg.util.ProductionStats.prognostic)
* [\_\_str\_\_](#sunweg.util.ProductionStats.__str__)
# sunweg
Sunweg API library.
# sunweg.device
Sunweg API devices.
## Phase Objects
```python
class Phase()
```
Phase details.
#### \_\_init\_\_
```python
def __init__(name: str, voltage: float, amperage: float,
status_voltage: Status, status_amperage: Status) -> None
```
Initialize Phase.
**Arguments**:
- `name` (`str`): phase name
- `voltage` (`float`): phase AC voltage in V
- `amperage` (`float`): phase AC amperage in A
- `status_voltage` (`Status`): phase AC voltage status
- `status_amperage` (`Status`): phase AC amperage status
#### name
```python
@property
def name() -> str
```
Get phase name.
**Returns**:
`str`: phase name
#### voltage
```python
@property
def voltage() -> float
```
Get phase AC voltage in V.
**Returns**:
`float`: phase AC voltage in V
#### amperage
```python
@property
def amperage() -> float
```
Get phase AC amperage in A.
**Returns**:
`float`: phase AC amperage in A
#### status\_voltage
```python
@property
def status_voltage() -> Status
```
Get phase AC voltage status.
**Returns**:
`Status`: phase AC voltage status
#### status\_amperage
```python
@property
def status_amperage() -> Status
```
Get phase AC amperage status.
**Returns**:
`Status`: phase AC amperage status
#### \_\_str\_\_
```python
def __str__() -> str
```
Cast Phase to str.
## String Objects
```python
class String()
```
String details.
#### \_\_init\_\_
```python
def __init__(name: str, voltage: float, amperage: float,
status: Status) -> None
```
Initialize String.
**Arguments**:
- `name` (`str`): string name
- `voltage` (`float`): string DC voltage in V
- `amperage` (`float`): string DC amperage in A
- `status` (`Status`): string status
#### name
```python
@property
def name() -> str
```
Get string name.
**Returns**:
`str`: string name
#### voltage
```python
@property
def voltage() -> float
```
Get string DC voltage in V.
**Returns**:
`float`: string DC voltage in V
#### amperage
```python
@property
def amperage() -> float
```
Get string DC amperage in A.
**Returns**:
`float`: string DC amperage in A
#### status
```python
@property
def status() -> Status
```
Get string status.
**Returns**:
`Status`: string status
#### \_\_str\_\_
```python
def __str__() -> str
```
Cast String to str.
## MPPT Objects
```python
class MPPT()
```
MPPT details.
#### \_\_init\_\_
```python
def __init__(name: str) -> None
```
Initialize MPPT.
**Arguments**:
- `name` (`srt`): MPPT name
#### name
```python
@property
def name() -> str
```
Get MPPT name.
**Returns**:
`str`: MPPT name
#### strings
```python
@property
def strings() -> list[String]
```
Get list of MPPT's String.
**Returns**:
`list[String]`: list of Strings
#### \_\_str\_\_
```python
def __str__() -> str
```
Cast MPPT to str.
## Inverter Objects
```python
class Inverter()
```
Inverter device.
#### \_\_init\_\_
```python
def __init__(id: int,
name: str,
sn: str,
status: Status,
temperature: int,
total_energy: float = 0,
total_energy_metric: str = "",
today_energy: float = 0,
today_energy_metric: str = "",
power_factor: float = 0,
frequency: float = 0,
power: float = 0,
power_metric: str = "") -> None
```
Initialize Inverter.
**Arguments**:
- `id` (`int`): inverter id
- `name` (`str`): inverter name
- `sn` (`str`): inverter serial number
- `status` (`Status`): inverter status
- `temperature` (`int`): inverter temperature
- `total_energy` (`float`): total generated energy
- `total_energy_metric` (`str`): total generated energy metric
- `today_energy` (`float`): total generated energy today
- `today_energy_metric` (`str`): total generated energy today metric
- `power_factor` (`float`): inverter power factor
- `frequency` (`float`): inverter output frequency in Hz
- `power` (`str`): inverter output power
- `power` (`str`): inverter output power metric
#### id
```python
@property
def id() -> int
```
Get inverter id.
**Returns**:
`int`: inverter id
#### name
```python
@property
def name() -> str
```
Get inverter name.
**Returns**:
`str`: inverter name
#### sn
```python
@property
def sn() -> str
```
Get inverter serial number.
**Returns**:
`str`: inverter serial number
#### status
```python
@property
def status() -> Status
```
Get inverter status.
**Returns**:
`Status`: inverter status
#### temperature
```python
@property
def temperature() -> int
```
Get inverter temperature.
**Returns**:
`int`: inverter temperature
#### today\_energy
```python
@property
def today_energy() -> float
```
Get inverter today generated energy.
**Returns**:
`float`: inverter today generated energy
#### today\_energy
```python
@today_energy.setter
def today_energy(value: float) -> None
```
Set inverter today generated energy.
**Arguments**:
- `value` (`float`): inverter today generated energy
#### today\_energy\_metric
```python
@property
def today_energy_metric() -> str
```
Get inverter today generated energy metric.
**Returns**:
`str`: inverter today generated energy metric
#### today\_energy\_metric
```python
@today_energy_metric.setter
def today_energy_metric(value: str) -> None
```
Set inverter today generated energy metric.
**Arguments**:
- `value` (`str`): inverter today generated energy metric
#### total\_energy
```python
@property
def total_energy() -> float
```
Get inverter total generated energy.
**Returns**:
`float`: inverter total generated energy
#### total\_energy
```python
@total_energy.setter
def total_energy(value: float) -> None
```
Set inverter total generated energy.
**Arguments**:
- `value` (`float`): inverter total generated energy
#### total\_energy\_metric
```python
@property
def total_energy_metric() -> str
```
Get inverter total generated energy metric.
**Returns**:
`str`: inverter total generated energy metric
#### total\_energy\_metric
```python
@total_energy_metric.setter
def total_energy_metric(value: str) -> None
```
Set inverter total generated energy metric.
**Arguments**:
- `value` (`str`): inverter total generated energy metric
#### power\_factor
```python
@property
def power_factor() -> float
```
Get inverter power factor.
**Returns**:
`float`: inverter power factor
#### power\_factor
```python
@power_factor.setter
def power_factor(value: float) -> None
```
Set inverter power factor.
**Arguments**:
- `value` (`float`): inverter power factor
#### frequency
```python
@property
def frequency() -> float
```
Get inverter frequency in Hz.
**Returns**:
`float`: inverter frequency in HZ
#### frequency
```python
@frequency.setter
def frequency(value: float) -> None
```
Set inverter frequency in Hz.
**Arguments**:
- `value` (`float`): inverter frequency in Hz
#### power
```python
@property
def power() -> float
```
Get inverter output power.
**Returns**:
`float`: inverter output power
#### power
```python
@power.setter
def power(value: float) -> None
```
Set inverter output power.
**Arguments**:
- `value` (`float`): inverter output power
#### power\_metric
```python
@property
def power_metric() -> str
```
Get inverter output power metric.
**Returns**:
`str`: inverter output power metric
#### power\_metric
```python
@power_metric.setter
def power_metric(value: str) -> None
```
Set inverter output power metric.
**Arguments**:
- `value` (`float`): inverter output power metric
#### is\_complete
```python
@property
def is_complete() -> bool
```
Is inverter data complete.
**Returns**:
`bool`: True when inverter data is complete
#### phases
```python
@property
def phases() -> list[Phase]
```
Get list of inverter's phases.
**Returns**:
`list[Phase]`: list of phases
#### mppts
```python
@property
def mppts() -> list[MPPT]
```
Get list of inverter's MPPTs.
**Returns**:
`list[MPPT]`: list of MPPTs
#### \_\_str\_\_
```python
def __str__() -> str
```
Cast Inverter to str.
# sunweg.const
Sunweg API constants.
#### SUNWEG\_URL
SunWEG API URL
#### SUNWEG\_LOGIN\_PATH
SunWEG API login path
#### SUNWEG\_PLANT\_LIST\_PATH
SunWEG API list plants path
#### SUNWEG\_PLANT\_DETAIL\_PATH
SunWEG API plant details path
#### SUNWEG\_INVERTER\_DETAIL\_PATH
SunWEG API inverter details path
#### SUNWEG\_MONTH\_STATS\_PATH
SunWEG API month history path
# sunweg.api
API Helper.
## SunWegApiError Objects
```python
class SunWegApiError(RuntimeError)
```
API Error.
## LoginError Objects
```python
class LoginError(SunWegApiError)
```
Login Error.
#### convert\_situation\_status
```python
def convert_situation_status(situation: int) -> Status
```
Convert situation to status.
**Arguments**:
- `situation` (`int`): situation
**Returns**:
`Status`: equivalent status
#### separate\_value\_metric
```python
def separate_value_metric(value_with_metric: str | None,
default_metric: str = "") -> tuple[float, str]
```
Separate the value from the metric.
**Arguments**:
- `value_with_metric` (`str | None`): value with metric separated by space
- `default_metric` (`str`): metric that should be returned if `value_with_metric` is None
**Returns**:
`tuple[float, str]`: tuple with value and metric
## APIHelper Objects
```python
class APIHelper()
```
Class to call sunweg.net api.
#### \_\_init\_\_
```python
def __init__(username: str, password: str) -> None
```
Initialize APIHelper for SunWEG platform.
**Arguments**:
- `username` (`str`): username for authentication
- `password` (`str`): password for authentication
#### authenticate
```python
def authenticate() -> bool
```
Authenticate with provided username and password.
**Returns**:
`bool`: True on authentication success
#### listPlants
```python
def listPlants(retry=True) -> list[Plant]
```
Retrieve the list of plants with incomplete inverter information.
You may want to call `complete_inverter()` to complete the Inverter information.
**Arguments**:
- `retry` (`bool`): reauthenticate if token expired and retry
**Returns**:
`list[Plant]`: list of Plant
#### plant
```python
def plant(id: int, retry=True) -> Plant | None
```
Retrieve plant detail by plant id.
**Arguments**:
- `id` (`int`): plant id
- `retry` (`bool`): reauthenticate if token expired and retry
**Returns**:
`Plant | None`: Plant or None if `id` not found.
#### inverter
```python
def inverter(id: int, retry=True) -> Inverter | None
```
Retrieve inverter detail by inverter id.
**Arguments**:
- `id` (`int`): inverter id
- `retry` (`bool`): reauthenticate if token expired and retry
**Returns**:
`Inverter | None`: Inverter or None if `id` not found.
#### complete\_inverter
```python
def complete_inverter(inverter: Inverter, retry=True) -> None
```
Complete inverter data.
**Arguments**:
- `inverter` (`Inverter`): inverter object to be completed with information
- `retry` (`bool`): reauthenticate if token expired and retry
#### month\_stats\_production
```python
def month_stats_production(year: int,
month: int,
plant: Plant,
inverter: Inverter | None = None,
retry: bool = True) -> list[ProductionStats]
```
Retrieve month energy production statistics.
**Arguments**:
- `year` (`int`): statistics year
- `month` (`int`): statistics month
- `plant` (`Plant`): statistics plant
- `inverter` (`Inverter | None`): statistics inverter, None for every inverter
- `retry` (`bool`): reauthenticate if token expired and retry
**Returns**:
`list[ProductionStats]`: list of daily energy production statistics
#### month\_stats\_production\_by\_id
```python
def month_stats_production_by_id(year: int,
month: int,
plant_id: int,
inverter_id: int | None = None,
retry: bool = True) -> list[ProductionStats]
```
Retrieve month energy production statistics.
**Arguments**:
- `year` (`int`): statistics year
- `month` (`int`): statistics month
- `plant_id` (`int`): id of statistics plant
- `inverter_id` (`int | None`): id of statistics inverter, None for every inverter
- `retry` (`bool`): reauthenticate if token expired and retry
**Returns**:
`list[ProductionStats]`: list of daily energy production statistics
# sunweg.plant
Sunweg API plant.
## Plant Objects
```python
class Plant()
```
Plant details.
#### \_\_init\_\_
```python
def __init__(id: int, name: str, total_power: float, kwh_per_kwp: float,
performance_rate: float, saving: float, today_energy: float,
today_energy_metric: str, total_energy: float,
total_carbon_saving: float, last_update: datetime | None) -> None
```
Initialize Plant.
**Arguments**:
- `id` (`int`): plant id
- `name` (`str`): plant name
- `total_power` (`float`): plant total power
- `kwh_per_kwp` (`float`): plant kWh/kWp
- `performance_rate` (`float`): plant performance rate
- `saving` (`float`): total saving in R$
- `today_energy` (`float`): today generated energy
- `today_energy_metric` (`str`): today generated energy metric
- `total_energy` (`float`): total generated energy in kWh
- `total_carbon_saving` (`float`): total of CO2 saved
- `last_update` (`datetime | None`): when the data was updated
#### id
```python
@property
def id() -> int
```
Get plant id.
**Returns**:
`int`: plant id
#### name
```python
@property
def name() -> str
```
Get plant name.
**Returns**:
`str`: plant name
#### total\_power
```python
@property
def total_power() -> float
```
Get plant total power.
**Returns**:
`float`: plant total power
#### kwh\_per\_kwp
```python
@property
def kwh_per_kwp() -> float
```
Get plant kWh/kWp.
**Returns**:
`float`: plant kWh/kWp
#### performance\_rate
```python
@property
def performance_rate() -> float
```
Get plant performance rate.
**Returns**:
`float`: plant performance rate
#### saving
```python
@property
def saving() -> float
```
Get plant saving in R$.
**Returns**:
`float`: plant saving in R$
#### today\_energy
```python
@property
def today_energy() -> float
```
Get plant today generated energy.
**Returns**:
`float`: plant today generated energy
#### today\_energy\_metric
```python
@property
def today_energy_metric() -> str
```
Get plant today generated energy metric.
**Returns**:
`str`: plant today generated energy metric
#### total\_energy
```python
@property
def total_energy() -> float
```
Get plant total generated energy in kWh.
**Returns**:
`float`: plant total generated energy in kWh
#### total\_carbon\_saving
```python
@property
def total_carbon_saving() -> float
```
Get plant total of CO2 saved.
**Returns**:
`float`: plant total of CO2 saved
#### last\_update
```python
@property
def last_update() -> datetime | None
```
Get when the plant data was updated.
**Returns**:
`datetime | None`: when the plant data was updated
#### inverters
```python
@property
def inverters() -> list[Inverter]
```
Get list of plant's inverters.
**Returns**:
`list[Inverter]`: list of inverters
#### \_\_str\_\_
```python
def __str__() -> str
```
Cast Plant to str.
# sunweg.util
Sunweg API util.
## Status Objects
```python
class Status(Enum)
```
Status enum.
## ProductionStats Objects
```python
class ProductionStats()
```
Energy production statistics
#### \_\_init\_\_
```python
def __init__(date: date, production: float, prognostic: float) -> None
```
Initialize energy production statistics.
**Arguments**:
- `date` (`date`): statistics date
- `production` (`float`): statistics production in kWh
- `prognostic`: statistics expected production in kWh
#### date
```python
@property
def date() -> date
```
Get date.
#### production
```python
@property
def production() -> float
```
Get energy production in kWh.
#### prognostic
```python
@property
def prognostic() -> float
```
Get expected energy production in kWh.
#### \_\_str\_\_
```python
def __str__() -> str
```
Cast Phase to str.
rokam-sunweg-cb8c67c/requirements.txt 0000664 0000000 0000000 00000000031 14625713532 0020120 0 ustar 00root root 0000000 0000000 python-dateutil
requests
rokam-sunweg-cb8c67c/setup.py 0000664 0000000 0000000 00000001454 14625713532 0016360 0 ustar 00root root 0000000 0000000 #!/bin/python
"""setup sunweg."""
import setuptools
with open("README.md", "r") as fh:
long_description = fh.read()
requires = [
"python-dateutil",
"requests",
]
setuptools.setup(
name="sunweg",
version="3.0.1",
author="rokam",
author_email="lucas@mindello.com.br",
description="A library to retrieve data from sunweg.net",
license="MIT",
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/rokam/sunweg",
install_requires=requires,
packages=setuptools.find_packages(exclude=["tests", "tests.*"]),
python_requires=">=3.10",
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
],
)
rokam-sunweg-cb8c67c/sunweg/ 0000775 0000000 0000000 00000000000 14625713532 0016152 5 ustar 00root root 0000000 0000000 rokam-sunweg-cb8c67c/sunweg/__init__.py 0000664 0000000 0000000 00000000032 14625713532 0020256 0 ustar 00root root 0000000 0000000 """Sunweg API library."""
rokam-sunweg-cb8c67c/sunweg/api.py 0000664 0000000 0000000 00000035240 14625713532 0017301 0 ustar 00root root 0000000 0000000 """API Helper."""
import json
from dateutil import parser
from typing import Any
from requests import Response, session
from .const import (
SUNWEG_INVERTER_DETAIL_PATH,
SUNWEG_LOGIN_PATH,
SUNWEG_MONTH_STATS_PATH,
SUNWEG_PLANT_DETAIL_PATH,
SUNWEG_PLANT_LIST_PATH,
SUNWEG_URL,
)
from .device import MPPT, Inverter, Phase, String
from .plant import Plant
from .util import ProductionStats, Status
class SunWegApiError(RuntimeError):
"""API Error."""
pass
class LoginError(SunWegApiError):
"""Login Error."""
pass
def convert_situation_status(situation: int) -> Status:
"""
Convert situation to status.
:param situation: situation
:type situation: int
:return: equivalent status
:rtype: Status
"""
if situation == 0:
return Status.ERROR
if situation == 1:
return Status.OK
return Status.WARN
def separate_value_metric(
value_with_metric: str | None, default_metric: str = "", metric_before: bool = False
) -> tuple[float, str]:
"""
Separate the value from the metric.
:param value_with_metric: value with metric separated by space
:type value_with_metric: str | None
:param default_metric: metric that should be returned if `value_with_metric` is None
:type default_metric: str
:param metric_before: true when metric appears before the value
:type metric_before: bool
:return: tuple with value and metric
:rtype: tuple[float, str]
"""
if value_with_metric is None or len(value_with_metric) == 0:
return (0.0, default_metric)
split = value_with_metric.split(" ")
if metric_before:
return (
float(split[0].replace(",", "."))
if len(split) < 2
else float(split[1].replace(",", ".")),
default_metric if len(split) < 2 else split[0],
)
return (
float(split[0].replace(",", ".")),
default_metric if len(split) < 2 else split[1],
)
class APIHelper:
"""Class to call sunweg.net api."""
SERVER_URI = SUNWEG_URL
def __init__(self, username: str, password: str) -> None:
"""
Initialize APIHelper for SunWEG platform.
:param username: username for authentication
:param password: password for authentication
:type username: str
:type password: str
"""
self._token = None
self._username = username
self._password = password
self.session = session()
def _set_username(self, username: str) -> None:
"""
Set username.
:param username: username for authentication
:type username: str
"""
self._username = username
username = property(None, _set_username)
def _set_password(self, password: str) -> None:
"""
Set password.
:param password: password for authentication
:type password: str
"""
self._password = password
password = property(None, _set_password)
def authenticate(self) -> bool:
"""
Authenticate with provided username and password.
:return: True on authentication success
:rtype: bool
"""
user_data = json.dumps(
{"usuario": self._username, "senha": self._password, "rememberMe": True},
default=lambda o: o.__dict__,
)
result = self._post(SUNWEG_LOGIN_PATH, user_data, False)
if not result["success"]:
return False
self._token = result["token"]
return result["success"]
def _headers(self):
"""Retrieve headers with authentication token."""
if self._token is None:
return {"Content-Type": "application/json"}
return {"Content-Type": "application/json", "X-Auth-Token-Update": self._token}
def listPlants(self, retry=True) -> list[Plant]:
"""
Retrieve the list of plants with incomplete inverter information.
You may want to call `complete_inverter()` to complete the Inverter information.
:param retry: reauthenticate if token expired and retry
:type retry: bool
:return: list of Plant
:rtype: list[Plant]
"""
try:
result = self._get(SUNWEG_PLANT_LIST_PATH)
ret_list = []
plantlist = (
result["nao_comissionadas"]
+ result["conectadas"]
+ result["falhas"]
+ result["alertas"]
+ result["atendimento"]
)
for plant in plantlist:
if (plant := self.plant(plant["id"])) is not None:
ret_list.append(plant)
return ret_list
except LoginError:
if retry:
self.authenticate()
return self.listPlants(False)
return []
def plant(self, id: int, retry=True) -> Plant | None:
"""
Retrieve plant detail by plant id.
:param id: plant id
:type id: int
:param retry: reauthenticate if token expired and retry
:type retry: bool
:return: Plant or None if `id` not found.
:rtype: Plant | None
"""
try:
result = self._get(SUNWEG_PLANT_DETAIL_PATH + str(id))
(today_energy, today_energy_metric) = separate_value_metric(
result["energiadia"], "kWh"
)
total_power = separate_value_metric(result["AcumuladoPotencia"])[0]
saving = separate_value_metric(result["economia"], metric_before=True)[0]
plant = Plant(
id=id,
name=result["usinas"]["nome"],
total_power=total_power,
kwh_per_kwp=float(0),
performance_rate=float(0),
saving=saving,
today_energy=today_energy,
today_energy_metric=today_energy_metric,
total_energy=float(result["energiaacumuladanumber"]),
total_carbon_saving=result["reduz_carbono_total_number"],
last_update=parser.parse(result["ultimaAtualizacao"])
if result["ultimaAtualizacao"] is not None
else None,
)
plant.inverters.extend(
[
Inverter(
id=inv["id"],
name=inv["nome"],
sn=inv["esn"],
status=Status(int(inv["situacao"])),
temperature=inv["temperatura"],
)
for inv in result["usinas"]["inversores"]
]
)
return plant
except LoginError:
if retry:
self.authenticate()
return self.plant(id, False)
return None
def inverter(self, id: int, retry=True) -> Inverter | None:
"""
Retrieve inverter detail by inverter id.
:param id: inverter id
:type id: int
:param retry: reauthenticate if token expired and retry
:type retry: bool
:return: Inverter or None if `id` not found.
:rtype: Inverter | None
"""
try:
result = self._get(SUNWEG_INVERTER_DETAIL_PATH + str(id))
(total_energy, total_energy_metric) = separate_value_metric(
result["energiaacumulada"], "kWh"
)
(today_energy, today_energy_metric) = separate_value_metric(
result["energiadodia"], "kWh"
)
(power, power_metric) = separate_value_metric(result["potenciaativa"], "kW")
inverter = Inverter(
id=id,
name=result["inversor"]["nome"],
sn=result["inversor"]["esn"],
total_energy=total_energy,
total_energy_metric=total_energy_metric,
today_energy=today_energy,
today_energy_metric=today_energy_metric,
power_factor=float(result["fatorpotencia"].replace(",", ".")),
frequency=float(result["frequencia"].replace(",", ".")),
power=power,
power_metric=power_metric,
status=Status(int(result["statusInversor"])),
temperature=result["temperatura"],
)
self._populate_MPPT(result=result, inverter=inverter)
return inverter
except LoginError:
if retry:
self.authenticate()
return self.inverter(id, False)
return None
def complete_inverter(self, inverter: Inverter, retry=True) -> None:
"""
Complete inverter data.
:param inverter: inverter object to be completed with information
:type inverter: Inverter
:param retry: reauthenticate if token expired and retry
:type retry: bool
"""
try:
result = self._get(SUNWEG_INVERTER_DETAIL_PATH + str(inverter.id))
(
inverter.total_energy,
inverter.total_energy_metric,
) = separate_value_metric(result["energiaacumulada"], "kWh")
(
inverter.today_energy,
inverter.today_energy_metric,
) = separate_value_metric(result["energiadodia"], "kWh")
(inverter.power, inverter.power_metric) = separate_value_metric(
result["potenciaativa"], "kW"
)
inverter.power_factor = float(result["fatorpotencia"].replace(",", "."))
inverter.frequency = float(result["frequencia"].replace(",", "."))
self._populate_MPPT(result=result, inverter=inverter)
except LoginError:
if retry:
self.authenticate()
self.complete_inverter(inverter, False)
def month_stats_production(
self,
year: int,
month: int,
plant: Plant,
inverter: Inverter | None = None,
retry: bool = True,
) -> list[ProductionStats]:
"""
Retrieve month energy production statistics.
:param year: statistics year
:type year: int
:param month: statistics month
:type month: int
:param plant: statistics plant
:type plant: Plant
:param inverter: statistics inverter, None for every inverter
:type inverter: Inverter | None
:param retry: reauthenticate if token expired and retry
:type retry: bool
:return: list of daily energy production statistics
:rtype: list[ProductionStats]
"""
return self.month_stats_production_by_id(
year, month, plant.id, inverter.id if inverter is not None else None, retry
)
def month_stats_production_by_id(
self,
year: int,
month: int,
plant_id: int,
inverter_id: int | None = None,
retry: bool = True,
) -> list[ProductionStats]:
"""
Retrieve month energy production statistics.
:param year: statistics year
:type year: int
:param month: statistics month
:type month: int
:param plant_id: id of statistics plant
:type plant_id: int
:param inverter_id: id of statistics inverter, None for every inverter
:type inverter_id: int | None
:param retry: reauthenticate if token expired and retry
:type retry: bool
:return: list of daily energy production statistics
:rtype: list[ProductionStats]
"""
inverter_str: str = str(inverter_id) if inverter_id is not None else ""
try:
result = self._get(
SUNWEG_MONTH_STATS_PATH
+ f"idusina={plant_id}&idinversor={inverter_str}&date={format(month,'02')}/{year}"
)
return [
ProductionStats(
parser.parse(item["tempoatual"]).date(),
float(item["energiapordia"]),
float(item["prognostico"]),
)
for item in result["graficomes"]
]
except LoginError:
if retry:
self.authenticate()
return self.month_stats_production_by_id(
year, month, plant_id, inverter_id, False
)
return []
def _populate_MPPT(self, result: dict, inverter: Inverter) -> None:
"""Populate MPPT information inside a inverter."""
for str_mppt in result["stringmppt"]:
mppt = MPPT(str_mppt["nomemppt"])
for str_string in str_mppt["strings"]:
string = String(
str_string["nome"],
float(result["inversor"]["leitura"][str_string["variaveltensao"]]),
float(
result["inversor"]["leitura"][str_string["variavelcorrente"]]
),
convert_situation_status(int(str_string["situacao"])),
)
mppt.strings.append(string)
inverter.mppts.append(mppt)
for phase_name in result["correnteCA"].keys():
if str(phase_name).endswith("status"):
continue
inverter.phases.append(
Phase(
phase_name,
float(result["tensaoca"][phase_name].replace(",", ".")),
float(result["correnteCA"][phase_name].replace(",", ".")),
Status(result["tensaoca"][phase_name + "status"]),
Status(result["correnteCA"][phase_name + "status"]),
)
)
def _get(self, path: str, launch_exception_on_error: bool = True) -> dict:
"""Do a get request returning a treated response."""
res = self.session.get(self.SERVER_URI + path, headers=self._headers())
result = self._treat_response(res, launch_exception_on_error)
return result
def _post(
self, path: str, data: Any | None, launch_exception_on_error: bool = True
) -> dict:
"""Do a post request returning a treated response."""
res = self.session.post(
self.SERVER_URI + path, data=data, headers=self._headers()
)
result = self._treat_response(res, launch_exception_on_error)
return result
def _treat_response(
self, response: Response, launch_exception_on_error: bool = True
) -> dict:
"""Treat the response from requests."""
if response.status_code == 401:
raise LoginError("Request failed: %s" % response)
if response.status_code != 200:
raise SunWegApiError("Request failed: %s" % response)
result = response.json()
if launch_exception_on_error and not result["success"]:
raise SunWegApiError(result["message"])
return result
rokam-sunweg-cb8c67c/sunweg/const.py 0000664 0000000 0000000 00000001262 14625713532 0017653 0 ustar 00root root 0000000 0000000 """Sunweg API constants."""
SUNWEG_URL = "https://api.sun.weg.net/v2/"
"""SunWEG API URL"""
SUNWEG_LOGIN_PATH = "login/autenticacao"
"""SunWEG API login path"""
SUNWEG_PLANT_LIST_PATH = (
"getpaineloperacao?procurar=&integrador="
+ "&franqueado=&manutencao=&portal=&alarme=&"
+ "planos=%5B0,1,2,3,4%5D&status=%5B1,2,3,4,5%5D&"
+ "limite=100&situacao=&paginaAtual=1"
)
"""SunWEG API list plants path"""
SUNWEG_PLANT_DETAIL_PATH = "viewresumov2?agrupado=false&id="
"""SunWEG API plant details path"""
SUNWEG_INVERTER_DETAIL_PATH = "inversores/view?id="
"""SunWEG API inverter details path"""
SUNWEG_MONTH_STATS_PATH = "usinas/graficomes?"
"""SunWEG API month history path"""
rokam-sunweg-cb8c67c/sunweg/device.py 0000664 0000000 0000000 00000027420 14625713532 0017770 0 ustar 00root root 0000000 0000000 """Sunweg API devices."""
from .util import Status
class Phase:
"""Phase details."""
def __init__(
self,
name: str,
voltage: float,
amperage: float,
status_voltage: Status,
status_amperage: Status,
) -> None:
"""
Initialize Phase.
:param name: phase name
:type name: str
:param voltage: phase AC voltage in V
:type voltage: float
:param amperage: phase AC amperage in A
:type amperage: float
:param status_voltage: phase AC voltage status
:type status_voltage: Status
:param status_amperage: phase AC amperage status
:type status_amperage: Status
"""
self._name = name
self._voltage = voltage
self._amperage = amperage
self._status_voltage = status_voltage
self._status_amperage = status_amperage
@property
def name(self) -> str:
"""
Get phase name.
:return: phase name
:rtype: str
"""
return self._name
@property
def voltage(self) -> float:
"""
Get phase AC voltage in V.
:return: phase AC voltage in V
:rtype: float
"""
return self._voltage
@property
def amperage(self) -> float:
"""
Get phase AC amperage in A.
:return: phase AC amperage in A
:rtype: float
"""
return self._amperage
@property
def status_voltage(self) -> Status:
"""
Get phase AC voltage status.
:return: phase AC voltage status
:rtype: Status
"""
return self._status_voltage
@property
def status_amperage(self) -> Status:
"""
Get phase AC amperage status.
:return: phase AC amperage status
:rtype: Status
"""
return self._status_amperage
def __str__(self) -> str:
"""Cast Phase to str."""
return str(self.__class__) + ": " + str(self.__dict__)
class String:
"""String details."""
def __init__(
self, name: str, voltage: float, amperage: float, status: Status
) -> None:
"""
Initialize String.
:param name: string name
:type name: str
:param voltage: string DC voltage in V
:type voltage: float
:param amperage: string DC amperage in A
:type amperage: float
:param status: string status
:type status: Status
"""
self._name = name
self._voltage = voltage
self._amperage = amperage
self._status = status
@property
def name(self) -> str:
"""
Get string name.
:return: string name
:rtype: str
"""
return self._name
@property
def voltage(self) -> float:
"""
Get string DC voltage in V.
:return: string DC voltage in V
:rtype: float
"""
return self._voltage
@property
def amperage(self) -> float:
"""
Get string DC amperage in A.
:return: string DC amperage in A
:rtype: float
"""
return self._amperage
@property
def status(self) -> Status:
"""
Get string status.
:return: string status
:rtype: Status
"""
return self._status
def __str__(self) -> str:
"""Cast String to str."""
return str(self.__class__) + ": " + str(self.__dict__)
class MPPT:
"""MPPT details."""
def __init__(self, name: str) -> None:
"""
Initialize MPPT.
:param name: MPPT name
:type name: srt
"""
self._name = name
self._strings: list[String] = []
@property
def name(self) -> str:
"""
Get MPPT name.
:return: MPPT name
:rtype: str
"""
return self._name
@property
def strings(self) -> list[String]:
"""
Get list of MPPT's String.
:return: list of Strings
:rtype: list[String]
"""
return self._strings
def __str__(self) -> str:
"""Cast MPPT to str."""
return str(self.__class__) + ": " + str(self.__dict__)
class Inverter:
"""Inverter device."""
def __init__(
self,
id: int,
name: str,
sn: str,
status: Status,
temperature: int,
total_energy: float = 0,
total_energy_metric: str = "",
today_energy: float = 0,
today_energy_metric: str = "",
power_factor: float = 0,
frequency: float = 0,
power: float = 0,
power_metric: str = "",
) -> None:
"""
Initialize Inverter.
:param id: inverter id
:type id: int
:param name: inverter name
:type name: str
:param sn: inverter serial number
:type sn: str
:param status: inverter status
:type status: Status
:param temperature: inverter temperature
:type temperature: int
:param total_energy: total generated energy
:type total_energy: float
:param total_energy_metric: total generated energy metric
:type total_energy_metric: str
:param today_energy: total generated energy today
:type today_energy: float
:param today_energy_metric: total generated energy today metric
:type today_energy_metric: str
:param power_factor: inverter power factor
:type power_factor: float
:param frequency: inverter output frequency in Hz
:type frequency: float
:param power: inverter output power
:type power: float
:param power: inverter output power metric
:type power: str
"""
self._id = id
self._name = name
self._sn = sn
self._total_energy = total_energy
self._total_energy_metric = total_energy_metric
self._today_energy = today_energy
self._today_energy_metric = today_energy_metric
self._power_factor = power_factor
self._frequency = frequency
self._power = power
self._power_metric = power_metric
self._status = status
self._temperature = temperature
self._phases: list[Phase] = []
self._mppts: list[MPPT] = []
@property
def id(self) -> int:
"""
Get inverter id.
:return: inverter id
:rtype: int
"""
return self._id
@property
def name(self) -> str:
"""
Get inverter name.
:return: inverter name
:rtype: str
"""
return self._name
@property
def sn(self) -> str:
"""
Get inverter serial number.
:return: inverter serial number
:rtype: str
"""
return self._sn
@property
def status(self) -> Status:
"""
Get inverter status.
:return: inverter status
:rtype: Status
"""
return self._status
@property
def temperature(self) -> int:
"""
Get inverter temperature.
:return: inverter temperature
:rtype: int
"""
return self._temperature
@property
def today_energy(self) -> float:
"""
Get inverter today generated energy.
:return: inverter today generated energy
:rtype: float
"""
return self._today_energy
@today_energy.setter
def today_energy(self, value: float) -> None:
"""
Set inverter today generated energy.
:param value: inverter today generated energy
:type value: float
"""
self._today_energy = value
@property
def today_energy_metric(self) -> str:
"""
Get inverter today generated energy metric.
:return: inverter today generated energy metric
:rtype: str
"""
return self._today_energy_metric
@today_energy_metric.setter
def today_energy_metric(self, value: str) -> None:
"""
Set inverter today generated energy metric.
:param value: inverter today generated energy metric
:type value: str
"""
self._today_energy_metric = value
@property
def total_energy(self) -> float:
"""
Get inverter total generated energy.
:return: inverter total generated energy
:rtype: float
"""
return self._total_energy
@total_energy.setter
def total_energy(self, value: float) -> None:
"""
Set inverter total generated energy.
:param value: inverter total generated energy
:type value: float
"""
self._total_energy = value
@property
def total_energy_metric(self) -> str:
"""
Get inverter total generated energy metric.
:return: inverter total generated energy metric
:rtype: str
"""
return self._total_energy_metric
@total_energy_metric.setter
def total_energy_metric(self, value: str) -> None:
"""
Set inverter total generated energy metric.
:param value: inverter total generated energy metric
:type value: str
"""
self._total_energy_metric = value
@property
def power_factor(self) -> float:
"""
Get inverter power factor.
:return: inverter power factor
:rtype: float
"""
return self._power_factor
@power_factor.setter
def power_factor(self, value: float) -> None:
"""
Set inverter power factor.
:param value: inverter power factor
:type value: float
"""
self._power_factor = value
@property
def frequency(self) -> float:
"""
Get inverter frequency in Hz.
:return: inverter frequency in HZ
:rtype: float
"""
return self._frequency
@frequency.setter
def frequency(self, value: float) -> None:
"""
Set inverter frequency in Hz.
:param value: inverter frequency in Hz
:type value: float
"""
self._frequency = value
@property
def power(self) -> float:
"""
Get inverter output power.
:return: inverter output power
:rtype: float
"""
return self._power
@power.setter
def power(self, value: float) -> None:
"""
Set inverter output power.
:param value: inverter output power
:type value: float
"""
self._power = value
@property
def power_metric(self) -> str:
"""
Get inverter output power metric.
:return: inverter output power metric
:rtype: str
"""
return self._power_metric
@power_metric.setter
def power_metric(self, value: str) -> None:
"""
Set inverter output power metric.
:param value: inverter output power metric
:type value: float
"""
self._power_metric = value
@property
def is_complete(self) -> bool:
"""
Is inverter data complete.
:return: True when inverter data is complete
:rtype: bool
"""
return (
self._today_energy != 0
or self._total_energy != 0
or self._power_factor != 0
or self._frequency != 0
or self._power != 0
)
@property
def phases(self) -> list[Phase]:
"""
Get list of inverter's phases.
:return: list of phases
:rtype: list[Phase]
"""
return self._phases
@property
def mppts(self) -> list[MPPT]:
"""
Get list of inverter's MPPTs.
:return: list of MPPTs
:rtype: list[MPPT]
"""
return self._mppts
def __str__(self) -> str:
"""Cast Inverter to str."""
return str(self.__class__) + ": " + str(self.__dict__)
rokam-sunweg-cb8c67c/sunweg/plant.py 0000664 0000000 0000000 00000011642 14625713532 0017646 0 ustar 00root root 0000000 0000000 """Sunweg API plant."""
from datetime import datetime
import warnings
from .device import Inverter
class Plant:
"""Plant details."""
def __init__(
self,
id: int,
name: str,
total_power: float,
kwh_per_kwp: float,
performance_rate: float,
saving: float,
today_energy: float,
today_energy_metric: str,
total_energy: float,
total_carbon_saving: float,
last_update: datetime | None,
) -> None:
"""
Initialize Plant.
:param id: plant id
:type id: int
:param name: plant name
:type name: str
:param total_power: plant total power
:type total_power: float
:param kwh_per_kwp: plant kWh/kWp
:type kwh_per_kwp: float
:param performance_rate: plant performance rate
:type performance_rate: float
:param saving: total saving in R$
:type saving: float
:param today_energy: today generated energy
:type today_energy: float
:param today_energy_metric: today generated energy metric
:type today_energy_metric: str
:param total_energy: total generated energy in kWh
:type total_energy: float
:param total_carbon_saving: total of CO2 saved
:type total_carbon_saving: float
:param last_update: when the data was updated
:type last_update: datetime | None
"""
self._id = id
self._name = name
self._total_power = total_power
self._kwh_per_kwp = kwh_per_kwp
self._performance_rate = performance_rate
self._saving = saving
self._today_energy = today_energy
self._today_energy_metric = today_energy_metric
self._total_energy = total_energy
self._total_carbon_saving = total_carbon_saving
self._last_update = last_update
self._inverters: list[Inverter] = []
@property
def id(self) -> int:
"""
Get plant id.
:return: plant id
:rtype: int
"""
return self._id
@property
def name(self) -> str:
"""
Get plant name.
:return: plant name
:rtype: str
"""
return self._name
@property
def total_power(self) -> float:
"""
Get plant total power.
:return: plant total power
:rtype: float
"""
return self._total_power
@property
def kwh_per_kwp(self) -> float:
"""
Deprecated as API v2 doesn't return it anymore.
Get plant kWh/kWp.
:return: plant kWh/kWp
:rtype: float
"""
warnings.warn(
"The 'kwh_per_kwp' property is deprecated and will return 0.",
DeprecationWarning,
stacklevel=2,
)
return self._kwh_per_kwp
@property
def performance_rate(self) -> float:
"""
Deprecated as API v2 doesn't return it anymore.
Get plant performance rate.
:return: plant performance rate
:rtype: float
"""
warnings.warn(
"The 'performance_rate' property is deprecated and will return 0.",
DeprecationWarning,
stacklevel=2,
)
return self._performance_rate
@property
def saving(self) -> float:
"""
Get plant saving in R$.
:return: plant saving in R$
:rtype: float
"""
return self._saving
@property
def today_energy(self) -> float:
"""
Get plant today generated energy.
:return: plant today generated energy
:rtype: float
"""
return self._today_energy
@property
def today_energy_metric(self) -> str:
"""
Get plant today generated energy metric.
:return: plant today generated energy metric
:rtype: str
"""
return self._today_energy_metric
@property
def total_energy(self) -> float:
"""
Get plant total generated energy in kWh.
:return: plant total generated energy in kWh
:rtype: float
"""
return self._total_energy
@property
def total_carbon_saving(self) -> float:
"""
Get plant total of CO2 saved.
:return: plant total of CO2 saved
:rtype: float
"""
return self._total_carbon_saving
@property
def last_update(self) -> datetime | None:
"""
Get when the plant data was updated.
:return: when the plant data was updated
:rtype: datetime | None
"""
return self._last_update
@property
def inverters(self) -> list[Inverter]:
"""
Get list of plant's inverters.
:return: list of inverters
:rtype: list[Inverter]
"""
return self._inverters
def __str__(self) -> str:
"""Cast Plant to str."""
return str(self.__class__) + ": " + str(self.__dict__)
rokam-sunweg-cb8c67c/sunweg/util.py 0000664 0000000 0000000 00000002227 14625713532 0017504 0 ustar 00root root 0000000 0000000 """Sunweg API util."""
from datetime import date
from enum import Enum
class Status(Enum):
"""Status enum."""
OK = 0
WARN = 2
ERROR = 1
class ProductionStats:
"""Energy production statistics"""
def __init__(self, date: date, production: float, prognostic: float) -> None:
"""
Initialize energy production statistics.
:param date: statistics date
:type date: date
:param production: statistics production in kWh
:type production: float
:param prognostic: statistics expected production in kWh
"""
self._date = date
self._production = production
self._prognostic = prognostic
@property
def date(self) -> date:
"""Get date."""
return self._date
@property
def production(self) -> float:
"""Get energy production in kWh."""
return self._production
@property
def prognostic(self) -> float:
"""Get expected energy production in kWh."""
return self._prognostic
def __str__(self) -> str:
"""Cast Phase to str."""
return str(self.__class__) + ": " + str(self.__dict__)
rokam-sunweg-cb8c67c/tests/ 0000775 0000000 0000000 00000000000 14625713532 0016004 5 ustar 00root root 0000000 0000000 rokam-sunweg-cb8c67c/tests/__init__.py 0000664 0000000 0000000 00000000023 14625713532 0020110 0 ustar 00root root 0000000 0000000 """Test sunweg."""
rokam-sunweg-cb8c67c/tests/bandit.yaml 0000664 0000000 0000000 00000000340 14625713532 0020126 0 ustar 00root root 0000000 0000000 # https://bandit.readthedocs.io/en/latest/config.html
tests:
- B103
- B108
- B306
- B307
- B313
- B314
- B315
- B316
- B317
- B318
- B319
- B320
- B325
- B601
- B602
- B604
- B608
- B609
rokam-sunweg-cb8c67c/tests/common.py 0000664 0000000 0000000 00000001755 14625713532 0017656 0 ustar 00root root 0000000 0000000 """Test sunweg common."""
from datetime import datetime
from sunweg.device import Inverter, MPPT, Phase, String
from sunweg.plant import Plant
from sunweg.util import Status
PLANT_MOCK = Plant(
id=1,
name="Plant",
total_power=29.2,
kwh_per_kwp=0.1,
performance_rate=0,
saving=0,
today_energy=123.1,
today_energy_metric="kWh",
total_energy=321.1,
total_carbon_saving=12.1,
last_update=datetime.now(),
)
INVERTER_MOCK = Inverter(
id=1,
name="Inverter",
sn="1234ABC",
total_energy=321.1,
total_energy_metric="kWh",
today_energy=123.1,
today_energy_metric="kWh",
power_factor=0.2,
frequency=60,
power=29,
power_metric="kW",
status=Status.OK,
temperature=70,
)
MPPT_MOCK = MPPT("MPPT")
STRING_MOCK = String(name="String", voltage=523.1, amperage=12.1, status=Status.OK)
PHASE_MOCK = Phase(
name="Phase",
voltage=230.1,
amperage=3.1,
status_voltage=Status.OK,
status_amperage=Status.OK,
)
rokam-sunweg-cb8c67c/tests/responses/ 0000775 0000000 0000000 00000000000 14625713532 0020025 5 ustar 00root root 0000000 0000000 rokam-sunweg-cb8c67c/tests/responses/auth_fail_response.json 0000664 0000000 0000000 00000000064 14625713532 0024572 0 ustar 00root root 0000000 0000000 {
"success": false,
"message": "Error message"
} rokam-sunweg-cb8c67c/tests/responses/auth_success_response.json 0000664 0000000 0000000 00000000601 14625713532 0025324 0 ustar 00root root 0000000 0000000 {"success":true,"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2NzcyNjQzODEsImlzcyI6Imh0dHA6XC9cL2llcy5nb3YiLCJleHAiOjE2Nzc4NjkxODEsImhvc3QiOiIxOTEuMzMuMTg1LjEyOSIsImRhdGEiOnsidXN1YXJpb2lkIjo2NTQzLCJpZHBlcmZpbCI6NiwiaWRyZWxhY2lvbmFkbyI6MTAxOCwiaWRmcmFucXVlYWRvIjpudWxsLCJhbGVydGFzIjp0cnVlLCJjb250cm9sZSI6dHJ1ZSwiZnJlZSI6ZmFsc2V9fQ.907NnIUaMhVl2HVzAuzPTvVdD-huHNg932Eu2zj7-0"} rokam-sunweg-cb8c67c/tests/responses/error_401_response.txt 0000664 0000000 0000000 00000000014 14625713532 0024214 0 ustar 00root root 0000000 0000000 Unauthorized rokam-sunweg-cb8c67c/tests/responses/error_500_response.txt 0000664 0000000 0000000 00000000025 14625713532 0024216 0 ustar 00root root 0000000 0000000 Internal server error rokam-sunweg-cb8c67c/tests/responses/inverter_success_response.json 0000664 0000000 0000000 00000003241 14625713532 0026224 0 ustar 00root root 0000000 0000000 {
"success": true,
"temperatura": 80,
"temperaturaStatus": 0,
"fatorpotencia": "0,00",
"stringmppt": [
{
"nomemppt": "MPPT 01",
"strings": [
{
"nome": "ST 01",
"variaveltensao": "Upv1",
"variavelcorrente": "Ipv1",
"situacao": 1
},
{
"nome": "ST 02",
"variaveltensao": "Upv2",
"variavelcorrente": "Ipv2",
"situacao": 1
}
]
},
{
"nomemppt": "MPPT 02",
"strings": [
{
"nome": "ST 03",
"variaveltensao": "Upv3",
"variavelcorrente": "Ipv3",
"situacao": 1
},
{
"nome": "ST 04",
"variaveltensao": "Upv4",
"variavelcorrente": "Ipv4",
"situacao": 1
}
]
}
],
"inversor": {
"id": 21255,
"nome": "Inverter Name",
"descricao": "Inverter Description",
"esn": "1234ABC",
"situacao": 1,
"tensaoca": 242,
"temperatura": 80,
"leitura": {
"Upv1": 429.6000000000000227373675443232059478759765625,
"Upv2": 415,
"Upv3": 418,
"Upv4": 525.13,
"Ipv1": 3,
"Ipv2": 2.1,
"Ipv3": 4.2,
"Ipv4": 5
}
},
"correnteCA": {
"faseA": "0,20",
"faseB": "0,50",
"faseC": "0,40",
"faseAstatus": 0,
"faseBstatus": 0,
"faseCstatus": 0
},
"tensaoca": {
"faseA": "11,10",
"faseB": "11,30",
"faseC": "10,20",
"faseAstatus": 1,
"faseBstatus": 1,
"faseCstatus": 1
},
"potenciaativa": "0,00 kW",
"energiadodia": "0,00 kWh",
"energiaacumulada": "23,20 kWh",
"frequencia": "59,85",
"statusInversor": 0
} rokam-sunweg-cb8c67c/tests/responses/list_plant_success_1_response.json 0000664 0000000 0000000 00000000155 14625713532 0026760 0 ustar 00root root 0000000 0000000 {"success":true,"conectadas":[{"id":16925}],"nao_comissionadas":[],"falhas":[],"alertas":[],"atendimento":[]} rokam-sunweg-cb8c67c/tests/responses/list_plant_success_2_response.json 0000664 0000000 0000000 00000000172 14625713532 0026760 0 ustar 00root root 0000000 0000000 {"success":true,"conectadas":[{"id":16925},{"id":16926}],"nao_comissionadas":[],"falhas":[],"alertas":[],"atendimento":[]} rokam-sunweg-cb8c67c/tests/responses/list_plant_success_none_response.json 0000664 0000000 0000000 00000000141 14625713532 0027552 0 ustar 00root root 0000000 0000000 {"success":true,"conectadas":[],"nao_comissionadas":[],"falhas":[],"alertas":[],"atendimento":[]} rokam-sunweg-cb8c67c/tests/responses/month_stats_fail_response.json 0000664 0000000 0000000 00000000064 14625713532 0026174 0 ustar 00root root 0000000 0000000 {
"success": false,
"message": "Error message"
} rokam-sunweg-cb8c67c/tests/responses/month_stats_success_response.json 0000664 0000000 0000000 00000023024 14625713532 0026732 0 ustar 00root root 0000000 0000000 {
"graficomes": [
{
"energiapordia": 116.9,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "Wed, 01 May 2024 00:00:00 GMT"
},
{
"energiapordia": 113.7,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "Thu, 02 May 2024 00:00:00 GMT"
},
{
"energiapordia": 112.4,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "Fri, 03 May 2024 00:00:00 GMT"
},
{
"energiapordia": 133.3,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "Sat, 04 May 2024 00:00:00 GMT"
},
{
"energiapordia": 129.2,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "Sun, 05 May 2024 00:00:00 GMT"
},
{
"energiapordia": 86.5,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "Mon, 06 May 2024 00:00:00 GMT"
},
{
"energiapordia": 116.2,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "Tue, 07 May 2024 00:00:00 GMT"
},
{
"energiapordia": 125.8,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "Wed, 08 May 2024 00:00:00 GMT"
},
{
"energiapordia": 127.5,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "Thu, 09 May 2024 00:00:00 GMT"
},
{
"energiapordia": 118.7,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "Fri, 10 May 2024 00:00:00 GMT"
},
{
"energiapordia": 122.6,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "Sat, 11 May 2024 00:00:00 GMT"
},
{
"energiapordia": 123.3,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "Sun, 12 May 2024 00:00:00 GMT"
},
{
"energiapordia": 96.3,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "Mon, 13 May 2024 00:00:00 GMT"
},
{
"energiapordia": 104.3,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "Tue, 14 May 2024 00:00:00 GMT"
},
{
"energiapordia": 120.9,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "Wed, 15 May 2024 00:00:00 GMT"
},
{
"energiapordia": 120.8,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "Thu, 16 May 2024 00:00:00 GMT"
},
{
"energiapordia": 115.1,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "Fri, 17 May 2024 00:00:00 GMT"
},
{
"energiapordia": 118.8,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "Sat, 18 May 2024 00:00:00 GMT"
},
{
"energiapordia": 114.5,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "Sun, 19 May 2024 00:00:00 GMT"
},
{
"energiapordia": 102.4,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "Mon, 20 May 2024 00:00:00 GMT"
},
{
"energiapordia": 106.9,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "Tue, 21 May 2024 00:00:00 GMT"
},
{
"energiapordia": 117.1,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "Wed, 22 May 2024 00:00:00 GMT"
},
{
"energiapordia": 105.6,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "Thu, 23 May 2024 00:00:00 GMT"
},
{
"energiapordia": 99.1,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "Fri, 24 May 2024 00:00:00 GMT"
},
{
"energiapordia": 104.4,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "Sat, 25 May 2024 00:00:00 GMT"
},
{
"energiapordia": 96.7,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "Sun, 26 May 2024 00:00:00 GMT"
},
{
"energiapordia": 110.4,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "Mon, 27 May 2024 00:00:00 GMT"
},
{
"energiapordia": 108.6,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "Tue, 28 May 2024 00:00:00 GMT"
},
{
"energiapordia": 58.3,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "2024-05-29"
},
{
"energiapordia": 0,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "2024-05-30"
},
{
"energiapordia": 0,
"prognostico": "111.0322580645161290322580645",
"tempoatual": "2024-05-31"
}
],
"graficomescalc": [
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "Wed, 01 May 2024 00:00:00 GMT"
},
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "Thu, 02 May 2024 00:00:00 GMT"
},
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "Fri, 03 May 2024 00:00:00 GMT"
},
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "Sat, 04 May 2024 00:00:00 GMT"
},
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "Sun, 05 May 2024 00:00:00 GMT"
},
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "Mon, 06 May 2024 00:00:00 GMT"
},
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "Tue, 07 May 2024 00:00:00 GMT"
},
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "Wed, 08 May 2024 00:00:00 GMT"
},
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "Thu, 09 May 2024 00:00:00 GMT"
},
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "Fri, 10 May 2024 00:00:00 GMT"
},
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "Sat, 11 May 2024 00:00:00 GMT"
},
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "Sun, 12 May 2024 00:00:00 GMT"
},
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "Mon, 13 May 2024 00:00:00 GMT"
},
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "Tue, 14 May 2024 00:00:00 GMT"
},
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "Wed, 15 May 2024 00:00:00 GMT"
},
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "Thu, 16 May 2024 00:00:00 GMT"
},
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "Fri, 17 May 2024 00:00:00 GMT"
},
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "Sat, 18 May 2024 00:00:00 GMT"
},
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "Sun, 19 May 2024 00:00:00 GMT"
},
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "Mon, 20 May 2024 00:00:00 GMT"
},
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "Tue, 21 May 2024 00:00:00 GMT"
},
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "Wed, 22 May 2024 00:00:00 GMT"
},
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "Thu, 23 May 2024 00:00:00 GMT"
},
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "Fri, 24 May 2024 00:00:00 GMT"
},
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "Sat, 25 May 2024 00:00:00 GMT"
},
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "Sun, 26 May 2024 00:00:00 GMT"
},
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "Mon, 27 May 2024 00:00:00 GMT"
},
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "Tue, 28 May 2024 00:00:00 GMT"
},
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "2024-05-29"
},
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "2024-05-30"
},
{
"energiapordia": 0,
"prognostico": 0,
"tempoatual": "2024-05-31"
}
],
"success": true
} rokam-sunweg-cb8c67c/tests/responses/plant_success_alt_response.json 0000664 0000000 0000000 00000001121 14625713532 0026337 0 ustar 00root root 0000000 0000000 {
"success": true,
"usinas": {
"id": 16925,
"nome": "Plant Name",
"inversores": [
{
"id": 21255,
"nome": "Inverter Name",
"descricao": "Inverter Description",
"esn": "1234ABC",
"situacao": 1,
"tensaoca": 242,
"temperatura": 80
}
]
},
"ultimaAtualizacao": null,
"AcumuladoPotencia": "25,23 kW",
"energiadia": "1,23 kWh",
"energiaacumuladanumber": "23.20",
"taxaPerformance": 1.48,
"KWHporkWp": "1,2",
"PerformanceRate": 0.1,
"reduz_carbono_total_number": 0.012296,
"economia": "12,78"
} rokam-sunweg-cb8c67c/tests/responses/plant_success_response.json 0000664 0000000 0000000 00000001142 14625713532 0025502 0 ustar 00root root 0000000 0000000 {
"success": true,
"usinas": {
"id": 16925,
"nome": "Plant Name",
"inversores": [
{
"id": 21255,
"nome": "Inverter Name",
"descricao": "Inverter Description",
"esn": "1234ABC",
"situacao": 1,
"tensaoca": 242,
"temperatura": 80
}
]
},
"ultimaAtualizacao": "2023-02-25 08:04:22",
"AcumuladoPotencia": "25,23 kW",
"energiadia": "1,23 kWh",
"energiaacumuladanumber": "23.20",
"taxaPerformance": 1.48,
"KWHporkWp": "1,2",
"PerformanceRate": 0.1,
"reduz_carbono_total_number": 0.012296,
"economia": "12,78"
} rokam-sunweg-cb8c67c/tests/test_api.py 0000664 0000000 0000000 00000042550 14625713532 0020174 0 ustar 00root root 0000000 0000000 """Test sunweg.api."""
from datetime import date, datetime
from os import path
import os
from unittest import TestCase
from unittest.mock import MagicMock, patch
import pytest
from requests import Response
from sunweg.api import (
APIHelper,
convert_situation_status,
SunWegApiError,
separate_value_metric,
)
from sunweg.device import Inverter, String
from sunweg.util import Status
from .common import INVERTER_MOCK, PLANT_MOCK
class Api_Test(TestCase):
"""APIHelper test case."""
responses: dict[str, Response] = {}
def setUp(self) -> None:
"""Set tests up."""
for file in os.listdir(path.join(path.dirname(__file__), "responses")):
filename = path.basename(file)
with open(path.join(path.dirname(__file__), "responses", file)) as f:
response = Response()
if filename.startswith("error"):
response.status_code = int(filename.split("_")[1])
response.reason = "".join(f.readlines())
else:
response.status_code = 200
response._content = "".join(f.readlines()).encode()
self.responses[filename] = response
def test_convert_situation_status(self) -> None:
"""Test the conversion from situation to status."""
status_ok: Status = convert_situation_status(1)
status_err: Status = convert_situation_status(0)
status_wrn: Status = convert_situation_status(2)
assert status_ok == Status.OK
assert status_err == Status.ERROR
assert status_wrn == Status.WARN
def test_separate_value_metric_comma(self) -> None:
"""Test the separation from value and metric of string with comma."""
(value, metric) = separate_value_metric("0,0")
assert value == 0
assert metric == ""
(value, metric) = separate_value_metric("1,0", "W")
assert value == 1.0
assert metric == "W"
(value, metric) = separate_value_metric("0,2 kW", "W")
assert value == 0.2
assert metric == "kW"
def test_separate_value_metric_dot(self) -> None:
"""Test the separation from value and metric of string with dot."""
(value, metric) = separate_value_metric("0.0")
assert value == 0
assert metric == ""
(value, metric) = separate_value_metric("1.0", "W")
assert value == 1.0
assert metric == "W"
(value, metric) = separate_value_metric("0.2 kW", "W")
assert value == 0.2
assert metric == "kW"
def test_separate_value_metric_none_int(self) -> None:
"""Test the separation from value and metric of string with dot."""
(value, metric) = separate_value_metric(None)
assert value == 0
assert metric == ""
(value, metric) = separate_value_metric("1", "W")
assert value == 1.0
assert metric == "W"
(value, metric) = separate_value_metric("2 kW", "W")
assert value == 2.0
assert metric == "kW"
def test_error500(self) -> None:
"""Test error 500."""
with patch(
"requests.Session.post",
return_value=self.responses["error_500_response.txt"],
):
api = APIHelper("user@acme.com", "password")
with pytest.raises(SunWegApiError) as e_info:
api.authenticate()
assert e_info.value.__str__() == "Request failed: "
def test_authenticate_success(self) -> None:
"""Test authentication success."""
with patch(
"requests.Session.post",
return_value=self.responses["auth_success_response.json"],
):
api = APIHelper("user@acme.com", "password")
assert api.authenticate()
def test_authenticate_failed(self) -> None:
"""Test authentication failed."""
with patch(
"requests.Session.post",
return_value=self.responses["auth_fail_response.json"],
):
api = APIHelper("user@acme.com", "password")
assert not api.authenticate()
def test_list_plants_none_success(self) -> None:
"""Test list plants with empty plant list."""
with patch(
"requests.Session.get",
return_value=self.responses["list_plant_success_none_response.json"],
), patch("sunweg.api.APIHelper.plant", return_value=PLANT_MOCK):
api = APIHelper("user@acme.com", "password")
assert len(api.listPlants()) == 0
def test_list_plants_1_success(self) -> None:
"""Test list plants with one plant in the list."""
with patch(
"requests.Session.get",
return_value=self.responses["list_plant_success_1_response.json"],
), patch("sunweg.api.APIHelper.plant", return_value=PLANT_MOCK):
api = APIHelper("user@acme.com", "password")
assert len(api.listPlants()) == 1
def test_list_plants_2_success(self) -> None:
"""Test list plants with two plant in the list."""
with patch(
"requests.Session.get",
return_value=self.responses["list_plant_success_2_response.json"],
), patch("sunweg.api.APIHelper.plant", return_value=PLANT_MOCK):
api = APIHelper("user@acme.com", "password")
assert len(api.listPlants()) == 2
def test_list_plants_401(self) -> None:
"""Test list plants with expired token."""
with patch(
"requests.Session.post",
return_value=self.responses["auth_success_response.json"],
), patch(
"requests.Session.get",
return_value=self.responses["error_401_response.txt"],
):
api = APIHelper("user@acme.com", "password")
assert len(api.listPlants()) == 0
def test_plant_success(self) -> None:
"""Test plant success."""
with patch(
"requests.Session.get",
return_value=self.responses["plant_success_response.json"],
), patch("sunweg.api.APIHelper.inverter", return_value=INVERTER_MOCK):
api = APIHelper("user@acme.com", "password")
plant = api.plant(16925)
assert plant is not None
assert plant.id == 16925
assert plant.name == "Plant Name"
assert plant.total_power == 25.23
assert plant.last_update == datetime(2023, 2, 25, 8, 4, 22)
assert plant.kwh_per_kwp == 0.0
assert plant.performance_rate == 0.0
assert plant.saving == 12.78
assert plant.today_energy == 1.23
assert plant.today_energy_metric == "kWh"
assert plant.total_carbon_saving == 0.012296
assert plant.total_energy == 23.2
assert plant.__str__().startswith("")
assert len(plant.inverters) == 1
for inverter in plant.inverters:
assert inverter.id == 21255
assert inverter.name == "Inverter Name"
assert inverter.frequency == 0
assert inverter.power == 0.0
assert inverter.power_metric == ""
assert inverter.power_factor == 0.0
assert inverter.sn == "1234ABC"
assert inverter.status == Status.ERROR
assert inverter.temperature == 80
assert inverter.today_energy == 0.0
assert inverter.today_energy_metric == ""
assert inverter.total_energy == 0.0
assert inverter.total_energy_metric == ""
assert not inverter.is_complete
def test_plant_success_alt(self) -> None:
"""Test plant success."""
with patch(
"requests.Session.get",
return_value=self.responses["plant_success_alt_response.json"],
), patch("sunweg.api.APIHelper.inverter", return_value=INVERTER_MOCK):
api = APIHelper("user@acme.com", "password")
plant = api.plant(16925)
assert plant is not None
assert plant.id == 16925
assert plant.name == "Plant Name"
assert plant.total_power == 25.23
assert plant.last_update is None
assert plant.kwh_per_kwp == 0.0
assert plant.performance_rate == 0.0
assert plant.saving == 12.78
assert plant.today_energy == 1.23
assert plant.today_energy_metric == "kWh"
assert plant.total_carbon_saving == 0.012296
assert plant.total_energy == 23.2
assert plant.__str__().startswith("")
assert len(plant.inverters) == 1
for inverter in plant.inverters:
assert inverter.id == 21255
assert inverter.name == "Inverter Name"
assert inverter.frequency == 0
assert inverter.power == 0.0
assert inverter.power_metric == ""
assert inverter.power_factor == 0.0
assert inverter.sn == "1234ABC"
assert inverter.status == Status.ERROR
assert inverter.temperature == 80
assert inverter.today_energy == 0.0
assert inverter.today_energy_metric == ""
assert inverter.total_energy == 0.0
assert inverter.total_energy_metric == ""
assert not inverter.is_complete
def test_plant_401(self) -> None:
"""Test plant with expired token."""
with patch(
"requests.Session.post",
return_value=self.responses["auth_success_response.json"],
), patch(
"requests.Session.get",
return_value=self.responses["error_401_response.txt"],
):
api = APIHelper("user@acme.com", "password")
assert api.plant(16925) is None
def test_inverter_success(self) -> None:
"""Test inverter success."""
with patch(
"requests.Session.get",
return_value=self.responses["inverter_success_response.json"],
):
api = APIHelper("user@acme.com", "password")
inverter = api.inverter(21255)
assert inverter is not None
assert inverter.id == 21255
assert inverter.name == "Inverter Name"
assert inverter.frequency == 59.85
assert inverter.power == 0.0
assert inverter.power_metric == "kW"
assert inverter.power_factor == 0.0
assert inverter.sn == "1234ABC"
assert inverter.status == Status.OK
assert inverter.temperature == 80
assert inverter.today_energy == 0.0
assert inverter.today_energy_metric == "kWh"
assert inverter.total_energy == 23.2
assert inverter.today_energy_metric == "kWh"
strings: list[String] = []
for mppt in inverter.mppts:
assert mppt.__str__().startswith("")
assert mppt.name != ""
strings.extend(mppt.strings)
assert len(strings) == 4
assert len(inverter.phases) == 3
assert inverter.__str__().startswith("")
for string in strings:
assert string.name != ""
assert string.amperage != 0
assert string.voltage != 0
assert string.status == Status.OK
assert string.__str__().startswith("")
for phase in inverter.phases:
assert phase.name != ""
assert phase.amperage != 0
assert phase.voltage != 0
assert phase.status_amperage == Status.OK
assert phase.status_voltage == Status.ERROR
assert phase.__str__().startswith("")
def test_inverter_401(self) -> None:
"""Test inverter with expired token."""
with patch(
"requests.Session.post",
return_value=self.responses["auth_success_response.json"],
), patch(
"requests.Session.get",
return_value=self.responses["error_401_response.txt"],
):
api = APIHelper("user@acme.com", "password")
assert api.inverter(21255) is None
def test_complete_inverter_success(self) -> None:
"""Test complete inverter success."""
with patch(
"requests.Session.get",
return_value=self.responses["inverter_success_response.json"],
):
api = APIHelper("user@acme.com", "password")
inverter = Inverter(
id=12345,
name="Other inverter name",
sn="1234ABCD",
status=Status.ERROR,
temperature=70,
)
api.complete_inverter(inverter)
assert inverter is not None
assert inverter.id == 12345
assert inverter.name == "Other inverter name"
assert inverter.frequency == 59.85
assert inverter.power == 0.0
assert inverter.power_factor == 0.0
assert inverter.sn == "1234ABCD"
assert inverter.status == Status.ERROR
assert inverter.temperature == 70
assert inverter.today_energy == 0.0
assert inverter.total_energy == 23.2
strings: list[String] = []
for mppt in inverter.mppts:
assert mppt.__str__().startswith("")
assert mppt.name != ""
strings.extend(mppt.strings)
assert len(strings) == 4
assert len(inverter.phases) == 3
assert inverter.__str__().startswith("")
for string in strings:
assert string.name != ""
assert string.amperage != 0
assert string.voltage != 0
assert string.status == Status.OK
assert string.__str__().startswith("")
for phase in inverter.phases:
assert phase.name != ""
assert phase.amperage != 0
assert phase.voltage != 0
assert phase.status_amperage == Status.OK
assert phase.status_voltage == Status.ERROR
assert phase.__str__().startswith("")
def test_complete_inverter_401(self) -> None:
"""Test complete inverter with expired token."""
with patch(
"requests.Session.post",
return_value=self.responses["auth_success_response.json"],
), patch(
"requests.Session.get",
return_value=self.responses["error_401_response.txt"],
):
api = APIHelper("user@acme.com", "password")
inverter = Inverter(
id=12345,
name="Other inverter name",
sn="1234ABCD",
status=Status.ERROR,
temperature=70,
)
api.complete_inverter(inverter)
assert not inverter.is_complete
def test_setters(self) -> None:
"""Test API setters."""
api = APIHelper("user@acme.com", "password")
assert api._username == "user@acme.com"
assert api._password == "password"
api.username = "user1@acme.com"
api.password = "password1"
assert api._username == "user1@acme.com"
assert api._password == "password1"
def test_month_stats_fail(self) -> None:
"""Test month stats with error from server."""
with patch(
"requests.Session.get",
return_value=self.responses["month_stats_fail_response.json"],
):
api = APIHelper("user@acme.com", "password")
plant = MagicMock()
plant.id = 1
with pytest.raises(SunWegApiError) as e_info:
api.month_stats_production(2013, 12, plant)
assert e_info.value.__str__() == "Error message"
def test_month_stats_401(self) -> None:
"""Test month stats with data from server with expired token."""
with patch(
"requests.Session.post",
return_value=self.responses["auth_success_response.json"],
), patch(
"requests.Session.get",
return_value=self.responses["error_401_response.txt"],
):
api = APIHelper("user@acme.com", "password")
plant = MagicMock()
plant.id = 1
stats = api.month_stats_production(2023, 12, plant)
assert isinstance(stats, list)
assert len(stats) == 0
def test_month_stats_success(self) -> None:
"""Test month stats with data from server."""
with patch(
"requests.Session.get",
return_value=self.responses["month_stats_success_response.json"],
):
api = APIHelper("user@acme.com", "password")
plant = MagicMock()
plant.id = 1
stats = api.month_stats_production(2023, 12, plant)
assert len(stats) > 0
i: int = 1
for stat in stats:
assert stat.date == date(2024, 5, i)
assert isinstance(stat.production, float)
assert stat.prognostic == 111.03225806451613
assert stat.__str__().startswith(
""
)
i += 1