Initial commit

This commit is contained in:
Marijn Koesen committed 2020-06-04 21:08:49 +02:00
commit f436385f67
7 files changed
+247

No files matched your search

+2
View File
@@ -0,0 +1,2 @@
__pycache__
*.pyc
+17
View File
@@ -0,0 +1,17 @@
FROM python:3
RUN mkdir /app && cd /app
WORKDIR /app
COPY requirements.txt /app
RUN pip install -r requirements.txt
COPY solaredge_modbus_mqtt.py /app
COPY solaredge_modbus_mqtt /app/solaredge_modbus_mqtt
#ENV MQTT_HOST
#ENV MQTT_USER
#ENV MQTT_PASS
#ENV MQTT_PORT
CMD ./solaredge_modbus_mqtt.py
+7
View File
@@ -0,0 +1,7 @@
### Installation
The easiest way is to run the docker container and set it to restart always. This basically makes it a system service that will always run.
```shell
docker run --restart=always -e MODBUS_HOST=192.168.1.1 solaredge_modbus_mqtt:latest
```
+2
View File
@@ -0,0 +1,2 @@
paho-mqtt=1.5.0
solaredge_modbus=0.2.0
+81
View File
@@ -0,0 +1,81 @@
#!/usr/bin/env python3
import argparse
import json
import os
import solaredge_modbus
import sys
import time
import paho.mqtt.client as mqtt
from solaredge_modbus_mqtt import SolarData
from pymodbus.exceptions import ConnectionException
if __name__ == "__main__":
argparser = argparse.ArgumentParser()
argparser.add_argument("--modbus-host", type=str, default=os.environ.get('MODBUS_HOST', '127.0.0.1'), help="ModbusTCP address")
argparser.add_argument("--modbus-port", type=int, default=os.environ.get('MODBUS_PORT', 1502), help="ModbusTCP port")
argparser.add_argument("--modbus-timeout", type=int, default=os.environ.get('MODBUS_TIMEOUT', 1), help="Connection timeout")
argparser.add_argument("--modbus-unit", type=int, default=os.environ.get('MODBUS_UNIT', 1), help="Modbus unit")
argparser.add_argument("--mqtt-host", type=str, default=os.environ.get('MQTT_HOST', '127.0.0.1'), help="MQTT Host")
argparser.add_argument("--mqtt-port", type=int, default=os.environ.get('MQTT_PORT', 1883), help="MQTT port")
argparser.add_argument("--mqtt-user", type=str, default=os.environ.get('MQTT_USER', None), help="MQTT Username")
argparser.add_argument("--mqtt-pass", type=str, default=os.environ.get('MQTT_PASS', None), help="MQTT Password")
argparser.add_argument("--poll-interval", type=int, default=os.environ.get('POLL_INTERVAL', 3), help="Waiting time between each poll")
args = argparser.parse_args()
try:
mqtt = mqtt.Client(client_id="", clean_session=True, userdata=None, protocol=mqtt.MQTTv311, transport="tcp")
mqtt.username_pw_set(args.mqtt_user, args.mqtt_pass)
mqtt.connect(args.mqtt_host, args.mqtt_port, 60)
except OSError as err:
print("Cannot connect to MQTT on {0}:{1}, error: {2}".format(args.mqtt_host, args.mqtt_port, err))
sys.exit(1)
inverter = solaredge_modbus.Inverter(
host=args.modbus_host,
port=args.modbus_port,
timeout=args.modbus_timeout,
unit=args.modbus_unit
)
run = True
publishedAutoDiscovery = False
while run:
try:
solar_data = SolarData(inverter.read_all())
except ConnectionException as err:
print("Cannot connect to SolarEdge Modbus server on {0}:{1}, error: {2}".format(args.modbus_host, args.modbus_port, err))
sys.exit(2)
mqtt.loop()
if (publishedAutoDiscovery == False):
for k, v in solar_data.data.items():
message = {
"name": "SolarEdge " + k.replace("_", " ").title(),
"unique_id": "solaredge_" + k,
"unit_of_measurement": SolarData.fields[k]['unit'] if 'unit' in SolarData.fields[k] else '',
"state_topic": "home/hal_beneden/sensor/solaredge_inverter/" + k,
"force_update": "True",
"device": {
"identifiers": ["solaredge_inverter"],
"name": "SolarEdge Inverter"
}
}
mqtt.publish(
'test/homeassistant/sensor/solaredge_inverter/' + str(k) + '/config',
json.dumps(message),
qos=0,
retain=True
)
publishedAutoDiscovery = True
for k, v in solar_data.data.items():
mqtt.publish('test/home/hal_beneden/sensor/solaredge_inverter/' + k, payload=str(v), qos=0, retain=False)
time.sleep(args.poll_interval)
+57
View File
@@ -0,0 +1,57 @@
import math
class SolarData:
fields = {
'model': {'field': 'c_model'},
'version': {'field': 'c_version'},
'serialnumber': {'field': 'c_serialnumber'},
'deviceaddress': {'field': 'c_deviceaddress'},
'sunspec_did': {'field': 'c_sunspec_did'},
'status': {},
'vendor_status': {},
'current': {'scaleField': 'current_scale', 'unit': 'A'},
'p1_current': {'scaleField': 'current_scale', 'unit': 'A'},
'p2_current': {'scaleField': 'current_scale', 'unit': 'A'},
'p3_current': {'scaleField': 'current_scale', 'unit': 'A'},
'p1_voltage': {'scaleField': 'voltage_scale', 'unit': 'V'},
'p2_voltage': {'scaleField': 'voltage_scale', 'unit': 'V'},
'p3_voltage': {'scaleField': 'voltage_scale', 'unit': 'V'},
'p1n_voltage': {'scaleField': 'voltage_scale', 'unit': 'V'},
'p2n_voltage': {'scaleField': 'voltage_scale', 'unit': 'V'},
'p3n_voltage': {'scaleField': 'voltage_scale', 'unit': 'V'},
'power_ac': {'scaleField': 'power_ac_scale', 'unit': 'W'},
'frequency': {'scaleField': 'frequency_scale', 'unit': 'Hz'},
'power_apparent': {'scaleField': 'power_apparent_scale', 'unit': 'W'},
'power_reactive': {'scaleField': 'power_reactive_scale', 'unit': 'W'},
'power_factor': {'scaleField': 'power_factor_scale', 'unit': '%'},
'energy_total': {'scaleField': 'energy_total_scale', 'unit': 'Wh'},
'current_dc': {'scaleField': 'current_dc_scale', 'unit': 'A'},
'voltage_dc': {'scaleField': 'voltage_dc_scale', 'unit': 'V'},
'power_dc': {'scaleField': 'power_dc_scale', 'unit': 'W'},
'temperature': {'scaleField': 'temperature_scale', 'unit': '°C'},
}
def __init__(self, data):
self.rawData = data
self.data = self._parseData(data)
def _parseData(self, data):
result = {}
for fieldName, config in self.fields.items():
value = data[config['field']] if 'field' in config else data[fieldName]
if 'scaleField' in config:
value = value * math.pow(10, data[config['scaleField']])
result[fieldName] = value
return result
def getModel(self):
return self.data['model']
+81
View File
@@ -0,0 +1,81 @@
import unittest
from . import SolarData
class TestStringMethods(unittest.TestCase):
def testParse(self):
solarData = SolarData({
'c_model': 'SE7K-ABCDEFGH4',
'c_version': '0004.0008.0028',
'c_serialnumber': '7F51B342',
'c_deviceaddress': 1,
'c_sunspec_did': 103,
'current': 301,
'p1_current': 99,
'p2_current': 102,
'p3_current': 100,
'current_scale': -2,
'p1_voltage': 4056,
'p2_voltage': 4066,
'p3_voltage': 4075,
'p1n_voltage': 2346,
'p2n_voltage': 2342,
'p3n_voltage': 2351,
'voltage_scale': -1,
'power_ac': 5970,
'power_ac_scale': -1,
'frequency': 4997,
'frequency_scale': -2,
'power_apparent': 7133,
'power_apparent_scale': -1,
'power_reactive': -3890,
'power_reactive_scale': -1,
'power_factor': -8376,
'power_factor_scale': -2,
'energy_total': 788428,
'energy_total_scale': 0,
'current_dc': 8126,
'current_dc_scale': -4,
'voltage_dc': 7471,
'voltage_dc_scale': -1,
'power_dc': 6071,
'power_dc_scale': -1,
'temperature': 4717,
'temperature_scale': -2,
'status': 4,
'vendor_status': 0
})
self.assertEqual('SE7K-ABCDEFGH4', solarData.data['model'])
self.assertEqual('0004.0008.0028', solarData.data['version'])
self.assertEqual('7F51B342', solarData.data['serialnumber'])
self.assertEqual(1, solarData.data['deviceaddress'])
self.assertEqual(103, solarData.data['sunspec_did'])
self.assertAlmostEqual(3.01, solarData.data['current'])
self.assertAlmostEqual(0.99, solarData.data['p1_current'])
self.assertAlmostEqual(1.02, solarData.data['p2_current'])
self.assertAlmostEqual(1.0, solarData.data['p3_current'])
self.assertAlmostEqual(405.6, solarData.data['p1_voltage'])
self.assertAlmostEqual(406.6, solarData.data['p2_voltage'])
self.assertAlmostEqual(407.5, solarData.data['p3_voltage'])
self.assertAlmostEqual(234.6, solarData.data['p1n_voltage'])
self.assertAlmostEqual(234.2, solarData.data['p2n_voltage'])
self.assertAlmostEqual(235.1, solarData.data['p3n_voltage'])
self.assertAlmostEqual(597.0, solarData.data['power_ac'])
self.assertAlmostEqual(49.97, solarData.data['frequency'])
self.assertAlmostEqual(713.3, solarData.data['power_apparent'])
self.assertAlmostEqual(-389.0, solarData.data['power_reactive'])
self.assertAlmostEqual(-83.76, solarData.data['power_factor'])
self.assertAlmostEqual(788428, solarData.data['energy_total'])
self.assertAlmostEqual(0.8126, solarData.data['current_dc'])
self.assertAlmostEqual(747.1, solarData.data['voltage_dc'])
self.assertAlmostEqual(607.1, solarData.data['power_dc'])
self.assertAlmostEqual(47.17, solarData.data['temperature'])
self.assertAlmostEqual(4, solarData.data['status'])
self.assertAlmostEqual(0, solarData.data['vendor_status'])
if __name__ == '__main__':
unittest.main()