added closable sensor and fixed authentication/connected state

This commit is contained in:
Boernsman 2020-10-23 17:33:35 +02:00
parent 5be1f64faf
commit baa087e988
5 changed files with 256 additions and 181 deletions

View File

@ -1,6 +1,6 @@
# Home Connect
Connects your Home Connect home appliences to nymea.
Connects your Home Connect home appliances to nymea.
## Supported Things

View File

@ -93,7 +93,7 @@ QUrl HomeConnect::getLoginUrl(const QUrl &redirectUrl, const QString &scope)
queryParams.addQueryItem("scope", scope);
queryParams.addQueryItem("state", QUuid::createUuid().toString());
queryParams.addQueryItem("nonce", QUuid::createUuid().toString());
m_codeChallenge = QUuid::createUuid().toString().remove('{').remove('}').remove("-");
m_codeChallenge = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
queryParams.addQueryItem("code_challenge", m_codeChallenge);
queryParams.addQueryItem("code_challenge_method", "plain");
url.setQuery(queryParams);
@ -107,19 +107,28 @@ void HomeConnect::onRefreshTimeout()
getAccessTokenFromRefreshToken(m_refreshToken);
}
bool HomeConnect::checkStatusCode(int status, const QByteArray &payload)
bool HomeConnect::checkStatusCode(QNetworkReply *reply, const QByteArray &rawData)
{
//TODO emit (dis)connected, (un)authenticated,
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(payload, &error);
if (error.error != QJsonParseError::NoError) {
qCWarning(dcHomeConnect()) << "Received invalide JSON object" << payload;
qCWarning(dcHomeConnect()) << "Status" << status;
// Check for the internet connection
if (reply->error() == QNetworkReply::NetworkError::HostNotFoundError ||
reply->error() == QNetworkReply::NetworkError::UnknownNetworkError ||
reply->error() == QNetworkReply::NetworkError::TemporaryNetworkFailureError) {
qCWarning(dcHomeConnect()) << "Connection error" << reply->errorString();
setConnected(false);
setAuthenticated(false);
return false;
} else {
setConnected(true);
}
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(rawData, &error);
switch (status){
case 400:
case 200: //The request was successful. Typically returned for successful GET requests.
case 204: //The request was successful. Typically returned for successful PUT/DELETE requests with no payload.
break;
case 400: //Error occurred (e.g. validation error - value is out of range)
if(!jsonDoc.toVariant().toMap().contains("error")) {
if(jsonDoc.toVariant().toMap().value("error").toString() == "invalid_client") {
qWarning(dcHomeConnect()) << "Client token provided doesnt correspond to client that generated auth code.";
@ -131,16 +140,54 @@ bool HomeConnect::checkStatusCode(int status, const QByteArray &payload)
qWarning(dcHomeConnect()) << "Expired authorization code.";
}
}
setAuthenticated(false);
return false;
case 401:
qWarning(dcHomeConnect()) << "Client does not have permission to use this API.";
setAuthenticated(false);
return false;
case 403:
qCWarning(dcHomeConnect()) << "Forbidden, Scope has not been granted or home appliance is not assigned to HC account";
setAuthenticated(false);
return false;
case 404:
qCWarning(dcHomeConnect()) << "Not Found. This resource is not available (e.g. no images on washing machine)";
return false;
case 405:
qWarning(dcHomeConnect()) << "Wrong HTTP method used.";
setAuthenticated(false);
return false;
case 408:
qCWarning(dcHomeConnect())<< "Request Timeout, API Server failed to produce an answer or has no connection to backend service";
return false;
case 409:
qCWarning(dcHomeConnect()) << "Conflict - Command/Query cannot be executed for the home appliance, the error response contains the error details";
qCWarning(dcHomeConnect()) << "Error" << jsonDoc.toVariant().toMap().value("error").toString();
return false;
case 415:
qCWarning(dcHomeConnect())<< "Unsupported Media Type. The request's Content-Type is not supported";
return false;
case 429:
qCWarning(dcHomeConnect())<< "Too Many Requests, the number of requests for a specific endpoint exceeded the quota of the client";
return false;
case 500:
qCWarning(dcHomeConnect())<< "Internal Server Error, in case of a server configuration error or any errors in resource files";
return false;
case 503:
qCWarning(dcHomeConnect())<< "Service Unavailable,if a required backend service is not available";
return false;
default:
break;
}
if (error.error != QJsonParseError::NoError) {
qCWarning(dcHomeConnect()) << "Received invalide JSON object" << rawData;
qCWarning(dcHomeConnect()) << "Status" << status;
setAuthenticated(false);
return false;
}
setAuthenticated(true);
return true;
}
@ -148,7 +195,7 @@ void HomeConnect::getAccessTokenFromRefreshToken(const QByteArray &refreshToken)
{
if (refreshToken.isEmpty()) {
qWarning(dcHomeConnect) << "No refresh token given!";
emit authenticationStatusChanged(false);
setAuthenticated(false);
return;
}
@ -166,37 +213,31 @@ void HomeConnect::getAccessTokenFromRefreshToken(const QByteArray &refreshToken)
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, [this, reply](){
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (status != 200 || reply->error() != QNetworkReply::NoError) {
qWarning(dcHomeConnect()) << "Access token error:" << reply->errorString() << reply->readAll();
emit authenticationStatusChanged(false);
return;
}
QJsonParseError error;
QJsonDocument data = QJsonDocument::fromJson(reply->readAll(), &error);
if (error.error != QJsonParseError::NoError) {
emit authenticationStatusChanged(false);
qCDebug(dcHomeConnect()) << "Received invalide JSON object" << data.toJson();
QByteArray rawData = reply->readAll();
if (!checkStatusCode(reply, rawData)) {
return;
}
QJsonDocument data = QJsonDocument::fromJson(rawData);
if(!data.toVariant().toMap().contains("access_token")) {
emit authenticationStatusChanged(false);
setAuthenticated(false);
return;
}
m_accessToken = data.toVariant().toMap().value("access_token").toByteArray();
if (data.toVariant().toMap().contains("expires_in")) {
int expireTime = data.toVariant().toMap().value("expires_in").toInt();
qCDebug(dcHomeConnect) << "Access token expires at" << QDateTime::currentDateTime().addSecs(expireTime).toString();
qCDebug(dcHomeConnect) << "Access token expires int" << expireTime << "s, at" << QDateTime::currentDateTime().addSecs(expireTime).toString();
if (!m_tokenRefreshTimer) {
qWarning(dcHomeConnect()) << "Access token refresh timer not initialized";
return;
}
if (expireTime < 20) {
qCWarning(dcHomeConnect()) << "Expire time too short";
return;
}
m_tokenRefreshTimer->start((expireTime - 20) * 1000);
}
emit authenticationStatusChanged(true);;
});
}
@ -227,15 +268,14 @@ void HomeConnect::getAccessTokenFromAuthorizationCode(const QByteArray &authoriz
QNetworkReply *reply = m_networkManager->post(request, query.toString().toUtf8());
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, [this, reply](){
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (status != 200 || reply->error() != QNetworkReply::NoError) {
qWarning(dcHomeConnect()) << reply->errorString() << status << reply->readAll();
emit authenticationStatusChanged(false);
QByteArray rawData = reply->readAll();
if (!checkStatusCode(reply, rawData)) {
return;
}
QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll());
QJsonDocument jsonDoc = QJsonDocument::fromJson(rawData);
if(!jsonDoc.toVariant().toMap().contains("access_token") || !jsonDoc.toVariant().toMap().contains("refresh_token") ) {
emit authenticationStatusChanged(false);
setAuthenticated(false);
return;
}
m_accessToken = jsonDoc.toVariant().toMap().value("access_token").toByteArray();
@ -243,15 +283,18 @@ void HomeConnect::getAccessTokenFromAuthorizationCode(const QByteArray &authoriz
if (jsonDoc.toVariant().toMap().contains("expires_in")) {
int expireTime = jsonDoc.toVariant().toMap().value("expires_in").toInt();
qCDebug(dcHomeConnect()) << "Token expires at" << QDateTime::currentDateTime().addSecs(expireTime).toString();
qCDebug(dcHomeConnect()) << "Token expires in" << expireTime << "s, at" << QDateTime::currentDateTime().addSecs(expireTime).toString();
if (!m_tokenRefreshTimer) {
qWarning(dcHomeConnect()) << "Token refresh timer not initialized";
emit authenticationStatusChanged(false);
setAuthenticated(false);
return;
}
if (expireTime < 20) {
qCWarning(dcHomeConnect()) << "Expire time too short";
return;
}
m_tokenRefreshTimer->start((expireTime - 20) * 1000);
}
emit authenticationStatusChanged(true);
});
}
@ -267,9 +310,10 @@ void HomeConnect::getHomeAppliances()
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, [this, reply](){
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QByteArray rawData = reply->readAll();
checkStatusCode(status, rawData);
if (!checkStatusCode(reply, rawData)) {
return;
}
QVariantMap dataMap = QJsonDocument::fromJson(rawData).toVariant().toMap().value("data").toMap();
QList<HomeAppliance> appliances;
@ -304,9 +348,10 @@ void HomeConnect::getPrograms(const QString &haId)
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, [this, haId, reply]{
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QByteArray rawData = reply->readAll();
checkStatusCode(status, rawData);
if (!checkStatusCode(reply, rawData)) {
return;
}
QVariantMap dataMap = QJsonDocument::fromJson(rawData).toVariant().toMap().value("data").toMap();
QVariantList programList = dataMap.value("programs").toList();
@ -334,9 +379,10 @@ void HomeConnect::getProgramsAvailable(const QString &haId)
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, [this, haId, reply]{
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QByteArray rawData = reply->readAll();
checkStatusCode(status, rawData);
if (!checkStatusCode(reply, rawData)) {
return;
}
QVariantMap dataMap = QJsonDocument::fromJson(rawData).toVariant().toMap().value("data").toMap();
QVariantList programList = dataMap.value("programs").toList();
@ -364,9 +410,10 @@ void HomeConnect::getProgramsActive(const QString &haId)
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, [this, haId, reply]{
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QByteArray rawData = reply->readAll();
checkStatusCode(status, rawData);
if (!checkStatusCode(reply, rawData)) {
return;
}
QVariantMap map = QJsonDocument::fromJson(rawData).toVariant().toMap();
QHash<QString, QVariant> options;
@ -396,9 +443,10 @@ void HomeConnect::getProgramsSelected(const QString &haId)
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, [this, haId, reply]{
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QByteArray rawData = reply->readAll();
checkStatusCode(status, rawData);
if (!checkStatusCode(reply, rawData)) {
return;
}
QVariantMap map = QJsonDocument::fromJson(rawData).toVariant().toMap();
QHash<QString, QVariant> options;
@ -428,10 +476,10 @@ void HomeConnect::getProgramsActiveOption(const QString &haId, const QString &op
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, [this, reply]{
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QByteArray rawData = reply->readAll();
checkStatusCode(status, rawData);
if (!checkStatusCode(reply, rawData)) {
return;
}
QVariantMap dataMap = QJsonDocument::fromJson(rawData).toVariant().toMap().value("data").toMap();
qCDebug(dcHomeConnect()) << "key" << dataMap.value("key").toString() << "value" << dataMap.value("value").toString() << dataMap.value("unit").toString();
});
@ -638,9 +686,10 @@ void HomeConnect::getStatus(const QString &haid)
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, [this, haid, reply]{
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QByteArray rawData = reply->readAll();
checkStatusCode(status, rawData);
if (!checkStatusCode(reply, rawData)) {
return;
}
QVariantMap dataMap = QJsonDocument::fromJson(rawData).toVariant().toMap().value("data").toMap();
QHash<QString, QVariant> statusList;
@ -676,9 +725,10 @@ void HomeConnect::getSettings(const QString &haid)
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, [this, haid, reply]{
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QByteArray rawData = reply->readAll();
checkStatusCode(status, rawData);
if (!checkStatusCode(reply, rawData)) {
return;
}
QVariantMap dataMap = QJsonDocument::fromJson(rawData).toVariant().toMap().value("data").toMap();
QVariantList settingsList = dataMap.value("settings").toList();
QHash<QString, QVariant> settings;
@ -800,21 +850,35 @@ QUuid HomeConnect::sendCommand(const QString &haid, const QString &command)
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, this, [this, commandId, reply]{
//TODO check status
QJsonParseError error;
QJsonDocument data = QJsonDocument::fromJson(reply->readAll(), &error);
if (error.error != QJsonParseError::NoError) {
qCDebug(dcHomeConnect()) << "Send command: Received invalide JSON object";
QByteArray rawData = reply->readAll();
if (!checkStatusCode(reply, rawData)) {
return;
}
qCDebug(dcHomeConnect()) << "Send command" << data.toJson();
if (data.toVariant().toMap().contains("data")) {
QVariantMap dataMap = data.toVariant().toMap().value("data").toMap();
QVariantMap map = QJsonDocument::fromJson(rawData).toVariant().toMap();
qCDebug(dcHomeConnect()) << "Send command" << map;
if (map.contains("data")) {
QVariantMap dataMap = map.value("data").toMap();
qCDebug(dcHomeConnect()) << "key" << dataMap.value("key").toString() << "value" << dataMap.value("value").toString() << dataMap.value("unit").toString();
} else if (data.toVariant().toMap().contains("error")) {
qCWarning(dcHomeConnect()) << "Send command" << data.toVariant().toMap().value("error").toMap().value("description").toString();
} else if (map.contains("error")) {
qCWarning(dcHomeConnect()) << "Send command" << map.value("error").toMap().value("description").toString();
}
emit commandExecuted(commandId, true);
});
return commandId;
}
void HomeConnect::setAuthenticated(bool state)
{
if (state != m_authenticated) {
m_authenticated = state;
emit authenticationStatusChanged(state);
}
}
void HomeConnect::setConnected(bool state)
{
if (state != m_connected) {
m_connected = state;
emit connectionChanged(state);
}
}

View File

@ -159,7 +159,13 @@ private:
NetworkAccessManager *m_networkManager = nullptr;
QTimer *m_tokenRefreshTimer = nullptr;
bool checkStatusCode(int status, const QByteArray &payload);
void setAuthenticated(bool state);
void setConnected(bool state);
bool m_authenticated = false;
bool m_connected = false;
bool checkStatusCode(QNetworkReply *reply, const QByteArray &rawData);
private slots:
void onRefreshTimeout();

View File

@ -77,11 +77,11 @@ IntegrationPluginHomeConnect::IntegrationPluginHomeConnect()
m_remoteControlActivationStateTypeIds.insert(dishwasherThingClassId, dishwasherRemoteControlActivationStateStateTypeId);
m_remoteControlActivationStateTypeIds.insert(washerThingClassId, washerRemoteControlActivationStateStateTypeId);
m_doorStateTypeIds.insert(dishwasherThingClassId, dishwasherDoorStateStateTypeId);
m_doorStateTypeIds.insert(washerThingClassId, washerDoorStateStateTypeId);
m_doorStateTypeIds.insert(dryerThingClassId, dryerDoorStateStateTypeId);
m_doorStateTypeIds.insert(ovenThingClassId, ovenDoorStateStateTypeId);
m_doorStateTypeIds.insert(coffeeMakerThingClassId, coffeeMakerDoorStateStateTypeId);
m_doorStateTypeIds.insert(dishwasherThingClassId, dishwasherClosedStateTypeId);
m_doorStateTypeIds.insert(washerThingClassId, washerClosedStateTypeId);
m_doorStateTypeIds.insert(dryerThingClassId, dryerClosedStateTypeId);
m_doorStateTypeIds.insert(ovenThingClassId, ovenClosedStateTypeId);
m_doorStateTypeIds.insert(coffeeMakerThingClassId, coffeeMakerClosedStateTypeId);
m_operationStateTypeIds.insert(ovenThingClassId, ovenOperationStateStateTypeId);
m_operationStateTypeIds.insert(dryerThingClassId, dryerOperationStateStateTypeId);
@ -140,21 +140,29 @@ IntegrationPluginHomeConnect::IntegrationPluginHomeConnect()
void IntegrationPluginHomeConnect::startPairing(ThingPairingInfo *info)
{
if (info->thingClassId() == homeConnectConnectionThingClassId) {
if (info->thingClassId() == homeConnectAccountThingClassId) {
bool simulationMode = configValue(homeConnectPluginSimulationModeParamTypeId).toBool();
bool controlEnabled = configValue(homeConnectPluginControlEnabledParamTypeId).toBool();
QByteArray clientKey = apiKeyStorage()->requestKey("homeconnect").data("clientKey");
QByteArray clientSecret = apiKeyStorage()->requestKey("homeconnect").data("clientSecret");
QByteArray clientKey = configValue(homeConnectPluginCustomClientKeyParamTypeId).toByteArray();
QByteArray clientSecret = configValue(homeConnectPluginCustomClientSecretParamTypeId).toByteArray();
if (clientKey.isEmpty() || clientSecret.isEmpty()) {
clientKey = apiKeyStorage()->requestKey("homeconnect").data("clientKey");
clientSecret = apiKeyStorage()->requestKey("homeconnect").data("clientSecret");
}
if (clientKey.isEmpty() || clientSecret.isEmpty()) {
info->finish(Thing::ThingErrorAuthenticationFailure, tr("Client key and/or seceret is not available."));
return;
}
HomeConnect *homeConnect = new HomeConnect(hardwareManager()->networkManager(), clientKey, clientSecret, simulationMode, this);
QString scope = "IdentifyAppliance Monitor Settings Dishwasher Washer Dryer WasherDryer Refrigerator Freezer WineCooler CoffeeMaker Hood CookProcessor";
if (controlEnabled)
scope.append(" Control");
QUrl url = homeConnect->getLoginUrl(QUrl("https://127.0.0.1:8888"), scope);
qCDebug(dcHomeConnect()) << "HomeConnect url:" << url;
m_setupHomeConnectConnections.insert(info->thingId(), homeConnect);
info->setOAuthUrl(url);
info->finish(Thing::ThingErrorNoError);
m_setupHomeConnectConnections.insert(info->thingId(), homeConnect);
} else {
qCWarning(dcHomeConnect()) << "Unhandled pairing metod!";
info->finish(Thing::ThingErrorCreationMethodNotSupported);
@ -165,7 +173,7 @@ void IntegrationPluginHomeConnect::confirmPairing(ThingPairingInfo *info, const
{
Q_UNUSED(username);
if (info->thingClassId() == homeConnectConnectionThingClassId) {
if (info->thingClassId() == homeConnectAccountThingClassId) {
qCDebug(dcHomeConnect()) << "Redirect url is" << secret;
QUrl url(secret);
QUrlQuery query(url);
@ -175,7 +183,6 @@ void IntegrationPluginHomeConnect::confirmPairing(ThingPairingInfo *info, const
if (!homeConnect) {
qWarning(dcHomeConnect()) << "No HomeConnect connection found for device:" << info->thingName();
m_setupHomeConnectConnections.remove(info->thingId());
homeConnect->deleteLater();
info->finish(Thing::ThingErrorHardwareFailure);
return;
}
@ -211,7 +218,7 @@ void IntegrationPluginHomeConnect::setupThing(ThingSetupInfo *info)
{
Thing *thing = info->thing();
if (thing->thingClassId() == homeConnectConnectionThingClassId) {
if (thing->thingClassId() == homeConnectAccountThingClassId) {
bool simulationMode = configValue(homeConnectPluginSimulationModeParamTypeId).toBool();
HomeConnect *homeConnect;
if (m_setupHomeConnectConnections.keys().contains(thing->id())) {
@ -225,8 +232,20 @@ void IntegrationPluginHomeConnect::setupThing(ThingSetupInfo *info)
pluginStorage()->beginGroup(thing->id().toString());
QByteArray refreshToken = pluginStorage()->value("refresh_token").toByteArray();
pluginStorage()->endGroup();
QByteArray clientKey = apiKeyStorage()->requestKey("homeconnect").data("clientKey");
QByteArray clientSecret = apiKeyStorage()->requestKey("homeconnect").data("clientSecret");
if (refreshToken.isEmpty()) {
info->finish(Thing::ThingErrorAuthenticationFailure, tr("Refresh token is not available."));
return;
}
QByteArray clientKey = configValue(homeConnectPluginCustomClientKeyParamTypeId).toByteArray();
QByteArray clientSecret = configValue(homeConnectPluginCustomClientSecretParamTypeId).toByteArray();
if (clientKey.isEmpty() || clientSecret.isEmpty()) {
clientKey = apiKeyStorage()->requestKey("homeconnect").data("clientKey");
clientSecret = apiKeyStorage()->requestKey("homeconnect").data("clientSecret");
}
if (clientKey.isEmpty() || clientSecret.isEmpty()) {
info->finish(Thing::ThingErrorAuthenticationFailure, tr("Client key and/or seceret is not available."));
return;
}
homeConnect = new HomeConnect(hardwareManager()->networkManager(), clientKey, clientSecret, simulationMode, this);
homeConnect->getAccessTokenFromRefreshToken(refreshToken);
m_asyncSetup.insert(homeConnect, info);
@ -269,14 +288,14 @@ void IntegrationPluginHomeConnect::postSetupThing(Thing *thing)
if (!m_pluginTimer60sec) {
m_pluginTimer60sec = hardwareManager()->pluginTimerManager()->registerTimer(60);
connect(m_pluginTimer60sec, &PluginTimer::timeout, this, [this]() {
foreach (Thing *thing, myThings().filterByThingClassId(homeConnectConnectionThingClassId)) {
Q_FOREACH (Thing *thing, myThings().filterByThingClassId(homeConnectAccountThingClassId)) {
HomeConnect *homeConnect = m_homeConnectConnections.value(thing);
if (!homeConnect) {
qWarning(dcHomeConnect()) << "No HomeConnect account found for" << thing->name();
continue;
}
homeConnect->getHomeAppliances();
Q_FOREACH(Thing *childThing, myThings().filterByParentId(thing->id())) {
Q_FOREACH (Thing *childThing, myThings().filterByParentId(thing->id())) {
QString haId = childThing->paramValue(m_idParamTypeIds.value(childThing->thingClassId())).toString();
homeConnect->getStatus(haId);
homeConnect->getSettings(haId);
@ -286,12 +305,12 @@ void IntegrationPluginHomeConnect::postSetupThing(Thing *thing)
});
}
if (thing->thingClassId() == homeConnectConnectionThingClassId) {
if (thing->thingClassId() == homeConnectAccountThingClassId) {
HomeConnect *homeConnect = m_homeConnectConnections.value(thing);
homeConnect->getHomeAppliances();
homeConnect->connectEventStream();
thing->setStateValue(homeConnectConnectionConnectedStateTypeId, true);
thing->setStateValue(homeConnectConnectionLoggedInStateTypeId, true);
thing->setStateValue(homeConnectAccountConnectedStateTypeId, true);
thing->setStateValue(homeConnectAccountLoggedInStateTypeId, true);
//TBD Set user name
} else if ((thing->thingClassId() == dryerThingClassId) ||
(thing->thingClassId() == fridgeThingClassId) ||
@ -481,7 +500,7 @@ void IntegrationPluginHomeConnect::executeAction(ThingActionInfo *info)
void IntegrationPluginHomeConnect::thingRemoved(Thing *thing)
{
qCDebug(dcHomeConnect) << "Delete " << thing->name();
if (thing->thingClassId() == homeConnectConnectionThingClassId) {
if (thing->thingClassId() == homeConnectAccountThingClassId) {
m_homeConnectConnections.take(thing)->deleteLater();
} else {
m_selectedProgram.remove(thing);
@ -546,7 +565,6 @@ void IntegrationPluginHomeConnect::executeBrowserItem(BrowserActionInfo *info)
if (!homeConnect)
return;
QString haid = thing->paramValue(m_idParamTypeIds.value(thing->thingClassId())).toString();
QUuid requestId = homeConnect->selectProgram(haid, info->browserAction().itemId(), QList<HomeConnect::Option> ());
m_selectedProgram.insert(thing, info->browserAction().itemId());
@ -581,8 +599,8 @@ void IntegrationPluginHomeConnect::parseKey(Thing *thing, const QString &key, co
thing->setStateValue(ovenTargetTemperatureStateTypeId, value);
} else if (key == "BSH.Common.Option.Duration") {
thing->setStateValue(ovenDurationStateTypeId, value);
} else if (key == "Cooking.Oven.Option.FastPreHeat") {
} else if (key == "BSH.Common.Option.StartInRelative") {
//} else if (key == "Cooking.Oven.Option.FastPreHeat") {
//} else if (key == "BSH.Common.Option.StartInRelative") {
} else if (key == "LaundryCare.Washer.Option.Temperature") {
thing->setStateValue(washerTemperatureStateTypeId, value.toString().split('.').last()); // Cold, 20, 40, 60°C
} else if (key == "LaundryCare.Washer.Option.SpinSpeed") {
@ -620,14 +638,13 @@ void IntegrationPluginHomeConnect::parseKey(Thing *thing, const QString &key, co
qCDebug(dcHomeConnect()) << "Unkown Coffee temperature string" << temperature;
}
} else if (key == "Cooking.Common.Option.Hood.VentingLevel") {
//TODO
} else if (key == "Cooking.Common.Option.Hood.IntensiveLevel") {
//TODO
} else if (key == "ConsumerProducts.CleaningRobot.Option.ReferenceMapId") {
} else if (key == "ConsumerProducts.CleaningRobot.Option.CleaningMode") {
thing->setStateValue(hoodVentingLevelStateTypeId, value);
//} else if (key == "Cooking.Common.Option.Hood.IntensiveLevel") {
//} else if (key == "ConsumerProducts.CleaningRobot.Option.ReferenceMapId") {
//} else if (key == "ConsumerProducts.CleaningRobot.Option.CleaningMode") {
// Program Progress Changes
} else if (key == "BSH.Common.Option.ElapsedProgramTime") {
//} else if (key == "BSH.Common.Option.ElapsedProgramTime") {
} else if (key == "BSH.Common.Option.RemainingProgramTime") {
QString time = QDateTime::fromMSecsSinceEpoch(QDateTime::currentMSecsSinceEpoch()+(value.toInt()*1000)).time().toString();
thing->setStateValue(m_endTimerStateTypeIds.value(thing->thingClassId()), time);
@ -635,7 +652,7 @@ void IntegrationPluginHomeConnect::parseKey(Thing *thing, const QString &key, co
if (m_progressStateTypeIds.contains(thing->thingClassId())) {
thing->setStateValue(m_progressStateTypeIds.value(thing->thingClassId()), value);
}
} else if (key == "ConsumerProducts.CleaningRobot.Option.ProcessPhase") {
//} else if (key == "ConsumerProducts.CleaningRobot.Option.ProcessPhase") {
} else if (key == "BSH.Common.Status.OperationState") {
if (m_operationStateTypeIds.contains(thing->thingClassId())) {
QString operationState = value.toString();
@ -683,12 +700,11 @@ void IntegrationPluginHomeConnect::parseKey(Thing *thing, const QString &key, co
if (m_progressStateTypeIds.contains(thing->thingClassId())) {
thing->setStateValue(m_progressStateTypeIds.value(thing->thingClassId()), 0);
}
} else if (key == "BSH.Common.Event.AlarmClockElapsed") {
//} else if (key == "BSH.Common.Event.AlarmClockElapsed") {
} else if (key == "Cooking.Oven.Event.PreheatFinished") {
emitEvent(Event(ovenPreheatFinishedEventTypeId, thing->id()));
// Home Appliance State Changes
} else if (key == "BSH.Common.Setting.PowerState") {
//Ignore
//} else if (key == "BSH.Common.Setting.PowerState") {
} else if (key == "BSH.Common.Status.RemoteControlActive") {
if (m_remoteControlActivationStateTypeIds.contains(thing->thingClassId())) {
thing->setStateValue(m_remoteControlActivationStateTypeIds.value(thing->thingClassId()), value);
@ -703,7 +719,7 @@ void IntegrationPluginHomeConnect::parseKey(Thing *thing, const QString &key, co
}
} else if (key == "BSH.Common.Status.DoorState") {
if (m_doorStateTypeIds.contains(thing->thingClassId())) {
thing->setStateValue(m_doorStateTypeIds.value(thing->thingClassId()), value.toString().split('.').last());
thing->setStateValue(m_doorStateTypeIds.value(thing->thingClassId()), value.toString().split('.').last() != "Open");
}
// Home Appliance Events
@ -736,32 +752,33 @@ void IntegrationPluginHomeConnect::parseKey(Thing *thing, const QString &key, co
void IntegrationPluginHomeConnect::parseSettingKey(Thing *thing, const QString &key, const QVariant &value)
{
if (key.contains("BSH.Common.Setting.PowerState")) {
} else if (key.contains("BSH.Common.Setting.TemperatureUnit")) {
} else if (key.contains("BSH.Common.Setting.LiquidVolumeUnit")) {
} else if (key.contains("Refrigeration.FridgeFreezer.Setting.SetpointTemperatureRefrigerator")) {
if (key.contains("Refrigeration.FridgeFreezer.Setting.SetpointTemperatureRefrigerator")) {
thing->setStateValue(fridgeFridgeTemperatureSettingStateTypeId, value);
} else if (key.contains("Refrigeration.FridgeFreezer.Setting.SetpointTemperatureFreezer")) {
thing->setStateValue(fridgeFreezerTemperatureStateTypeId, value);
} else if (key.contains("Refrigeration.Common.Setting.BottleCooler.SetpointTemperature")) {
} else if (key.contains("Refrigeration.Common.Setting.ChillerLeft.SetpointTemperature")) {
} else if (key.contains("Refrigeration.Common.Setting.ChillerCommon.SetpointTemperature")) {
} else if (key.contains("Refrigeration.Common.Setting.ChillerRight.SetpointTemperature")) {
} else if (key.contains("Refrigeration.Common.Setting.WineCompartment.SetpointTemperature")) {
} else if (key.contains("Refrigeration.Common.Setting.WineCompartment2.SetpointTemperature")) {
} else if (key.contains("Refrigeration.Common.Setting.WineCompartment3.SetpointTemperature")) {
} else if (key.contains("Refrigeration.FridgeFreezer.Setting.SuperModeRefrigerator")) {
} else if (key.contains("Refrigeration.FridgeFreezer.Setting.SuperModeFreezer")) {
} else if (key.contains("Refrigeration.Common.Setting.EcoMode")) {
} else if (key.contains("Refrigeration.Common.Setting.SabbathMode")) {
} else if (key.contains("Refrigeration.Common.Setting.VacationMode")) {
} else if (key.contains("Refrigeration.Common.Setting.FreshMode")) {
} else if (key.contains("Cooking.Common.Setting.Lighting")) {
} else if (key.contains("Cooking.Common.Setting.LightingBrightness")) {
} else if (key.contains("BSH.Common.Setting.AmbientLightEnabled")) {
} else if (key.contains("BSH.Common.Setting.AmbientLightBrightness")) {
} else if (key.contains("BSH.Common.Setting.AmbientLightColor")) {
} else if (key.contains("BSH.Common.Setting.AmbientLightCustomColor")) {
// For future improvements
//} else if (key.contains("BSH.Common.Setting.PowerState")) {
//} else if (key.contains("BSH.Common.Setting.TemperatureUnit")) {
//} else if (key.contains("BSH.Common.Setting.LiquidVolumeUnit")) {
//} else if (key.contains("Refrigeration.Common.Setting.BottleCooler.SetpointTemperature")) {
//} else if (key.contains("Refrigeration.Common.Setting.ChillerLeft.SetpointTemperature")) {
//} else if (key.contains("Refrigeration.Common.Setting.ChillerCommon.SetpointTemperature")) {
//} else if (key.contains("Refrigeration.Common.Setting.ChillerRight.SetpointTemperature")) {
//} else if (key.contains("Refrigeration.Common.Setting.WineCompartment.SetpointTemperature")) {
//} else if (key.contains("Refrigeration.Common.Setting.WineCompartment2.SetpointTemperature")) {
//} else if (key.contains("Refrigeration.Common.Setting.WineCompartment3.SetpointTemperature")) {
//} else if (key.contains("Refrigeration.FridgeFreezer.Setting.SuperModeRefrigerator")) {
//} else if (key.contains("Refrigeration.FridgeFreezer.Setting.SuperModeFreezer")) {
//} else if (key.contains("Refrigeration.Common.Setting.EcoMode")) {
//} else if (key.contains("Refrigeration.Common.Setting.SabbathMode")) {
//} else if (key.contains("Refrigeration.Common.Setting.VacationMode")) {
//} else if (key.contains("Refrigeration.Common.Setting.FreshMode")) {
//} else if (key.contains("Cooking.Common.Setting.Lighting")) {
//} else if (key.contains("Cooking.Common.Setting.LightingBrightness")) {
//} else if (key.contains("BSH.Common.Setting.AmbientLightEnabled")) {
//} else if (key.contains("BSH.Common.Setting.AmbientLightBrightness")) {
//} else if (key.contains("BSH.Common.Setting.AmbientLightColor")) {
//} else if (key.contains("BSH.Common.Setting.AmbientLightCustomColor")) {
}
}
@ -771,7 +788,7 @@ void IntegrationPluginHomeConnect::onConnectionChanged(bool connected)
Thing *thing = m_homeConnectConnections.key(homeConnect);
if (!thing)
return;
thing->setStateValue(homeConnectConnectionConnectedStateTypeId, connected);
thing->setStateValue(homeConnectAccountConnectedStateTypeId, connected);
if (!connected) {
Q_FOREACH(Thing *child, myThings().filterByParentId(thing->id())) {
child->setStateValue(m_connectedStateTypeIds.value(child->thingClassId()), connected);
@ -796,7 +813,7 @@ void IntegrationPluginHomeConnect::onAuthenticationStatusChanged(bool authentica
if (!thing)
return;
thing->setStateValue(homeConnectConnectionLoggedInStateTypeId, authenticated);
thing->setStateValue(homeConnectAccountLoggedInStateTypeId, authenticated);
if (!authenticated) {
//refresh access token needs to be refreshed
pluginStorage()->beginGroup(thing->id().toString());

View File

@ -16,6 +16,20 @@
"displayName": "Control enabled",
"defaultValue": false,
"type": "bool"
},
{
"id": "19cedaab-b8a0-4cab-80c7-0cae5fce124d",
"name": "customClientKey",
"displayName": "Custom client key",
"defaultValue": "",
"type": "QString"
},
{
"id": "49b828ab-f495-4332-900b-99a9d37565b1",
"name": "customClientSecret",
"displayName": "Custom client secret",
"defaultValue": "",
"type": "QString"
}
],
"apiKeys": ["homeconnect"],
@ -27,8 +41,8 @@
"thingClasses": [
{
"id": "babc1a39-730a-4516-95bf-ff51a8ce887a",
"name": "homeConnectConnection",
"displayName": "Home Connect connection",
"name": "homeConnectAccount",
"displayName": "Home Connect account",
"interfaces": ["account"],
"createMethods": ["user"],
"setupMethod": "oauth",
@ -66,7 +80,7 @@
"id": "96845b7d-4c20-43a0-a810-ec505df3ee88",
"name": "oven",
"displayName": "Oven",
"interfaces": ["connectable"],
"interfaces": ["connectable", "closablesensor"],
"createMethods": ["auto"],
"browsable": true,
"paramTypes": [
@ -90,15 +104,11 @@
},
{
"id": "e892ca9e-5b31-41f5-a568-44474091f0f6",
"name": "doorState",
"displayName": "Door state",
"displayNameEvent": "Door state changed",
"defaultValue": "Open",
"type": "QString",
"possibleValues": [
"Open",
"Closed"
]
"name": "closed",
"displayName": "Door closed",
"displayNameEvent": "Door closed changed",
"defaultValue": false,
"type": "bool"
},
{
"id": "c9f9bd33-513f-4834-a504-c2c1611fb4be",
@ -241,15 +251,11 @@
},
{
"id": "8cbb3746-7e04-4fc8-93eb-b774b606a057",
"name": "doorState",
"displayName": "Door state",
"displayNameEvent": "Door state changed",
"defaultValue": "Open",
"type": "QString",
"possibleValues": [
"Open",
"Closed"
]
"name": "closed",
"displayName": "Door closed",
"displayNameEvent": "Door closed changed",
"defaultValue": false,
"type": "bool"
},
{
"id": "2b45fbfc-d3d7-4dd4-91f8-4a789405246e",
@ -369,15 +375,11 @@
},
{
"id": "5011efa7-9915-4ecc-b717-6ced369abcb7",
"name": "doorState",
"displayName": "Door state",
"displayNameEvent": "Door state changed",
"defaultValue": "Open",
"type": "QString",
"possibleValues": [
"Open",
"Closed"
]
"name": "closed",
"displayName": "Door closed",
"displayNameEvent": "Door closed changed",
"defaultValue": false,
"type": "bool"
},
{
"id": "693fc8d2-d9a9-4109-b8c5-f3d22091500c",
@ -540,16 +542,11 @@
},
{
"id": "81aa609e-69c0-4d5c-98f0-46e6f14c7eaa",
"name": "doorState",
"displayName": "Door state",
"displayNameEvent": "Door state changed",
"defaultValue": "Open",
"type": "QString",
"possibleValues": [
"Open",
"Closed",
"Locked"
]
"name": "closed",
"displayName": "Door closed",
"displayNameEvent": "Door closed changed",
"defaultValue": false,
"type": "bool"
},
{
"id": "52fde732-ed14-437e-8fbf-461d2ed19654",
@ -682,15 +679,11 @@
},
{
"id": "99bba70c-5ead-4076-8b71-720a931668a4",
"name": "doorState",
"displayName": "Door state",
"displayNameEvent": "Door state changed",
"defaultValue": "Open",
"type": "QString",
"possibleValues": [
"Open",
"Closed"
]
"name": "closed",
"displayName": "Door closed",
"displayNameEvent": "Door closed changed",
"defaultValue": false,
"type": "bool"
},
{
"id": "bc13977e-0ea1-4804-af00-311ae62c6c06",
@ -785,16 +778,11 @@
},
{
"id": "99296e86-09bf-4b74-b122-ee82b6bfdb62",
"name": "doorState",
"displayName": "Door state",
"displayNameEvent": "Door state changed",
"defaultValue": "Open",
"type": "QString",
"possibleValues": [
"Open",
"Closed",
"Locked"
]
"name": "closed",
"displayName": "Door closed",
"displayNameEvent": "Door closed changed",
"defaultValue": false,
"type": "bool"
},
{
"id": "2ad3e2f8-c955-4e1d-b394-1e71a16f03bb",