Merge PR #213: Wattsonic: Refactor connection structure and improve gen2 / gen3 support
This commit is contained in:
commit
525e4ac3fc
@ -1,4 +1,44 @@
|
||||
# Wattsonic
|
||||
|
||||
This plugin allows to connect wattsonic hybrid inverters to nymea
|
||||
This plugin allows to connect wattsonic hybrid inverters to nymea. If there are any meters and energy storages connected to the system, they should be detected automatically.
|
||||
|
||||
## Supported inverters
|
||||
|
||||
Currently all `Gen2` and `Gen3` single and three phase inverters should be supported.
|
||||
|
||||
The full list of supported models can be found here:
|
||||
|
||||
Single Phase Hybrid
|
||||
|
||||
* 3.0K-30A-1P
|
||||
* 3.6K-30A-1P
|
||||
* 4.2K-30A-1P
|
||||
* 4.6K-30A-1P
|
||||
* 5.0K-30A-1P
|
||||
* 6.0K-30A-1P
|
||||
* 7.0K-30A-1P
|
||||
* 8.0K-30A-1P
|
||||
* 3.0K-30A-1P-S
|
||||
* 3.6K-30A-1P-S
|
||||
|
||||
Three Phase Hybrid
|
||||
|
||||
* 4.0K-25A-3P
|
||||
* 5.0K-25A-3P
|
||||
* 6.0K-25A-3P
|
||||
* 8.0K-25A-3P
|
||||
* 10K-25A-3P
|
||||
* 12K-25A-3P
|
||||
* 10K-40A-3P
|
||||
* 12K-40A-3P
|
||||
* 15K-40A-3P
|
||||
* 20K-40A-3P
|
||||
|
||||
|
||||
Three Phase Hybrid (25-50K)
|
||||
|
||||
* 25K-100A-3P
|
||||
* 30K-100A-3P
|
||||
* 36K-100A-3P
|
||||
* 40K-100A-3P
|
||||
* 50K-100A-3P
|
||||
|
||||
@ -54,8 +54,8 @@ void IntegrationPluginWattsonic::discoverThings(ThingDiscoveryInfo *info)
|
||||
|
||||
foreach (const WattsonicDiscovery::Result &result, discovery->discoveryResults()) {
|
||||
|
||||
QString name = "Wattsonic inverter (" + result.serialNumber + ")";
|
||||
ThingDescriptor descriptor(inverterThingClassId, name, QString("Modbus ID: %1").arg(result.slaveId));
|
||||
QString name = "Wattsonic " + result.inverterInfo.type + + " " + result.inverterInfo.model;
|
||||
ThingDescriptor descriptor(inverterThingClassId, name, QString("Serial number: %1").arg(result.serialNumber));
|
||||
qCDebug(dcWattsonic()) << "Discovered:" << descriptor.title() << descriptor.description();
|
||||
|
||||
ParamList params {
|
||||
@ -157,16 +157,16 @@ void IntegrationPluginWattsonic::setupWattsonicConnection(ThingSetupInfo *info)
|
||||
return;
|
||||
}
|
||||
|
||||
WattsonicModbusRtuConnection *connection = new WattsonicModbusRtuConnection(hardwareManager()->modbusRtuResource()->getModbusRtuMaster(uuid), slaveId, this);
|
||||
WattsonicInverter *connection = new WattsonicInverter(hardwareManager()->modbusRtuResource()->getModbusRtuMaster(uuid), slaveId, this);
|
||||
m_connections.insert(thing, connection);
|
||||
|
||||
// Only for setup
|
||||
connect(info, &ThingSetupInfo::aborted, connection, &WattsonicModbusRtuConnection::deleteLater);
|
||||
connect(info, &ThingSetupInfo::aborted, connection, &WattsonicInverter::deleteLater);
|
||||
connect(info, &ThingSetupInfo::aborted, this, [=](){
|
||||
m_connections.take(info->thing())->deleteLater();
|
||||
});
|
||||
|
||||
connect(connection, &WattsonicModbusRtuConnection::initializationFinished, info, [=](bool success){
|
||||
connect(connection, &WattsonicInverter::customInitializationFinished, info, [=](bool success){
|
||||
qCDebug(dcWattsonic()) << "Initialization finished" << success;
|
||||
if (info->isInitialSetup() && !success) {
|
||||
m_connections.take(info->thing())->deleteLater();
|
||||
@ -179,7 +179,7 @@ void IntegrationPluginWattsonic::setupWattsonicConnection(ThingSetupInfo *info)
|
||||
});
|
||||
|
||||
// Runtime connections
|
||||
connect(connection, &WattsonicModbusRtuConnection::reachableChanged, thing, [connection, thing, this](bool reachable){
|
||||
connect(connection, &WattsonicInverter::reachableChanged, thing, [connection, thing, this](bool reachable){
|
||||
qCDebug(dcWattsonic()) << "Reachable state changed" << reachable;
|
||||
if (reachable) {
|
||||
connection->initialize();
|
||||
@ -188,11 +188,11 @@ void IntegrationPluginWattsonic::setupWattsonicConnection(ThingSetupInfo *info)
|
||||
setConnectedStates(thing, reachable);
|
||||
});
|
||||
|
||||
connect(connection, &WattsonicModbusRtuConnection::reachableChanged, thing, [this, thing](bool reachable){
|
||||
connect(connection, &WattsonicInverter::reachableChanged, thing, [this, thing](bool reachable){
|
||||
setConnectedStates(thing, reachable);
|
||||
});
|
||||
|
||||
connect(connection, &WattsonicModbusRtuConnection::updateFinished, thing, [this, connection, thing](){
|
||||
connect(connection, &WattsonicInverter::updateFinished, thing, [this, connection, thing](){
|
||||
qCDebug(dcWattsonic()) << "Update finished:" << thing->name() << connection;
|
||||
|
||||
// We received an update, make sure we are connected
|
||||
@ -207,7 +207,13 @@ void IntegrationPluginWattsonic::setupWattsonicConnection(ThingSetupInfo *info)
|
||||
|
||||
// Check if a meter is connected or not. We detect a meter by reading the totalEnergyPurchasedFromGrid totalEnergyInjectedToGrid registers,
|
||||
// As of now, there is no other way to detect the presence of the meter. Voltage is always there, even if there is no meter connected.
|
||||
bool meterDetected = connection->totalEnergyPurchasedFromGrid() != 0 || connection->totalEnergyInjectedToGrid() != 0;
|
||||
|
||||
// Some meters do not have the total counters, for whatever reason...
|
||||
bool meterDetected = connection->totalEnergyInjectedToGrid() != 0 ||
|
||||
connection->totalEnergyPurchasedFromGrid() != 0 ||
|
||||
connection->phaseAPower() != 0 ||
|
||||
connection->phaseBPower() != 0 ||
|
||||
connection->phaseCPower() != 0;
|
||||
|
||||
if (meterDetected) {
|
||||
if (meters.isEmpty()) {
|
||||
@ -232,21 +238,21 @@ void IntegrationPluginWattsonic::setupWattsonicConnection(ThingSetupInfo *info)
|
||||
if (!meters.isEmpty()) {
|
||||
Thing *meter = meters.first();
|
||||
meter->setStateValue(meterCurrentPowerStateTypeId, connection->totalPowerOnMeter() * -1.0);
|
||||
meter->setStateValue(meterTotalEnergyConsumedStateTypeId, connection->totalEnergyPurchasedFromGrid() / 10.0);
|
||||
meter->setStateValue(meterTotalEnergyProducedStateTypeId, connection->totalEnergyInjectedToGrid() / 10.0);
|
||||
meter->setStateValue(meterTotalEnergyConsumedStateTypeId, connection->totalEnergyPurchasedFromGrid());
|
||||
meter->setStateValue(meterTotalEnergyProducedStateTypeId, connection->totalEnergyInjectedToGrid());
|
||||
meter->setStateValue(meterCurrentPowerPhaseAStateTypeId, connection->phaseAPower() * -1.0);
|
||||
meter->setStateValue(meterCurrentPowerPhaseBStateTypeId, connection->phaseBPower() * -1.0);
|
||||
meter->setStateValue(meterCurrentPowerPhaseCStateTypeId, connection->phaseCPower() * -1.0);
|
||||
meter->setStateValue(meterVoltagePhaseAStateTypeId, connection->gridPhaseAVoltage() / 10.0);
|
||||
meter->setStateValue(meterVoltagePhaseBStateTypeId, connection->gridPhaseBVoltage() / 10.0);
|
||||
meter->setStateValue(meterVoltagePhaseCStateTypeId, connection->gridPhaseCVoltage() / 10.0);
|
||||
meter->setStateValue(meterVoltagePhaseAStateTypeId, connection->gridPhaseAVoltage());
|
||||
meter->setStateValue(meterVoltagePhaseBStateTypeId, connection->gridPhaseBVoltage());
|
||||
meter->setStateValue(meterVoltagePhaseCStateTypeId, connection->gridPhaseCVoltage());
|
||||
// The phase current registers don't seem to contain proper values. Calculating ourselves instead
|
||||
// meter->setStateValue(meterCurrentPhaseAStateTypeId, connection->gridPhaseACurrent() / 10.0);
|
||||
// meter->setStateValue(meterCurrentPhaseBStateTypeId, connection->gridPhaseBCurrent() / 10.0);
|
||||
// meter->setStateValue(meterCurrentPhaseCStateTypeId, connection->gridPhaseCCurrent() / 10.0);
|
||||
meter->setStateValue(meterCurrentPhaseAStateTypeId, (connection->phaseAPower() * -1.0) / (connection->gridPhaseAVoltage() / 10.0));
|
||||
meter->setStateValue(meterCurrentPhaseBStateTypeId, (connection->phaseBPower() * -1.0) / (connection->gridPhaseBVoltage() / 10.0));
|
||||
meter->setStateValue(meterCurrentPhaseCStateTypeId, (connection->phaseCPower() * -1.0) / (connection->gridPhaseCVoltage() / 10.0));
|
||||
// meter->setStateValue(meterCurrentPhaseAStateTypeId, connection->gridPhaseACurrent());
|
||||
// meter->setStateValue(meterCurrentPhaseBStateTypeId, connection->gridPhaseBCurrent());
|
||||
// meter->setStateValue(meterCurrentPhaseCStateTypeId, connection->gridPhaseCCurrent());
|
||||
meter->setStateValue(meterCurrentPhaseAStateTypeId, (connection->phaseAPower() * -1.0) / connection->gridPhaseAVoltage());
|
||||
meter->setStateValue(meterCurrentPhaseBStateTypeId, (connection->phaseBPower() * -1.0) / connection->gridPhaseBVoltage());
|
||||
meter->setStateValue(meterCurrentPhaseCStateTypeId, (connection->phaseCPower() * -1.0) / connection->gridPhaseCVoltage());
|
||||
qCInfo(dcWattsonic()) << "Updating meter:" << meter->stateValue(meterCurrentPowerStateTypeId).toDouble() << "W" << meter->stateValue(meterTotalEnergyProducedStateTypeId).toDouble() << "kWh";
|
||||
}
|
||||
|
||||
@ -273,14 +279,15 @@ void IntegrationPluginWattsonic::setupWattsonicConnection(ThingSetupInfo *info)
|
||||
|
||||
if (!batteries.isEmpty() && connection->SOC() > 0) {
|
||||
Thing *battery = batteries.first();
|
||||
QHash<WattsonicModbusRtuConnection::BatteryMode, QString> map {
|
||||
{WattsonicModbusRtuConnection::BatteryModeDischarge, "discharging"},
|
||||
{WattsonicModbusRtuConnection::BatteryModeCharge, "charging"}
|
||||
QHash<WattsonicInverter::BatteryMode, QString> map {
|
||||
{WattsonicInverter::BatteryModeDischarge, "discharging"},
|
||||
{WattsonicInverter::BatteryModeCharge, "charging"}
|
||||
};
|
||||
|
||||
battery->setStateValue(batteryChargingStateStateTypeId, map.value(connection->batteryMode()));
|
||||
battery->setStateValue(batteryCurrentPowerStateTypeId, connection->batteryPower() * -1.0);
|
||||
battery->setStateValue(batteryBatteryLevelStateTypeId, connection->SOC() / 100.0);
|
||||
battery->setStateValue(batteryBatteryCriticalStateTypeId, connection->SOC() < 500);
|
||||
battery->setStateValue(batteryBatteryLevelStateTypeId, connection->SOC());
|
||||
battery->setStateValue(batteryBatteryCriticalStateTypeId, connection->SOC() < 5);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2024, nymea GmbH
|
||||
* Copyright 2013 - 2025, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
@ -36,8 +36,7 @@
|
||||
#include <network/networkdevicemonitor.h>
|
||||
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
#include "wattsonicmodbusrtuconnection.h"
|
||||
#include "wattsonicinverter.h"
|
||||
|
||||
class IntegrationPluginWattsonic: public IntegrationPlugin
|
||||
{
|
||||
@ -58,7 +57,7 @@ private:
|
||||
void setupWattsonicConnection(ThingSetupInfo *info);
|
||||
|
||||
PluginTimer *m_pluginTimer = nullptr;
|
||||
QHash<Thing *, WattsonicModbusRtuConnection *> m_connections;
|
||||
QHash<Thing *, WattsonicInverter *> m_connections;
|
||||
|
||||
void setConnectedStates(Thing * thing, bool connected);
|
||||
};
|
||||
|
||||
@ -48,8 +48,7 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"blocks": [
|
||||
],
|
||||
"blocks": [ ],
|
||||
"registers": [
|
||||
{
|
||||
"id": "serialNumber",
|
||||
@ -61,6 +60,16 @@
|
||||
"description": "Serial number",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "equipmentInfo",
|
||||
"address": 10008,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"readSchedule": "init",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "EquipmentInfo",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "firmwareVersion",
|
||||
"address": 10011,
|
||||
@ -140,7 +149,8 @@
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Total grid injection energy on meter",
|
||||
"defaultValue": 0,
|
||||
"unit": "1/100 kWh",
|
||||
"staticScaleFactor": -2,
|
||||
"unit": "kWh",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
@ -152,7 +162,8 @@
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Total purchasing energy from grid on meter",
|
||||
"defaultValue": 0,
|
||||
"unit": "1/100 kWh",
|
||||
"staticScaleFactor": -2,
|
||||
"unit": "kWh",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
@ -164,7 +175,8 @@
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Grid Phase A Voltage",
|
||||
"defaultValue": 0,
|
||||
"unit": "1/10 V",
|
||||
"staticScaleFactor": -1,
|
||||
"unit": "V",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
@ -176,7 +188,8 @@
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Grid Phase A Current",
|
||||
"defaultValue": 0,
|
||||
"unit": "1/10 A",
|
||||
"staticScaleFactor": -1,
|
||||
"unit": "A",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
@ -188,7 +201,8 @@
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Grid Phase B Voltage",
|
||||
"defaultValue": 0,
|
||||
"unit": "1/10 V",
|
||||
"staticScaleFactor": -1,
|
||||
"unit": "V",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
@ -200,7 +214,8 @@
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Grid Phase B Current",
|
||||
"defaultValue": 0,
|
||||
"unit": "1/10 A",
|
||||
"staticScaleFactor": -1,
|
||||
"unit": "A",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
@ -212,7 +227,8 @@
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Grid Phase C Voltage",
|
||||
"defaultValue": 0,
|
||||
"unit": "1/10 V",
|
||||
"staticScaleFactor": -1,
|
||||
"unit": "V",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
@ -224,7 +240,8 @@
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Grid Phase C Current",
|
||||
"defaultValue": 0,
|
||||
"unit": "1/10 A",
|
||||
"staticScaleFactor": -1,
|
||||
"unit": "A",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
@ -248,7 +265,8 @@
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Total PV Generation from installation",
|
||||
"defaultValue": 0,
|
||||
"unit": "1/10 kWh",
|
||||
"staticScaleFactor": -1,
|
||||
"unit": "kWh",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
@ -263,111 +281,180 @@
|
||||
"unit": "W",
|
||||
"access": "RO"
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
"id": "totalBackupP",
|
||||
"address": 30230,
|
||||
"size": 2,
|
||||
"type": "int32",
|
||||
"readSchedule": "update",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Total_Backup_P/AC Active Power",
|
||||
"unit": "W",
|
||||
"defaultValue": 0,
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "batteryVoltageDc",
|
||||
"id": "batteryVoltageDcGen3",
|
||||
"address": 30254,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"readSchedule": "update",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Battery voltage DC",
|
||||
"description": "Battery voltage DC (gen3)",
|
||||
"defaultValue": 0,
|
||||
"unit": "1/10 V",
|
||||
"staticScaleFactor": -1,
|
||||
"unit": "V",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "batteryMode",
|
||||
"id": "batteryModeGen3",
|
||||
"address": 30256,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"readSchedule": "update",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Battery mode",
|
||||
"description": "Battery mode (gen3)",
|
||||
"enum": "BatteryMode",
|
||||
"defaultValue": "BatteryModeDischarge",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "batteryPower",
|
||||
"id": "batteryPowerGen3",
|
||||
"address": 30258,
|
||||
"size": 2,
|
||||
"type": "int32",
|
||||
"readSchedule": "update",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Battery power",
|
||||
"description": "Battery power (gen3)",
|
||||
"defaultValue": 0,
|
||||
"unit": "W",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "totalEnergyInjectedToGrid",
|
||||
"id": "totalEnergyInjectedToGridGen3",
|
||||
"address": 31102,
|
||||
"size": 2,
|
||||
"type": "uint32",
|
||||
"readSchedule": "update",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Total energy injected to grid",
|
||||
"description": "Total energy injected to grid (gen3)",
|
||||
"defaultValue": 0,
|
||||
"unit": "1/10 kWh",
|
||||
"staticScaleFactor": -1,
|
||||
"unit": "kWh",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "totalEnergyPurchasedFromGrid",
|
||||
"id": "totalEnergyPurchasedFromGridGen3",
|
||||
"address": 31104,
|
||||
"size": 2,
|
||||
"type": "uint32",
|
||||
"readSchedule": "update",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Total energy purchased from grid",
|
||||
"description": "Total energy purchased from grid (gen3)",
|
||||
"defaultValue": 0,
|
||||
"unit": "1/10 kWh",
|
||||
"staticScaleFactor": -1,
|
||||
"unit": "kWh",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "batteryStrings",
|
||||
"address": 32001,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"readSchedule": "update",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Battery strings",
|
||||
"defaultValue": 0,
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "SOC",
|
||||
"id": "SOCGen3",
|
||||
"address": 33000,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"readSchedule": "update",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "SOC",
|
||||
"description": "SOC (gen3)",
|
||||
"defaultValue": 0,
|
||||
"unit": "% * 100",
|
||||
"staticScaleFactor": -2,
|
||||
"unit": "%",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "SOH",
|
||||
"id": "SOHGen3",
|
||||
"address": 33001,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"readSchedule": "update",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "SOH",
|
||||
"description": "SOH (gen3)",
|
||||
"defaultValue": 0,
|
||||
"unit": "% * 100",
|
||||
"staticScaleFactor": -2,
|
||||
"unit": "%",
|
||||
"access": "RO"
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
"id": "batteryVoltageDcGen2",
|
||||
"address": 40254,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"readSchedule": "update",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Battery voltage DC (gen2)",
|
||||
"defaultValue": 0,
|
||||
"staticScaleFactor": -1,
|
||||
"unit": "V",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "batteryModeGen2",
|
||||
"address": 40256,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Battery mode (gen2)",
|
||||
"enum": "BatteryMode",
|
||||
"defaultValue": "BatteryModeDischarge",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "batteryPowerGen2",
|
||||
"address": 40258,
|
||||
"size": 2,
|
||||
"type": "int32",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Battery power (gen2)",
|
||||
"defaultValue": 0,
|
||||
"unit": "W",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "totalEnergyInjectedToGridGen2",
|
||||
"address": 41102,
|
||||
"size": 2,
|
||||
"type": "uint32",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Total energy injected to grid (gen2)",
|
||||
"defaultValue": 0,
|
||||
"staticScaleFactor": -1,
|
||||
"unit": "kWh",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "totalEnergyPurchasedFromGridGen2",
|
||||
"address": 41104,
|
||||
"size": 2,
|
||||
"type": "uint32",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "Total energy purchased from grid (gen2)",
|
||||
"defaultValue": 0,
|
||||
"staticScaleFactor": -1,
|
||||
"unit": "kWh",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "SOCGen2",
|
||||
"address": 43000,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "SOC (gen2)",
|
||||
"defaultValue": 0,
|
||||
"staticScaleFactor": -2,
|
||||
"unit": "%",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "SOHGen2",
|
||||
"address": 43001,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"registerType": "holdingRegister",
|
||||
"description": "SOH (gen2)",
|
||||
"defaultValue": 0,
|
||||
"staticScaleFactor": -2,
|
||||
"unit": "%",
|
||||
"access": "RO"
|
||||
}
|
||||
]
|
||||
|
||||
@ -8,8 +8,10 @@ include(../modbus.pri)
|
||||
|
||||
HEADERS += \
|
||||
wattsonicdiscovery.h \
|
||||
integrationpluginwattsonic.h
|
||||
integrationpluginwattsonic.h \
|
||||
wattsonicinverter.h
|
||||
|
||||
SOURCES += \
|
||||
wattsonicdiscovery.cpp \
|
||||
integrationpluginwattsonic.cpp
|
||||
integrationpluginwattsonic.cpp \
|
||||
wattsonicinverter.cpp
|
||||
|
||||
@ -43,21 +43,22 @@ WattsonicDiscovery::WattsonicDiscovery(ModbusRtuHardwareResource *modbusRtuResou
|
||||
void WattsonicDiscovery::startDiscovery(quint16 slaveId)
|
||||
{
|
||||
qCInfo(dcWattsonic()) << "Discovery: Searching for Wattsonic device on modbus RTU...";
|
||||
m_candidateMasters.clear();
|
||||
m_verifiedMasters.clear();
|
||||
|
||||
QList<ModbusRtuMaster*> candidateMasters;
|
||||
foreach (ModbusRtuMaster *master, m_modbusRtuResource->modbusRtuMasters()) {
|
||||
if (master->baudrate() == 9600 && master->dataBits() == 8 && master->stopBits() == 1 && master->parity() == QSerialPort::NoParity) {
|
||||
candidateMasters.append(master);
|
||||
m_candidateMasters.append(master);
|
||||
}
|
||||
}
|
||||
|
||||
if (candidateMasters.isEmpty()) {
|
||||
if (m_candidateMasters.isEmpty()) {
|
||||
qCWarning(dcWattsonic()) << "Discovery: No usable modbus RTU master found.";
|
||||
emit discoveryFinished(false);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (ModbusRtuMaster *master, candidateMasters) {
|
||||
foreach (ModbusRtuMaster *master, m_candidateMasters) {
|
||||
if (master->connected()) {
|
||||
tryConnect(master, slaveId);
|
||||
} else {
|
||||
@ -74,18 +75,35 @@ QList<WattsonicDiscovery::Result> WattsonicDiscovery::discoveryResults() const
|
||||
void WattsonicDiscovery::tryConnect(ModbusRtuMaster *master, quint16 slaveId)
|
||||
{
|
||||
qCDebug(dcWattsonic()) << "Discovery: Scanning modbus RTU master" << master->modbusUuid() << "Slave ID:" << slaveId;
|
||||
m_verifiedMasters.append(master);
|
||||
|
||||
ModbusRtuReply *reply = master->readHoldingRegister(slaveId, 10000, 8);
|
||||
connect(reply, &ModbusRtuReply::finished, this, [this, master, reply, slaveId](){
|
||||
WattsonicInverter *connection = new WattsonicInverter(master, slaveId, this);
|
||||
connect(connection, &WattsonicInverter::reachableChanged, this, [connection](bool reachable){
|
||||
if (reachable) {
|
||||
qCDebug(dcWattsonic()) << "Discovery: The connection is now reachable. Starting the initialization";
|
||||
connection->initialize();
|
||||
} else {
|
||||
connection->deleteLater();
|
||||
}
|
||||
});
|
||||
|
||||
if (reply->error() == ModbusRtuReply::NoError) {
|
||||
QString serialNumber = ModbusDataUtils::convertToString(reply->result(), ModbusDataUtils::ByteOrderBigEndian);
|
||||
qCDebug(dcWattsonic()) << "Discovery: Test reply finished!" << reply->error() << reply->result() << serialNumber;
|
||||
|
||||
Result result {master->modbusUuid(), serialNumber, slaveId};
|
||||
m_discoveryResults.append(result);
|
||||
connect(connection, &WattsonicInverter::customInitializationFinished, this, [this, connection, master, slaveId](bool success){
|
||||
|
||||
if (!success) {
|
||||
qCDebug(dcWattsonic()) << "Initialization failed on" << master << "skip ";
|
||||
return;
|
||||
}
|
||||
|
||||
emit discoveryFinished(true);
|
||||
qCDebug(dcWattsonic()) << "Discovery: Init finished successfully:" << connection->serialNumber()
|
||||
<< connection->inverterInfo().type << connection->inverterInfo().model << connection->generation();
|
||||
|
||||
Result result {master->modbusUuid(), connection->serialNumber(), slaveId, connection->generation(), connection->inverterInfo()};
|
||||
m_discoveryResults.append(result);
|
||||
|
||||
if (m_verifiedMasters.count() == m_candidateMasters.count()) {
|
||||
qCDebug(dcWattsonic()) << "Discovery: Verified all masters. Finish discovery with" << m_discoveryResults.count() << "discovered inverters.";
|
||||
emit discoveryFinished(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2023, nymea GmbH
|
||||
* Copyright 2013 - 2025, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
@ -32,8 +32,11 @@
|
||||
#define WATTSONICDISCOVERY_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <hardware/modbus/modbusrtuhardwareresource.h>
|
||||
|
||||
#include "wattsonicinverter.h"
|
||||
|
||||
class WattsonicDiscovery : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -44,12 +47,15 @@ public:
|
||||
QUuid modbusRtuMasterId;
|
||||
QString serialNumber;
|
||||
quint16 slaveId;
|
||||
WattsonicInverter::Generation generation;
|
||||
WattsonicInverter::Info inverterInfo;
|
||||
};
|
||||
|
||||
void startDiscovery(quint16 slaveId);
|
||||
|
||||
QList<Result> discoveryResults() const;
|
||||
|
||||
|
||||
signals:
|
||||
void discoveryFinished(bool modbusRtuMasterAvailable);
|
||||
|
||||
@ -58,7 +64,8 @@ private slots:
|
||||
|
||||
private:
|
||||
ModbusRtuHardwareResource *m_modbusRtuResource = nullptr;
|
||||
|
||||
QList<ModbusRtuMaster *> m_candidateMasters;
|
||||
QList<ModbusRtuMaster *> m_verifiedMasters;
|
||||
QList<Result> m_discoveryResults;
|
||||
};
|
||||
|
||||
|
||||
1241
wattsonic/wattsonicinverter.cpp
Normal file
1241
wattsonic/wattsonicinverter.cpp
Normal file
File diff suppressed because it is too large
Load Diff
82
wattsonic/wattsonicinverter.h
Normal file
82
wattsonic/wattsonicinverter.h
Normal file
@ -0,0 +1,82 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2025, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project is distributed in the hope that
|
||||
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef WATTSONICINVERTER_H
|
||||
#define WATTSONICINVERTER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "wattsonicmodbusrtuconnection.h"
|
||||
|
||||
class WattsonicInverter : public WattsonicModbusRtuConnection
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
struct Info {
|
||||
QString type;
|
||||
QString model;
|
||||
};
|
||||
|
||||
enum Generation {
|
||||
GenerationUnknwon,
|
||||
Generation2,
|
||||
Generation3
|
||||
};
|
||||
Q_ENUM(Generation)
|
||||
|
||||
explicit WattsonicInverter(ModbusRtuMaster *modbusRtuMaster, quint16 slaveId, QObject *parent = nullptr);
|
||||
|
||||
Generation generation() const;
|
||||
WattsonicInverter::Info inverterInfo() const;
|
||||
|
||||
// Generation specific registers
|
||||
float batteryVoltageDc() const;
|
||||
BatteryMode batteryMode() const;
|
||||
qint32 batteryPower() const;
|
||||
float totalEnergyInjectedToGrid() const;
|
||||
float totalEnergyPurchasedFromGrid() const;
|
||||
float SOC() const;
|
||||
float SOH() const;
|
||||
|
||||
bool update() override;
|
||||
|
||||
static WattsonicInverter::Info getInverterInfo(quint16 equipmentInfo);
|
||||
|
||||
signals:
|
||||
void generationChanged();
|
||||
void customInitializationFinished(bool success);
|
||||
|
||||
private:
|
||||
WattsonicInverter::Info m_inverterInfo;
|
||||
Generation m_generation = GenerationUnknwon;
|
||||
|
||||
};
|
||||
|
||||
#endif // WATTSONICINVERTER_H
|
||||
Reference in New Issue
Block a user