nymea/plugins/deviceplugins/netatmo/devicepluginnetatmo.cpp

321 lines
14 KiB
C++

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.guru> *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*!
\page netatmo.html
\title Netatmo
\brief Plugin for the Netatmo weather stations.
\ingroup plugins
\ingroup guh-plugins
This plugin allows to receive data from you netatmo weather station.
\chapter Plugin properties
Following JSON file contains the definition and the description of all available \l{DeviceClass}{DeviceClasses}
and \l{Vendor}{Vendors} of this \l{DevicePlugin}.
For more details how to read this JSON file please check out the documentation for \l{The plugin JSON File}.
\quotefile plugins/deviceplugins/netatmo/devicepluginnetatmo.json
*/
#include "devicepluginnetatmo.h"
#include "plugin/device.h"
#include "plugininfo.h"
#include <QDebug>
#include <QUrlQuery>
#include <QJsonDocument>
DevicePluginNetatmo::DevicePluginNetatmo()
{
}
DeviceManager::HardwareResources DevicePluginNetatmo::requiredHardware() const
{
return DeviceManager::HardwareResourceNetworkManager | DeviceManager::HardwareResourceTimer;
}
DeviceManager::DeviceSetupStatus DevicePluginNetatmo::setupDevice(Device *device)
{
if (device->deviceClassId() == connectionDeviceClassId) {
qCDebug(dcNetatmo) << "Setup netatmo connection";
OAuth2 *authentication = new OAuth2("561c015d49c75f0d1cce6e13", "GuvKkdtu7JQlPD47qTTepRR9hQ0CUPAj4Tae3Ohcq", this);
authentication->setUrl(QUrl("https://api.netatmo.net/oauth2/token"));
authentication->setUsername(device->paramValue(usernameParamTypeId).toString());
authentication->setPassword(device->paramValue(passwordParamTypeId).toString());
authentication->setScope("read_station read_thermostat write_thermostat");
m_authentications.insert(authentication, device);
m_asyncSetups.append(device);
connect(authentication, &OAuth2::authenticationChanged, this, &DevicePluginNetatmo::onAuthenticationChanged);
authentication->startAuthentication();
return DeviceManager::DeviceSetupStatusAsync;
} else if (device->deviceClassId() == indoorDeviceClassId) {
qCDebug(dcNetatmo) << "Setup netatmo indoor base station" << device->params();
NetatmoBaseStation *indoor = new NetatmoBaseStation(device->paramValue(nameParamTypeId).toString(),
device->paramValue(macParamTypeId).toString(),
device->paramValue(connectionParamTypeId).toString(), this);
device->setParentId(DeviceId(indoor->connectionId()));
m_indoorDevices.insert(indoor, device);
connect(indoor, SIGNAL(statesChanged()), this, SLOT(onIndoorStatesChanged()));
return DeviceManager::DeviceSetupStatusSuccess;
} else if (device->deviceClassId() == outdoorDeviceClassId) {
qCDebug(dcNetatmo) << "Setup netatmo outdoor module" << device->params();
NetatmoOutdoorModule *outdoor = new NetatmoOutdoorModule(device->paramValue(nameParamTypeId).toString(),
device->paramValue(macParamTypeId).toString(),
device->paramValue(connectionParamTypeId).toString(),
device->paramValue(baseStationParamTypeId).toString(),this);
device->setParentId(DeviceId(outdoor->connectionId()));
m_outdoorDevices.insert(outdoor, device);
connect(outdoor, SIGNAL(statesChanged()), this, SLOT(onOutdoorStatesChanged()));
return DeviceManager::DeviceSetupStatusSuccess;
}
return DeviceManager::DeviceSetupStatusFailure;
}
void DevicePluginNetatmo::deviceRemoved(Device *device)
{
if (device->deviceClassId() == connectionDeviceClassId) {
OAuth2 * authentication = m_authentications.key(device);
m_authentications.remove(authentication);
authentication->deleteLater();
} else if (device->deviceClassId() == indoorDeviceClassId) {
NetatmoBaseStation *indoor = m_indoorDevices.key(device);
m_indoorDevices.remove(indoor);
indoor->deleteLater();
} else if (device->deviceClassId() == outdoorDeviceClassId) {
NetatmoOutdoorModule *outdoor = m_outdoorDevices.key(device);
m_outdoorDevices.remove(outdoor);
outdoor->deleteLater();
}
}
void DevicePluginNetatmo::networkManagerReplyReady(QNetworkReply *reply)
{
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
// update values request
if (m_refreshRequest.keys().contains(reply)) {
Device *device = m_refreshRequest.take(reply);
// check HTTP status code
if (status != 200) {
qCWarning(dcNetatmo) << "Device list reply HTTP error:" << status << reply->errorString();
device->setStateValue(availableStateTypeId, false);
reply->deleteLater();
return;
}
// check JSON file
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll(), &error);
if (error.error != QJsonParseError::NoError) {
qCWarning(dcNetatmo) << "Device list reply JSON error:" << error.errorString();
reply->deleteLater();
return;
}
//qCDebug(dcNetatmo) << jsonDoc.toJson();
processRefreshData(jsonDoc.toVariant().toMap(), device->id().toString());
}
reply->deleteLater();
}
void DevicePluginNetatmo::guhTimer()
{
foreach (OAuth2 *authentication, m_authentications.keys()) {
if (authentication->authenticated()) {
refreshData(m_authentications.value(authentication), authentication->token());
} else {
authentication->startAuthentication();
}
}
}
DeviceManager::DeviceError DevicePluginNetatmo::executeAction(Device *device, const Action &action)
{
Q_UNUSED(device)
Q_UNUSED(action)
return DeviceManager::DeviceErrorNoError;
}
void DevicePluginNetatmo::refreshData(Device *device, const QString &token)
{
QUrlQuery query;
query.addQueryItem("access_token", token);
QUrl url("https://api.netatmo.com/api/devicelist");
url.setQuery(query);
QNetworkReply *reply = networkManagerGet(QNetworkRequest(url));
m_refreshRequest.insert(reply, device);
}
void DevicePluginNetatmo::processRefreshData(const QVariantMap &data, const QString &connectionId)
{
if (data.contains("body")) {
// check devices
if (data.value("body").toMap().contains("devices")) {
QVariantList deviceList = data.value("body").toMap().value("devices").toList();
// check devices
foreach (QVariant deviceVariant, deviceList) {
QVariantMap deviceMap = deviceVariant.toMap();
// we support currently only NAMain devices
if (deviceMap.value("type").toString() == "NAMain") {
Device *indoorDevice = findIndoorDevice(deviceMap.value("_id").toString());
// check if we have to create the device (auto)
if (!indoorDevice) {
DeviceDescriptor descriptor(indoorDeviceClassId, "Indoor Station", deviceMap.value("station_name").toString());
ParamList params;
params.append(Param(nameParamTypeId, deviceMap.value("station_name").toString()));
params.append(Param(macParamTypeId, deviceMap.value("_id").toString()));
params.append(Param(connectionParamTypeId, connectionId));
descriptor.setParams(params);
emit autoDevicesAppeared(indoorDeviceClassId, QList<DeviceDescriptor>() << descriptor);
} else {
if (m_indoorDevices.values().contains(indoorDevice)) {
m_indoorDevices.key(indoorDevice)->updateStates(deviceMap);
}
}
}
}
}
// check modules
if (data.value("body").toMap().contains("modules")) {
QVariantList modulesList = data.value("body").toMap().value("modules").toList();
// check devices
foreach (QVariant moduleVariant, modulesList) {
QVariantMap moduleMap = moduleVariant.toMap();
// we support currently only NAModule1
if (moduleMap.value("type").toString() == "NAModule1") {
Device *outdoorDevice = findOutdoorDevice(moduleMap.value("_id").toString());
// check if we have to create the device (auto)
if (!outdoorDevice) {
DeviceDescriptor descriptor(outdoorDeviceClassId, "Outdoor Module", moduleMap.value("module_name").toString());
ParamList params;
params.append(Param(nameParamTypeId, moduleMap.value("module_name").toString()));
params.append(Param(macParamTypeId, moduleMap.value("_id").toString()));
params.append(Param(connectionParamTypeId, connectionId));
params.append(Param(baseStationParamTypeId, moduleMap.value("main_device").toString()));
descriptor.setParams(params);
emit autoDevicesAppeared(outdoorDeviceClassId, QList<DeviceDescriptor>() << descriptor);
} else {
if (m_outdoorDevices.values().contains(outdoorDevice)) {
m_outdoorDevices.key(outdoorDevice)->updateStates(moduleMap);
}
}
}
}
}
}
}
Device *DevicePluginNetatmo::findIndoorDevice(const QString &macAddress)
{
foreach (Device *device, myDevices()) {
if (device->deviceClassId() == indoorDeviceClassId) {
if (device->paramValue(macParamTypeId).toString() == macAddress) {
return device;
}
}
}
return 0;
}
Device *DevicePluginNetatmo::findOutdoorDevice(const QString &macAddress)
{
foreach (Device *device, myDevices()) {
if (device->deviceClassId() == outdoorDeviceClassId) {
if (device->paramValue(macParamTypeId).toString() == macAddress) {
return device;
}
}
}
return 0;
}
void DevicePluginNetatmo::onAuthenticationChanged()
{
OAuth2 *authentication = static_cast<OAuth2 *>(sender());
Device *device = m_authentications.value(authentication);
if (!device)
return;
// set the available state
device->setStateValue(availableStateTypeId, authentication->authenticated());
// check if this is was a setup athentication
if (m_asyncSetups.contains(device)) {
if (authentication->authenticated()) {
m_asyncSetups.removeAll(device);
emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusSuccess);
refreshData(device, authentication->token());
} else {
emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusFailure);
}
}
}
void DevicePluginNetatmo::onIndoorStatesChanged()
{
NetatmoBaseStation *indoor = static_cast<NetatmoBaseStation *>(sender());
Device *device = m_indoorDevices.value(indoor);
device->setStateValue(updateTimeStateTypeId, indoor->lastUpdate());
device->setStateValue(temperatureStateTypeId, indoor->temperature());
device->setStateValue(temperatureMinStateTypeId, indoor->minTemperature());
device->setStateValue(temperatureMaxStateTypeId, indoor->maxTemperature());
device->setStateValue(pressureStateTypeId, indoor->pressure());
device->setStateValue(humidityStateTypeId, indoor->humidity());
device->setStateValue(co2StateTypeId, indoor->co2());
device->setStateValue(noiseStateTypeId, indoor->noise());
device->setStateValue(wifiStrengthStateTypeId, indoor->wifiStrength());
}
void DevicePluginNetatmo::onOutdoorStatesChanged()
{
NetatmoOutdoorModule *outdoor = static_cast<NetatmoOutdoorModule *>(sender());
Device *device = m_outdoorDevices.value(outdoor);
device->setStateValue(updateTimeStateTypeId, outdoor->lastUpdate());
device->setStateValue(temperatureStateTypeId, outdoor->temperature());
device->setStateValue(temperatureMinStateTypeId, outdoor->minTemperature());
device->setStateValue(temperatureMaxStateTypeId, outdoor->maxTemperature());
device->setStateValue(humidityStateTypeId, outdoor->humidity());
device->setStateValue(signalStrengthStateTypeId, outdoor->signalStrength());
device->setStateValue(batteryStateTypeId, outdoor->battery());
}