Skip to content
This repository has been archived by the owner on Dec 8, 2024. It is now read-only.

update next branch to be up to date with main #21

Merged
merged 8 commits into from
Nov 8, 2024
Merged
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
4 changes: 2 additions & 2 deletions .github/workflows/builder.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 }}"
4 changes: 4 additions & 0 deletions sma/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
<!-- https://developers.home-assistant.io/docs/add-ons/presentation#keeping-a-changelog -->

## 0.1.3

- Extend package to support tripower inverters

## 0.1.0

- Add homewizard support
Expand Down
4 changes: 1 addition & 3 deletions sma/config.yaml
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -20,7 +20,6 @@ options:
username: "auto_user"
password: "auto_password"
enable_homewizard: false
full_data_packet: false
schema:
enable_mqtt: bool?
mqtt:
Expand All @@ -29,7 +28,6 @@ schema:
username: str?
password: str?
enable_homewizard: bool
full_data_packet: bool?
homewizard_manual_addresses:
- str?
homewizard_destination_addresses:
Expand Down
84 changes: 76 additions & 8 deletions sma/src/emeter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -144,3 +211,4 @@ def initEmeterPacket(self, serNo):

pSerNo = self.offsetOf(self.meterPacket, DSRC, self._headerLength)
self.storeU32BE(pSerNo, serNo)

16 changes: 9 additions & 7 deletions sma/src/homewizard.py
Original file line number Diff line number Diff line change
@@ -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:
Expand Down Expand Up @@ -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()

Expand Down
85 changes: 9 additions & 76 deletions sma/src/mqtt.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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()]
Expand Down
Loading