diff --git a/kostal/integrationpluginkostal.cpp b/kostal/integrationpluginkostal.cpp index 4715277..5e9d742 100644 --- a/kostal/integrationpluginkostal.cpp +++ b/kostal/integrationpluginkostal.cpp @@ -1,6 +1,6 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* Copyright 2013 - 2022, nymea GmbH +* Copyright 2013 - 2024, nymea GmbH * Contact: contact@nymea.io * * This file is part of nymea. @@ -49,24 +49,34 @@ void IntegrationPluginKostal::discoverThings(ThingDiscoveryInfo *info) } // Create a discovery with the info as parent for auto deleting the object once the discovery info is done - KostalDiscovery *discovery = new KostalDiscovery(hardwareManager()->networkDeviceDiscovery(), 1502, 71, info); + KostalDiscovery *discovery = new KostalDiscovery(hardwareManager()->networkDeviceDiscovery(), + info->params().paramValue(kostalInverterDiscoveryPortParamTypeId).toUInt(), + info->params().paramValue(kostalInverterDiscoverySlaveIdParamTypeId).toUInt(), info); + connect(discovery, &KostalDiscovery::discoveryFinished, info, [=](){ foreach (const KostalDiscovery::KostalDiscoveryResult &result, discovery->discoveryResults()) { - ThingDescriptor descriptor(kostalInverterThingClassId, result.manufacturerName + " " + result.productName, "Serial: " + result.serialNumber + " - " + result.networkDeviceInfo.address().toString()); + ThingDescriptor descriptor(kostalInverterThingClassId, result.manufacturerName + " " + result.productName, + "Serial: " + result.serialNumber + " - " + result.networkDeviceInfo.address().toString()); + qCDebug(dcKostal()) << "Discovered:" << descriptor.title() << descriptor.description(); + ParamList params; + params << Param(kostalInverterThingSerialNumberParamTypeId, result.serialNumber); + params << Param(kostalInverterThingMacAddressParamTypeId, result.networkDeviceInfo.thingParamValueMacAddress()); + params << Param(kostalInverterThingHostNameParamTypeId, result.networkDeviceInfo.thingParamValueHostName()); + params << Param(kostalInverterThingAddressParamTypeId, result.networkDeviceInfo.thingParamValueAddress()); + // Note: if we discover also the port and modbusaddress, we must fill them in from the discovery here, for now everywhere the defaults... + descriptor.setParams(params); // Check if we already have set up this device - Things existingThings = myThings().filterByParam(kostalInverterThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress()); + + // Note: we introduced the serial number later, if the current thing has no serialnumber, we use the mac + Things existingThings = myThings().filterByParam(kostalInverterThingSerialNumberParamTypeId, result.serialNumber); if (existingThings.count() == 1) { - qCDebug(dcKostal()) << "This Kostal inverter already exists in the system:" << result.networkDeviceInfo; + qCDebug(dcKostal()) << "This Kostal inverter with this serial number already exists in the system:" << result.networkDeviceInfo; descriptor.setThingId(existingThings.first()->id()); } - ParamList params; - params << Param(kostalInverterThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress()); - // Note: if we discover also the port and modbusaddress, we must fill them in from the discovery here, for now everywhere the defaults... - descriptor.setParams(params); info->addThingDescriptor(descriptor); } @@ -103,7 +113,7 @@ void IntegrationPluginKostal::setupThing(ThingSetupInfo *info) } // Create the monitor - NetworkDeviceMonitor *monitor = hardwareManager()->networkDeviceDiscovery()->registerMonitor(macAddress); + NetworkDeviceMonitor *monitor = hardwareManager()->networkDeviceDiscovery()->registerMonitor(thing); m_monitors.insert(thing, monitor); QHostAddress address = monitor->networkDeviceInfo().address(); diff --git a/kostal/integrationpluginkostal.h b/kostal/integrationpluginkostal.h index a7e5c0d..aafa86a 100644 --- a/kostal/integrationpluginkostal.h +++ b/kostal/integrationpluginkostal.h @@ -1,6 +1,6 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* Copyright 2013 - 2022, nymea GmbH +* Copyright 2013 - 2024, nymea GmbH * Contact: contact@nymea.io * * This file is part of nymea. diff --git a/kostal/integrationpluginkostal.json b/kostal/integrationpluginkostal.json index 74d39fe..43f3efd 100644 --- a/kostal/integrationpluginkostal.json +++ b/kostal/integrationpluginkostal.json @@ -1,8 +1,8 @@ { "name": "Kostal", - "displayName": "Kostal", - "id": "51a2c7d5-084d-4474-a65a-e0447ab9ac45", - "vendors": [ + "displayName": "Kostal", + "id": "51a2c7d5-084d-4474-a65a-e0447ab9ac45", + "vendors": [ { "name": "kostal", "displayName": "KOSTAL Solar Electric", @@ -13,8 +13,24 @@ "displayName": "KOSTAL Inverter", "id": "7dc6db14-6f5a-4ac8-9684-4c6a526bd0de", "createMethods": ["discovery", "user"], - "interfaces": ["solarinverter", "connectable"], + "interfaces": ["solarinverter", "connectable", "networkdevice"], "providedInterfaces": [ "energymeter", "energystorage"], + "discoveryParamTypes": [ + { + "id": "6cf2cb34-dc61-4ad1-9ae1-1177785f2d4a", + "name":"port", + "displayName": "Port", + "type": "int", + "defaultValue": 1502 + }, + { + "id": "2a6d75d9-286d-48cb-83fd-acfb99258ba7", + "name":"slaveId", + "displayName": "Slave ID", + "type": "int", + "defaultValue": 71 + } + ], "paramTypes": [ { "id": "906f6099-d0e1-4297-a2b3-f8ec4482c578", @@ -24,6 +40,29 @@ "inputType": "MacAddress", "defaultValue": "" }, + { + "id": "c922c2ec-40d9-40cd-81dd-68141b178a0c", + "name": "hostName", + "displayName": "Host name", + "type": "QString", + "inputType": "TextLine", + "defaultValue": "" + }, + { + "id": "b8aeb35e-06e9-4e52-90ce-dd8712fb7527", + "name": "address", + "displayName": "IP address", + "type": "QString", + "inputType": "IPv4Address", + "defaultValue": "" + }, + { + "id": "7c701af9-a172-47a0-bb7b-30f37f63ac95", + "name": "serialNumber", + "displayName": "serialNumber", + "type": "QString", + "defaultValue": "" + }, { "id": "9d2175af-afb9-4b31-b3dc-e53a369bad9e", "name":"port", diff --git a/kostal/kostaldiscovery.cpp b/kostal/kostaldiscovery.cpp index a272532..765745a 100644 --- a/kostal/kostaldiscovery.cpp +++ b/kostal/kostaldiscovery.cpp @@ -1,6 +1,6 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* Copyright 2013 - 2022, nymea GmbH +* Copyright 2013 - 2024, nymea GmbH * Contact: contact@nymea.io * * This file is part of nymea. @@ -44,23 +44,17 @@ void KostalDiscovery::startDiscovery() { qCInfo(dcKostal()) << "Discovery: Start searching for Kostal inverters in the network..."; NetworkDeviceDiscoveryReply *discoveryReply = m_networkDeviceDiscovery->discover(); + m_startDateTime = QDateTime::currentDateTime(); // Imedialty check any new device gets discovered - connect(discoveryReply, &NetworkDeviceDiscoveryReply::networkDeviceInfoAdded, this, &KostalDiscovery::checkNetworkDevice); + connect(discoveryReply, &NetworkDeviceDiscoveryReply::hostAddressDiscovered, this, &KostalDiscovery::checkNetworkDevice); // Check what might be left on finished - connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, discoveryReply, &NetworkDeviceDiscoveryReply::deleteLater); + connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, discoveryReply, &NetworkDeviceDiscoveryReply::deleteLater); connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){ qCDebug(dcKostal()) << "Discovery: Network discovery finished. Found" << discoveryReply->networkDeviceInfos().count() << "network devices"; m_networkDeviceInfos = discoveryReply->networkDeviceInfos(); - // Send a report request to nework device info not sent already... - foreach (const NetworkDeviceInfo &networkDeviceInfo, m_networkDeviceInfos) { - if (!m_verifiedNetworkDeviceInfos.contains(networkDeviceInfo)) { - checkNetworkDevice(networkDeviceInfo); - } - } - // Give the last connections added right before the network discovery finished a chance to check the device... QTimer::singleShot(3000, this, [this](){ qCDebug(dcKostal()) << "Discovery: Grace period timer triggered."; @@ -74,19 +68,15 @@ QList KostalDiscovery::discoveryResults( return m_discoveryResults; } -void KostalDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo) +void KostalDiscovery::checkNetworkDevice(const QHostAddress &address) { // Create a kostal connection and try to initialize it. // Only if initialized successfully and all information have been fetched correctly from // the device we can assume this is what we are locking for (ip, port, modbus address, correct registers). // We cloud tough also filter the result only for certain software versions, manufactueres or whatever... - if (m_verifiedNetworkDeviceInfos.contains(networkDeviceInfo)) - return; - - KostalModbusTcpConnection *connection = new KostalModbusTcpConnection(networkDeviceInfo.address(), m_port, m_modbusAddress, this); + KostalModbusTcpConnection *connection = new KostalModbusTcpConnection(address, m_port, m_modbusAddress, this); m_connections.append(connection); - m_verifiedNetworkDeviceInfos.append(networkDeviceInfo); connect(connection, &KostalModbusTcpConnection::reachableChanged, this, [=](bool reachable){ if (!reachable) { @@ -98,7 +88,7 @@ void KostalDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDeviceI // Modbus TCP connected...ok, let's try to initialize it! connect(connection, &KostalModbusTcpConnection::initializationFinished, this, [=](bool success){ if (!success) { - qCDebug(dcKostal()) << "Discovery: Initialization failed on" << networkDeviceInfo.address().toString() << "Continue...";; + qCDebug(dcKostal()) << "Discovery: Initialization failed on" << address.toString() << "Continue...";; cleanupConnection(connection); return; } @@ -110,7 +100,7 @@ void KostalDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDeviceI result.articleNumber = connection->inverterArticleNumber(); result.softwareVersionIoController = connection->softwareVersionIoController(); result.softwareVersionMainController = connection->softwareVersionMainController(); - result.networkDeviceInfo = networkDeviceInfo; + result.address = address; m_discoveryResults.append(result); qCDebug(dcKostal()) << "Discovery: --> Found" << result.manufacturerName << result.productName @@ -118,7 +108,7 @@ void KostalDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDeviceI << "Serial number:" << result.serialNumber << "Software version main controller:" << result.softwareVersionMainController << "Software version IO controller:" << result.softwareVersionIoController - << result.networkDeviceInfo; + << result.address.toString(); // Done with this connection @@ -127,7 +117,7 @@ void KostalDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDeviceI // Initializing... if (!connection->initialize()) { - qCDebug(dcKostal()) << "Discovery: Unable to initialize connection on" << networkDeviceInfo.address().toString() << "Continue...";; + qCDebug(dcKostal()) << "Discovery: Unable to initialize connection on" << address.toString() << "Continue...";; cleanupConnection(connection); } }); @@ -135,14 +125,14 @@ void KostalDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDeviceI // If we get any error...skip this host... connect(connection->modbusTcpMaster(), &ModbusTcpMaster::connectionErrorOccurred, this, [=](QModbusDevice::Error error){ if (error != QModbusDevice::NoError) { - qCDebug(dcKostal()) << "Discovery: Connection error on" << networkDeviceInfo.address().toString() << "Continue...";; + qCDebug(dcKostal()) << "Discovery: Connection error on" << address.toString() << "Continue...";; cleanupConnection(connection); } }); // If check reachability failed...skip this host... connect(connection, &KostalModbusTcpConnection::checkReachabilityFailed, this, [=](){ - qCDebug(dcKostal()) << "Discovery: Check reachability failed on" << networkDeviceInfo.address().toString() << "Continue...";; + qCDebug(dcKostal()) << "Discovery: Check reachability failed on" << address.toString() << "Continue...";; cleanupConnection(connection); }); @@ -161,11 +151,15 @@ void KostalDiscovery::finishDiscovery() { qint64 durationMilliSeconds = QDateTime::currentMSecsSinceEpoch() - m_startDateTime.toMSecsSinceEpoch(); + // Fill in all network device infos we have + for (int i = 0; i < m_discoveryResults.count(); i++) + m_discoveryResults[i].networkDeviceInfo = m_networkDeviceInfos.get(m_discoveryResults.at(i).address); + // Cleanup any leftovers...we don't care any more foreach (KostalModbusTcpConnection *connection, m_connections) cleanupConnection(connection); - qCInfo(dcKostal()) << "Discovery: Finished the discovery process. Found" << m_discoveryResults.count() << "Kostal Inverters in" << QTime::fromMSecsSinceStartOfDay(durationMilliSeconds).toString("mm:ss.zzz"); - + qCInfo(dcKostal()) << "Discovery: Finished the discovery process. Found" << m_discoveryResults.count() + << "Kostal Inverters in" << QTime::fromMSecsSinceStartOfDay(durationMilliSeconds).toString("mm:ss.zzz"); emit discoveryFinished(); } diff --git a/kostal/kostaldiscovery.h b/kostal/kostaldiscovery.h index 26ef3c3..172c09c 100644 --- a/kostal/kostaldiscovery.h +++ b/kostal/kostaldiscovery.h @@ -1,6 +1,6 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* Copyright 2013 - 2022, nymea GmbH +* Copyright 2013 - 2024, nymea GmbH * Contact: contact@nymea.io * * This file is part of nymea. @@ -50,6 +50,7 @@ public: QString articleNumber; QString softwareVersionMainController; QString softwareVersionIoController; + QHostAddress address; NetworkDeviceInfo networkDeviceInfo; } KostalDiscoveryResult; @@ -64,17 +65,14 @@ private: NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr; quint16 m_port; quint16 m_modbusAddress; - QDateTime m_startDateTime; NetworkDeviceInfos m_networkDeviceInfos; - NetworkDeviceInfos m_verifiedNetworkDeviceInfos; QList m_connections; - QList m_discoveryResults; - void checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo); + void checkNetworkDevice(const QHostAddress &address); void cleanupConnection(KostalModbusTcpConnection *connection); void finishDiscovery();