diff --git a/.github/workflows/builder.yaml b/.github/workflows/builder.yaml index 398da1f..b9f2dee 100644 --- a/.github/workflows/builder.yaml +++ b/.github/workflows/builder.yaml @@ -22,7 +22,7 @@ jobs: changed: ${{ steps.changed_addons.outputs.changed }} steps: - name: Check out the repository - uses: actions/checkout@v4.1.7 + uses: actions/checkout@v4.2.0 - name: Get changed files id: changed_files @@ -72,7 +72,7 @@ jobs: steps: - name: Check out repository - uses: actions/checkout@v4.1.7 + uses: actions/checkout@v4.2.0 - name: Get information id: info diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 565b8a9..e80563b 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -18,7 +18,7 @@ jobs: addons: ${{ steps.addons.outputs.addons_list }} steps: - name: ⤵️ Check out code from GitHub - uses: actions/checkout@v4.1.7 + uses: actions/checkout@v4.2.0 - name: 🔍 Find add-on directories id: addons @@ -33,9 +33,9 @@ jobs: path: ${{ fromJson(needs.find.outputs.addons) }} steps: - name: ⤵️ Check out code from GitHub - uses: actions/checkout@v4.1.7 + uses: actions/checkout@v4.2.0 - name: 🚀 Run Home Assistant Add-on Lint - uses: frenck/action-addon-linter@v2.16 + uses: frenck/action-addon-linter@v2.17 with: path: "./${{ matrix.path }}" diff --git a/sma/CHANGELOG.md b/sma/CHANGELOG.md index 70fa2a6..4fe72e8 100644 --- a/sma/CHANGELOG.md +++ b/sma/CHANGELOG.md @@ -1,5 +1,9 @@ +## 0.1.3 + +- Extend package to support tripower inverters + ## 0.1.0 - Add homewizard support diff --git a/sma/config.yaml b/sma/config.yaml index 1b043df..a0c4327 100644 --- a/sma/config.yaml +++ b/sma/config.yaml @@ -1,6 +1,6 @@ name: "SMA Energy Meter emulator" description: "Simulate one or more SMA energy meters based on mqtt messages." -version: "0.1.0" +version: "0.1.3" slug: sma url: "https://github.com/Roeland54/SMA-Energy-Meter-emulator" arch: @@ -20,7 +20,6 @@ options: username: "auto_user" password: "auto_password" enable_homewizard: false - full_data_packet: false schema: enable_mqtt: bool? mqtt: @@ -29,7 +28,6 @@ schema: username: str? password: str? enable_homewizard: bool - full_data_packet: bool? homewizard_manual_addresses: - str? homewizard_destination_addresses: diff --git a/sma/src/emeter.py b/sma/src/emeter.py index 76a9a4d..5b87237 100644 --- a/sma/src/emeter.py +++ b/sma/src/emeter.py @@ -3,18 +3,18 @@ class emeterPacket: SMA_POSITIVE_ACTIVE_POWER_L1 = 0x00150400 SMA_POSITIVE_ACTIVE_POWER_L2 = 0x00290400 SMA_POSITIVE_ACTIVE_POWER_L3 = 0x003D0400 - SMA_POSITIVE_ACITVE_ENERGY = 0x00010800 - SMA_POSITIVE_ACITVE_ENERGY_L1 = 0x00150800 - SMA_POSITIVE_ACITVE_ENERGY_L2 = 0x00290800 - SMA_POSITIVE_ACITVE_ENERGY_L3 = 0x003D0800 + SMA_POSITIVE_ACTIVE_ENERGY = 0x00010800 + SMA_POSITIVE_ACTIVE_ENERGY_L1 = 0x00150800 + SMA_POSITIVE_ACTIVE_ENERGY_L2 = 0x00290800 + SMA_POSITIVE_ACTIVE_ENERGY_L3 = 0x003D0800 SMA_NEGATIVE_ACTIVE_POWER = 0x00020400 SMA_NEGATIVE_ACTIVE_POWER_L1 = 0x00160400 SMA_NEGATIVE_ACTIVE_POWER_L2 = 0x002A0400 SMA_NEGATIVE_ACTIVE_POWER_L3 = 0x003E0400 - SMA_NEGATIVE_ENERGY = 0x00020800 - SMA_NEGATIVE_ENERGY_L1 = 0x00160800 - SMA_NEGATIVE_ENERGY_L2 = 0x002A0800 - SMA_NEGATIVE_ENERGY_L3 = 0x003E0800 + SMA_NEGATIVE_ACTIVE_ENERGY = 0x00020800 + SMA_NEGATIVE_ACTIVE_ENERGY_L1 = 0x00160800 + SMA_NEGATIVE_ACTIVE_ENERGY_L2 = 0x002A0800 + SMA_NEGATIVE_ACTIVE_ENERGY_L3 = 0x003E0800 SMA_POSITIVE_REACTIVE_POWER = 0x00030400 SMA_POSITIVE_REACTIVE_POWER_L1 = 0x00170400 SMA_POSITIVE_REACTIVE_POWER_L2 = 0x002B0400 @@ -76,6 +76,73 @@ def begin(self, timeStampMs): self.storeU32BE(self._pMeterTime, timeStampMs) self._length = self.INITIAL_PAYLOAD_LENGTH + # Add dummy values for measurements to make sure the package always contains these. Solves tripower inverters not recognizing the data as valid. + # Totals + self.addMeasurementValue(emeterPacket.SMA_POSITIVE_ACTIVE_POWER, 0) + self.addCounterValue(emeterPacket.SMA_POSITIVE_ACTIVE_ENERGY, 0) + self.addMeasurementValue(emeterPacket.SMA_NEGATIVE_ACTIVE_POWER, 0) + self.addCounterValue(emeterPacket.SMA_NEGATIVE_ACTIVE_ENERGY, 0) + self.addMeasurementValue(emeterPacket.SMA_POSITIVE_REACTIVE_POWER, 0) + self.addCounterValue(emeterPacket.SMA_POSITIVE_REACTIVE_ENERGY, 0) + self.addMeasurementValue(emeterPacket.SMA_NEGATIVE_REACTIVE_POWER, 0) + self.addCounterValue(emeterPacket.SMA_NEGATIVE_REACTIVE_ENERGY, 0) + self.addMeasurementValue(emeterPacket.SMA_POSITIVE_APPARENT_POWER, 0) + self.addCounterValue(emeterPacket.SMA_POSITIVE_APPARENT_ENERGY, 0) + self.addMeasurementValue(emeterPacket.SMA_NEGATIVE_APPARENT_POWER, 0) + self.addCounterValue(emeterPacket.SMA_NEGATIVE_APPARENT_ENERGY, 0) + self.addMeasurementValue(emeterPacket.SMA_POWER_FACTOR, 0) + + #L1 + self.addMeasurementValue(emeterPacket.SMA_POSITIVE_ACTIVE_POWER_L1, 0) + self.addCounterValue(emeterPacket.SMA_POSITIVE_ACTIVE_ENERGY_L1, 0) + self.addMeasurementValue(emeterPacket.SMA_NEGATIVE_ACTIVE_POWER_L1, 0) + self.addCounterValue(emeterPacket.SMA_NEGATIVE_ACTIVE_ENERGY_L1, 0) + self.addMeasurementValue(emeterPacket.SMA_POSITIVE_REACTIVE_POWER_L1, 0) + self.addCounterValue(emeterPacket.SMA_POSITIVE_REACTIVE_ENERGY_L1, 0) + self.addMeasurementValue(emeterPacket.SMA_NEGATIVE_REACTIVE_POWER_L1, 0) + self.addCounterValue(emeterPacket.SMA_NEGATIVE_REACTIVE_ENERGY_L1, 0) + self.addMeasurementValue(emeterPacket.SMA_POSITIVE_APPARENT_POWER_L1, 0) + self.addCounterValue(emeterPacket.SMA_POSITIVE_APPARENT_ENERGY_L1, 0) + self.addMeasurementValue(emeterPacket.SMA_NEGATIVE_APPARENT_POWER_L1, 0) + self.addCounterValue(emeterPacket.SMA_NEGATIVE_APPARENT_ENERGY_L1, 0) + self.addMeasurementValue(emeterPacket.SMA_CURRENT_L1, 0) + self.addMeasurementValue(emeterPacket.SMA_VOLTAGE_L1, 0) + self.addMeasurementValue(emeterPacket.SMA_POWER_FACTOR_L1, 0) + + #L2 + self.addMeasurementValue(emeterPacket.SMA_POSITIVE_ACTIVE_POWER_L2, 0) + self.addCounterValue(emeterPacket.SMA_POSITIVE_ACTIVE_ENERGY_L2, 0) + self.addMeasurementValue(emeterPacket.SMA_NEGATIVE_ACTIVE_POWER_L2, 0) + self.addCounterValue(emeterPacket.SMA_NEGATIVE_ACTIVE_ENERGY_L2, 0) + self.addMeasurementValue(emeterPacket.SMA_POSITIVE_REACTIVE_POWER_L2, 0) + self.addCounterValue(emeterPacket.SMA_POSITIVE_REACTIVE_ENERGY_L2, 0) + self.addMeasurementValue(emeterPacket.SMA_NEGATIVE_REACTIVE_POWER_L2, 0) + self.addCounterValue(emeterPacket.SMA_NEGATIVE_REACTIVE_ENERGY_L2, 0) + self.addMeasurementValue(emeterPacket.SMA_POSITIVE_APPARENT_POWER_L2, 0) + self.addCounterValue(emeterPacket.SMA_POSITIVE_APPARENT_ENERGY_L2, 0) + self.addMeasurementValue(emeterPacket.SMA_NEGATIVE_APPARENT_POWER_L2, 0) + self.addCounterValue(emeterPacket.SMA_NEGATIVE_APPARENT_ENERGY_L2, 0) + self.addMeasurementValue(emeterPacket.SMA_CURRENT_L2, 0) + self.addMeasurementValue(emeterPacket.SMA_VOLTAGE_L2, 0) + self.addMeasurementValue(emeterPacket.SMA_POWER_FACTOR_L2, 0) + + #L3 + self.addMeasurementValue(emeterPacket.SMA_POSITIVE_ACTIVE_POWER_L3, 0) + self.addCounterValue(emeterPacket.SMA_POSITIVE_ACTIVE_ENERGY_L3, 0) + self.addMeasurementValue(emeterPacket.SMA_NEGATIVE_ACTIVE_POWER_L3, 0) + self.addCounterValue(emeterPacket.SMA_NEGATIVE_ACTIVE_ENERGY_L3, 0) + self.addMeasurementValue(emeterPacket.SMA_POSITIVE_REACTIVE_POWER_L3, 0) + self.addCounterValue(emeterPacket.SMA_POSITIVE_REACTIVE_ENERGY_L3, 0) + self.addMeasurementValue(emeterPacket.SMA_NEGATIVE_REACTIVE_POWER_L3, 0) + self.addCounterValue(emeterPacket.SMA_NEGATIVE_REACTIVE_ENERGY_L3, 0) + self.addMeasurementValue(emeterPacket.SMA_POSITIVE_APPARENT_POWER_L3, 0) + self.addCounterValue(emeterPacket.SMA_POSITIVE_APPARENT_ENERGY_L3, 0) + self.addMeasurementValue(emeterPacket.SMA_NEGATIVE_APPARENT_POWER_L3, 0) + self.addCounterValue(emeterPacket.SMA_NEGATIVE_APPARENT_ENERGY_L3, 0) + self.addMeasurementValue(emeterPacket.SMA_CURRENT_L3, 0) + self.addMeasurementValue(emeterPacket.SMA_VOLTAGE_L3, 0) + self.addMeasurementValue(emeterPacket.SMA_POWER_FACTOR_L3, 0) + def addMeasurementValue(self, id, value): self._pPacketPos = self.storeU32BE(self._pPacketPos, id) self._pPacketPos = self.storeU32BE(self._pPacketPos, value) @@ -144,3 +211,4 @@ def initEmeterPacket(self, serNo): pSerNo = self.offsetOf(self.meterPacket, DSRC, self._headerLength) self.storeU32BE(pSerNo, serNo) + diff --git a/sma/src/homewizard.py b/sma/src/homewizard.py index 48f4ad4..0b8bd29 100644 --- a/sma/src/homewizard.py +++ b/sma/src/homewizard.py @@ -1,11 +1,13 @@ -from zeroconf import Zeroconf, ServiceBrowser, ServiceStateChange -from config import settings, workingdata +import hashlib +import json import logging +import time + import requests -import hashlib +from config import settings, workingdata from emeter import emeterPacket -import time -import json +from zeroconf import ServiceBrowser, ServiceStateChange, Zeroconf + def setup_homewizard(): if settings.get("enable_homewizard", False) is False: @@ -82,11 +84,11 @@ def update_homewizard(): # Sum the total energy imports (t1 and t2) total_power_import_kwh = data['total_power_import_t1_kwh'] + data['total_power_import_t2_kwh'] - packet.addCounterValue(emeterPacket.SMA_POSITIVE_ENERGY, round(total_power_import_kwh * 1000 * 3600)) + packet.addCounterValue(emeterPacket.SMA_POSITIVE_ACTIVE_ENERGY, round(total_power_import_kwh * 1000 * 3600)) # Sum the total energy exports (t1 and t2) total_power_export_kwh = data['total_power_export_t1_kwh'] + data['total_power_export_t2_kwh'] - packet.addCounterValue(emeterPacket.SMA_NEGATIVE_ENERGY, round(total_power_export_kwh * 1000 * 3600)) + packet.addCounterValue(emeterPacket.SMA_NEGATIVE_ACTIVE_ENERGY, round(total_power_export_kwh * 1000 * 3600)) packet.end() diff --git a/sma/src/mqtt.py b/sma/src/mqtt.py index e12f020..439e156 100644 --- a/sma/src/mqtt.py +++ b/sma/src/mqtt.py @@ -1,12 +1,14 @@ import json import logging -import time import os +import time + import paho.mqtt.client as mqtt import util from config import settings, workingdata from emeter import emeterPacket + def setup_mqtt(): if settings.get("enable_mqtt", False) is False: return None @@ -52,81 +54,12 @@ def on_message(client, userdata, msg): packet = emeterPacket(int(serial_number)) packet.begin(int(time.time() * 1000)) - if settings.get("full_data_packet", False) is False: - packet.addMeasurementValue(emeterPacket.SMA_POSITIVE_ACTIVE_POWER, round(data['powerIn'] * 10)) - packet.addMeasurementValue(emeterPacket.SMA_NEGATIVE_ACTIVE_POWER, round(data['powerOut'] *10)) - packet.addMeasurementValue(emeterPacket.SMA_POSITIVE_REACTIVE_POWER, 0) - packet.addMeasurementValue(emeterPacket.SMA_NEGATIVE_REACTIVE_POWER, 0) - - packet.addCounterValue(emeterPacket.SMA_POSITIVE_ACTIVE_ENERGY, round(data['energyIn'] * 1000 * 3600)) - packet.addCounterValue(emeterPacket.SMA_NEGATIVE_ACTIVE_ENERGY, round(data['energyOut'] * 1000 * 3600)) - else: - # Totals - packet.addMeasurementValue(emeterPacket.SMA_POSITIVE_ACTIVE_POWER, round(data['powerIn'] * 10)) - packet.addCounterValue(emeterPacket.SMA_POSITIVE_ACTIVE_ENERGY, round(data['energyIn'] * 1000 * 3600)) - packet.addMeasurementValue(emeterPacket.SMA_NEGATIVE_ACTIVE_POWER, round(data['powerOut'] *10)) - packet.addCounterValue(emeterPacket.SMA_NEGATIVE_ACTIVE_ENERGY, round(data['energyOut'] * 1000 * 3600)) - packet.addMeasurementValue(emeterPacket.SMA_POSITIVE_REACTIVE_POWER, 0) - packet.addCounterValue(emeterPacket.SMA_POSITIVE_REACTIVE_ENERGY, 0) - packet.addMeasurementValue(emeterPacket.SMA_NEGATIVE_REACTIVE_POWER, 0) - packet.addCounterValue(emeterPacket.SMA_NEGATIVE_REACTIVE_ENERGY, 0) - packet.addMeasurementValue(emeterPacket.SMA_POSITIVE_APPARENT_POWER, 0) - packet.addCounterValue(emeterPacket.SMA_POSITIVE_APPARENT_ENERGY, 0) - packet.addMeasurementValue(emeterPacket.SMA_NEGATIVE_APPARENT_POWER, 0) - packet.addCounterValue(emeterPacket.SMA_NEGATIVE_APPARENT_ENERGY, 0) - packet.addMeasurementValue(emeterPacket.SMA_POWER_FACTOR, 900) # assume almost perfect power factor of 0.9 for default value - - #L1 - packet.addMeasurementValue(emeterPacket.SMA_POSITIVE_ACTIVE_POWER_L1, 0) - packet.addCounterValue(emeterPacket.SMA_POSITIVE_ACTIVE_ENERGY_L1, 0) - packet.addMeasurementValue(emeterPacket.SMA_NEGATIVE_ACTIVE_POWER_L1, 0) - packet.addCounterValue(emeterPacket.SMA_NEGATIVE_ACTIVE_ENERGY_L1, 0) - packet.addMeasurementValue(emeterPacket.SMA_POSITIVE_REACTIVE_POWER_L1, 0) - packet.addCounterValue(emeterPacket.SMA_POSITIVE_REACTIVE_ENERGY_L1, 0) - packet.addMeasurementValue(emeterPacket.SMA_NEGATIVE_REACTIVE_POWER_L1, 0) - packet.addCounterValue(emeterPacket.SMA_NEGATIVE_REACTIVE_ENERGY_L1, 0) - packet.addMeasurementValue(emeterPacket.SMA_POSITIVE_APPARENT_POWER_L1, 0) - packet.addCounterValue(emeterPacket.SMA_POSITIVE_APPARENT_ENERGY_L1, 0) - packet.addMeasurementValue(emeterPacket.SMA_NEGATIVE_APPARENT_POWER_L1, 0) - packet.addCounterValue(emeterPacket.SMA_NEGATIVE_APPARENT_ENERGY_L1, 0) - packet.addMeasurementValue(emeterPacket.SMA_CURRENT_L1, 0) - packet.addMeasurementValue(emeterPacket.SMA_VOLTAGE_L1, 0) - packet.addMeasurementValue(emeterPacket.SMA_POWER_FACTOR_L1, 900) # assume almost perfect power factor of 0.9 for default value - - #L2 - packet.addMeasurementValue(emeterPacket.SMA_POSITIVE_ACTIVE_POWER_L2, 0) - packet.addCounterValue(emeterPacket.SMA_POSITIVE_ACTIVE_ENERGY_L2, 0) - packet.addMeasurementValue(emeterPacket.SMA_NEGATIVE_ACTIVE_POWER_L2, 0) - packet.addCounterValue(emeterPacket.SMA_NEGATIVE_ACTIVE_ENERGY_L2, 0) - packet.addMeasurementValue(emeterPacket.SMA_POSITIVE_REACTIVE_POWER_L2, 0) - packet.addCounterValue(emeterPacket.SMA_POSITIVE_REACTIVE_ENERGY_L2, 0) - packet.addMeasurementValue(emeterPacket.SMA_NEGATIVE_REACTIVE_POWER_L2, 0) - packet.addCounterValue(emeterPacket.SMA_NEGATIVE_REACTIVE_ENERGY_L2, 0) - packet.addMeasurementValue(emeterPacket.SMA_POSITIVE_APPARENT_POWER_L2, 0) - packet.addCounterValue(emeterPacket.SMA_POSITIVE_APPARENT_ENERGY_L2, 0) - packet.addMeasurementValue(emeterPacket.SMA_NEGATIVE_APPARENT_POWER_L2, 0) - packet.addCounterValue(emeterPacket.SMA_NEGATIVE_APPARENT_ENERGY_L2, 0) - packet.addMeasurementValue(emeterPacket.SMA_CURRENT_L2, 0) - packet.addMeasurementValue(emeterPacket.SMA_VOLTAGE_L2, 0) - packet.addMeasurementValue(emeterPacket.SMA_POWER_FACTOR_L2, 900) # assume almost perfect power factor of 0.9 for default value - - #L3 - packet.addMeasurementValue(emeterPacket.SMA_POSITIVE_ACTIVE_POWER_L3, 0) - packet.addCounterValue(emeterPacket.SMA_POSITIVE_ACTIVE_ENERGY_L3, 0) - packet.addMeasurementValue(emeterPacket.SMA_NEGATIVE_ACTIVE_POWER_L3, 0) - packet.addCounterValue(emeterPacket.SMA_NEGATIVE_ACTIVE_ENERGY_L3, 0) - packet.addMeasurementValue(emeterPacket.SMA_POSITIVE_REACTIVE_POWER_L3, 0) - packet.addCounterValue(emeterPacket.SMA_POSITIVE_REACTIVE_ENERGY_L3, 0) - packet.addMeasurementValue(emeterPacket.SMA_NEGATIVE_REACTIVE_POWER_L3, 0) - packet.addCounterValue(emeterPacket.SMA_NEGATIVE_REACTIVE_ENERGY_L3, 0) - packet.addMeasurementValue(emeterPacket.SMA_POSITIVE_APPARENT_POWER_L3, 0) - packet.addCounterValue(emeterPacket.SMA_POSITIVE_APPARENT_ENERGY_L3, 0) - packet.addMeasurementValue(emeterPacket.SMA_NEGATIVE_APPARENT_POWER_L3, 0) - packet.addCounterValue(emeterPacket.SMA_NEGATIVE_APPARENT_ENERGY_L3, 0) - packet.addMeasurementValue(emeterPacket.SMA_CURRENT_L3, 0) - packet.addMeasurementValue(emeterPacket.SMA_VOLTAGE_L3, 0) - packet.addMeasurementValue(emeterPacket.SMA_POWER_FACTOR_L3, 900) # assume almost perfect power factor of 0.9 for default value - + # Totals + packet.addMeasurementValue(emeterPacket.SMA_POSITIVE_ACTIVE_POWER, round(data['powerIn'] * 10)) + packet.addCounterValue(emeterPacket.SMA_POSITIVE_ACTIVE_ENERGY, round(data['energyIn'] * 1000 * 3600)) + packet.addMeasurementValue(emeterPacket.SMA_NEGATIVE_ACTIVE_POWER, round(data['powerOut'] *10)) + packet.addCounterValue(emeterPacket.SMA_NEGATIVE_ACTIVE_ENERGY, round(data['energyOut'] * 1000 * 3600)) + packet.end() packet_data = packet.getData()[:packet.getLength()]