Add login during setup for SMA inverters

master
Simon Stürz 2022-02-18 08:52:38 +01:00
parent d1eaeb62e6
commit 76437b34c1
5 changed files with 91 additions and 17 deletions

View File

@ -177,6 +177,32 @@ void IntegrationPluginSma::discoverThings(ThingDiscoveryInfo *info)
}
}
void IntegrationPluginSma::startPairing(ThingPairingInfo *info)
{
info->finish(Thing::ThingErrorNoError, QT_TR_NOOP("Please enter the password of your inverter. If no password has been explicitly set, leave it empty to use the default password for SMA inverters."));
}
void IntegrationPluginSma::confirmPairing(ThingPairingInfo *info, const QString &username, const QString &secret)
{
Q_UNUSED(username)
// Init with the default password
QString password = "0000";
if (!secret.isEmpty()) {
qCDebug(dcSma()) << "Pairing: Using password" << secret;
password = secret;
} else {
qCDebug(dcSma()) << "Pairing: Secret is empty. Using default password" << password;
}
// Just store details, we'll test the login in setupDevice
pluginStorage()->beginGroup(info->thingId().toString());
pluginStorage()->setValue("password", password);
pluginStorage()->endGroup();
info->finish(Thing::ThingErrorNoError);
}
void IntegrationPluginSma::setupThing(ThingSetupInfo *info)
{
Thing *thing = info->thing();
@ -213,7 +239,9 @@ void IntegrationPluginSma::setupThing(ThingSetupInfo *info)
connect(sunnyWebBox, &SunnyWebBox::plantOverviewReceived, this, &IntegrationPluginSma::onPlantOverviewReceived);
m_sunnyWebBoxes.insert(info->thing(), sunnyWebBox);
});
} else if (thing->thingClassId() == speedwireMeterThingClassId) {
QHostAddress address = QHostAddress(thing->paramValue(speedwireMeterThingHostParamTypeId).toString());
quint32 serialNumber = static_cast<quint32>(thing->paramValue(speedwireMeterThingSerialNumberParamTypeId).toUInt());
quint16 modelId = static_cast<quint16>(thing->paramValue(speedwireMeterThingModelIdParamTypeId).toUInt());
@ -258,7 +286,9 @@ void IntegrationPluginSma::setupThing(ThingSetupInfo *info)
m_speedwireMeters.insert(thing, meter);
info->finish(Thing::ThingErrorNoError);
} else if (thing->thingClassId() == speedwireInverterThingClassId) {
QHostAddress address = QHostAddress(thing->paramValue(speedwireInverterThingHostParamTypeId).toString());
quint32 serialNumber = static_cast<quint32>(thing->paramValue(speedwireInverterThingSerialNumberParamTypeId).toUInt());
quint16 modelId = static_cast<quint16>(thing->paramValue(speedwireInverterThingModelIdParamTypeId).toUInt());
@ -276,6 +306,31 @@ void IntegrationPluginSma::setupThing(ThingSetupInfo *info)
qCDebug(dcSma()) << "Inverter: Interface initialized successfully.";
QString password;
pluginStorage()->beginGroup(info->thing()->id().toString());
password = pluginStorage()->value("password", "0000").toString();
pluginStorage()->endGroup();
// Connection exists only as long info exists
connect(inverter, &SpeedwireInverter::loginFinished, info, [=](bool success){
if (!success) {
qCWarning(dcSma()) << "Failed to set up inverter. Wrong password.";
delete inverter;
info->finish(Thing::ThingErrorAuthenticationFailure, QT_TR_NOOP("Failed to log in with the given password. Please try again."));
return;
}
qCDebug(dcSma()) << "Inverter set up successfully.";
m_speedwireInverters.insert(thing, inverter);
info->finish(Thing::ThingErrorNoError);
// Note: the data is already refreshing here
});
// Make sure an aborted setup will clean up the object
connect(info, &ThingSetupInfo::aborted, inverter, &SpeedwireInverter::deleteLater);
// Runtime connections
connect(inverter, &SpeedwireInverter::reachableChanged, this, [=](bool reachable){
thing->setStateValue(speedwireInverterConnectedStateTypeId, reachable);
});
@ -296,14 +351,11 @@ void IntegrationPluginSma::setupThing(ThingSetupInfo *info)
thing->setStateValue(speedwireInverterCurrentPowerMpp1StateTypeId, inverter->powerDcMpp1());
thing->setStateValue(speedwireInverterCurrentPowerMpp2StateTypeId, inverter->powerDcMpp2());
});
m_speedwireInverters.insert(thing, inverter);
info->finish(Thing::ThingErrorNoError);
qCDebug(dcSma()) << "Inverter: Start connecting using password" << password;
inverter->startConnecting(password);
// Initial refresh data
inverter->refresh();
} else {
Q_ASSERT_X(false, "setupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8());
}

View File

@ -48,6 +48,10 @@ public:
void init() override;
void discoverThings(ThingDiscoveryInfo *info) override;
void startPairing(ThingPairingInfo *info) override;
void confirmPairing(ThingPairingInfo *info, const QString &username, const QString &secret) override;
void setupThing(ThingSetupInfo *info) override;
void postSetupThing(Thing *thing) override;
void thingRemoved(Thing *thing) override;

View File

@ -315,6 +315,7 @@
"name": "speedwireInverter",
"displayName": "SMA Inverter",
"createMethods": ["discovery", "user"],
"setupMethod": "EnterPin",
"interfaces": [ "solarinverter" ],
"paramTypes": [
{

View File

@ -1,6 +1,6 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2021, nymea GmbH
* Copyright 2013 - 2022, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
@ -39,11 +39,9 @@ SpeedwireInverter::SpeedwireInverter(const QHostAddress &address, quint16 modelI
m_modelId(modelId),
m_serialNumber(serialNumber)
{
qCDebug(dcSma()) << "Inverter: setup interface on" << m_address.toString();
m_interface = new SpeedwireInterface(m_address, false, this);
connect(m_interface, &SpeedwireInterface::dataReceived, this, &SpeedwireInverter::processData);
}
bool SpeedwireInverter::initialize()
@ -197,7 +195,7 @@ SpeedwireInverterReply *SpeedwireInverter::sendLoginRequest(const QString &passw
encodedPassword[i] = (passwordData.at(i) + (loginAsUser ? 0x88 : 0xBB) % 0xff);
}
// Add endoced password
// Add encoded password
for (int i = 0; i < encodedPassword.count(); i++) {
stream << static_cast<quint8>(encodedPassword.at(i));
}
@ -328,6 +326,12 @@ SpeedwireInverterReply *SpeedwireInverter::sendDeviceTypeRequest()
return createReply(request);
}
void SpeedwireInverter::startConnecting(const QString &password)
{
m_password = password;
refresh();
}
void SpeedwireInverter::refresh()
{
// Only refresh if not already busy...
@ -1082,10 +1086,16 @@ void SpeedwireInverter::setState(State state)
if (reply->error() != SpeedwireInverterReply::ErrorNoError) {
if (reply->error() == SpeedwireInverterReply::ErrorTimeout) {
qCWarning(dcSma()) << "Inverter: Failed to query data from inverter:" << reply->request().command() << reply->error();
// TODO: try to send identify request and retry 3 times before giving up,
// still need to figure out why the inverter stops responding sometimes and how we can
// make it communicative again, a reconfugre always fixes this issue...somehow...
setState(StateDisconnected);
return;
}
// Reachable, but received an inverter error, probably not logged
if (reply->error() == SpeedwireInverterReply::ErrorInverterError) {
qCDebug(dcSma()) << "Inverter: Query data request finished with inverter error. Try to login...";
setState(StateLogin);
@ -1093,10 +1103,14 @@ void SpeedwireInverter::setState(State state)
}
}
// We where able to read data...emit the signal for the setup just incase
emit loginFinished(true);
qCDebug(dcSma()) << "Inverter: Query request finished successfully" << reply->request().command();
processAcPowerResponse(reply->responseData());
if (deviceInformationFetched) {
if (m_deviceInformationFetched) {
setState(StateQueryData);
} else {
setState(StateGetInformation);
@ -1104,18 +1118,18 @@ void SpeedwireInverter::setState(State state)
});
break;
}
case StateLogin: {
setState(StateLogin);
SpeedwireInverterReply *loginReply = sendLoginRequest();
SpeedwireInverterReply *loginReply = sendLoginRequest(m_password);
connect(loginReply, &SpeedwireInverterReply::finished, this, [=](){
if (loginReply->error() != SpeedwireInverterReply::ErrorNoError) {
qCWarning(dcSma()) << "Inverter: Failed to login to inverter:" << loginReply->error();
emit loginFinished(false);
setState(StateDisconnected);
return;
}
qCDebug(dcSma()) << "Inverter: Login request finished successfully.";
emit loginFinished(true);
setReachable(true);
// Logged in successfully, reinit the data fetch process
@ -1145,7 +1159,7 @@ void SpeedwireInverter::setState(State state)
qCDebug(dcSma()) << "Inverter: Get device information finished successfully.";
processDeviceTypeResponse(deviceTypeReply->responsePayload());
deviceInformationFetched = true;
m_deviceInformationFetched = true;
setState(StateQueryData);
});
});

View File

@ -1,6 +1,6 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2021, nymea GmbH
* Copyright 2013 - 2022, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
@ -101,19 +101,22 @@ public:
SpeedwireInverterReply *sendDeviceTypeRequest();
// Start connecting
void startConnecting();
void startConnecting(const QString &password = "0000");
public slots:
void refresh();
signals:
void reachableChanged(bool reachable);
void loginFinished(bool success);
void stateChanged(State state);
void valuesUpdated();
private:
SpeedwireInterface *m_interface = nullptr;
QHostAddress m_address;
QString m_password;
bool m_initialized = false;
quint16 m_modelId = 0;
quint32 m_serialNumber = 0;
@ -122,7 +125,7 @@ private:
State m_state = StateDisconnected;
quint16 m_packetId = 1;
bool deviceInformationFetched = false;
bool m_deviceInformationFetched = false;
SpeedwireInverterReply *m_currentReply = nullptr;
QQueue<SpeedwireInverterReply *> m_replyQueue;