Add kostal model filtering and handle interrupted discovery
This commit is contained in:
parent
2d732359f6
commit
d8092607d7
@ -374,8 +374,8 @@ void SunSpecConnection::scanNextSunspecBaseRegister()
|
|||||||
|
|
||||||
void SunSpecConnection::scanModelsOnBaseRegister(quint16 offset)
|
void SunSpecConnection::scanModelsOnBaseRegister(quint16 offset)
|
||||||
{
|
{
|
||||||
qCDebug(dcSunSpec()) << "Reading SunSpec models header" << this << "using SunSpec base register" << m_baseRegister << "offset:" << offset;
|
|
||||||
quint16 startRegisterAddress = m_baseRegister + offset;
|
quint16 startRegisterAddress = m_baseRegister + offset;
|
||||||
|
qCDebug(dcSunSpec()) << "Reading SunSpec model header" << this << "using SunSpec base register" << m_baseRegister << "offset:" << offset << "=" << startRegisterAddress;
|
||||||
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, startRegisterAddress, 2);
|
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, startRegisterAddress, 2);
|
||||||
QModbusReply *reply = m_modbusTcpClient->sendReadRequest(request, m_slaveId);
|
QModbusReply *reply = m_modbusTcpClient->sendReadRequest(request, m_slaveId);
|
||||||
|
|
||||||
@ -391,7 +391,7 @@ void SunSpecConnection::scanModelsOnBaseRegister(quint16 offset)
|
|||||||
}
|
}
|
||||||
|
|
||||||
connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
|
connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
|
||||||
connect(reply, &QModbusReply::finished, this, [this, reply, offset] {
|
connect(reply, &QModbusReply::finished, this, [this, reply, offset, startRegisterAddress] {
|
||||||
if (reply->error() == QModbusDevice::NoError) {
|
if (reply->error() == QModbusDevice::NoError) {
|
||||||
|
|
||||||
const QModbusDataUnit unit = reply->result();
|
const QModbusDataUnit unit = reply->result();
|
||||||
@ -406,7 +406,7 @@ void SunSpecConnection::scanModelsOnBaseRegister(quint16 offset)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
qCDebug(dcSunSpec()) << "Discovered SunSpec model on" << this << modelId << "with length" << modelLength;
|
qCDebug(dcSunSpec()) << "Discovered SunSpec model on" << this << "[" << startRegisterAddress + 2 << "-" << startRegisterAddress + 2 + modelLength << "]" << "(base: " << m_baseRegister << "offset:" << offset << "length:" << modelLength << ") | Model ID:" << modelId << static_cast<SunSpecModelFactory::ModelId>(modelId);
|
||||||
ModuleDiscoveryResult result;
|
ModuleDiscoveryResult result;
|
||||||
result.modbusStartRegister = modbusStartRegister;
|
result.modbusStartRegister = modbusStartRegister;
|
||||||
result.modelId = modelId;
|
result.modelId = modelId;
|
||||||
@ -417,9 +417,14 @@ void SunSpecConnection::scanModelsOnBaseRegister(quint16 offset)
|
|||||||
scanModelsOnBaseRegister(offset + 2 + modelLength);
|
scanModelsOnBaseRegister(offset + 2 + modelLength);
|
||||||
} else {
|
} else {
|
||||||
qCWarning(dcSunSpec()) << "Error occured while reading model header from" << this << "using offset" << offset << m_modbusTcpClient->errorString();
|
qCWarning(dcSunSpec()) << "Error occured while reading model header from" << this << "using offset" << offset << m_modbusTcpClient->errorString();
|
||||||
// FIXME: check if models have already been found, finish with success in that case so we show at least the models found so far...
|
if (!m_modelDiscoveryResult.isEmpty()) {
|
||||||
setDiscoveryRunning(false);
|
qCWarning(dcSunSpec()) << "Error occured but already discovered" << m_modelDiscoveryResult.count() << "models. Continue with the discovered models, but the discovery may be incomplete due to header reading errors.";
|
||||||
emit discoveryFinished(false);
|
qCDebug(dcSunSpec()) << "Scan for SunSpec models on" << this << m_baseRegister << "finished successfully";
|
||||||
|
processDiscoveryResult();
|
||||||
|
} else {
|
||||||
|
setDiscoveryRunning(false);
|
||||||
|
emit discoveryFinished(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,12 @@ Connect nymea to SunSpec devices over Modbus TCP
|
|||||||
* SunSpec Storage
|
* SunSpec Storage
|
||||||
* Model ID 124
|
* Model ID 124
|
||||||
|
|
||||||
|
|
||||||
|
## Tested connections
|
||||||
|
|
||||||
|
* SolarEdge (with custom battery)
|
||||||
|
* Kostal [Documentation](
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
* The package 'nymea-plugin-sunspec' must be installed.
|
* The package 'nymea-plugin-sunspec' must be installed.
|
||||||
|
|||||||
@ -167,9 +167,8 @@ void IntegrationPluginSunSpec::discoverThings(ThingDiscoveryInfo *info)
|
|||||||
title += networkDeviceInfo.address().toString() + " (" + networkDeviceInfo.hostName() + ")";
|
title += networkDeviceInfo.address().toString() + " (" + networkDeviceInfo.hostName() + ")";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Kostal does not provide usefull information for filterin in the discovery
|
|
||||||
|
|
||||||
// Generic or not discoverable sunspec connection, show all network results
|
// Generic or not discoverable sunspec connection, show all network results
|
||||||
|
// - Kostal does not provide usefull information for filterin in the discovery
|
||||||
if (networkDeviceInfo.hostName().isEmpty()) {
|
if (networkDeviceInfo.hostName().isEmpty()) {
|
||||||
title += networkDeviceInfo.address().toString();
|
title += networkDeviceInfo.address().toString();
|
||||||
} else {
|
} else {
|
||||||
@ -210,7 +209,8 @@ void IntegrationPluginSunSpec::discoverThings(ThingDiscoveryInfo *info)
|
|||||||
void IntegrationPluginSunSpec::setupThing(ThingSetupInfo *info)
|
void IntegrationPluginSunSpec::setupThing(ThingSetupInfo *info)
|
||||||
{
|
{
|
||||||
Thing *thing = info->thing();
|
Thing *thing = info->thing();
|
||||||
qCDebug(dcSunSpec()) << "Setup thing" << thing->name();
|
qCDebug(dcSunSpec()) << "Setup thing" << thing;
|
||||||
|
qCDebug(dcSunSpec()) << thing->params();
|
||||||
|
|
||||||
if (thing->thingClassId() == sunspecConnectionThingClassId || thing->thingClassId() == solarEdgeConnectionThingClassId || thing->thingClassId() == kostalConnectionThingClassId) {
|
if (thing->thingClassId() == sunspecConnectionThingClassId || thing->thingClassId() == solarEdgeConnectionThingClassId || thing->thingClassId() == kostalConnectionThingClassId) {
|
||||||
setupConnection(info);
|
setupConnection(info);
|
||||||
@ -359,48 +359,90 @@ void IntegrationPluginSunSpec::processDiscoveryResult(Thing *thing, SunSpecConne
|
|||||||
{
|
{
|
||||||
qCDebug(dcSunSpec()) << "Processing discovery result from" << thing->name() << connection;
|
qCDebug(dcSunSpec()) << "Processing discovery result from" << thing->name() << connection;
|
||||||
|
|
||||||
// Now process the other models and check if we can create any auto device if not already added
|
// Note: from kostal devices is known, that they add inverter
|
||||||
foreach (SunSpecModel *model, connection->models()) {
|
// as normal and float version, but we need only one.
|
||||||
|
// Lets filter the duplicated information for kostal connections
|
||||||
|
if (thing->thingClassId() == kostalConnectionThingClassId) {
|
||||||
|
QHash<quint16, SunSpecModel *> filteredModels;
|
||||||
|
foreach (SunSpecModel *model, connection->models()) {
|
||||||
|
switch (model->modelId()) {
|
||||||
|
case SunSpecModelFactory::ModelIdInverterSinglePhaseFloat:
|
||||||
|
if (filteredModels.contains(SunSpecModelFactory::ModelIdInverterSinglePhase)) {
|
||||||
|
qCDebug(dcSunSpec()) << "Kostal: Filter out" << model;
|
||||||
|
} else {
|
||||||
|
filteredModels.insert(model->modelId(), model);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SunSpecModelFactory::ModelIdInverterSplitPhaseFloat:
|
||||||
|
if (filteredModels.contains(SunSpecModelFactory::ModelIdInverterSplitPhase)) {
|
||||||
|
qCDebug(dcSunSpec()) << "Kostal: Filter out" << model;
|
||||||
|
} else {
|
||||||
|
filteredModels.insert(model->modelId(), model);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SunSpecModelFactory::ModelIdInverterThreePhaseFloat:
|
||||||
|
if (filteredModels.contains(SunSpecModelFactory::ModelIdInverterThreePhase)) {
|
||||||
|
qCDebug(dcSunSpec()) << "Kostal: Filter out" << model;
|
||||||
|
} else {
|
||||||
|
filteredModels.insert(model->modelId(), model);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
filteredModels.insert(model->modelId(), model);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the filtered list
|
||||||
|
checkAutoSetupModels(thing, filteredModels.values());
|
||||||
|
} else {
|
||||||
|
// Process all models
|
||||||
|
checkAutoSetupModels(thing, connection->models());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IntegrationPluginSunSpec::checkAutoSetupModels(Thing *connectionThing, QList<SunSpecModel *> models)
|
||||||
|
{
|
||||||
|
// Process the models and check if we can create any auto device if not already added
|
||||||
|
foreach (SunSpecModel *model, models) {
|
||||||
// Make sure we have not added this model yet
|
// Make sure we have not added this model yet
|
||||||
if (sunspecThingAlreadyAdded(model->modelId(), model->modbusStartRegister(), thing->id())) {
|
if (sunspecThingAlreadyAdded(model->modelId(), model->modbusStartRegister(), connectionThing->id())) {
|
||||||
qCDebug(dcSunSpec()) << "Thing already set up for" << model;
|
qCDebug(dcSunSpec()) << "Thing already set up for" << model;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Make sure to not add duplicated models like inverter + inverter float..
|
|
||||||
|
|
||||||
switch (model->modelId()) {
|
switch (model->modelId()) {
|
||||||
case SunSpecModelFactory::ModelIdCommon:
|
case SunSpecModelFactory::ModelIdCommon:
|
||||||
// Skip the common model, we already handled this one for each thing model
|
// Skip the common model, we already handled this one for each thing model
|
||||||
break;
|
break;
|
||||||
case SunSpecModelFactory::ModelIdInverterSinglePhase:
|
case SunSpecModelFactory::ModelIdInverterSinglePhase:
|
||||||
case SunSpecModelFactory::ModelIdInverterSinglePhaseFloat:
|
case SunSpecModelFactory::ModelIdInverterSinglePhaseFloat:
|
||||||
autocreateSunSpecModelThing(sunspecSinglePhaseInverterThingClassId, QT_TR_NOOP("Single Phase Inverter"), thing->id(), model);
|
autocreateSunSpecModelThing(sunspecSinglePhaseInverterThingClassId, QT_TR_NOOP("Single Phase Inverter"), connectionThing->id(), model);
|
||||||
break;
|
break;
|
||||||
case SunSpecModelFactory::ModelIdInverterSplitPhase:
|
case SunSpecModelFactory::ModelIdInverterSplitPhase:
|
||||||
case SunSpecModelFactory::ModelIdInverterSplitPhaseFloat:
|
case SunSpecModelFactory::ModelIdInverterSplitPhaseFloat:
|
||||||
autocreateSunSpecModelThing(sunspecSplitPhaseInverterThingClassId, QT_TR_NOOP("Split Phase Inverter"), thing->id(), model);
|
autocreateSunSpecModelThing(sunspecSplitPhaseInverterThingClassId, QT_TR_NOOP("Split Phase Inverter"), connectionThing->id(), model);
|
||||||
break;
|
break;
|
||||||
case SunSpecModelFactory::ModelIdInverterThreePhase:
|
case SunSpecModelFactory::ModelIdInverterThreePhase:
|
||||||
case SunSpecModelFactory::ModelIdInverterThreePhaseFloat:
|
case SunSpecModelFactory::ModelIdInverterThreePhaseFloat:
|
||||||
autocreateSunSpecModelThing(sunspecThreePhaseInverterThingClassId, QT_TR_NOOP("Three Phase Inverter"), thing->id(), model);
|
autocreateSunSpecModelThing(sunspecThreePhaseInverterThingClassId, QT_TR_NOOP("Three Phase Inverter"), connectionThing->id(), model);
|
||||||
break;
|
break;
|
||||||
case SunSpecModelFactory::ModelIdMeterSinglePhase:
|
case SunSpecModelFactory::ModelIdMeterSinglePhase:
|
||||||
case SunSpecModelFactory::ModelIdMeterSinglePhaseFloat:
|
case SunSpecModelFactory::ModelIdMeterSinglePhaseFloat:
|
||||||
autocreateSunSpecModelThing(sunspecSinglePhaseMeterThingClassId, QT_TR_NOOP("Single Phase Meter"), thing->id(), model);
|
autocreateSunSpecModelThing(sunspecSinglePhaseMeterThingClassId, QT_TR_NOOP("Single Phase Meter"), connectionThing->id(), model);
|
||||||
break;
|
break;
|
||||||
case SunSpecModelFactory::ModelIdMeterSplitSinglePhaseAbn:
|
case SunSpecModelFactory::ModelIdMeterSplitSinglePhaseAbn:
|
||||||
case SunSpecModelFactory::ModelIdMeterSplitSinglePhaseFloat:
|
case SunSpecModelFactory::ModelIdMeterSplitSinglePhaseFloat:
|
||||||
autocreateSunSpecModelThing(sunspecSplitPhaseMeterThingClassId, QT_TR_NOOP("Split Phase Meter"), thing->id(), model);
|
autocreateSunSpecModelThing(sunspecSplitPhaseMeterThingClassId, QT_TR_NOOP("Split Phase Meter"), connectionThing->id(), model);
|
||||||
break;
|
break;
|
||||||
case SunSpecModelFactory::ModelIdMeterThreePhase:
|
case SunSpecModelFactory::ModelIdMeterThreePhase:
|
||||||
case SunSpecModelFactory::ModelIdDeltaConnectThreePhaseAbcMeter:
|
case SunSpecModelFactory::ModelIdDeltaConnectThreePhaseAbcMeter:
|
||||||
case SunSpecModelFactory::ModelIdMeterThreePhaseWyeConnect:
|
case SunSpecModelFactory::ModelIdMeterThreePhaseWyeConnect:
|
||||||
case SunSpecModelFactory::ModelIdMeterThreePhaseDeltaConnect:
|
case SunSpecModelFactory::ModelIdMeterThreePhaseDeltaConnect:
|
||||||
autocreateSunSpecModelThing(sunspecThreePhaseMeterThingClassId, QT_TR_NOOP("Three Phase Meter"), thing->id(), model);
|
autocreateSunSpecModelThing(sunspecThreePhaseMeterThingClassId, QT_TR_NOOP("Three Phase Meter"), connectionThing->id(), model);
|
||||||
break;
|
break;
|
||||||
case SunSpecModelFactory::ModelIdStorage:
|
case SunSpecModelFactory::ModelIdStorage:
|
||||||
autocreateSunSpecModelThing(sunspecStorageThingClassId, QT_TR_NOOP("Storage"), thing->id(), model);
|
autocreateSunSpecModelThing(sunspecStorageThingClassId, QT_TR_NOOP("Storage"), connectionThing->id(), model);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
qCWarning(dcSunSpec()) << "Plugin has no implementation for detected" << model;
|
qCWarning(dcSunSpec()) << "Plugin has no implementation for detected" << model;
|
||||||
|
|||||||
@ -83,6 +83,7 @@ private:
|
|||||||
|
|
||||||
bool sunspecThingAlreadyAdded(uint modelId, uint modbusAddress, const ThingId &parentId);
|
bool sunspecThingAlreadyAdded(uint modelId, uint modbusAddress, const ThingId &parentId);
|
||||||
void processDiscoveryResult(Thing *thing, SunSpecConnection *connection);
|
void processDiscoveryResult(Thing *thing, SunSpecConnection *connection);
|
||||||
|
void checkAutoSetupModels(Thing *connectionThing, QList<SunSpecModel *> models);
|
||||||
|
|
||||||
// SunSpec things
|
// SunSpec things
|
||||||
void setupConnection(ThingSetupInfo *info);
|
void setupConnection(ThingSetupInfo *info);
|
||||||
|
|||||||
@ -1495,14 +1495,14 @@
|
|||||||
"name":"port",
|
"name":"port",
|
||||||
"displayName": "Port",
|
"displayName": "Port",
|
||||||
"type": "int",
|
"type": "int",
|
||||||
"defaultValue": 502
|
"defaultValue": 1502
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "48b133da-cce3-47f3-9c7c-470026af7829",
|
"id": "48b133da-cce3-47f3-9c7c-470026af7829",
|
||||||
"name":"slaveId",
|
"name":"slaveId",
|
||||||
"displayName": "Slave ID",
|
"displayName": "Slave ID",
|
||||||
"type": "int",
|
"type": "int",
|
||||||
"defaultValue": 1
|
"defaultValue": 71
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"stateTypes":[
|
"stateTypes":[
|
||||||
|
|||||||
Reference in New Issue
Block a user