Merge PR #187: Wattsonic: Add modbus slave ID as discovery parameter
This commit is contained in:
commit
bf97e0f97e
@ -42,11 +42,11 @@ IntegrationPluginWattsonic::IntegrationPluginWattsonic()
|
|||||||
|
|
||||||
void IntegrationPluginWattsonic::discoverThings(ThingDiscoveryInfo *info)
|
void IntegrationPluginWattsonic::discoverThings(ThingDiscoveryInfo *info)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (info->thingClassId() == inverterThingClassId) {
|
if (info->thingClassId() == inverterThingClassId) {
|
||||||
|
|
||||||
WattsonicDiscovery *discovery = new WattsonicDiscovery(hardwareManager()->modbusRtuResource(), info);
|
WattsonicDiscovery *discovery = new WattsonicDiscovery(hardwareManager()->modbusRtuResource(), info);
|
||||||
connect(discovery, &WattsonicDiscovery::discoveryFinished, info, [=](bool modbusRtuMasterAvailable){
|
connect(discovery, &WattsonicDiscovery::discoveryFinished, info, [=](bool modbusRtuMasterAvailable){
|
||||||
|
|
||||||
if (!modbusRtuMasterAvailable) {
|
if (!modbusRtuMasterAvailable) {
|
||||||
info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("No suitable Modbus RTU connection available. Please set up a Modbus RTU master with a baudrate of 9600, 8 data bits, 1 stop bit and no parity."));
|
info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("No suitable Modbus RTU connection available. Please set up a Modbus RTU master with a baudrate of 9600, 8 data bits, 1 stop bit and no parity."));
|
||||||
return;
|
return;
|
||||||
@ -54,13 +54,14 @@ void IntegrationPluginWattsonic::discoverThings(ThingDiscoveryInfo *info)
|
|||||||
|
|
||||||
foreach (const WattsonicDiscovery::Result &result, discovery->discoveryResults()) {
|
foreach (const WattsonicDiscovery::Result &result, discovery->discoveryResults()) {
|
||||||
|
|
||||||
QString name = "Wattsonic hybrid inverter";
|
QString name = "Wattsonic inverter (" + result.serialNumber + ")";
|
||||||
ThingDescriptor descriptor(inverterThingClassId, name, result.serialNumber);
|
ThingDescriptor descriptor(inverterThingClassId, name, QString("Modbus ID: %1").arg(result.slaveId));
|
||||||
qCDebug(dcWattsonic()) << "Discovered:" << descriptor.title() << descriptor.description();
|
qCDebug(dcWattsonic()) << "Discovered:" << descriptor.title() << descriptor.description();
|
||||||
|
|
||||||
ParamList params {
|
ParamList params {
|
||||||
{inverterThingModbusMasterUuidParamTypeId, result.modbusRtuMasterId},
|
{inverterThingModbusMasterUuidParamTypeId, result.modbusRtuMasterId},
|
||||||
{inverterThingSlaveAddressParamTypeId, result.slaveId}
|
{inverterThingSlaveAddressParamTypeId, result.slaveId},
|
||||||
|
{inverterThingSerialNumberParamTypeId, result.serialNumber}
|
||||||
};
|
};
|
||||||
descriptor.setParams(params);
|
descriptor.setParams(params);
|
||||||
|
|
||||||
@ -76,7 +77,8 @@ void IntegrationPluginWattsonic::discoverThings(ThingDiscoveryInfo *info)
|
|||||||
|
|
||||||
info->finish(Thing::ThingErrorNoError);
|
info->finish(Thing::ThingErrorNoError);
|
||||||
});
|
});
|
||||||
discovery->startDiscovery();
|
|
||||||
|
discovery->startDiscovery(info->params().paramValue(inverterDiscoverySlaveAddressParamTypeId).toUInt());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,21 +111,7 @@ void IntegrationPluginWattsonic::setupThing(ThingSetupInfo *info)
|
|||||||
|
|
||||||
void IntegrationPluginWattsonic::postSetupThing(Thing *thing)
|
void IntegrationPluginWattsonic::postSetupThing(Thing *thing)
|
||||||
{
|
{
|
||||||
if (thing->thingClassId() == inverterThingClassId) {
|
Q_UNUSED(thing)
|
||||||
Things meters = myThings().filterByParentId(thing->id()).filterByThingClassId(meterThingClassId);
|
|
||||||
if (meters.isEmpty()) {
|
|
||||||
qCInfo(dcWattsonic()) << "No energy meter set up yet. Creating thing...";
|
|
||||||
ThingDescriptor descriptor(meterThingClassId, "Wattsonic energy meter", QString(), thing->id());
|
|
||||||
emit autoThingsAppeared({descriptor});
|
|
||||||
}
|
|
||||||
Things batteries = myThings().filterByParentId(thing->id()).filterByThingClassId(batteryThingClassId);
|
|
||||||
if (batteries.isEmpty()) {
|
|
||||||
qCInfo(dcWattsonic()) << "No battery set up yet. Creating thing...";
|
|
||||||
ThingDescriptor descriptor(batteryThingClassId, "Wattsonic energy storage", QString(), thing->id());
|
|
||||||
emit autoThingsAppeared({descriptor});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (!m_pluginTimer) {
|
if (!m_pluginTimer) {
|
||||||
qCDebug(dcWattsonic()) << "Starting plugin timer...";
|
qCDebug(dcWattsonic()) << "Starting plugin timer...";
|
||||||
@ -215,7 +203,32 @@ void IntegrationPluginWattsonic::setupWattsonicConnection(ThingSetupInfo *info)
|
|||||||
inverter->setStateValue(inverterTotalEnergyProducedStateTypeId, connection->totalPVGenerationFromInstallation() * 0.1);
|
inverter->setStateValue(inverterTotalEnergyProducedStateTypeId, connection->totalPVGenerationFromInstallation() * 0.1);
|
||||||
qCInfo(dcWattsonic()) << "Updating inverter:" << inverter->stateValue(inverterCurrentPowerStateTypeId).toDouble() << "W" << inverter->stateValue(inverterTotalEnergyProducedStateTypeId).toDouble() << "kWh";
|
qCInfo(dcWattsonic()) << "Updating inverter:" << inverter->stateValue(inverterCurrentPowerStateTypeId).toDouble() << "W" << inverter->stateValue(inverterTotalEnergyProducedStateTypeId).toDouble() << "kWh";
|
||||||
|
|
||||||
Things meters = myThings().filterByParentId(thing->id()).filterByThingClassId(meterThingClassId);
|
Things meters = myThings().filterByParentId(thing->id()).filterByThingClassId(meterThingClassId);
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
if (meterDetected) {
|
||||||
|
if (meters.isEmpty()) {
|
||||||
|
qCInfo(dcWattsonic()) << "No energy meter set up yet but measurements detected. Creating meter thing...";
|
||||||
|
QString parentSerialNumber = thing->paramValue(inverterThingSerialNumberParamTypeId).toString();
|
||||||
|
QString name = "Wattsonic energy meter";
|
||||||
|
if (!parentSerialNumber.isEmpty())
|
||||||
|
name.append(" (" + parentSerialNumber + ")");
|
||||||
|
|
||||||
|
ThingDescriptor descriptor(meterThingClassId, name, QString(), thing->id());
|
||||||
|
emit autoThingsAppeared({descriptor});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No meter detected
|
||||||
|
if (!meters.isEmpty()) {
|
||||||
|
// Remove existing meter thing
|
||||||
|
qCInfo(dcWattsonic()) << "Meter set up yet but energy measurments detected in the registers. Removing meter thing...";
|
||||||
|
emit autoThingDisappeared(meters.first()->id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!meters.isEmpty()) {
|
if (!meters.isEmpty()) {
|
||||||
Thing *meter = meters.first();
|
Thing *meter = meters.first();
|
||||||
meter->setStateValue(meterCurrentPowerStateTypeId, connection->totalPowerOnMeter() * -1.0);
|
meter->setStateValue(meterCurrentPowerStateTypeId, connection->totalPowerOnMeter() * -1.0);
|
||||||
@ -238,6 +251,26 @@ void IntegrationPluginWattsonic::setupWattsonicConnection(ThingSetupInfo *info)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Things batteries = myThings().filterByParentId(thing->id()).filterByThingClassId(batteryThingClassId);
|
Things batteries = myThings().filterByParentId(thing->id()).filterByThingClassId(batteryThingClassId);
|
||||||
|
|
||||||
|
// Check if a battery is connected or not. We detect a battery by reading the DC voltage, if there is no voltage, there is no battery
|
||||||
|
if (connection->batteryVoltageDc() > 0) {
|
||||||
|
if (batteries.isEmpty()) {
|
||||||
|
qCInfo(dcWattsonic()) << "No battery set up yet but DC voltage detected in the registers. Creating battery thing...";
|
||||||
|
QString parentSerialNumber = thing->paramValue(inverterThingSerialNumberParamTypeId).toString();
|
||||||
|
QString name = "Wattsonic energy storage";
|
||||||
|
if (!parentSerialNumber.isEmpty())
|
||||||
|
name.append(" (" + parentSerialNumber + ")");
|
||||||
|
|
||||||
|
ThingDescriptor descriptor(batteryThingClassId, name, QString(), thing->id());
|
||||||
|
emit autoThingsAppeared({descriptor});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!batteries.isEmpty()) {
|
||||||
|
qCInfo(dcWattsonic()) << "Battery set up yet but no DC voltage detected in the registers. Removing battery thing...";
|
||||||
|
emit autoThingDisappeared(batteries.first()->id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!batteries.isEmpty() && connection->SOC() > 0) {
|
if (!batteries.isEmpty() && connection->SOC() > 0) {
|
||||||
Thing *battery = batteries.first();
|
Thing *battery = batteries.first();
|
||||||
QHash<WattsonicModbusRtuConnection::BatteryMode, QString> map {
|
QHash<WattsonicModbusRtuConnection::BatteryMode, QString> map {
|
||||||
|
|||||||
@ -14,13 +14,22 @@
|
|||||||
"id": "688bef8d-2ba8-4eb3-b30e-16193eba02fb",
|
"id": "688bef8d-2ba8-4eb3-b30e-16193eba02fb",
|
||||||
"createMethods": ["discovery", "user"],
|
"createMethods": ["discovery", "user"],
|
||||||
"interfaces": ["solarinverter", "connectable"],
|
"interfaces": ["solarinverter", "connectable"],
|
||||||
|
"discoveryParamTypes": [
|
||||||
|
{
|
||||||
|
"id": "a4e58882-d26a-43ee-813f-11c6ca74ee6d",
|
||||||
|
"name": "slaveAddress",
|
||||||
|
"displayName": "Modbus slave address",
|
||||||
|
"type": "uint",
|
||||||
|
"defaultValue": 247
|
||||||
|
}
|
||||||
|
],
|
||||||
"paramTypes": [
|
"paramTypes": [
|
||||||
{
|
{
|
||||||
"id": "55a7d9ed-5f4f-41a2-8dc1-c6a5a79512d2",
|
"id": "55a7d9ed-5f4f-41a2-8dc1-c6a5a79512d2",
|
||||||
"name": "slaveAddress",
|
"name": "slaveAddress",
|
||||||
"displayName": "Modbus slave address",
|
"displayName": "Modbus slave address",
|
||||||
"type": "uint",
|
"type": "uint",
|
||||||
"defaultValue": 1
|
"defaultValue": 247
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "4f1238b5-07e0-4516-b84a-71670141ef81",
|
"id": "4f1238b5-07e0-4516-b84a-71670141ef81",
|
||||||
@ -28,6 +37,13 @@
|
|||||||
"displayName": "Modbus RTU master",
|
"displayName": "Modbus RTU master",
|
||||||
"type": "QUuid",
|
"type": "QUuid",
|
||||||
"defaultValue": ""
|
"defaultValue": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "47a655ca-bc68-42b0-8252-bde20de56974",
|
||||||
|
"name": "serialNumber",
|
||||||
|
"displayName": "Serial number",
|
||||||
|
"type": "QString",
|
||||||
|
"defaultValue": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"stateTypes": [
|
"stateTypes": [
|
||||||
|
|||||||
@ -275,6 +275,18 @@
|
|||||||
"defaultValue": 0,
|
"defaultValue": 0,
|
||||||
"access": "RO"
|
"access": "RO"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "batteryVoltageDc",
|
||||||
|
"address": 30254,
|
||||||
|
"size": 1,
|
||||||
|
"type": "uint16",
|
||||||
|
"readSchedule": "update",
|
||||||
|
"registerType": "holdingRegister",
|
||||||
|
"description": "Battery voltage DC",
|
||||||
|
"defaultValue": 0,
|
||||||
|
"unit": "1/10 V",
|
||||||
|
"access": "RO"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "batteryMode",
|
"id": "batteryMode",
|
||||||
"address": 30256,
|
"address": 30256,
|
||||||
|
|||||||
@ -33,8 +33,6 @@
|
|||||||
|
|
||||||
#include <modbusdatautils.h>
|
#include <modbusdatautils.h>
|
||||||
|
|
||||||
QList<int> slaveIdCandidates = {247};
|
|
||||||
|
|
||||||
WattsonicDiscovery::WattsonicDiscovery(ModbusRtuHardwareResource *modbusRtuResource, QObject *parent):
|
WattsonicDiscovery::WattsonicDiscovery(ModbusRtuHardwareResource *modbusRtuResource, QObject *parent):
|
||||||
QObject{parent},
|
QObject{parent},
|
||||||
m_modbusRtuResource(modbusRtuResource)
|
m_modbusRtuResource(modbusRtuResource)
|
||||||
@ -42,7 +40,7 @@ WattsonicDiscovery::WattsonicDiscovery(ModbusRtuHardwareResource *modbusRtuResou
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WattsonicDiscovery::startDiscovery()
|
void WattsonicDiscovery::startDiscovery(quint16 slaveId)
|
||||||
{
|
{
|
||||||
qCInfo(dcWattsonic()) << "Discovery: Searching for Wattsonic device on modbus RTU...";
|
qCInfo(dcWattsonic()) << "Discovery: Searching for Wattsonic device on modbus RTU...";
|
||||||
|
|
||||||
@ -54,16 +52,16 @@ void WattsonicDiscovery::startDiscovery()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (candidateMasters.isEmpty()) {
|
if (candidateMasters.isEmpty()) {
|
||||||
qCWarning(dcWattsonic()) << "No usable modbus RTU master found.";
|
qCWarning(dcWattsonic()) << "Discovery: No usable modbus RTU master found.";
|
||||||
emit discoveryFinished(false);
|
emit discoveryFinished(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (ModbusRtuMaster *master, candidateMasters) {
|
foreach (ModbusRtuMaster *master, candidateMasters) {
|
||||||
if (master->connected()) {
|
if (master->connected()) {
|
||||||
tryConnect(master, 0);
|
tryConnect(master, slaveId);
|
||||||
} else {
|
} else {
|
||||||
qCWarning(dcWattsonic()) << "Modbus RTU master" << master->modbusUuid().toString() << "is not connected.";
|
qCWarning(dcWattsonic()) << "Discovery: Modbus RTU master" << master->modbusUuid().toString() << "is not connected.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,28 +71,21 @@ QList<WattsonicDiscovery::Result> WattsonicDiscovery::discoveryResults() const
|
|||||||
return m_discoveryResults;
|
return m_discoveryResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WattsonicDiscovery::tryConnect(ModbusRtuMaster *master, quint16 slaveIdIndex)
|
void WattsonicDiscovery::tryConnect(ModbusRtuMaster *master, quint16 slaveId)
|
||||||
{
|
{
|
||||||
quint8 slaveId = slaveIdCandidates.at(slaveIdIndex);
|
qCDebug(dcWattsonic()) << "Discovery: Scanning modbus RTU master" << master->modbusUuid() << "Slave ID:" << slaveId;
|
||||||
qCDebug(dcWattsonic()) << "Scanning modbus RTU master" << master->modbusUuid() << "Slave ID:" << slaveId;
|
|
||||||
|
|
||||||
ModbusRtuReply *reply = master->readHoldingRegister(slaveId, 10000, 8);
|
ModbusRtuReply *reply = master->readHoldingRegister(slaveId, 10000, 8);
|
||||||
connect(reply, &ModbusRtuReply::finished, this, [=](){
|
connect(reply, &ModbusRtuReply::finished, this, [this, master, reply, slaveId](){
|
||||||
|
|
||||||
if (reply->error() == ModbusRtuReply::NoError) {
|
if (reply->error() == ModbusRtuReply::NoError) {
|
||||||
|
|
||||||
QString serialNumber = ModbusDataUtils::convertToString(reply->result(), ModbusDataUtils::ByteOrderBigEndian);
|
QString serialNumber = ModbusDataUtils::convertToString(reply->result(), ModbusDataUtils::ByteOrderBigEndian);
|
||||||
qCDebug(dcWattsonic()) << "Test reply finished!" << reply->error() << reply->result() << serialNumber;
|
qCDebug(dcWattsonic()) << "Discovery: Test reply finished!" << reply->error() << reply->result() << serialNumber;
|
||||||
|
|
||||||
Result result {master->modbusUuid(), serialNumber, slaveId};
|
Result result {master->modbusUuid(), serialNumber, slaveId};
|
||||||
m_discoveryResults.append(result);
|
m_discoveryResults.append(result);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (slaveIdIndex < slaveIdCandidates.count() - 1) {
|
emit discoveryFinished(true);
|
||||||
tryConnect(master, slaveIdIndex+1);
|
|
||||||
} else {
|
|
||||||
emit discoveryFinished(true);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,7 +46,7 @@ public:
|
|||||||
quint16 slaveId;
|
quint16 slaveId;
|
||||||
};
|
};
|
||||||
|
|
||||||
void startDiscovery();
|
void startDiscovery(quint16 slaveId);
|
||||||
|
|
||||||
QList<Result> discoveryResults() const;
|
QList<Result> discoveryResults() const;
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ signals:
|
|||||||
void discoveryFinished(bool modbusRtuMasterAvailable);
|
void discoveryFinished(bool modbusRtuMasterAvailable);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void tryConnect(ModbusRtuMaster *master, quint16 slaveIdIndex);
|
void tryConnect(ModbusRtuMaster *master, quint16 slaveId);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ModbusRtuHardwareResource *m_modbusRtuResource = nullptr;
|
ModbusRtuHardwareResource *m_modbusRtuResource = nullptr;
|
||||||
|
|||||||
Reference in New Issue
Block a user