diff --git a/Hive.pro b/Hive.pro index cbe44a81..fb8b8ad2 100644 --- a/Hive.pro +++ b/Hive.pro @@ -4,4 +4,3 @@ SUBDIRS += libhive server plugins server.depends = libhive plugins plugins.depends = libhive - diff --git a/libhive/device.cpp b/libhive/device.cpp index 080a8dca..1afbefca 100644 --- a/libhive/device.cpp +++ b/libhive/device.cpp @@ -1,5 +1,7 @@ #include "device.h" +#include + Device::Device(const QUuid &pluginId, const QUuid &id, const QUuid &deviceClassId, QObject *parent): QObject(parent), m_id(id), @@ -75,12 +77,16 @@ QVariant Device::stateValue(const QUuid &stateTypeId) const void Device::setStateValue(const QUuid &stateTypeId, const QVariant &value) { + qDebug() << "setting state for id" << stateTypeId; for (int i = 0; i < m_states.count(); ++i) { + qDebug() << "got state id" << m_states.at(i).stateTypeId(); if (m_states.at(i).stateTypeId() == stateTypeId) { State newState(stateTypeId, m_id); newState.setValue(value); m_states[i] = newState; + qDebug() << "set state for device" << value; return; } } + qDebug() << "failed setting state" << value; } diff --git a/libhive/devicemanager.cpp b/libhive/devicemanager.cpp index 4e64b7c5..5cfe6766 100644 --- a/libhive/devicemanager.cpp +++ b/libhive/devicemanager.cpp @@ -15,13 +15,15 @@ Q_IMPORT_PLUGIN(DevicePluginElro) Q_IMPORT_PLUGIN(DevicePluginIntertechno) Q_IMPORT_PLUGIN(DevicePluginMeisterAnker) +Q_IMPORT_PLUGIN(DevicePluginWifiDetector) DeviceManager::DeviceManager(QObject *parent) : - QObject(parent) + QObject(parent), + m_radio433(0) { - m_radio433 = new Radio433(this); - connect(m_radio433, &Radio433::dataReceived, this, &DeviceManager::radio433SignalReceived); + m_pluginTimer.setInterval(15000); + connect(&m_pluginTimer, &QTimer::timeout, this, &DeviceManager::timerEvent); QMetaObject::invokeMethod(this, "loadPlugins", Qt::QueuedConnection); QMetaObject::invokeMethod(this, "loadConfiguredDevices", Qt::QueuedConnection); @@ -66,7 +68,12 @@ DeviceManager::DeviceError DeviceManager::addConfiguredDevice(const QUuid &devic Device *device = new Device(plugin->pluginId(), deviceClassId, this); device->setName(deviceClass.name()); device->setParams(params); - m_configuredDevices.append(device); + if (setupDevice(device)) { + m_configuredDevices.append(device); + } else { + qWarning() << "Failed to set up device."; + return DeviceErrorSetupFailed; + } storeConfiguredDevices(); @@ -160,6 +167,9 @@ void DeviceManager::loadConfiguredDevices() device->setName(settings.value("devicename").toString()); device->setParams(settings.value("params").toMap()); settings.endGroup(); + + setupDevice(device); + m_configuredDevices.append(device); qDebug() << "found stored device" << device->name() << idString; } @@ -183,8 +193,50 @@ void DeviceManager::radio433SignalReceived(QList rawData) foreach (Device *device, m_configuredDevices) { DeviceClass deviceClass = m_supportedDevices.value(device->deviceClassId()); DevicePlugin *plugin = m_devicePlugins.value(deviceClass.pluginId()); - if (plugin->requiredHardware() == HardwareResourceRadio433) { - plugin->receiveData(rawData); + if (plugin->requiredHardware().testFlag(HardwareResourceRadio433)) { + plugin->radioData(rawData); } } } + +void DeviceManager::timerEvent() +{ + foreach (Device *device, m_configuredDevices) { + DeviceClass deviceClass = m_supportedDevices.value(device->deviceClassId()); + DevicePlugin *plugin = m_devicePlugins.value(deviceClass.pluginId()); + if (plugin->requiredHardware().testFlag(HardwareResourceTimer)) { + plugin->hiveTimer(); + } + } +} + +bool DeviceManager::setupDevice(Device *device) +{ + DeviceClass deviceClass = findDeviceClass(device->deviceClassId()); + DevicePlugin *plugin = m_devicePlugins.value(deviceClass.pluginId()); + + QList states; + foreach (const StateType &stateType, deviceClass.states()) { + State state(stateType.id(), device->id()); + state.setValue(stateType.defaultValue()); + states.append(state); + } + device->setStates(states); + + if (plugin->requiredHardware().testFlag(HardwareResourceRadio433)) { + if (!m_radio433) { + m_radio433 = new Radio433(); + connect(m_radio433, &Radio433::dataReceived, this, &DeviceManager::radio433SignalReceived); + } + } + + if (plugin->requiredHardware().testFlag(HardwareResourceTimer)) { + if (!m_pluginTimer.isActive()) { + m_pluginTimer.start(); + // Additionally fire off one event to initialize stuff + QTimer::singleShot(0, this, SLOT(timerEvent())); + } + } + + return true; +} diff --git a/libhive/devicemanager.h b/libhive/devicemanager.h index 470d3d3c..34484e30 100644 --- a/libhive/devicemanager.h +++ b/libhive/devicemanager.h @@ -6,6 +6,7 @@ #include "action.h" #include +#include class Device; class DevicePlugin; @@ -16,16 +17,20 @@ class DeviceManager : public QObject Q_OBJECT public: enum HardwareResource { + HardwareResourceNone = 0x00, HardwareResourceRadio433 = 0x01, - HardwareResourceRadio868 = 0x02 + HardwareResourceRadio868 = 0x02, + HardwareResourceTimer = 0x04 }; + Q_DECLARE_FLAGS(HardwareResources, HardwareResource) enum DeviceError { DeviceErrorNoError, DeviceErrorDeviceNotFound, DeviceErrorDeviceClassNotFound, DeviceErrorMissingParameter, - DeviceErrorPluginNotFound + DeviceErrorPluginNotFound, + DeviceErrorSetupFailed }; explicit DeviceManager(QObject *parent = 0); @@ -54,16 +59,22 @@ private slots: void storeConfiguredDevices(); void radio433SignalReceived(QList rawData); + void timerEvent(); private: + bool setupDevice(Device *device); + QHash m_supportedDevices; QList m_configuredDevices; QHash m_devicePlugins; + // Hardware Resources Radio433* m_radio433; + QTimer m_pluginTimer; friend class DevicePlugin; }; +Q_DECLARE_OPERATORS_FOR_FLAGS(DeviceManager::HardwareResources) #endif // DEVICEMANAGER_H diff --git a/libhive/deviceplugin.h b/libhive/deviceplugin.h index 4395f0f5..eec5cb59 100644 --- a/libhive/deviceplugin.h +++ b/libhive/deviceplugin.h @@ -26,16 +26,18 @@ public: virtual QUuid pluginId() const = 0; virtual QList supportedDevices() const = 0; - virtual DeviceManager::HardwareResource requiredHardware() const = 0; + virtual DeviceManager::HardwareResources requiredHardware() const = 0; // Hardware input - virtual void receiveData(QList rawData) = 0; + virtual void radioData(QList rawData) {Q_UNUSED(rawData)} + virtual void hiveTimer() {} virtual QVariantMap configuration() const; virtual void setConfiguration(const QVariantMap &configuration); public slots: - virtual void executeAction(Device *device, const Action &action) = 0; + virtual void executeAction(Device *device, const Action &action) {Q_UNUSED(device) Q_UNUSED(action)} + signals: void emitTrigger(const Trigger &trigger); diff --git a/libhive/statetype.cpp b/libhive/statetype.cpp index 1001471b..016bd028 100644 --- a/libhive/statetype.cpp +++ b/libhive/statetype.cpp @@ -29,3 +29,13 @@ void StateType::setType(const QVariant::Type &type) { m_type = type; } + +QVariant StateType::defaultValue() const +{ + return m_defaultValue; +} + +void StateType::setDefaultValue(const QVariant &defaultValue) +{ + m_defaultValue = defaultValue; +} diff --git a/libhive/statetype.h b/libhive/statetype.h index d01cdaf5..5706b36c 100644 --- a/libhive/statetype.h +++ b/libhive/statetype.h @@ -17,10 +17,14 @@ public: QVariant::Type type() const; void setType(const QVariant::Type &type); + QVariant defaultValue() const; + void setDefaultValue(const QVariant &defaultValue); + private: QUuid m_id; QString m_name; QVariant::Type m_type; + QVariant m_defaultValue; }; #endif // STATETYPE_H diff --git a/plugins/deviceplugins/devicepluginelro/devicepluginelro.cpp b/plugins/deviceplugins/devicepluginelro/devicepluginelro.cpp index 3274a09b..97db080a 100644 --- a/plugins/deviceplugins/devicepluginelro/devicepluginelro.cpp +++ b/plugins/deviceplugins/devicepluginelro/devicepluginelro.cpp @@ -139,7 +139,7 @@ QList DevicePluginElro::supportedDevices() const return ret; } -DeviceManager::HardwareResource DevicePluginElro::requiredHardware() const +DeviceManager::HardwareResources DevicePluginElro::requiredHardware() const { return DeviceManager::HardwareResourceRadio433; } @@ -249,7 +249,7 @@ void DevicePluginElro::executeAction(Device *device, const Action &action) transmitData(rawData); } -void DevicePluginElro::receiveData(QList rawData) +void DevicePluginElro::radioData(QList rawData) { // filter right here a wrong signal length if(rawData.length() != 49){ diff --git a/plugins/deviceplugins/devicepluginelro/devicepluginelro.h b/plugins/deviceplugins/devicepluginelro/devicepluginelro.h index df147779..ee5e47df 100644 --- a/plugins/deviceplugins/devicepluginelro/devicepluginelro.h +++ b/plugins/deviceplugins/devicepluginelro/devicepluginelro.h @@ -14,12 +14,12 @@ public: explicit DevicePluginElro(); QList supportedDevices() const override; - DeviceManager::HardwareResource requiredHardware() const override; + DeviceManager::HardwareResources requiredHardware() const override; - QString pluginName() const; - QUuid pluginId() const; + QString pluginName() const override; + QUuid pluginId() const override; - void receiveData(QList rawData); + void radioData(QList rawData) override; public slots: void executeAction(Device *device, const Action &action) override; diff --git a/plugins/deviceplugins/devicepluginintertechno/devicepluginintertechno.cpp b/plugins/deviceplugins/devicepluginintertechno/devicepluginintertechno.cpp index 617e175f..4675c85a 100644 --- a/plugins/deviceplugins/devicepluginintertechno/devicepluginintertechno.cpp +++ b/plugins/deviceplugins/devicepluginintertechno/devicepluginintertechno.cpp @@ -176,7 +176,7 @@ QList DevicePluginIntertechno::supportedDevices() const return ret; } -DeviceManager::HardwareResource DevicePluginIntertechno::requiredHardware() const +DeviceManager::HardwareResources DevicePluginIntertechno::requiredHardware() const { return DeviceManager::HardwareResourceRadio433; } @@ -315,7 +315,7 @@ void DevicePluginIntertechno::executeAction(Device *device, const Action &action } -void DevicePluginIntertechno::receiveData(QList rawData) +void DevicePluginIntertechno::radioData(QList rawData) { diff --git a/plugins/deviceplugins/devicepluginintertechno/devicepluginintertechno.h b/plugins/deviceplugins/devicepluginintertechno/devicepluginintertechno.h index d60e1f59..55962352 100644 --- a/plugins/deviceplugins/devicepluginintertechno/devicepluginintertechno.h +++ b/plugins/deviceplugins/devicepluginintertechno/devicepluginintertechno.h @@ -14,12 +14,12 @@ public: explicit DevicePluginIntertechno(); QList supportedDevices() const override; - DeviceManager::HardwareResource requiredHardware() const override; + DeviceManager::HardwareResources requiredHardware() const override; - QString pluginName() const; - QUuid pluginId() const; + QString pluginName() const override; + QUuid pluginId() const override; - void receiveData(QList rawData); + void radioData(QList rawData) override; public slots: void executeAction(Device *device, const Action &action) override; diff --git a/plugins/deviceplugins/devicepluginmeisteranker/devicepluginmeisteranker.cpp b/plugins/deviceplugins/devicepluginmeisteranker/devicepluginmeisteranker.cpp index 64b51f36..bfc9bc60 100644 --- a/plugins/deviceplugins/devicepluginmeisteranker/devicepluginmeisteranker.cpp +++ b/plugins/deviceplugins/devicepluginmeisteranker/devicepluginmeisteranker.cpp @@ -83,7 +83,7 @@ QList DevicePluginMeisterAnker::supportedDevices() const return ret; } -DeviceManager::HardwareResource DevicePluginMeisterAnker::requiredHardware() const +DeviceManager::HardwareResources DevicePluginMeisterAnker::requiredHardware() const { return DeviceManager::HardwareResourceRadio433; } @@ -103,7 +103,7 @@ void DevicePluginMeisterAnker::executeAction(Device *device, const Action &actio } -void DevicePluginMeisterAnker::receiveData(QList rawData) +void DevicePluginMeisterAnker::radioData(QList rawData) { // filter right here a wrong signal length if(rawData.length() != 49){ diff --git a/plugins/deviceplugins/devicepluginmeisteranker/devicepluginmeisteranker.h b/plugins/deviceplugins/devicepluginmeisteranker/devicepluginmeisteranker.h index d5a92c95..c1bbe39f 100644 --- a/plugins/deviceplugins/devicepluginmeisteranker/devicepluginmeisteranker.h +++ b/plugins/deviceplugins/devicepluginmeisteranker/devicepluginmeisteranker.h @@ -15,12 +15,12 @@ public: explicit DevicePluginMeisterAnker(); QList supportedDevices() const override; - DeviceManager::HardwareResource requiredHardware() const override; + DeviceManager::HardwareResources requiredHardware() const override; - QString pluginName() const; - QUuid pluginId() const; + QString pluginName() const override; + QUuid pluginId() const override; - void receiveData(QList rawData); + void radioData(QList rawData) override; public slots: void executeAction(Device *device, const Action &action) override; diff --git a/plugins/deviceplugins/deviceplugins.pro b/plugins/deviceplugins/deviceplugins.pro index f9e78ffa..475c4c9c 100644 --- a/plugins/deviceplugins/deviceplugins.pro +++ b/plugins/deviceplugins/deviceplugins.pro @@ -1,3 +1,3 @@ TEMPLATE = subdirs -SUBDIRS += devicepluginelro devicepluginintertechno devicepluginmeisteranker +SUBDIRS += devicepluginelro devicepluginintertechno devicepluginmeisteranker devicepluginwifidetector diff --git a/plugins/deviceplugins/devicepluginwifidetector/devicepluginwifidetector.cpp b/plugins/deviceplugins/devicepluginwifidetector/devicepluginwifidetector.cpp new file mode 100644 index 00000000..8f77a4b9 --- /dev/null +++ b/plugins/deviceplugins/devicepluginwifidetector/devicepluginwifidetector.cpp @@ -0,0 +1,118 @@ +#include "devicepluginwifidetector.h" + +#include "device.h" +#include "devicemanager.h" + +#include +#include + +QUuid pluginUuid = QUuid("8e0f791e-b273-4267-8605-b7c2f55a68ab"); +QUuid detectorId = QUuid("bd216356-f1ec-4324-9785-6982d2174e17"); +QUuid inRangeStateTypeId = QUuid("cb43e1b5-4f61-4538-bfa2-c33055c542cf"); +QUuid inRangeTriggerTypeId = QUuid("7cae711a-a0af-41b4-b3bf-38d3e23b41ba"); + +DevicePluginWifiDetector::DevicePluginWifiDetector() +{ +} + +QList DevicePluginWifiDetector::supportedDevices() const +{ + QList ret; + + DeviceClass deviceClassWifiDetector(pluginId(), detectorId); + deviceClassWifiDetector.setName("WiFi Device"); + + QVariantList detectorParams; + QVariantMap macParam; + macParam.insert("name", "mac"); + macParam.insert("type", "string"); + detectorParams.append(macParam); + + deviceClassWifiDetector.setParams(detectorParams); + + QList detectorStates; + + StateType inRangeState(inRangeStateTypeId); + inRangeState.setName("inRange"); + inRangeState.setType(QVariant::Bool); + inRangeState.setDefaultValue(false); + detectorStates.append(inRangeState); + + deviceClassWifiDetector.setStates(detectorStates); + + QList detectorTriggers; + + QVariantList detectorTriggerParams; + QVariantMap paramInRange; + paramInRange.insert("name", "inRange"); + paramInRange.insert("type", "bool"); + detectorTriggerParams.append(paramInRange); + + TriggerType inRangeTrigger(inRangeTriggerTypeId); + inRangeTrigger.setName("inRange"); + inRangeTrigger.setParameters(detectorTriggerParams); + detectorTriggers.append(inRangeTrigger); + + deviceClassWifiDetector.setTriggers(detectorTriggers); + ret.append(deviceClassWifiDetector); + + return ret; +} + +DeviceManager::HardwareResources DevicePluginWifiDetector::requiredHardware() const +{ + return DeviceManager::HardwareResourceTimer; +} + +QString DevicePluginWifiDetector::pluginName() const +{ + return "WiFi Detector"; +} + +QUuid DevicePluginWifiDetector::pluginId() const +{ + return pluginUuid; +} + +void DevicePluginWifiDetector::hiveTimer() +{ + QProcess *p = new QProcess(this); + connect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processFinished(int,QProcess::ExitStatus))); + p->start(QStringLiteral("arp-scan"), QStringList() << "--localnet" << "-I" << "eth2" ); +} + +void DevicePluginWifiDetector::processFinished(int exitCode, QProcess::ExitStatus exitStatus) +{ + QProcess *p = static_cast(sender()); + p->deleteLater(); + + QList watchedDevices = deviceManager()->findConfiguredDevices(supportedDevices().first().id()); + if (watchedDevices.isEmpty()) { + return; + } + + QStringList foundDevices; + while(p->canReadLine()) { + QString result = QString::fromLatin1(p->readLine()); + QStringList lineParts = result.split('\t'); + if (lineParts.count() > 1) { + QString addr = lineParts.at(1); + foundDevices << addr; + } + } + + foreach (Device *device, watchedDevices) { + bool wasInRange = device->stateValue(inRangeStateTypeId).toBool(); + bool wasFound = foundDevices.contains(device->params().value("mac").toString()); + if (wasInRange != wasFound) { + device->setStateValue(inRangeStateTypeId, wasFound); + + QVariantMap params; + params.insert("inRange", wasFound); + Trigger trigger(inRangeTriggerTypeId, device->id(), params); + + qDebug() << "Device" << device->name() << QStringLiteral("is now ") + (wasFound ? "in" : "out of") + " range"; + emit emitTrigger(trigger); + } + } +} diff --git a/plugins/deviceplugins/devicepluginwifidetector/devicepluginwifidetector.h b/plugins/deviceplugins/devicepluginwifidetector/devicepluginwifidetector.h new file mode 100644 index 00000000..1192b5c6 --- /dev/null +++ b/plugins/deviceplugins/devicepluginwifidetector/devicepluginwifidetector.h @@ -0,0 +1,30 @@ +#ifndef DEVICEPLUGINWIFIDETECTOR_H +#define DEVICEPLUGINWIFIDETECTOR_H + +#include "deviceplugin.h" + +#include + +class DevicePluginWifiDetector : public DevicePlugin +{ + Q_OBJECT + + Q_PLUGIN_METADATA(IID "org.hiveyourhome.DevicePlugin" FILE "devicepluginwifidetector.json") + Q_INTERFACES(DevicePlugin) + +public: + explicit DevicePluginWifiDetector(); + + QList supportedDevices() const override; + DeviceManager::HardwareResources requiredHardware() const override; + + QString pluginName() const override; + QUuid pluginId() const override; + + void hiveTimer() override; + +private slots: + void processFinished(int exitCode, QProcess::ExitStatus exitStatus); +}; + +#endif // DEVICEPLUGINWIFIDETECTOR_H diff --git a/plugins/deviceplugins/devicepluginwifidetector/devicepluginwifidetector.json b/plugins/deviceplugins/devicepluginwifidetector/devicepluginwifidetector.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/plugins/deviceplugins/devicepluginwifidetector/devicepluginwifidetector.json @@ -0,0 +1 @@ +{} diff --git a/plugins/deviceplugins/devicepluginwifidetector/devicepluginwifidetector.pro b/plugins/deviceplugins/devicepluginwifidetector/devicepluginwifidetector.pro new file mode 100644 index 00000000..7d95d0a2 --- /dev/null +++ b/plugins/deviceplugins/devicepluginwifidetector/devicepluginwifidetector.pro @@ -0,0 +1,11 @@ +include(../../plugins.pri) + +TARGET = $$qtLibraryTarget(hive_devicepluginwifidetector) + +SOURCES += \ + devicepluginwifidetector.cpp + +HEADERS += \ + devicepluginwifidetector.h + + diff --git a/server/jsonrpcserver.cpp b/server/jsonrpcserver.cpp index c4fa6006..8fd17a65 100644 --- a/server/jsonrpcserver.cpp +++ b/server/jsonrpcserver.cpp @@ -105,6 +105,9 @@ void JsonRPCServer::handleDevicesMessage(int clientId, int commandId, const QStr case DeviceManager::DeviceErrorMissingParameter: sendErrorResponse(clientId, commandId, "Error creating device. Missing parameter."); break; + case DeviceManager::DeviceErrorSetupFailed: + sendErrorResponse(clientId, commandId, "Error creating device. Device setup failed."); + break; default: sendErrorResponse(clientId, commandId, "Unknown error."); } diff --git a/server/server.pro b/server/server.pro index 28a77b1f..2a00299d 100644 --- a/server/server.pro +++ b/server/server.pro @@ -28,3 +28,4 @@ HEADERS += hivecore.h \ LIBS += -L../plugins/deviceplugins/devicepluginelro/ -lhive_devicepluginelro LIBS += -L../plugins/deviceplugins/devicepluginintertechno/ -lhive_devicepluginintertechno LIBS += -L../plugins/deviceplugins/devicepluginmeisteranker/ -lhive_devicepluginmeisteranker +LIBS += -L../plugins/deviceplugins/devicepluginwifidetector/ -lhive_devicepluginwifidetector diff --git a/tests/addconfigureddevice.sh b/tests/addconfigureddevice.sh index 2ea6af66..ff84823a 100755 --- a/tests/addconfigureddevice.sh +++ b/tests/addconfigureddevice.sh @@ -4,7 +4,7 @@ if [ -z $1 ]; then echo "usage $0 host device" elif [ $1 == "list" ]; then - echo "elroremote elroswitch intertechnoremote meisteranker" + echo "elroremote elroswitch intertechnoremote meisteranker wifidetector" elif [ -z $2 ]; then echo "usage $0 host device" else @@ -20,5 +20,8 @@ else elif [ $2 == "meisteranker" ]; then # Adds an intertechno remote control (echo '{"id":1, "method":"Devices.AddConfiguredDevice", "params":{"deviceClass": "{e37e9f34-95b9-4a22-ae4f-e8b874eec871}","params":{"id":"1"}}}'; sleep 1) | nc $1 1234 + elif [ $2 == "wifidetector" ]; then + # Adds a WiFi detector + (echo '{"id":1, "method":"Devices.AddConfiguredDevice", "params":{"deviceClass": "{bd216356-f1ec-4324-9785-6982d2174e17}","params":{"mac":"90:cf:15:1b:ce:bb"}}}'; sleep 1) | nc $1 1234 fi fi