Complete rewrite, uses Fronius Push API now
This commit contains a complete rewrite - HTTP push API server based on bottle - no more dependency to pyfronius
This commit is contained in:
parent
f486503b1e
commit
b4a833601f
26 changed files with 344 additions and 349 deletions
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -1,10 +1,6 @@
|
|||
__pycache__
|
||||
bin
|
||||
include
|
||||
lib
|
||||
share
|
||||
fronius2mqtt.bbprojectd
|
||||
pip-selfcheck.json
|
||||
pyvenv.cfg
|
||||
pyfronius
|
||||
fronius2mqtt/fronius2mqtt.egg-info
|
||||
fronius2mqtt.yaml
|
||||
.DS_Store
|
||||
|
|
8
.idea/.gitignore
vendored
Normal file
8
.idea/.gitignore
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
14
.idea/fronius2mqtt.iml
Normal file
14
.idea/fronius2mqtt.iml
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.9 (fronius2mqtt)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="PyDocumentationSettings">
|
||||
<option name="format" value="PLAIN" />
|
||||
<option name="myDocStringFormat" value="Plain" />
|
||||
</component>
|
||||
</module>
|
6
.idea/inspectionProfiles/profiles_settings.xml
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
Normal file
|
@ -0,0 +1,6 @@
|
|||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
4
.idea/misc.xml
Normal file
4
.idea/misc.xml
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.9 (fronius2mqtt)" project-jdk-type="Python SDK" />
|
||||
</project>
|
8
.idea/modules.xml
Normal file
8
.idea/modules.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/fronius2mqtt.iml" filepath="$PROJECT_DIR$/.idea/fronius2mqtt.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
25
README.md
25
README.md
|
@ -1,6 +1,13 @@
|
|||
# fronius2mqtt
|
||||
|
||||
A Fronius HTTP API to MQTT bridge
|
||||
|
||||
Attention: This is a complete rewrite of the bridge.
|
||||
While the old fronius2mqtt brigde used polling the inverters to fetch the data, the new version make use of the push feature provided by Fronius Symo.
|
||||
|
||||
The daemon offers no HTTP endpoints which can be configured in the configuration interface of each inverter.
|
||||
It forwards the data directly to MQTT.
|
||||
|
||||
## Install
|
||||
|
||||
- clone the git repository
|
||||
|
@ -9,5 +16,21 @@ A Fronius HTTP API to MQTT bridge
|
|||
|
||||
## Configuration
|
||||
|
||||
- copy ```fronius2mqtt.yaml.example```
|
||||
Each configuration option is also available as command line argument.
|
||||
|
||||
- copy ```fronius2mqtt.conf.example```
|
||||
- configure as you like
|
||||
|
||||
| option | default | arguments | comment |
|
||||
|----------------|--------------------------|---------------------|--------------------------------------------------------------------|
|
||||
| mqtt_host | 'localhost' | -m, --mqtt_host | The hostname of the MQTT server. |
|
||||
| mqtt_port | 1883 | --mqtt_port | The port of the MQTT server. |
|
||||
| mqtt_keepalive | 30 | --mqtt_keepalive | The keep alive interval for the MQTT server connection in seconds. |
|
||||
| mqtt_clientid | 'fronius2mqtt' | --mqtt_clientid | The clientid to send to the MQTT server. |
|
||||
| mqtt_user | - | -u, --mqtt_user | The username for the MQTT server connection. |
|
||||
| mqtt_password | - | -p, --mqtt_password | The password for the MQTT server connection. |
|
||||
| mqtt_topic | 'fronius' | -t, --mqtt_topic | The topic to publish MQTT message. |
|
||||
| http_host | 'localhost' | --http_host | The address of the HTTP server. |
|
||||
| http_port | 8080 | --http_port | The port of the HTTP server. |
|
||||
| verbose | - | -v, --verbose | Be verbose while running. |
|
||||
| - | '/etc/fronius2mqtt.conf' | -c, --config | The path to the config file. |
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from fronius2mqtt import config
|
||||
from fronius2mqtt import daemon
|
||||
|
||||
def main():
|
||||
cfg = config.Config()
|
||||
cfg.read()
|
||||
d = daemon.Daemon(cfg)
|
||||
d.run()
|
||||
|
||||
main()
|
||||
|
260
fronius2mqtt
Executable file
260
fronius2mqtt
Executable file
|
@ -0,0 +1,260 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import paho.mqtt.client as mqtt
|
||||
|
||||
from bottle import request, route, post, run
|
||||
|
||||
|
||||
mqtt_client = None
|
||||
daemon_args = None
|
||||
|
||||
|
||||
def extract_request_body():
|
||||
body = request.body
|
||||
string = body.getvalue().decode('utf-8')
|
||||
return string
|
||||
|
||||
|
||||
def extract_request_data():
|
||||
json_string = extract_request_body()
|
||||
data = json.loads(json_string)
|
||||
return data
|
||||
|
||||
|
||||
@route('/')
|
||||
def index():
|
||||
return "Hello World!"
|
||||
|
||||
|
||||
@post('/current_data_inverter/<device>')
|
||||
def current_data_inverter(device):
|
||||
data = extract_request_data()
|
||||
if not 'Body' in data:
|
||||
return "Empty"
|
||||
topic_base = "{}/{}/current_data_inverter".format(daemon_args.mqtt_topic, device)
|
||||
if 'PAC' in data['Body'] and 'Values' in data['Body']['PAC']:
|
||||
for k, v in data['Body']['PAC']['Values'].items():
|
||||
topic = "{}/pac/{}".format(topic_base, k)
|
||||
mqtt_client.publish(topic, v)
|
||||
if 'DAY_ENERGY' in data['Body'] and 'Values' in data['Body']['DAY_ENERGY']:
|
||||
for k, v in data['Body']['DAY_ENERGY']['Values'].items():
|
||||
topic = "{}/day_energy/{}".format(topic_base, k)
|
||||
mqtt_client.publish(topic, v)
|
||||
if 'YEAR_ENERGY' in data['Body'] and 'Values' in data['Body']['YEAR_ENERGY']:
|
||||
for k, v in data['Body']['YEAR_ENERGY']['Values'].items():
|
||||
topic = "{}/year_energy/{}".format(topic_base, k)
|
||||
mqtt_client.publish(topic, v)
|
||||
if 'TOTAL_ENERGY' in data['Body'] and 'Values' in data['Body']['TOTAL_ENERGY']:
|
||||
for k, v in data['Body']['TOTAL_ENERGY']['Values'].items():
|
||||
topic = "{}/total_energy/{}".format(topic_base, k)
|
||||
mqtt_client.publish(topic, v)
|
||||
return "OK"
|
||||
|
||||
|
||||
@post('/current_data_meter/<device>')
|
||||
def current_data_meter(device):
|
||||
data = extract_request_data()
|
||||
if not 'Body' in data:
|
||||
return "Empty"
|
||||
topic_base = "{}/{}/current_data_meter".format(daemon_args.mqtt_topic, device)
|
||||
for m, d in data['Body'].items():
|
||||
for k, v in d.items():
|
||||
if v is not None and type(v) in [int, float, str] :
|
||||
topic = "{}/{}/{}".format(topic_base, m, k.lower())
|
||||
mqtt_client.publish(topic, v)
|
||||
return "OK"
|
||||
|
||||
|
||||
@post('/current_data_powerflow/<device>')
|
||||
def current_data_powerflow(device):
|
||||
data = extract_request_data()
|
||||
if not 'Body' in data:
|
||||
return "Empty"
|
||||
topic_base = "{}/{}/current_data_powerflow".format(daemon_args.mqtt_topic, device)
|
||||
if 'Site' in data['Body']:
|
||||
for k, v in data['Body']['Site'].items():
|
||||
if v is not None:
|
||||
topic = "{}/site/{}".format(topic_base, k.lower())
|
||||
mqtt_client.publish(topic, v)
|
||||
if 'Inverters' in data['Body']:
|
||||
for i, d in data['Body']['Inverters'].items():
|
||||
for k, v in d.items():
|
||||
if v is not None:
|
||||
topic = "{}/{}/{}".format(topic_base, i, k.lower())
|
||||
mqtt_client.publish(topic, v)
|
||||
return "OK"
|
||||
|
||||
|
||||
@post('/current_data_storages/<device>')
|
||||
def current_data_storages(device):
|
||||
data = extract_request_data()
|
||||
if not 'Body' in data:
|
||||
return "Empty"
|
||||
topic_base = "{}/{}/current_data_storages".format(daemon_args.mqtt_topic, device)
|
||||
for s, d in data['Body'].items():
|
||||
if 'Controller' in d:
|
||||
for k, v in d['Controller'].items():
|
||||
if v is not None and type(v) in [int, float, str] :
|
||||
topic = "{}/{}/{}".format(topic_base, s, k.lower())
|
||||
mqtt_client.publish(topic, v)
|
||||
if 'Modules' in d:
|
||||
for m in d['Modules']:
|
||||
serial = m['Details']['Serial']
|
||||
for k, v in m.items():
|
||||
if v is not None and type(v) in [int, float, str] :
|
||||
topic = "{}/{}/{}/{}".format(topic_base, s, serial.lower(), k.lower())
|
||||
mqtt_client.publish(topic, v)
|
||||
return "OK"
|
||||
|
||||
|
||||
@post('/current_data_sensorcard/<device>')
|
||||
def current_data_sensorcard(device):
|
||||
data = extract_request_data()
|
||||
if not 'Body' in data:
|
||||
return "Empty"
|
||||
topic_base = "{}/{}/current_data_sensorcard".format(daemon_args.mqtt_topic, device)
|
||||
# TODO not yet implemented
|
||||
return "OK"
|
||||
|
||||
|
||||
@post('/current_data_stringcontrol/<device>')
|
||||
def current_data_stringcontrol(device):
|
||||
data = extract_request_data()
|
||||
if not 'Body' in data:
|
||||
return "Empty"
|
||||
topic_base = "{}/{}/current_data_stringcontrol".format(daemon_args.mqtt_topic, device)
|
||||
# TODO not yet implemented
|
||||
return "OK"
|
||||
|
||||
|
||||
@post('/datamanager_io_states/<device>')
|
||||
def datamanager_io_states(device):
|
||||
data = extract_request_data()
|
||||
topic_base = "{}/{}/datamanager_io_states".format(daemon_args.mqtt_topic, device)
|
||||
for p, d in data.items():
|
||||
for k, v in d.items():
|
||||
if v is not None:
|
||||
topic = "{}/{}/{}".format(topic_base, p.replace(' ', '_'), k.lower())
|
||||
mqtt_client.publish(topic, v)
|
||||
return "OK"
|
||||
|
||||
|
||||
@post('/logdata_errors_and_events/<device>')
|
||||
def logdata_errors_and_events(device):
|
||||
data = extract_request_data()
|
||||
if not 'Body' in data:
|
||||
return "Empty"
|
||||
topic_base = "{}/{}/logdata_errors_and_events".format(daemon_args.mqtt_topic, device)
|
||||
return "OK"
|
||||
|
||||
|
||||
@post('/logdata_data/<device>')
|
||||
def logdata_data(device):
|
||||
data = extract_request_data()
|
||||
if not 'Body' in data:
|
||||
return "Empty"
|
||||
topic_base = "{}/{}/logdata_data".format(daemon_args.mqtt_topic, device)
|
||||
return "OK"
|
||||
|
||||
|
||||
def start_mqtt():
|
||||
global daemon_args
|
||||
mqtt_client = mqtt.Client(daemon_args.mqtt_clientid)
|
||||
if daemon_args.verbose:
|
||||
mqtt_client.enable_logger()
|
||||
if daemon_args.mqtt_user is not None and daemon_args.mqtt_password is not None:
|
||||
mqtt_client.username_pw_set(daemon_args.mqtt_user, daemon_args.mqtt_password)
|
||||
mqtt_client.connect(daemon_args.mqtt_host, daemon_args.mqtt_port, daemon_args.mqtt_keepalive)
|
||||
mqtt_client.loop_start()
|
||||
return mqtt_client
|
||||
|
||||
|
||||
def start_http():
|
||||
global daemon_args
|
||||
run(host=daemon_args.http_host, port=daemon_args.http_port, debug=daemon_args.verbose)
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(
|
||||
prog='fronius2mqtt',
|
||||
description='Send the data from Fronius HTTP push to MQTT',
|
||||
epilog='Have a lot of fun!')
|
||||
parser.add_argument('-m', '--mqtt_host', type=str,
|
||||
default='localhost',
|
||||
help='The hostname of the MQTT server. Default is localhost')
|
||||
parser.add_argument('--mqtt_port', type=int,
|
||||
default=1883,
|
||||
help='The port of the MQTT server. Default is 1883')
|
||||
parser.add_argument('--mqtt_keepalive', type=int,
|
||||
default=30,
|
||||
help='The keep alive interval for the MQTT server connection in seconds. Default is 30')
|
||||
parser.add_argument('--mqtt_clientid', type=str,
|
||||
default='fronius2mqtt',
|
||||
help='The clientid to send to the MQTT server. Default is fronius2mqtt')
|
||||
parser.add_argument('-u', '--mqtt_user', type=str,
|
||||
help='The username for the MQTT server connection.')
|
||||
parser.add_argument('-p', '--mqtt_password', type=str,
|
||||
help='The password for the MQTT server connection.')
|
||||
parser.add_argument('-t', '--mqtt_topic', type=str,
|
||||
default='home/fronius',
|
||||
help='The topic to publish MQTT message. Default is home/fronius')
|
||||
parser.add_argument('--http_host', type=str,
|
||||
default='localhost',
|
||||
help='The address of the HTTP server. Default is localhost')
|
||||
parser.add_argument('--http_port', type=int,
|
||||
default=8080,
|
||||
help='The port of the HTTP server. Default is 8080')
|
||||
parser.add_argument('-c', '--config', type=str,
|
||||
default='/etc/fronius2mqtt.conf',
|
||||
help='The path to the config file. Default is /etc/fronius2mqtt.conf')
|
||||
parser.add_argument('-v', '--verbose',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help='Be verbose while running.')
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
|
||||
def parse_config():
|
||||
global daemon_args
|
||||
|
||||
if not os.path.isfile(daemon_args.config):
|
||||
return
|
||||
|
||||
with open(daemon_args.config, "r") as config_file:
|
||||
data = json.load(config_file)
|
||||
if 'mqtt_host' in data:
|
||||
daemon_args.mqtt_host = data['mqtt_host']
|
||||
if 'mqtt_port' in data:
|
||||
daemon_args.mqtt_port = data['mqtt_port']
|
||||
if 'mqtt_keepalive' in data:
|
||||
daemon_args.mqtt_keepalive = data['mqtt_keepalive']
|
||||
if 'mqtt_clientid' in data:
|
||||
daemon_args.mqtt_clientid = data['mqtt_clientid']
|
||||
if 'mqtt_user' in data:
|
||||
daemon_args.mqtt_user = data['mqtt_user']
|
||||
if 'mqtt_password' in data:
|
||||
daemon_args.mqtt_password = data['mqtt_password']
|
||||
if 'mqtt_topic' in data:
|
||||
daemon_args.mqtt_topic = data['mqtt_topic']
|
||||
if 'http_host' in data:
|
||||
daemon_args.http_host = data['http_host']
|
||||
if 'http_port' in data:
|
||||
daemon_args.http_port = data['http_port']
|
||||
if 'verbose' in data:
|
||||
daemon_args.verbose = data['verbose']
|
||||
|
||||
|
||||
def main():
|
||||
global daemon_args, mqtt_client
|
||||
daemon_args = parse_args()
|
||||
parse_config()
|
||||
mqtt_client = start_mqtt()
|
||||
start_http()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,13 +1,6 @@
|
|||
[program:fronius2mqtt]
|
||||
command=/opt/service/fronius2mqtt/run
|
||||
process_name=%(program_name)s
|
||||
directory=/opt/service/fronius2mqtt
|
||||
umask=022
|
||||
autostart=true
|
||||
redirect_stderr=true
|
||||
stdout_logfile=/var/log/fronius2mqtt/main.log
|
||||
stdout_logfile_maxbytes=2MB
|
||||
stdout_logfile_backups=1
|
||||
stdout_capture_maxbytes=0
|
||||
stdout_events_enabled=false
|
||||
environment=LOGDIR=/var/log/fronius2mqtt
|
||||
{
|
||||
"mqtt_user": "fronius2mqtt",
|
||||
"mqtt_password": "t0p_s3cr3t",
|
||||
"http_host": "0.0.0.0",
|
||||
"http_port": 80
|
||||
}
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
mqtt:
|
||||
host: localhost
|
||||
port: 1883
|
||||
user: user
|
||||
password: secret
|
||||
topic: "mqtt/topic/for/fronius"
|
||||
qos: 1
|
||||
retain: true
|
||||
fronius:
|
||||
- inverter:
|
||||
host: "inverter.myfroniusfarm"
|
||||
device: 1
|
||||
topic: "inverter_1"
|
||||
- inverter:
|
||||
host: "inverter.myfroniusfarm"
|
||||
device: 2
|
||||
topic: "inverter_2"
|
||||
- storage:
|
||||
host: "storage.myfroniusfarm"
|
||||
device: 0
|
||||
topic: "battery_1"
|
||||
- meter:
|
||||
host: "storage.myfroniusfarm"
|
||||
device: 0
|
||||
topic: "meter"
|
||||
- flow:
|
||||
host: "storage.myfroniusfarm"
|
||||
topic: "flow"
|
|
@ -1,62 +0,0 @@
|
|||
import yaml
|
||||
import logging
|
||||
import logging.config
|
||||
|
||||
class Config:
|
||||
"""Class for parsing fronius2mqtt.yaml."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize Config class."""
|
||||
logging.config.fileConfig('logging.conf')
|
||||
self._mqtt = {}
|
||||
self._fronius = {}
|
||||
|
||||
|
||||
def read(self, file='fronius2mqtt.yaml'):
|
||||
"""Read config."""
|
||||
logging.debug("Reading %s", file)
|
||||
try:
|
||||
with open(file, 'r') as filehandle:
|
||||
config = yaml.load(filehandle)
|
||||
self._parse_mqtt(config)
|
||||
self._parse_fronius(config)
|
||||
except FileNotFoundError as ex:
|
||||
logging.error("Error while reading %s: %s", file, ex)
|
||||
|
||||
def _parse_mqtt(self, config):
|
||||
"""Parse the mqtt section of fronius2mqtt.yaml."""
|
||||
if "mqtt" in config:
|
||||
self._mqtt = config["mqtt"]
|
||||
if not "host" in self._mqtt:
|
||||
raise ValueError("MQTT host not set")
|
||||
if not "port" in self._mqtt:
|
||||
raise ValueError("MQTT port not set")
|
||||
if not "user" in self._mqtt:
|
||||
raise ValueError("MQTT user not set")
|
||||
if not "password" in self._mqtt:
|
||||
raise ValueError("MQTT password not set")
|
||||
if not "topic" in self._mqtt:
|
||||
raise ValueError("MQTT topic not set")
|
||||
if not "qos" in self._mqtt:
|
||||
self._mqtt["qos"] = 0
|
||||
if not "retain" in self._mqtt:
|
||||
self._mqtt["retain"] = False
|
||||
|
||||
def _parse_fronius(self, config):
|
||||
"""Parse the fronius section of fronius2mqtt.yaml."""
|
||||
if "fronius" in config:
|
||||
self._fronius = config["fronius"]
|
||||
for item in self._fronius:
|
||||
if len(item) != 1:
|
||||
raise ValueError("Fronius device configuration contains more than one item.")
|
||||
for (type, properties) in item.items():
|
||||
if not "host" in properties:
|
||||
raise ValueError("Missing host for Fronius device")
|
||||
if not "topic" in properties:
|
||||
raise ValueError("Missing topic for Fronius device")
|
||||
|
||||
def mqtt(self):
|
||||
return self._mqtt
|
||||
|
||||
def fronius(self):
|
||||
return self._fronius
|
|
@ -1,27 +0,0 @@
|
|||
|
||||
import time
|
||||
|
||||
from fronius2mqtt import froniusfactory
|
||||
from fronius2mqtt import mqtt
|
||||
|
||||
class Daemon:
|
||||
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
self.devices = []
|
||||
self._init_mqtt()
|
||||
self._init_fronius()
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
for device in self.devices:
|
||||
device.update_and_publish(self.mqtt)
|
||||
time.sleep(5)
|
||||
|
||||
def _init_mqtt(self):
|
||||
self.mqtt = mqtt.Mqtt(self.config.mqtt())
|
||||
self.mqtt.connect()
|
||||
|
||||
def _init_fronius(self):
|
||||
factory = froniusfactory.FroniusFactory(self.config.fronius())
|
||||
self.devices = factory.create_devices()
|
|
@ -1,16 +0,0 @@
|
|||
|
||||
import logging
|
||||
|
||||
class Device:
|
||||
|
||||
def update(self):
|
||||
raise NotImplementedError("update not implemented")
|
||||
|
||||
def update_and_publish(self, mqtt):
|
||||
data = self.update()
|
||||
for (key, value) in data.items():
|
||||
if 'value' in value:
|
||||
mqtt.publish("{}/{}".format(self.topic, key), value['value'])
|
||||
else:
|
||||
logging.info("Ignore %s: %s", key, value)
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
from pyfronius import fronius
|
||||
from fronius2mqtt import device
|
||||
|
||||
class Flow(device.Device):
|
||||
|
||||
def __init__(self, config):
|
||||
self.host = config['host']
|
||||
self.topic = config['topic']
|
||||
self.fronius = fronius.Fronius(self.host)
|
||||
|
||||
def update(self):
|
||||
data = self.fronius.current_power_flow()
|
||||
return data
|
|
@ -1,43 +0,0 @@
|
|||
|
||||
from fronius2mqtt import inverter
|
||||
from fronius2mqtt import storage
|
||||
from fronius2mqtt import flow
|
||||
from fronius2mqtt import meter
|
||||
|
||||
class FroniusFactory:
|
||||
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
|
||||
def create_devices(self):
|
||||
devices = []
|
||||
for c in self.config:
|
||||
(type, properties) = c.popitem()
|
||||
if not type in factory:
|
||||
raise ValueError("Not a valid device: {}".format(type))
|
||||
device = factory[type](properties)
|
||||
devices.append(device)
|
||||
return devices
|
||||
|
||||
def _create_inverter(properties):
|
||||
device = inverter.Inverter(properties)
|
||||
return device
|
||||
|
||||
def _create_storage(properties):
|
||||
device = storage.Storage(properties)
|
||||
return device
|
||||
|
||||
def _create_meter(properties):
|
||||
device = meter.Meter(properties)
|
||||
return device
|
||||
|
||||
def _create_flow(properties):
|
||||
device = flow.Flow(properties)
|
||||
return device
|
||||
|
||||
factory = {
|
||||
"inverter" : _create_inverter,
|
||||
"storage" : _create_storage,
|
||||
"meter" : _create_meter,
|
||||
"flow" : _create_flow
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
|
||||
from pyfronius import fronius
|
||||
from fronius2mqtt import device
|
||||
|
||||
class Inverter(device.Device):
|
||||
|
||||
def __init__(self, config):
|
||||
self.host = config['host']
|
||||
if 'device' in config:
|
||||
self.device = config['device']
|
||||
self.topic = config['topic']
|
||||
self.fronius = fronius.Fronius(self.host)
|
||||
|
||||
def update(self):
|
||||
if hasattr(self, 'device'):
|
||||
data = self.fronius.current_inverter_data(self.device)
|
||||
else:
|
||||
data = self.fronius.current_system_inverter_data()
|
||||
return data
|
|
@ -1,19 +0,0 @@
|
|||
|
||||
from pyfronius import fronius
|
||||
from fronius2mqtt import device
|
||||
|
||||
class Meter(device.Device):
|
||||
|
||||
def __init__(self, config):
|
||||
self.host = config['host']
|
||||
if 'device' in config:
|
||||
self.device = config['device']
|
||||
self.topic = config['topic']
|
||||
self.fronius = fronius.Fronius(self.host)
|
||||
|
||||
def update(self):
|
||||
if hasattr(self, 'device'):
|
||||
data = self.fronius.current_meter_data(self.device)
|
||||
else:
|
||||
data = self.fronius.current_system_meter_data()
|
||||
return data
|
|
@ -1,22 +0,0 @@
|
|||
|
||||
import logging
|
||||
import paho.mqtt.client as mqtt
|
||||
|
||||
class Mqtt:
|
||||
|
||||
def __init__(self, config):
|
||||
self._config = config
|
||||
|
||||
def connect(self):
|
||||
self._client = mqtt.Client()
|
||||
self._client.username_pw_set(self._config['user'], self._config['password'])
|
||||
self._client.connect(self._config['host'], self._config['port'])
|
||||
self._client.loop_start()
|
||||
|
||||
def disconnect(self):
|
||||
self.client.disconnect()
|
||||
|
||||
def publish(self, topic, payload):
|
||||
topic = "{}/{}".format(self._config['topic'], topic)
|
||||
logging.info("Publish %s: %s, %s, %s", topic, payload, self._config["qos"], self._config["retain"])
|
||||
self._client.publish(topic, payload, self._config["qos"], self._config["retain"])
|
|
@ -1,18 +0,0 @@
|
|||
|
||||
from pyfronius import fronius
|
||||
from fronius2mqtt import device
|
||||
|
||||
class Storage(device.Device):
|
||||
|
||||
def __init__(self, config):
|
||||
self.host = config['host']
|
||||
if 'device' in config:
|
||||
self.device = config['device']
|
||||
else:
|
||||
self.device = 0
|
||||
self.topic = config['topic']
|
||||
self.fronius = fronius.Fronius(self.host)
|
||||
|
||||
def update(self):
|
||||
data = self.fronius.current_storage_data(self.device)
|
||||
return data
|
|
@ -1,17 +0,0 @@
|
|||
from setuptools import setup
|
||||
|
||||
setup(name='fronius2mqtt',
|
||||
version='0.2',
|
||||
description='Fronius 2 MQTT bridge',
|
||||
url='https://github.com/gbeine/fronius2mqtt',
|
||||
author='Gerrit',
|
||||
author_email='mail@gerritbeine.de',
|
||||
license='MIT',
|
||||
packages=['fronius2mqtt'],
|
||||
requires=[
|
||||
'logging',
|
||||
'paho.mqtt',
|
||||
'pyfronius',
|
||||
'pyyaml',
|
||||
],
|
||||
zip_safe=False)
|
10
install
10
install
|
@ -1,10 +1,8 @@
|
|||
#!/bin/sh
|
||||
|
||||
python3 -m venv .
|
||||
python3 -m venv venv
|
||||
|
||||
git clone https://github.com/gbeine/pyfronius.git
|
||||
. venv/bin/activate
|
||||
|
||||
. bin/activate
|
||||
pip install wheel paho-mqtt pyyaml
|
||||
pip install -e pyfronius
|
||||
pip install -e fronius2mqtt
|
||||
pip install bottle
|
||||
pip install paho.mqtt
|
||||
|
|
21
logging.conf
21
logging.conf
|
@ -1,21 +0,0 @@
|
|||
[loggers]
|
||||
keys=root
|
||||
|
||||
[handlers]
|
||||
keys=consoleHandler
|
||||
|
||||
[formatters]
|
||||
keys=simpleFormatter
|
||||
|
||||
[logger_root]
|
||||
level=DEBUG
|
||||
handlers=consoleHandler
|
||||
|
||||
[handler_consoleHandler]
|
||||
class=StreamHandler
|
||||
level=DEBUG
|
||||
formatter=simpleFormatter
|
||||
args=(sys.stdout,)
|
||||
|
||||
[formatter_simpleFormatter]
|
||||
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
|
6
run
6
run
|
@ -1,7 +1,5 @@
|
|||
#!/bin/sh
|
||||
|
||||
touch ${LOGDIR}/.tmpfs
|
||||
. venv/bin/activate
|
||||
|
||||
. bin/activate
|
||||
|
||||
exec bin/fronius2mqtt
|
||||
exec /usr/bin/env python fronius2mqtt
|
||||
|
|
Loading…
Reference in a new issue