add netatmo outdoor module

pull/135/head
Simon Stürz 2015-10-14 22:56:57 +02:00 committed by Michael Zanetti
parent 2693b1c8bd
commit e194b3af83
10 changed files with 453 additions and 33 deletions

View File

@ -806,7 +806,10 @@ void DeviceManager::loadPlugins()
}
QPluginLoader loader(fi.absoluteFilePath());
DevicePlugin *pluginIface = qobject_cast<DevicePlugin*>(loader.instance());
DevicePlugin *pluginIface = qobject_cast<DevicePlugin *>(loader.instance());
if (!pluginIface)
qCWarning(dcDeviceManager) << "Could not load plugin interface of" << entry;
if (verifyPluginMetadata(loader.metaData().value("MetaData").toObject()) && pluginIface) {
pluginIface->initPlugin(loader.metaData().value("MetaData").toObject(), this);
qCDebug(dcDeviceManager) << "*** Loaded plugin" << pluginIface->pluginName();
@ -1218,7 +1221,7 @@ bool DeviceManager::verifyPluginMetadata(const QJsonObject &data)
requiredFields << "name" << "id" << "vendors";
foreach (const QString &field, requiredFields) {
if (!data.contains("name")) {
if (!data.contains(field)) {
qCWarning(dcDeviceManager) << "Error loading plugin. Incomplete metadata. Missing field:" << field;
qCWarning(dcDeviceManager) << data;
return false;

View File

@ -137,6 +137,7 @@ private:
friend class DeviceManager;
};
Q_DECLARE_INTERFACE(DevicePlugin, "guru.guh.DevicePlugin")
#endif
#endif // DEVICEPLUGIN_H

View File

@ -47,6 +47,7 @@
#include "plugin/device.h"
#include "plugininfo.h"
#include <QDebug>
#include <QUrlQuery>
#include <QJsonDocument>
@ -83,19 +84,31 @@ DeviceManager::DeviceSetupStatus DevicePluginNetatmo::setupDevice(Device *device
device->paramValue("mac address").toString(),
device->paramValue("connection id").toString(), this);
connect(indoor, &NetatmoBaseStation::statesChanged, this, &DevicePluginNetatmo::onIndoorStatesChanged);
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("name").toString(),
device->paramValue("mac address").toString(),
device->paramValue("connection id").toString(), this);
m_outdoorDevices.insert(outdoor, device);
connect(outdoor, SIGNAL(statesChanged()), this, SLOT(onOutdoorStatesChanged()));
return DeviceManager::DeviceSetupStatusSuccess;
}
return DeviceManager::DeviceSetupStatusFailure;
}
void DevicePluginNetatmo::deviceRemoved(Device *device)
{
OAuth2 * authentication = m_authentications.key(device);
m_authentications.remove(authentication);
authentication->deleteLater();
if (device->deviceClassId() == connectionDeviceClassId) {
OAuth2 * authentication = m_authentications.key(device);
m_authentications.remove(authentication);
authentication->deleteLater();
}
}
void DevicePluginNetatmo::networkManagerReplyReady(QNetworkReply *reply)
@ -137,11 +150,21 @@ void DevicePluginNetatmo::guhTimer()
if (device->deviceClassId() == connectionDeviceClassId) {
OAuth2 *authentication = m_authentications.key(device);
// TODO: check if authenticated
refreshData(device, authentication->token());
if (authentication->authenticated()) {
refreshData(device, authentication->token());
}
}
}
}
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;
@ -157,18 +180,18 @@ void DevicePluginNetatmo::refreshData(Device *device, const QString &token)
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();
//QVariantList modulesList = data.value("body").toMap().value("modules").toList();
// check devices
foreach (QVariant deviceVariant, deviceList) {
QVariantMap deviceMap = deviceVariant.toMap();
// we support currently only NAMain devices
if (deviceMap.value("type").toString() == "NAMain") {
NetatmoBaseStation *indoor = findIndoorDevice(deviceMap.value("_id").toString());
Device *indoorDevice = findIndoorDevice(deviceMap.value("_id").toString());
// check if we have to create the device (auto)
if (!indoor) {
if (!indoorDevice) {
DeviceDescriptor descriptor(indoorDeviceClassId, "Indoor Station", deviceMap.value("station_name").toString());
ParamList params;
params.append(Param("name", deviceMap.value("station_name").toString()));
@ -177,7 +200,36 @@ void DevicePluginNetatmo::processRefreshData(const QVariantMap &data, const QStr
descriptor.setParams(params);
emit autoDevicesAppeared(indoorDeviceClassId, QList<DeviceDescriptor>() << descriptor);
} else {
indoor->updateStates(deviceMap);
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("name", moduleMap.value("module_name").toString()));
params.append(Param("mac address", moduleMap.value("_id").toString()));
params.append(Param("connection id", connectionId));
descriptor.setParams(params);
emit autoDevicesAppeared(outdoorDeviceClassId, QList<DeviceDescriptor>() << descriptor);
} else {
if (m_outdoorDevices.values().contains(outdoorDevice)) {
m_outdoorDevices.key(outdoorDevice)->updateStates(moduleMap);
}
}
}
}
@ -185,12 +237,25 @@ void DevicePluginNetatmo::processRefreshData(const QVariantMap &data, const QStr
}
}
NetatmoBaseStation *DevicePluginNetatmo::findIndoorDevice(const QString &macAddress)
Device *DevicePluginNetatmo::findIndoorDevice(const QString &macAddress)
{
foreach (NetatmoBaseStation *bs, m_indoorDevices.keys()) {
if (bs->macAddress() == macAddress) {
return bs;
foreach (Device *device, myDevices()) {
if (device->deviceClassId() == indoorDeviceClassId) {
if (device->paramValue("mac address").toString() == macAddress) {
return device;
}
}
}
return 0;
}
Device *DevicePluginNetatmo::findOutdoorDevice(const QString &macAddress)
{
foreach (Device *device, myDevices()) {
if (device->deviceClassId() == outdoorDeviceClassId) {
if (device->paramValue("mac address").toString() == macAddress) {
return device;
}
}
}
return 0;
@ -215,4 +280,34 @@ void DevicePluginNetatmo::onAuthenticationChanged()
}
}
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());
}

View File

@ -21,13 +21,13 @@
#ifndef DEVICEPLUGINNETATMO_H
#define DEVICEPLUGINNETATMO_H
#include <QHash>
#include <QDebug>
#include <QTimer>
#include "plugin/deviceplugin.h"
#include "network/oauth2.h"
#include "netatmobasestation.h"
#include "netatmooutdoormodule.h"
#include <QHash>
#include <QTimer>
class DevicePluginNetatmo : public DevicePlugin
{
@ -45,22 +45,28 @@ public:
void guhTimer() override;
public slots:
DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
private:
QList<Device *> m_asyncSetups;
QHash<OAuth2 *, Device *> m_authentications;
QHash<NetatmoBaseStation *, Device *> m_indoorDevices;
QHash<NetatmoOutdoorModule *, Device *> m_outdoorDevices;
QHash<QNetworkReply *, Device *> m_refreshRequest;
void refreshData(Device *device, const QString &token);
void processRefreshData(const QVariantMap &data, const QString &connectionId);
NetatmoBaseStation *findIndoorDevice(const QString &macAddress);
Device *findIndoorDevice(const QString &macAddress);
Device *findOutdoorDevice(const QString &macAddress);
private slots:
void onAuthenticationChanged();
void onIndoorStatesChanged();
void onOutdoorStatesChanged();
};

View File

@ -1,19 +1,24 @@
{
"name": "Netatmo",
"idName": "Netatmo",
"id": "3af70774-4b43-46fe-96a4-22108d4efb1b",
"id": "69d14951-0c02-4877-bcef-dffdf48b7ccb",
"vendors": [
{
"name": "Netatmo",
"idName": "netatmo",
"id": "31828f6f-c63d-4d44-b2b1-d0f70b3ce933",
"id": "4b46b4ed-5ec9-4aa4-afc3-92d3f80e6351",
"deviceClasses": [
{
"deviceClassId": "728d5a67-27a3-400e-b83c-2765f5196f69",
"name": "Netatmo Connection",
"idName": "connection",
"name": "Netatmo Connection",
"createMethods": ["user"],
"paramTypes": [
{
"name": "name",
"type": "QString",
"inputType": "TextLine"
},
{
"name": "username",
"type": "QString",
@ -25,7 +30,7 @@
"inputType": "Password"
}
],
"stateType": [
"stateTypes": [
{
"id": "2f79bc1d-27ed-480a-b583-728363c83ea6",
"idName": "available",
@ -36,9 +41,9 @@
]
},
{
"deviceClassId": "d79d373c-c992-4d75-bd01-a0f8ab346ac5",
"name": "Indoor Station",
"deviceClassId": "1c809049-04f2-4710-99f5-6ed379a2934f",
"idName": "indoor",
"name": "Indoor Station",
"createMethods": ["auto"],
"paramTypes": [
{
@ -134,6 +139,90 @@
"defaultValue": 0
}
]
},
{
"deviceClassId": "6cc01d62-7317-4ec4-8ac4-a4cab762c179",
"idName": "outdoor",
"name": "Outdoor Station",
"createMethods": ["auto"],
"paramTypes": [
{
"name": "name",
"type": "QString",
"inputType": "TextLine",
"readOnly": true
},
{
"name": "mac address",
"type": "QString",
"inputType": "TextLine",
"readOnly": true
},
{
"name": "basestation id",
"type": "QString",
"inputType": "TextLine",
"readOnly": true
}
],
"stateTypes": [
{
"id": "50da9f6b-c350-401c-a72e-2e4036f3975d",
"idName": "updateTime",
"name": "last update",
"unit": "UnixTime",
"type": "int",
"defaultValue": 0
},
{
"id": "3cb25538-e463-40ae-92f9-8f34f0c06b92",
"idName": "temperature",
"name": "temperature",
"unit": "DegreeCelsius",
"type": "double",
"defaultValue": 0
},
{
"id": "ae8bb713-8805-4efd-89a1-bca44a1f1690",
"idName": "temperatureMin",
"name": "temperature minimum",
"unit": "DegreeCelsius",
"type": "double",
"defaultValue": 0
},
{
"id": "dd30507e-037b-4c74-bcca-e04b94c7c5fe",
"idName": "temperatureMax",
"name": "temperature maximum",
"unit": "DegreeCelsius",
"type": "double",
"defaultValue": 0
},
{
"id": "e2db5f01-196a-48d1-8874-6b8cbfe0d8c9",
"idName": "humidity",
"name": "humidity",
"unit": "Percentage",
"type": "int",
"defaultValue": 0
},
{
"id": "0faa3d08-9004-46fb-a5aa-a59b75e454cc",
"idName": "signalStrength",
"name": "signal strength",
"unit": "Percentage",
"type": "int",
"defaultValue": 0
},
{
"id": "15d8fae1-ba47-42e1-994d-530e8017c965",
"idName": "battery",
"name": "battery",
"unit": "Percentage",
"type": "int",
"defaultValue": 0
}
]
}
]
}

View File

@ -4,10 +4,12 @@ TARGET = $$qtLibraryTarget(guh_devicepluginnetatmo)
SOURCES += \
devicepluginnetatmo.cpp \
netatmobasestation.cpp
netatmobasestation.cpp \
netatmooutdoormodule.cpp
HEADERS += \
devicepluginnetatmo.h \
netatmobasestation.h
netatmobasestation.h \
netatmooutdoormodule.h

View File

@ -20,6 +20,8 @@
#include "netatmobasestation.h"
#include <QVariantMap>
NetatmoBaseStation::NetatmoBaseStation(const QString &name, const QString &macAddress, const QString &connectionId, QObject *parent) :
QObject(parent),
m_name(name),
@ -90,5 +92,33 @@ int NetatmoBaseStation::wifiStrength() const
void NetatmoBaseStation::updateStates(const QVariantMap &data)
{
Q_UNUSED(data)
// check data timestamp
if (data.contains("last_status_store")) {
m_lastUpdate = data.value("last_status_store").toInt();
}
// update dashboard data
if (data.contains("dashboard_data")) {
QVariantMap measurments = data.value("dashboard_data").toMap();
m_pressure = measurments.value("AbsolutePressure").toDouble();
m_humidity = measurments.value("Humidity").toInt();
m_noise = measurments.value("Noise").toInt();
m_temperature = measurments.value("Temperature").toDouble();
m_minTemperature = measurments.value("min_temp").toDouble();
m_maxTemperature = measurments.value("max_temp").toDouble();
m_co2 = measurments.value("CO2").toInt();
}
// update wifi signal strength
if (data.contains("wifi_status")) {
int wifiStrength = data.value("wifi_status").toInt();
if (wifiStrength <= 56) {
m_wifiStrength = 100;
} else if (wifiStrength >= 86) {
m_wifiStrength = 0;
} else {
int delta = 30 - (wifiStrength - 56);
m_wifiStrength = qRound(100.0 * delta / 30.0);
}
}
emit statesChanged();
}

View File

@ -67,6 +67,7 @@ private:
signals:
void statesChanged();
};
#endif // NETATMOBASESTATION_H

View File

@ -0,0 +1,125 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stuerz <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/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "netatmooutdoormodule.h"
#include <QVariantMap>
NetatmoOutdoorModule::NetatmoOutdoorModule(const QString &name, const QString &macAddress, const QString &connectionId, QObject *parent) :
QObject(parent),
m_name(name),
m_macAddress(macAddress),
m_connectionId(connectionId)
{
}
QString NetatmoOutdoorModule::name() const
{
return m_name;
}
QString NetatmoOutdoorModule::macAddress() const
{
return m_macAddress;
}
QString NetatmoOutdoorModule::connectionId() const
{
return m_connectionId;
}
int NetatmoOutdoorModule::lastUpdate() const
{
return m_lastUpdate;
}
int NetatmoOutdoorModule::humidity() const
{
return m_humidity;
}
double NetatmoOutdoorModule::temperature() const
{
return m_temperature;
}
double NetatmoOutdoorModule::minTemperature() const
{
return m_minTemperature;
}
double NetatmoOutdoorModule::maxTemperature() const
{
return m_maxTemperature;
}
int NetatmoOutdoorModule::signalStrength() const
{
return m_signalStrength;
}
int NetatmoOutdoorModule::battery() const
{
return m_battery;
}
void NetatmoOutdoorModule::updateStates(const QVariantMap &data)
{
// check data timestamp
if (data.contains("last_message")) {
m_lastUpdate = data.value("last_message").toInt();
}
// update dashboard data
if (data.contains("dashboard_data")) {
QVariantMap measurments = data.value("dashboard_data").toMap();
m_humidity = measurments.value("Humidity").toInt();
m_temperature = measurments.value("Temperature").toDouble();
m_minTemperature = measurments.value("min_temp").toDouble();
m_maxTemperature = measurments.value("max_temp").toDouble();
}
// update battery strength
if (data.contains("battery_vp")) {
int battery = data.value("battery_vp").toInt();
if (battery >= 6000) {
m_battery = 100;
} else if (battery <= 3600) {
m_battery = 0;
} else {
int delta = battery - 3600;
m_battery = qRound(100.0 * delta / 2400);
}
}
// update signal strength
if (data.contains("rf_status")) {
int signalStrength = data.value("rf_status").toInt();
if (signalStrength <= 60) {
m_signalStrength = 100;
} else if (signalStrength >= 90) {
m_signalStrength = 0;
} else {
int delta = 30 - (signalStrength - 60);
m_signalStrength = qRound(100.0 * delta / 30.0);
}
}
emit statesChanged();
}

View File

@ -0,0 +1,68 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stuerz <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/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef NETATMOOUTDOORMODULE_H
#define NETATMOOUTDOORMODULE_H
#include <QObject>
class NetatmoOutdoorModule : public QObject
{
Q_OBJECT
public:
explicit NetatmoOutdoorModule(const QString &name, const QString &macAddress, const QString &connectionId, QObject *parent = 0);
// Params
QString name() const;
QString macAddress() const;
QString connectionId() const;
// States
int lastUpdate() const;
int humidity() const;
double temperature() const;
double minTemperature() const;
double maxTemperature() const;
int signalStrength() const;
int battery() const;
void updateStates(const QVariantMap &data);
private:
// Params
QString m_name;
QString m_macAddress;
QString m_connectionId;
// States
int m_lastUpdate;
int m_humidity;
double m_temperature;
double m_minTemperature;
double m_maxTemperature;
int m_signalStrength;
int m_battery;
signals:
void statesChanged();
};
#endif // NETATMOOUTDOORMODULE_H