-
-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a validator for the config + create-passhash
* JSON schame validator * Create passhash flag * Updated README
- Loading branch information
Showing
5 changed files
with
293 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,22 +3,111 @@ | |
Script to retrieve current Solar PV data from the Solarman API, and send Power (W) and Energy (kWh) metrics to a MQTT broker, for further use in home automation. Several PV vendors use the Solarman Smart platform for statistics. One example is the Trannergy PV converter. | ||
|
||
```lang=bash | ||
usage: run.py [-h] [-d] [-s] [-i INTERVAL] [-f FILE] [-v] | ||
usage: run.py [-h] [-d] [-s] [-i INTERVAL] [-f FILE] [--validate] [--create-passhash CREATE_PASSHASH] [-v] | ||
Collect data from Trannergy / Solarman API | ||
optional arguments: | ||
-h, --help show this help message and exit | ||
-d, --daemon run as a service (default) | ||
-s, --single single run and exit | ||
-i INTERVAL, --interval INTERVAL run interval in seconds (default 300 sec.) | ||
-f FILE, --file FILE config file (default ./config.json) | ||
-v, --version show program's version number and exit | ||
-h, --help show this help message and exit | ||
-d, --daemon run as a service | ||
-s, --single single run and exit | ||
-i INTERVAL, --interval INTERVAL | ||
run interval in seconds (default 300 sec.) | ||
-f FILE, --file FILE config file (default ./config.json) | ||
--validate validate config file and exit | ||
--create-passhash CREATE_PASSHASH | ||
create passhash from provided passwordand exit | ||
-v, --version show program's version number and exit | ||
``` | ||
|
||
## Usage | ||
|
||
You can run this script as a Docker container or in Python 3. Either way a configuration file is required. See the sample `config.sample.json` file in this repository for reference. Also, a Solarman API appid+secret is required, which can be requested via <mailto:[email protected]>. | ||
You can run this script as a Docker container or in Python 3. Either way a configuration file is required. See the sample `config.sample.json` file in this repository for reference. Also, a Solarman API appid+secret is required, which can be requested via <mailto:[email protected]>. | ||
|
||
## Were do I get te required information for the config file? | ||
|
||
Create a new config file by copying the sample file and filling in the required information. | ||
|
||
The first part covers your account: | ||
|
||
```lang=json | ||
{ | ||
"name": "Trannergy", | ||
"url": "api.solarmanpv.com", | ||
"appid": "", | ||
"secret": "", | ||
"username": "", | ||
"passhash": "", | ||
} | ||
``` | ||
|
||
* **name**: is free text to identify the platform. | ||
* **url**: is the base URL of the API. | ||
* **appid**: is the appid for the API (See Usage). | ||
* **secret**: is the secret for the API (See Usage). | ||
* **username**: is the username for the API (emailadres). | ||
* **passhash**: is a sha256 hash of your password. This can be generated via `--create-passhash`. | ||
|
||
The second part covers the PV inverter and logger ID's. These can be retrieved via the Solarman API. | ||
|
||
```lang=json | ||
{ | ||
"stationId": 123, | ||
"inverterId": 456, | ||
"loggerId": 789 | ||
} | ||
``` | ||
|
||
* **stationId**: is the ID of the station. This is the value of `stationList[0].id`. | ||
|
||
```lang=bash | ||
curl --location --request POST 'https://api.solarmanpv.com//station/v1.0/list?language=en' \ | ||
--header 'Content-Type: application/json' \ | ||
--header 'Authorization: bearer TOKEN' \ | ||
--data-raw '{"size":20,"page":1}' | ||
``` | ||
|
||
* **inverterId**: is the ID of the inverter. This is the value of `deviceListItems[0].deviceSn` | ||
|
||
```lang=bash | ||
curl --location --request POST 'https://api.solarmanpv.com//station/v1.0/device?language=en' \ | ||
--header 'Content-Type: application/json' \ | ||
--header 'Authorization: bearer TOKEN' \ | ||
--data-raw '{"size":10,"page":1,"stationId":1234567,"deviceType":"INVERTER"}' | ||
``` | ||
|
||
* **loggerId**: is the ID of the logger. This is the value of `deviceListItems[0].deviceSn`. | ||
|
||
```lang=bash | ||
curl --location --request POST 'https://api.solarmanpv.com//station/v1.0/device?language=en' \ | ||
--header 'Content-Type: application/json' \ | ||
--header 'Authorization: bearer TOKEN' \ | ||
--data-raw '{"size":10,"page":1,"stationId":1234567,"deviceType":"COLLECTOR"}' | ||
``` | ||
|
||
A bearer TOKEN to use in the requests above can be retrieved by adding your APPID, APPSECRET, USERNAME, PASSHASH in this request: | ||
|
||
```lang=bash | ||
curl --location --request POST 'https://api.solarmanpv.com//account/v1.0/token?appId=APPID&language=en' \ | ||
--header 'Content-Type: application/json' \ | ||
--data-raw '{ | ||
"appSecret": "APPSECRET", | ||
"email": "USERNAME", | ||
"password": "PASSHASH" | ||
}' | ||
``` | ||
|
||
The final section covers the MQTT broker, to where the metrics will be published. | ||
|
||
```lang=json | ||
{ | ||
"broker": "mqtt.example.com", | ||
"port": 1883, | ||
"topic": "solarman", | ||
"username": "", | ||
"password": "" | ||
} | ||
``` | ||
|
||
## MQTT topics | ||
|
||
|
@@ -49,7 +138,7 @@ solarmanpv/inverter/deviceType | |
solarmanpv/inverter/attributes # contains all inverter datalist entries. | ||
``` | ||
|
||
#### Attributes: | ||
#### Attributes: | ||
|
||
```lang=text | ||
SN: XXXXXXXXXX | ||
|
@@ -153,15 +242,15 @@ sensor: | |
state_class: measurement | ||
``` | ||
|
||
Repeat for every station topic needed. | ||
Repeat for every station topic needed. | ||
|
||
``` | ||
```lang=yaml | ||
sensor: | ||
- platform: mqtt | ||
name: "solarmanpv_inverter" | ||
state_topic: "solarmanpv/inverter/deviceState" | ||
json_attributes_topic: "solarmanpv/inverter/attributes" | ||
- platform: mqtt | ||
name: "solarmanpv_logger" | ||
state_topic: "solarmanpv/logger/deviceState" | ||
|
@@ -178,7 +267,7 @@ sensor: | |
'3' : 'Offline'} %} | ||
{% set state = states.sensor.solarmanpv_inverter.state %} | ||
{{ mapper[state] if state in mapper else 'Unknown' }} | ||
- platform: template | ||
sensors: | ||
solarmanpv_logger_device_state: | ||
|
@@ -190,12 +279,11 @@ sensor: | |
'3' : 'Offline'} %} | ||
{% set state = states.sensor.solarmanpv_logger.state %} | ||
{{ mapper[state] if state in mapper else 'Unknown' }} | ||
``` | ||
|
||
### Templates | ||
|
||
```lang=text | ||
```lang=yaml | ||
template: | ||
- sensor: | ||
- name: solarmanpv_inverter_dc_voltage_pv1 | ||
|
@@ -207,25 +295,25 @@ template: | |
unit_of_measurement: 'A' | ||
state: "{{ state_attr('sensor.solarmanpv_inverter', 'DC_Current_PV1') }}" | ||
state_class: measurement | ||
- sensor: | ||
- name: solarmanpv_inverter_dc_power_pv1 | ||
unit_of_measurement: 'W' | ||
state: "{{ state_attr('sensor.solarmanpv_inverter', 'DC_Power_PV1') }}" | ||
state_class: measurement | ||
- sensor: | ||
- name: solarmanpv_inverter_dc_power_pv1 | ||
unit_of_measurement: 'W' | ||
state: "{{ state_attr('sensor.solarmanpv_inverter', 'DC_Power_PV1') }}" | ||
state_class: measurement | ||
- sensor: | ||
- name: solarmanpv_inverter_total_production_1 | ||
unit_of_measurement: 'kWh' | ||
state: "{{ state_attr('sensor.solarmanpv_inverter', 'Total_Production_1') }}" | ||
state_class: total_increasing | ||
- sensor: | ||
- name: solarmanpv_inverter_daily_production_1 | ||
unit_of_measurement: 'kWh' | ||
|
@@ -237,13 +325,13 @@ template: | |
unit_of_measurement: '°C' | ||
state: "{{ state_attr('sensor.solarmanpv_inverter', 'AC_Radiator_Temp') }}" | ||
state_class: measurement | ||
- sensor: | ||
- name: solarmanpv_inverter_ac_voltage_1 | ||
unit_of_measurement: 'V' | ||
state: "{{ state_attr('sensor.solarmanpv_inverter', 'AC_Voltage_1') }}" | ||
state_class: measurement | ||
- sensor: | ||
- name: solarmanpv_inverter_ac_current_1 | ||
unit_of_measurement: 'A' | ||
|
@@ -263,6 +351,10 @@ template: | |
![Screenshot](https://github.com/mpepping/solarman-mqtt/raw/main/doc/images/screenshot.png "Screenshot") | ||
![Screenshot](https://github.com/mpepping/solarman-mqtt/raw/main/doc/images/screenshot_haenergy.png "Screenshot") | ||
|
||
## Running | ||
|
||
The easiest way to run is via a container. Current version is available at <https://github.com/mpepping/solarman-mqtt/pkgs/container/solarman-mqtt> | ||
|
||
### Using Docker | ||
|
||
Docker example to run this script every 5 minutes and providing a config file: | ||
|
@@ -296,4 +388,3 @@ services: | |
### Using Python | ||
|
||
Run `pip install -r requirements.txt` and start `python3 run.py`. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
""" | ||
Validate the JSON schema and contents used for the config file. | ||
""" | ||
|
||
import hashlib | ||
import sys | ||
from jsonschema import validate | ||
from jsonschema.exceptions import ValidationError | ||
from jsonschema.exceptions import SchemaError | ||
|
||
schema = { | ||
"$schema": "https://json-schema.org/draft/2020-12/schema", | ||
"title": "solarman-mqtt-schema", | ||
"type": "object", | ||
"required": [ | ||
"name", | ||
"url", | ||
"appid", | ||
"secret", | ||
"username", | ||
"passhash", | ||
"stationId", | ||
"inverterId", | ||
"loggerId" | ||
], | ||
"properties": { | ||
"name": { | ||
"type": "string", | ||
}, | ||
"url": { | ||
"type": "string" | ||
}, | ||
"appid": { | ||
"type": "string", | ||
"minLength": 15, | ||
"maxLength": 15 | ||
}, | ||
"secret": { | ||
"type": "string", | ||
"minLength": 32, | ||
"maxLength": 32 | ||
}, | ||
"username": { | ||
"type": "string" | ||
}, | ||
"passhash": { | ||
"type": "string", | ||
"minLength": 64, | ||
"maxLength": 64 | ||
}, | ||
"stationId": { | ||
"type": "number", | ||
"minimum": 100000, | ||
"maximum": 9999999 | ||
}, | ||
"inverterId": { | ||
"type": "string", | ||
"minLength": 10, | ||
}, | ||
"loggerId": { | ||
"type": "string", | ||
"minLength": 10, | ||
"maxLength": 10 | ||
}, | ||
"debug" : { | ||
"type": "boolean", | ||
"optional": True | ||
}, | ||
"mqtt": { | ||
"type": "object", | ||
"properties": { | ||
"broker": { | ||
"type": "string", | ||
}, | ||
"port": { | ||
"type": "integer", | ||
"minimum": 1024, | ||
"maximum": 65535 | ||
}, | ||
"topic": { | ||
"type": "string", | ||
}, | ||
"username": { | ||
"type": "string", | ||
}, | ||
"password": { | ||
"type": "string", | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
VALID = """ | ||
The provided config file is valid. This check validates if: | ||
* The config file is a valid JSON file | ||
* The config file contains the required keys | ||
* The config file contains the correct types for the provided keys | ||
Although that is not a guarantee that the contents are valid. If | ||
you still have issues, please check all values and try again. | ||
If you need any further help, please see: | ||
<https://github.com/mpepping/solarman-mqtt> | ||
""" | ||
|
||
def check(config): | ||
""" | ||
Main | ||
:return: | ||
""" | ||
try: | ||
# validate(instance=json.load(open(config)), schema=schema) | ||
validate(instance=config, schema=schema) | ||
except ValidationError as err: | ||
print(err.message) | ||
sys.exit(1) | ||
except SchemaError as err: | ||
print(err.message) | ||
sys.exit(1) | ||
|
||
|
||
print(VALID) | ||
sys.exit(0) | ||
|
||
|
||
def hash_password(password): | ||
""" | ||
Hash the password | ||
""" | ||
encoded=password.encode('utf-8') | ||
result = hashlib.sha256(encoded) | ||
_hash = result.hexdigest() | ||
return _hash |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
paho_mqtt==1.5.1 | ||
jsonschema==4.4.0 |
Oops, something went wrong.