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

Commit

Permalink
Merge pull request #16 from cyrill-wyss/main
Browse files Browse the repository at this point in the history
Allow emulator to be used with sunny tripower
  • Loading branch information
Roeland54 authored Oct 3, 2024
2 parents ea38fa3 + 356f66a commit cf36762
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 19 deletions.
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.2

- Extend package to support tripower inverters

## 0.1.0

- Add homewizard support
Expand Down
2 changes: 1 addition & 1 deletion 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.2"
slug: sma
url: "https://github.com/Roeland54/SMA-Energy-Meter-emulator"
arch:
Expand Down
125 changes: 122 additions & 3 deletions sma/src/emeter.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,62 @@
class emeterPacket:
SMA_POSITIVE_ACTIVE_POWER = 0x00010400
SMA_POSITIVE_REACTIVE_POWER = 0x00030400
SMA_POSITIVE_ACTIVE_POWER_L1 = 0x00150400
SMA_POSITIVE_ACTIVE_POWER_L2 = 0x00290400
SMA_POSITIVE_ACTIVE_POWER_L3 = 0x003D0400
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_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
SMA_POSITIVE_REACTIVE_POWER_L3 = 0x003F0400
SMA_POSITIVE_REACTIVE_ENERGY = 0x00030800
SMA_POSITIVE_REACTIVE_ENERGY_L1 = 0x00170800
SMA_POSITIVE_REACTIVE_ENERGY_L2 = 0x002B0800
SMA_POSITIVE_REACTIVE_ENERGY_L3 = 0x003F0800
SMA_NEGATIVE_REACTIVE_POWER = 0x00040400
SMA_POSITIVE_ENERGY = 0x00010800
SMA_NEGATIVE_ENERGY = 0x00020800
SMA_NEGATIVE_REACTIVE_POWER_L1 = 0x00180400
SMA_NEGATIVE_REACTIVE_POWER_L2 = 0x002C0400
SMA_NEGATIVE_REACTIVE_POWER_L3 = 0x00400400
SMA_NEGATIVE_REACTIVE_ENERGY = 0x00040800
SMA_NEGATIVE_REACTIVE_ENERGY_L1 = 0x00180800
SMA_NEGATIVE_REACTIVE_ENERGY_L2 = 0x002C0800
SMA_NEGATIVE_REACTIVE_ENERGY_L3 = 0x00400800
SMA_POSITIVE_APPARENT_POWER = 0x00090400
SMA_POSITIVE_APPARENT_POWER_L1 = 0x001D0400
SMA_POSITIVE_APPARENT_POWER_L2 = 0x00310400
SMA_POSITIVE_APPARENT_POWER_L3 = 0x00450400
SMA_POSITIVE_APPARENT_ENERGY = 0x00090800
SMA_POSITIVE_APPARENT_ENERGY_L1 = 0x001D0800
SMA_POSITIVE_APPARENT_ENERGY_L2 = 0x00310800
SMA_POSITIVE_APPARENT_ENERGY_L3 = 0x00450800
SMA_NEGATIVE_APPARENT_POWER = 0x000A0400
SMA_NEGATIVE_APPARENT_POWER_L1 = 0x001E0400
SMA_NEGATIVE_APPARENT_POWER_L2 = 0x00320400
SMA_NEGATIVE_APPARENT_POWER_L3 = 0x00460400
SMA_NEGATIVE_APPARENT_ENERGY = 0x000A0800
SMA_NEGATIVE_APPARENT_ENERGY_L1 = 0x001E0800
SMA_NEGATIVE_APPARENT_ENERGY_L2 = 0x00320800
SMA_NEGATIVE_APPARENT_ENERGY_L3 = 0x00460800
SMA_POWER_FACTOR = 0x000D0400
SMA_POWER_FACTOR_L1 = 0x00210400
SMA_POWER_FACTOR_L2 = 0x00350400
SMA_POWER_FACTOR_L3 = 0x00490400
SMA_CURRENT_L1 = 0x001F0400
SMA_CURRENT_L2 = 0x00330400
SMA_CURRENT_L3 = 0x00470400
SMA_VOLTAGE_L1 = 0x00020400
SMA_VOLTAGE_L2 = 0x00340400
SMA_VOLTAGE_L3 = 0x00480400
SMA_VERSION = 0x90000000

INITIAL_PAYLOAD_LENGTH = 12
Expand Down Expand Up @@ -92,3 +144,70 @@ def initEmeterPacket(self, serNo):

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

# 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)
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
16 changes: 8 additions & 8 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,14 +54,12 @@ def on_message(client, userdata, msg):
packet = emeterPacket(int(serial_number))
packet.begin(int(time.time() * 1000))

# 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.addMeasurementValue(emeterPacket.SMA_POSITIVE_REACTIVE_POWER, 0)
packet.addMeasurementValue(emeterPacket.SMA_NEGATIVE_REACTIVE_POWER, 0)

packet.addCounterValue(emeterPacket.SMA_POSITIVE_ENERGY, round(data['energyIn'] * 1000 * 3600))
packet.addCounterValue(emeterPacket.SMA_NEGATIVE_ENERGY, round(data['energyOut'] * 1000 * 3600))

packet.addCounterValue(emeterPacket.SMA_NEGATIVE_ACTIVE_ENERGY, round(data['energyOut'] * 1000 * 3600))

packet.end()

packet_data = packet.getData()[:packet.getLength()]
Expand Down Expand Up @@ -96,4 +96,4 @@ def set_mqtt_settings():
settings["mqtt"]["broker"] = broker_host
settings["mqtt"]["port"] = broker_port
settings["mqtt"]["username"] = broker_user
settings["mqtt"]["password"] = broker_pass
settings["mqtt"]["password"] = broker_pass

0 comments on commit cf36762

Please sign in to comment.