Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.idea/
.pytest_cache
**__pycache__
dist
*.egg-info
build/
7 changes: 7 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
language: python
python:
- "3.6"
install:
- pip install -e .[tests]
script:
- pytest
373 changes: 373 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,37 @@
[![Build Status](https://travis-ci.org/timvancann/growatt_api_client.svg?branch=master)](https://travis-ci.org/timvancann/growatt_api_client)

## Growatt API client

This Python 3 script logs in to server.growatt.com and retrieves data on solar panels.

### Example

You can get your plant info with the following snippet:

```python
import datetime
from growat_api.growatt_api import GrowattApi, Timespan

username = ...
password = ...


api = GrowattApi()
login_res = api.login(username, password)
user_id = login_res['userId']
plant_info = api.plant_list(user_id)
print(plant_info)

plant_id = plant_info['data'][0]['plantId']
plant_detail = api.plant_detail(plant_id, Timespan.day, datetime.date.today())
print(plant_detail)
```

## Todays total energy

To get todays total energy across all plants in `kWh` you can use

```python
from growat_api.growatt_api import todays_energy_total
todays_energy_total(username='...', password='...')
```
Empty file added growatt_api/__init__.py
Empty file.
41 changes: 24 additions & 17 deletions growattapi.py → growatt_api/growatt_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import datetime
import hashlib
import json
from typing import List, Dict

import requests
import sys

Expand Down Expand Up @@ -51,12 +53,16 @@ def plant_list(self, user_id):
data = json.loads(response.content.decode("utf-8"))
return data["back"]

def plant_detail(self, plant_id, timespan, date):
def plant_detail(self, plant_id, timespan=Timespan.day, date=None):
assert timespan in Timespan
if timespan == Timespan.day:
date_str = date.strftime("%Y-%m-%d")
elif timespan == Timespan.month:

if not date:
date = datetime.date.today()

if timespan == Timespan.month:
date_str = date.strftime("%Y-%m")
else:
date_str = date.strftime("%Y-%m-%d")

response = self.session.get(
self.get_url("PlantDetailAPI.do"),
Expand All @@ -65,19 +71,20 @@ def plant_detail(self, plant_id, timespan, date):
data = json.loads(response.content.decode("utf-8"))
return data["back"]

def _extract_energy(self, plant_info_data: List[Dict[str, str]], key: str) -> float:
kwhs = [_[key] for _ in plant_info_data]
energies = [float(_.split(' ')[0]) for _ in kwhs]
return sum(energies)

if __name__ == "__main__":
username = sys.argv[1]
password = sys.argv[2]

assert hash_password("banaan") == "31d674be46e1ba6b54388a671cc9accb"
def _plant_info(self, username: str, password: str):
login_res = self.login(username, password)
user_id = login_res['userId']
return self.plant_list(user_id)

api = GrowattApi()
login_res = api.login(username, password)
user_id = login_res["userId"]
plant_info = api.plant_list(user_id)
print(plant_info)
def todays_energy_total(self, username: str, password: str):
plant_info = self._plant_info(username, password)
return self._extract_energy(plant_info['data'], 'todayEnergy')

plant_id = plant_info["data"][0]["plantId"]
plant_detail = api.plant_detail(plant_id, Timespan.day, datetime.date.today())
print(plant_detail)
def global_energy_total(self, username: str, password: str):
plant_info = self._plant_info(username, password)
return self._extract_energy(plant_info['data'], 'totalEnergy')
1 change: 0 additions & 1 deletion requirements.txt

This file was deleted.

34 changes: 34 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from setuptools import setup

requirements = ["requests"]
try:
import enum
except:
requirements += ["enum34"]

with open("README.md", 'r') as f:
long_description = f.read()

setup(
name='Growatt API',
description='A package to query the growatt server',
version='0.0.3',
author=['Sjoerd Langkemper', 'Tim van Cann'],
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/timvancann/growatt_api_client",
license="OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)",
packages=['growatt_api'],
install_requires=requirements,
extras_require={
'tests': [
'pytest==4.1.1',
'requests-mock==1.5.2'
]
},
classifiers=[
"Development Status :: 4 - Beta",
"License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)",
"Programming Language :: Python",
],
)
90 changes: 90 additions & 0 deletions tests/test_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import json

import requests_mock

from growatt_api import growatt_api as victim


def test_hash():
assert victim.hash_password("banaan") == "31d674be46e1ba6b54388a671cc9accb"


def test_extract_single_energy():
plant_info_data = [
{'plantMoneyText': '137.9 ',
'plantName': 'my plant',
'plantId': '107658',
'isHaveStorage': 'false',
'todayEnergy': '0.6 kWh',
'totalEnergy': '114.9 kWh',
'currentPower': '142 W'}
]

assert victim.GrowattApi()._extract_energy(plant_info_data, 'todayEnergy') == 0.6


def test_extract_multiple_energy():
plant_info_data = [
{'plantMoneyText': '137.9 ',
'plantName': 'my plant',
'plantId': '107658',
'isHaveStorage': 'false',
'todayEnergy': '0.6 kWh',
'totalEnergy': '114.9 kWh',
'currentPower': '142 W'},
{'plantMoneyText': '137.9 ',
'plantName': 'my plant',
'plantId': '107658',
'isHaveStorage': 'false',
'todayEnergy': '0.6 kWh',
'totalEnergy': '114.9 kWh',
'currentPower': '142 W'}
]

assert victim.GrowattApi()._extract_energy(plant_info_data, 'todayEnergy') == 1.2


def test_login():
api = victim.GrowattApi()
with requests_mock.mock() as m:
m.post('https://server.growatt.com/LoginAPI.do',
text=json.dumps({'back': {'userId': '1'}}))

login_res = api.login('foo', 'bar')
assert login_res == {'userId': '1'}


def test_plant_list():
api = victim.GrowattApi()
with requests_mock.mock() as m:
m.get('https://server.growatt.com/PlantDetailAPI.do',
text=json.dumps({'back': {'data': 'some-data'}}))

login_res = api.plant_detail('1')
assert login_res == {'data': 'some-data'}


def test_today_energy_total():
with requests_mock.mock() as m:
m.post('https://server.growatt.com/LoginAPI.do',
text=json.dumps({'back': {'userId': '1'}}))

dummy_plant_info = {'data': [{'plantMoneyText': '137.9 ',
'plantName': 'my plant',
'plantId': '107658',
'isHaveStorage': 'false',
'todayEnergy': '0.6 kWh',
'totalEnergy': '114.9 kWh',
'currentPower': '142 W'}],
'totalData': {'currentPowerSum': '142 W',
'CO2Sum': '114.9 T',
'isHaveStorage': 'false',
'eTotalMoneyText': '137.9 ',
'todayEnergySum': '0.6 kWh',
'totalEnergySum': '114.9 kWh'},
'success': True}

m.get('https://server.growatt.com/PlantListAPI.do?userId=1',
text=json.dumps({'back': dummy_plant_info}))

assert victim.GrowattApi().todays_energy_total('foo', 'bar') == 0.6