diff --git a/debian/changelog b/debian/changelog index f3c04da1..b23bbf76 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,7 +1,14 @@ -nymea-plugins (0.10.0) UNRELEASED; urgency=medium +nymea-plugins (0.10.0) xenial; urgency=medium + [ Michael Zanetti ] + * Elgato/Avea: Fix handling white channel in set color action + * New plugin: Generic MQTT client plugin (mqttclient) + * PhilipsHue: Improve naming of hue devices using nymea's system name + and syncing to bridge + * New plugin: Sonoff-Tasmota devices plugin (tasmota) + * Netatmo: fix after upstream API change, make use of new interfaces - -- Michael Zanetti Thu, 29 Nov 2018 12:48:11 +0100 + -- Jenkins Sun, 09 Dec 2018 18:56:19 +0100 nymea-plugins (0.9.39) xenial; urgency=medium diff --git a/debian/control b/debian/control index a0c0bbde..8b33459e 100644 --- a/debian/control +++ b/debian/control @@ -681,6 +681,21 @@ Description: nymea.io plugin for remote ssh connection This package will install the nymea.io plugin for remote ssh connection +Package: nymea-plugin-unipi +Architecture: any +Depends: ${shlibs:Depends}, + ${misc:Depends}, + nymea-plugins-translations, +Description: nymea.io plugin for remote ssh connection + The nymea daemon is a plugin based IoT (Internet of Things) server. The + server works like a translator for devices, things and services and + allows them to interact. + With the powerful rule engine you are able to connect any device available + in the system and create individual scenes and behaviors for your environment. + . + This package will install the nymea.io plugin for unipi devices + + Package: nymea-plugins-translations Section: misc Architecture: all @@ -743,6 +758,7 @@ Depends: nymea-plugin-commandlauncher, nymea-plugin-gpio, nymea-plugin-mqttclient, nymea-plugin-remotessh, + nymea-plugin-unipi, Replaces: guh-plugins-maker Description: Plugins for nymea IoT server - Meta package for makers, tinkers and hackers The nymea daemon is a plugin based IoT (Internet of Things) server. The diff --git a/debian/copyright b/debian/copyright index ee080ae0..e0309381 100644 --- a/debian/copyright +++ b/debian/copyright @@ -36,9 +36,10 @@ License: LGPL-2.1 Copyright: 2016, Bernhard Trinnes Files: plantcare/* + unipi/* License: LGPL-2.1 -Copyright: 2016-2017, Simon Stürz - 2016, Bernhard Trinnes +Copyright: 2016-2018, Simon Stürz + 2016-2018, Bernhard Trinnes Files: pushbullet/* usbwde/* diff --git a/debian/nymea-plugin-unipi.install.in b/debian/nymea-plugin-unipi.install.in new file mode 100644 index 00000000..c8a07bf7 --- /dev/null +++ b/debian/nymea-plugin-unipi.install.in @@ -0,0 +1 @@ +usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_devicepluginunipi.so diff --git a/nymea-plugins.pro b/nymea-plugins.pro index 2782d88a..76d51a18 100644 --- a/nymea-plugins.pro +++ b/nymea-plugins.pro @@ -38,12 +38,14 @@ PLUGIN_DIRS = \ tasmota \ tcpcommander \ udpcommander \ + unipi \ unitec \ usbwde \ wakeonlan \ wemo \ ws2812 \ + CONFIG+=all message(============================================) diff --git a/unipi/devicepluginunipi.cpp b/unipi/devicepluginunipi.cpp new file mode 100644 index 00000000..05fe779a --- /dev/null +++ b/unipi/devicepluginunipi.cpp @@ -0,0 +1,819 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2017 Bernhard Trinnes * + * Copyright (C) 2018 Simon Stürz * + * * + * This file is part of nymea. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "devicepluginunipi.h" +#include "plugininfo.h" +#include + +DevicePluginUniPi::DevicePluginUniPi() +{ + +} + +DevicePluginUniPi::~DevicePluginUniPi() +{ + hardwareManager()->pluginTimerManager()->unregisterTimer(m_refreshTimer); +} + +void DevicePluginUniPi::init() +{ +} + +DeviceManager::DeviceSetupStatus DevicePluginUniPi::setupDevice(Device *device) +{ + connectToEvok(); + + if(myDevices().empty()) { + m_refreshTimer = hardwareManager()->pluginTimerManager()->registerTimer(60); + connect(m_refreshTimer, &PluginTimer::timeout, this, &DevicePluginUniPi::onRefreshTimer); + } + + if (device->deviceClassId() == relayOutputDeviceClassId) { + + m_usedRelais.insert(device->paramValue(relayOutputDeviceNumberParamTypeId).toString(), device); + return DeviceManager::DeviceSetupStatusSuccess; + } + + if (device->deviceClassId() == digitalOutputDeviceClassId) { + + m_usedDigitalOutputs.insert(device->paramValue(digitalOutputDeviceNumberParamTypeId).toString(), device); + return DeviceManager::DeviceSetupStatusSuccess; + } + + if (device->deviceClassId() == digitalInputDeviceClassId) { + + m_usedDigitalInputs.insert(device->paramValue(digitalInputDeviceNumberParamTypeId).toString(), device); + requestAllData(); + return DeviceManager::DeviceSetupStatusSuccess; + } + + if (device->deviceClassId() == analogInputDeviceClassId) { + + m_usedAnalogInputs.insert(device->paramValue(analogInputDeviceInputNumberParamTypeId).toString(), device); + requestAllData(); + return DeviceManager::DeviceSetupStatusSuccess; + } + + if (device->deviceClassId() == analogOutputDeviceClassId) { + + m_usedAnalogOutputs.insert(device->paramValue(analogOutputDeviceOutputNumberParamTypeId).toString(), device); + return DeviceManager::DeviceSetupStatusSuccess; + } + + if (device->deviceClassId() == blindDeviceClassId) { + + if (device->paramValue(blindDeviceOutputTypeOpenParamTypeId) == GpioType::Relay) { + m_usedRelais.insert(device->paramValue(blindDeviceOutputOpenParamTypeId).toString(), device); + } else if (device->paramValue(blindDeviceOutputTypeOpenParamTypeId) == GpioType::DigitalOutput) { + m_usedDigitalOutputs.insert(device->paramValue(blindDeviceOutputOpenParamTypeId).toString(), device); + } + + if (device->paramValue(blindDeviceOutputTypeCloseParamTypeId) == GpioType::Relay) { + m_usedRelais.insert(device->paramValue(blindDeviceOutputCloseParamTypeId).toString(), device); + } else if (device->paramValue(blindDeviceOutputTypeOpenParamTypeId) == GpioType::DigitalOutput) { + m_usedDigitalOutputs.insert(device->paramValue(blindDeviceOutputCloseParamTypeId).toString(), device); + } + + return DeviceManager::DeviceSetupStatusSuccess; + } + + if (device->deviceClassId() == lightDeviceClassId) { + + if (device->paramValue(lightDeviceOutputTypeParamTypeId) == GpioType::Relay) { + m_usedRelais.insert(device->paramValue(lightDeviceOutputParamTypeId).toString(), device); + } else if (device->paramValue(lightDeviceOutputParamTypeId) == GpioType::DigitalOutput) { + m_usedDigitalOutputs.insert(device->paramValue(lightDeviceOutputParamTypeId).toString(), device); + } + return DeviceManager::DeviceSetupStatusSuccess; + } + + if (device->deviceClassId() == dimmerSwitchDeviceClassId) { + m_usedDigitalInputs.insert(device->paramValue(dimmerSwitchDeviceInputNumberParamTypeId).toString(), device); + DimmerSwitch* dimmerSwitch = new DimmerSwitch(this); + + connect(dimmerSwitch, &DimmerSwitch::pressed, this, &DevicePluginUniPi::onDimmerSwitchPressed); + connect(dimmerSwitch, &DimmerSwitch::longPressed, this, &DevicePluginUniPi::onDimmerSwitchLongPressed); + connect(dimmerSwitch, &DimmerSwitch::doublePressed, this, &DevicePluginUniPi::onDimmerSwitchDoublePressed); + connect(dimmerSwitch, &DimmerSwitch::dimValueChanged, this, &DevicePluginUniPi::onDimmerSwitchDimValueChanged); + m_dimmerSwitches.insert(dimmerSwitch, device); + return DeviceManager::DeviceSetupStatusSuccess; + } + + if (device->deviceClassId() == temperatureSensorDeviceClassId) { + + m_usedTemperatureSensors.insert(device->paramValue(temperatureSensorDeviceAddressParamTypeId).toString(), device); + requestAllData(); + return DeviceManager::DeviceSetupStatusSuccess; + } + + return DeviceManager::DeviceSetupStatusFailure; +} + +DeviceManager::DeviceError DevicePluginUniPi::discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) +{ + Q_UNUSED(params); + qSort(m_relais); + qSort(m_digitalOutputs); + qSort(m_digitalInputs); + qSort(m_analogInputs); + qSort(m_analogOutputs); + + const DeviceClass deviceClass = deviceManager()->findDeviceClass(deviceClassId); + if (deviceClass.vendorId() == unipiVendorId) { + + if (deviceClassId == relayOutputDeviceClassId) { + // Create the list of available relais + QList deviceDescriptors; + for (int i = 0; i < m_relais.count(); i++) { + const QString circuit = m_relais.at(i); + + // Offer only gpios which arn't in use already + if (m_usedRelais.contains(circuit)){ + continue; + } + DeviceDescriptor descriptor(deviceClassId, QString("Relay %1").arg(circuit), circuit); + ParamList parameters; + parameters.append(Param(relayOutputDeviceNumberParamTypeId, circuit)); + + descriptor.setParams(parameters); + deviceDescriptors.append(descriptor); + } + emit devicesDiscovered(deviceClassId, deviceDescriptors); + return DeviceManager::DeviceErrorAsync; + } + + if (deviceClassId == digitalOutputDeviceClassId) { + // Create the list of available digital outputs + QList deviceDescriptors; + for (int i = 0; i < m_digitalOutputs.count(); i++) { + const QString circuit = m_digitalOutputs.at(i); + + // Offer only gpios which arn't in use already + if (m_usedDigitalOutputs.contains(circuit)){ + continue; + } + DeviceDescriptor descriptor(deviceClassId, QString("Digital output %1").arg(circuit), circuit); + ParamList parameters; + parameters.append(Param(digitalOutputDeviceNumberParamTypeId, circuit)); + descriptor.setParams(parameters); + deviceDescriptors.append(descriptor); + } + emit devicesDiscovered(deviceClassId, deviceDescriptors); + return DeviceManager::DeviceErrorAsync; + } + + if (deviceClassId == digitalInputDeviceClassId) { + // Create the list of available digital inputs + QList deviceDescriptors; + for (int i = 0; i < m_digitalInputs.count(); i++) { + const QString circuit = m_digitalInputs.at(i); + + // Offer only digital inputs which arn't in use already + if (m_usedDigitalInputs.contains(circuit)){ + continue; + } + DeviceDescriptor descriptor(deviceClassId, QString("Digital input %1").arg(circuit), circuit); + ParamList parameters; + parameters.append(Param(digitalInputDeviceNumberParamTypeId, circuit)); + descriptor.setParams(parameters); + deviceDescriptors.append(descriptor); + } + emit devicesDiscovered(deviceClassId, deviceDescriptors); + return DeviceManager::DeviceErrorAsync; + } + + if (deviceClassId == analogInputDeviceClassId) { + // Create the list of available digital inputs + QList deviceDescriptors; + for (int i = 0; i < m_analogInputs.count(); i++) { + const QString circuit = m_analogInputs.at(i); + + // Offer only analog inputs which aren't in use already + if (m_usedAnalogInputs.contains(circuit)){ + continue; + } + DeviceDescriptor descriptor(deviceClassId, QString("Analog input %1").arg(circuit), circuit); + ParamList parameters; + parameters.append(Param(analogInputDeviceInputNumberParamTypeId, circuit)); + descriptor.setParams(parameters); + deviceDescriptors.append(descriptor); + } + emit devicesDiscovered(deviceClassId, deviceDescriptors); + return DeviceManager::DeviceErrorAsync; + } + + if (deviceClassId == analogOutputDeviceClassId) { + // Create the list of available digital inputs + QList deviceDescriptors; + for (int i = 0; i < m_analogOutputs.count(); i++) { + const QString circuit = m_analogOutputs.at(i); + + // Offer only digital inputs which arn't in use already + if (m_usedAnalogOutputs.contains(circuit)){ + continue; + } + DeviceDescriptor descriptor(deviceClassId, QString("Analog Output %1").arg(circuit), circuit); + ParamList parameters; + parameters.append(Param(analogOutputDeviceOutputNumberParamTypeId, circuit)); + descriptor.setParams(parameters); + deviceDescriptors.append(descriptor); + } + emit devicesDiscovered(deviceClassId, deviceDescriptors); + return DeviceManager::DeviceErrorAsync; + } + + if (deviceClassId == blindDeviceClassId) { + // Create the list of available gpios + QList deviceDescriptors; + for (int i = 0; i < (m_relais.count()-1); i++) { + + const QString openingCircuit = m_relais.at(i); + + // Offer only relais which aren't in use already + if (m_usedRelais.contains(openingCircuit)){ + continue; + } + for (int a = (i+1); a < (m_relais.count()); a++) { + + const QString closingCircuit = m_relais.at(a); + // Offer only relais which aren't in use already + if (!m_usedRelais.contains(closingCircuit)){ + + DeviceDescriptor descriptor(deviceClassId, "Blind", QString("Opening relay %1 | Closing relay %2").arg(openingCircuit, closingCircuit)); + ParamList parameters; + parameters.append(Param(blindDeviceOutputOpenParamTypeId, openingCircuit)); + parameters.append(Param(blindDeviceOutputCloseParamTypeId, closingCircuit)); + parameters.append(Param(blindDeviceOutputTypeOpenParamTypeId, GpioType::Relay)); + parameters.append(Param(blindDeviceOutputTypeCloseParamTypeId, GpioType::Relay)); + descriptor.setParams(parameters); + deviceDescriptors.append(descriptor); + break; + } + } + } + + for (int i = 0; i < (m_digitalOutputs.count()-1); i++) { + + const QString openingCircuit = m_digitalOutputs.at(i); + + // Offer only relais which aren't in use already + if (m_usedDigitalOutputs.contains(openingCircuit)){ + continue; + } + for (int a = (i+1); a < (m_digitalOutputs.count()); a++) { + + const QString closingCircuit = m_digitalOutputs.at(a); + // Offer only relais which aren't in use already + if (!m_usedDigitalOutputs.contains(closingCircuit)){ + + DeviceDescriptor descriptor(deviceClassId, "Blind", QString("Opening output %1 | Closing output %2").arg(openingCircuit, closingCircuit)); + ParamList parameters; + parameters.append(Param(blindDeviceOutputOpenParamTypeId, openingCircuit)); + parameters.append(Param(blindDeviceOutputCloseParamTypeId, closingCircuit)); + parameters.append(Param(blindDeviceOutputTypeOpenParamTypeId, GpioType::DigitalOutput)); + parameters.append(Param(blindDeviceOutputTypeCloseParamTypeId, GpioType::DigitalOutput)); + descriptor.setParams(parameters); + deviceDescriptors.append(descriptor); + break; + } + } + } + + emit devicesDiscovered(deviceClassId, deviceDescriptors); + return DeviceManager::DeviceErrorAsync; + } + + if (deviceClassId == lightDeviceClassId) { + // Create the list of available gpios + QList deviceDescriptors; + for (int i = 0; i < m_relais.count(); i++) { + const QString circuit = m_relais.at(i); + + // Offer only gpios which arn't in use already + if (m_usedRelais.contains(circuit)){ + continue; + } + DeviceDescriptor descriptor(deviceClassId, "Light", QString("Relay %1").arg(circuit)); + ParamList parameters; + parameters.append(Param(lightDeviceOutputParamTypeId, circuit)); + parameters.append(Param(lightDeviceOutputTypeParamTypeId, GpioType::Relay)); + descriptor.setParams(parameters); + deviceDescriptors.append(descriptor); + } + + for (int i = 0; i < m_digitalOutputs.count(); i++) { + const QString circuit = m_digitalOutputs.at(i); + + // Offer only gpios which arn't in use already + if (m_usedDigitalOutputs.contains(circuit)){ + continue; + } + DeviceDescriptor descriptor(deviceClassId, "Light", QString("Digital output %1").arg(circuit)); + ParamList parameters; + parameters.append(Param(lightDeviceOutputParamTypeId, circuit)); + parameters.append(Param(lightDeviceOutputTypeParamTypeId, GpioType::DigitalOutput)); + descriptor.setParams(parameters); + deviceDescriptors.append(descriptor); + } + emit devicesDiscovered(deviceClassId, deviceDescriptors); + return DeviceManager::DeviceErrorAsync; + } + + if (deviceClassId == dimmerSwitchDeviceClassId) { + // Create the list of available digital inputs + QList deviceDescriptors; + for (int i = 0; i < m_digitalInputs.count(); i++) { + const QString circuit = m_digitalInputs.at(i); + + // Offer only digital inputs which arn't in use already + if (m_usedDigitalInputs.contains(circuit)){ + continue; + } + DeviceDescriptor descriptor(deviceClassId, "Dimmer switch", QString("Digital Input %1").arg(circuit)); + ParamList parameters; + parameters.append(Param(dimmerSwitchDeviceInputNumberParamTypeId, circuit)); + descriptor.setParams(parameters); + deviceDescriptors.append(descriptor); + } + emit devicesDiscovered(deviceClassId, deviceDescriptors); + return DeviceManager::DeviceErrorAsync; + } + + if (deviceClassId == temperatureSensorDeviceClassId) { + // Create the list of available temperature sensor + QList deviceDescriptors; + for (int i = 0; i < m_temperatureSensors.count(); i++) { + const QString circuit = m_temperatureSensors.at(i); + + // Offer only temperature sensors which aren't in use already + if (m_usedTemperatureSensors.contains(circuit)){ + continue; + } + DeviceDescriptor descriptor(deviceClassId, "Temperature Sensor", circuit); + ParamList parameters; + parameters.append(Param(temperatureSensorDeviceAddressParamTypeId, circuit)); + descriptor.setParams(parameters); + deviceDescriptors.append(descriptor); + } + emit devicesDiscovered(deviceClassId, deviceDescriptors); + return DeviceManager::DeviceErrorAsync; + } + } + return DeviceManager::DeviceErrorDeviceClassNotFound; +} + +void DevicePluginUniPi::setOutput(const QString &circuit, bool value) +{ + QJsonObject json; + json["cmd"] = "set"; + json["dev"] = "relay"; + json["circuit"] = circuit; + json["value"] = value; + + QJsonDocument doc(json); + QByteArray bytes = doc.toJson(QJsonDocument::Compact); + qCDebug(dcUniPi()) << "Send command" << bytes; + m_webSocket->sendBinaryMessage(bytes); +} + +void DevicePluginUniPi::connectToEvok() +{ + if ((m_webSocket == NULL) || !m_webSocket) { + + int port = configValue(uniPiPluginPortParamTypeId).toInt(); + + m_webSocket = new QWebSocket(); + connect(m_webSocket, &QWebSocket::connected, this, &DevicePluginUniPi::onWebSocketConnected); + connect(m_webSocket, &QWebSocket::disconnected, this, &DevicePluginUniPi::onWebSocketDisconnected); + + QUrl url = QUrl("ws://localhost/ws"); + url.setPort(port); + qCDebug(dcUniPi()) << "Conneting to:" << url.toString(); + m_webSocket->open(url); + } else { + requestAllData(); + } +} + + +void DevicePluginUniPi::deviceRemoved(Device *device) +{ + if(device->deviceClassId() == relayOutputDeviceClassId) { + m_usedRelais.remove(device->paramValue(relayOutputDeviceNumberParamTypeId).toString()); + } else if(device->deviceClassId() == digitalOutputDeviceClassId) { + m_usedDigitalOutputs.remove(device->paramValue(digitalOutputDeviceNumberParamTypeId).toString()); + } else if(device->deviceClassId() == digitalInputDeviceClassId) { + m_usedDigitalInputs.remove(device->paramValue(digitalInputDeviceNumberParamTypeId).toString()); + } else if (device->deviceClassId() == analogOutputDeviceClassId) { + m_usedAnalogOutputs.remove(device->paramValue(analogOutputDeviceOutputNumberParamTypeId).toString()); + } else if (device->deviceClassId() == analogInputDeviceClassId) { + m_usedAnalogInputs.remove(device->paramValue(analogInputDeviceInputNumberParamTypeId).toString()); + + } else if (device->deviceClassId() == blindDeviceClassId) { + if (device->paramValue(blindDeviceOutputTypeOpenParamTypeId) == GpioType::Relay) { + m_usedRelais.remove(device->paramValue(blindDeviceOutputOpenParamTypeId).toString()); + } else if (device->paramValue(blindDeviceOutputOpenParamTypeId) == GpioType::DigitalOutput) { + m_usedDigitalOutputs.remove(device->paramValue(blindDeviceOutputOpenParamTypeId).toString()); + } + + if (device->paramValue(blindDeviceOutputTypeCloseParamTypeId) == GpioType::Relay) { + m_usedRelais.remove(device->paramValue(blindDeviceOutputCloseParamTypeId).toString()); + } else if (device->paramValue(blindDeviceOutputOpenParamTypeId) == GpioType::DigitalOutput) { + m_usedDigitalOutputs.remove(device->paramValue(blindDeviceOutputCloseParamTypeId).toString()); + } + + } else if (device->deviceClassId() == lightDeviceClassId) { + if (device->paramValue(lightDeviceOutputTypeParamTypeId) == GpioType::Relay) { + m_usedRelais.remove(device->paramValue(lightDeviceOutputParamTypeId).toString()); + } else if (device->paramValue(lightDeviceOutputParamTypeId) == GpioType::DigitalOutput) { + m_usedDigitalOutputs.remove(device->paramValue(lightDeviceOutputParamTypeId).toString()); + } + } else if (device->deviceClassId() == dimmerSwitchDeviceClassId) { + m_usedDigitalInputs.remove(device->paramValue(dimmerSwitchDeviceInputNumberParamTypeId).toString()); + DimmerSwitch *dimmerSwitch = m_dimmerSwitches.key(device); + m_dimmerSwitches.remove(dimmerSwitch); + dimmerSwitch->deleteLater(); + } else if (device->deviceClassId() == temperatureSensorDeviceClassId) { + m_usedTemperatureSensors.remove(device->paramValue(temperatureSensorDeviceAddressParamTypeId).toString()); + } + + + if (myDevices().isEmpty()) { + m_webSocket->close(); + m_webSocket->deleteLater(); + } +} + +DeviceManager::DeviceError DevicePluginUniPi::executeAction(Device *device, const Action &action) +{ + if (m_webSocket->state() != QAbstractSocket::ConnectedState) + return DeviceManager::DeviceErrorHardwareNotAvailable; + + if (device->deviceClassId() == relayOutputDeviceClassId) { + + if (action.actionTypeId() == relayOutputPowerActionTypeId) { + QString relayNumber = device->paramValue(relayOutputDeviceNumberParamTypeId).toString(); + int stateValue = action.param(relayOutputPowerActionPowerParamTypeId).value().toInt(); + setOutput(relayNumber, stateValue); + + return DeviceManager::DeviceErrorNoError; + } + return DeviceManager::DeviceErrorActionTypeNotFound; + } + + if (device->deviceClassId() == digitalOutputDeviceClassId) { + if (action.actionTypeId() == digitalOutputPowerActionTypeId) { + QString digitalOutputNumber = device->paramValue(digitalOutputDeviceNumberParamTypeId).toString(); + bool stateValue = action.param(digitalOutputPowerActionPowerParamTypeId).value().toBool(); + setOutput(digitalOutputNumber, stateValue); + + return DeviceManager::DeviceErrorNoError; + } + return DeviceManager::DeviceErrorActionTypeNotFound; + } + + if (device->deviceClassId() == analogOutputDeviceClassId) { + + if (action.actionTypeId() == analogOutputOutputValueActionTypeId) { + QString analogOutputNumber = device->paramValue(analogOutputDeviceOutputNumberParamTypeId).toString(); + double analogValue = action.param(analogOutputOutputValueActionOutputValueParamTypeId).value().toDouble(); + + QJsonObject json; + json["cmd"] = "set"; + json["dev"] = "ao"; + json["circuit"] = analogOutputNumber; + json["value"] = analogValue; + + QJsonDocument doc(json); + QByteArray bytes = doc.toJson(QJsonDocument::Compact); + qCDebug(dcUniPi()) << "Send command" << bytes; + m_webSocket->sendTextMessage(bytes); + return DeviceManager::DeviceErrorNoError; + } + return DeviceManager::DeviceErrorActionTypeNotFound; + } + + if (device->deviceClassId() == blindDeviceClassId) { + QString circuitOpen = device->paramValue(blindDeviceOutputOpenParamTypeId).toString(); + QString circuitClose = device->paramValue(blindDeviceOutputCloseParamTypeId).toString(); + + if (action.actionTypeId() == blindCloseActionTypeId) { + + setOutput(circuitOpen, false); + setOutput(circuitClose, true); + return DeviceManager::DeviceErrorNoError; + } + if (action.actionTypeId() == blindOpenActionTypeId) { + + setOutput(circuitClose, false); + setOutput(circuitOpen, true); + return DeviceManager::DeviceErrorNoError; + } + if (action.actionTypeId() == blindStopActionTypeId) { + setOutput(circuitOpen, false); + setOutput(circuitClose, false); + + return DeviceManager::DeviceErrorNoError; + } + return DeviceManager::DeviceErrorActionTypeNotFound; + } + + if (device->deviceClassId() == lightDeviceClassId) { + + QString circuit = device->paramValue(lightDeviceOutputParamTypeId).toString(); + bool stateValue = action.param(lightPowerActionPowerParamTypeId).value().toBool(); + + setOutput(circuit, stateValue); + return DeviceManager::DeviceErrorNoError; + } + + return DeviceManager::DeviceErrorDeviceClassNotFound; +} + + +void DevicePluginUniPi::onWebSocketConnected() +{ + qCDebug(dcUniPi()) << "WebSocket connected"; + + connect(m_webSocket, &QWebSocket::textMessageReceived, + this, &DevicePluginUniPi::onWebSocketTextMessageReceived); + + requestAllData(); +} + +void DevicePluginUniPi::onWebSocketDisconnected() +{ + qCDebug(dcUniPi()) << "WebSocket disconnected"; + +} + +void DevicePluginUniPi::requestAllData() +{ + QJsonObject json; + json["cmd"] = "all"; + + QJsonDocument doc(json); + QByteArray bytes = doc.toJson(); + m_webSocket->sendTextMessage(bytes); +} + +void DevicePluginUniPi::onWebSocketTextMessageReceived(const QString &message) +{ + QJsonArray array; + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(message.toUtf8(), &error); + + + if(error.error != QJsonParseError::NoError) { + qCWarning(dcUniPi) << "failed to parse data" << message << ":" << error.errorString(); + return; + } + + // check validity of the document + if(!doc.isNull()) { + if(doc.isObject()) { + array.append(doc.object()); + } else if (doc.isArray()){ + array = doc.array();; + }else { + qCDebug(dcUniPi()) << "Document is not an object nor an array"; + } + } else { + qCDebug(dcUniPi()) << "Invalid JSON"; + return; + } + + for (int levelIndex = 0; levelIndex < array.size(); ++levelIndex) { + QJsonObject obj; + obj = array[levelIndex].toObject(); + + if (obj["dev"] == "relay") { + qCDebug(dcUniPi()) << "Relay:" << "Circuit:" << obj["circuit"].toString() << "Value:" << obj["value"].toInt() << "Relay Type:" << obj["relay_type"].toString() ; + + QString circuit = obj["circuit"].toString(); + bool value = obj["value"].toBool(); + + if ((obj["relay_type"].toString() == "physical") || (obj["relay_type"].toString() == "")) { + + if (!m_relais.contains(circuit)) { + //New Device detected + m_relais.append(circuit); + } else { + if (m_usedRelais.contains(circuit)) { + Device *device = m_usedRelais.value(circuit); + if (device->deviceClassId() == relayOutputDeviceClassId) { + device->setStateValue(relayOutputPowerStateTypeId, value); + } else if (device->deviceClassId() == blindDeviceClassId) { + if (circuit == device->paramValue(blindDeviceOutputOpenParamTypeId).toString()) { + if (value) { + if (device->stateValue(blindStatusStateTypeId).toString().contains("stopped")) { + device->setStateValue(blindStatusStateTypeId, "opening"); + } else if (device->stateValue(blindStatusStateTypeId).toString().contains("closing")) { + device->setStateValue(blindStatusStateTypeId, "opening"); + } else if (device->stateValue(blindStatusStateTypeId).toString().contains("opening")) { + //state unchanged + } + } else { + if (device->stateValue(blindStatusStateTypeId).toString().contains("stopped")) { + // state unchanged + } else if (device->stateValue(blindStatusStateTypeId).toString().contains("closing")) { + // state unchanged + } else if (device->stateValue(blindStatusStateTypeId).toString().contains("opening")) { + device->setStateValue(blindStatusStateTypeId, "stopped"); + } + } + } + if (circuit == device->paramValue(blindDeviceOutputCloseParamTypeId).toString()) { + if (value) { + if (device->stateValue(blindStatusStateTypeId).toString().contains("stopped")) { + device->setStateValue(blindStatusStateTypeId, "closing"); + } else if (device->stateValue(blindStatusStateTypeId).toString().contains("closing")) { + //state unchanged + } else if (device->stateValue(blindStatusStateTypeId).toString().contains("opening")) { + device->setStateValue(blindStatusStateTypeId, "closing"); + } + } else { + if (device->stateValue(blindStatusStateTypeId).toString().contains("stopped")) { + // state unchanged + } else if (device->stateValue(blindStatusStateTypeId).toString().contains("closing")) { + device->setStateValue(blindStatusStateTypeId, "stopped"); + } else if (device->stateValue(blindStatusStateTypeId).toString().contains("opening")) { + // state unchanged + } + } + } + + } else if (device->deviceClassId() == lightDeviceClassId) { + device->setStateValue(lightPowerStateTypeId, value); + } + } + } + } else if (obj["relay_type"].toString() == "digital") { + if (!m_digitalOutputs.contains(obj["circuit"].toString())){ + //New Device detected + m_digitalOutputs.append(obj["circuit"].toString()); + } else { + if (m_usedDigitalOutputs.contains(obj["circuit"].toString())) { + Device *device = m_usedDigitalOutputs.value(obj["circuit"].toString()); + if (device->deviceClassId() == digitalOutputDeviceClassId) { + device->setStateValue(digitalOutputPowerStateTypeId, obj["value"].toBool()); + } else if (device->deviceClassId() == blindDeviceClassId) { + if (circuit == device->paramValue(blindDeviceOutputOpenParamTypeId).toString()) { + if (value && device->stateValue(blindStatusStateTypeId).toString().contains("stopped")) { + device->setStateValue(blindStatusStateTypeId, "opening"); + } else if (!value && device->stateValue(blindStatusStateTypeId).toString().contains("opening")) { + device->setStateValue(blindStatusStateTypeId, "stopped"); + } else { + qCWarning(dcUniPi()) << "blind" << device << "Output open:" << value << "Status: " << device->stateValue(blindStatusStateTypeId).toString(); + device->setStateValue(blindStatusStateTypeId, "stopped"); + } + } + if (circuit == device->paramValue(blindDeviceOutputCloseParamTypeId).toString()) { + if (value && device->stateValue(blindStatusStateTypeId).toString().contains("stopped")) { + device->setStateValue(blindStatusStateTypeId, "closing"); + } else if (!value && device->stateValue(blindStatusStateTypeId).toString().contains("closing")) { + device->setStateValue(blindStatusStateTypeId, "stopped"); + } else { + qCWarning(dcUniPi()) << "blind" << device << "Output close:" << value << "Status: " << device->stateValue(blindStatusStateTypeId).toString(); + device->setStateValue(blindStatusStateTypeId, "stopped"); + } + } + } else if (device->deviceClassId() == lightDeviceClassId) { + device->setStateValue(lightPowerStateTypeId, obj["value"].toBool()); + } + } + } + } + } + + if (obj["dev"] == "input") { + qCDebug(dcUniPi()) << "Input:" << obj["dev"].toString() << "Circuit:" << obj["circuit"].toString() << "Value:" << obj["value"].toInt(); + + if (!m_digitalInputs.contains(obj["circuit"].toString())){ + //New Device detected + m_digitalInputs.append(obj["circuit"].toString()); + } else { + if (m_usedDigitalInputs.contains(obj["circuit"].toString())) { + bool value = obj["value"].toBool(); + Device *device = m_usedDigitalInputs.value(obj["circuit"].toString()); + if (device->deviceClassId() == digitalInputDeviceClassId) { + device->setStateValue(digitalInputInputStatusStateTypeId, value); + } else if (device->deviceClassId() == dimmerSwitchDeviceClassId) { + device->setStateValue(dimmerSwitchStatusStateTypeId, value); + DimmerSwitch *dimmerSwitch = m_dimmerSwitches.key(device); + dimmerSwitch->setPower(value); + } + } + } + } + + if (obj["dev"] == "ao") { + qCDebug(dcUniPi()) << "Analog Output:" << obj["dev"] << "Circuit:" << obj["circuit"].toString() << "Value:" << obj["value"].toDouble(); + + if (!m_analogOutputs.contains(obj["circuit"].toString())){ + //New Device detected + m_analogOutputs.append(obj["circuit"].toString()); + } else { + if (m_usedAnalogOutputs.contains(obj["circuit"].toString())) { + double value = obj["value"].toDouble(); + Device *device = m_usedAnalogOutputs.value(obj["circuit"].toString()); + + if (device->deviceClassId() == analogOutputDeviceClassId) { + device->setStateValue(analogOutputOutputValueStateTypeId, value); + } + } + } + } + + if (obj["dev"] == "ai") { + qCDebug(dcUniPi()) << "Analog Input:" << obj["dev"] << "Circuit:" << obj["circuit"].toString() << "Value:" << obj["value"].toDouble(); + + if (!m_analogInputs.contains(obj["circuit"].toString())){ + //New analog output detected + m_analogInputs.append(obj["circuit"].toString()); + } else { + if (m_usedAnalogInputs.contains(obj["circuit"].toString())) { + double value = obj["value"].toDouble(); + Device *device = m_usedAnalogInputs.value(obj["circuit"].toString()); + + if (device->deviceClassId() == analogInputDeviceClassId) { + device->setStateValue(analogInputInputValueStateTypeId, value); + } + } + } + } + + if (obj["dev"] == "temp") { + qCDebug(dcUniPi()) << "Temperature Sensor:" << obj["typ"].toString() << "Address:" << obj["circuit"].toString() << "Value:" << obj["value"].toDouble() << "Connected:" << !(QVariant(obj["lost"]).toBool()); + if (!m_temperatureSensors.contains(obj["circuit"].toString())){ + //New temperature sensor detected + m_temperatureSensors.append(obj["circuit"].toString()); + } else { + //Updating states of already added temperature sensor + if (m_usedTemperatureSensors.contains(obj["circuit"].toString())) { + double value = obj["value"].toDouble(); + bool connected = !(obj["lost"]).toBool(); + Device *device = m_usedTemperatureSensors.value(obj["circuit"].toString()); + + if (device->deviceClassId() == temperatureSensorDeviceClassId) { + device->setStateValue(temperatureSensorTemperatureStateTypeId, value); + device->setStateValue(temperatureSensorConnectedStateTypeId, connected); + } + } + } + } + } +} + +void DevicePluginUniPi::onRefreshTimer() +{ + connectToEvok(); +} + +void DevicePluginUniPi::onDimmerSwitchPressed() +{ + DimmerSwitch *dimmerSwitch = static_cast(sender()); + Device *device = m_dimmerSwitches.value(dimmerSwitch); + emit emitEvent(Event(dimmerSwitchPressedEventTypeId, device->id())); +} + +void DevicePluginUniPi::onDimmerSwitchLongPressed() +{ + DimmerSwitch *dimmerSwitch = static_cast(sender()); + Device *device = m_dimmerSwitches.value(dimmerSwitch); + emit emitEvent(Event(dimmerSwitchLongPressedEventTypeId, device->id())); +} + +void DevicePluginUniPi::onDimmerSwitchDoublePressed() +{ + DimmerSwitch *dimmerSwitch = static_cast(sender()); + Device *device = m_dimmerSwitches.value(dimmerSwitch); + emit emitEvent(Event(dimmerSwitchDoublePressedEventTypeId, device->id())); +} + +void DevicePluginUniPi::onDimmerSwitchDimValueChanged(int dimValue) +{ + DimmerSwitch *dimmerSwitch = static_cast(sender()); + Device *device = m_dimmerSwitches.value(dimmerSwitch); + device->setStateValue(dimmerSwitchDimValueStateTypeId, dimValue); +} diff --git a/unipi/devicepluginunipi.h b/unipi/devicepluginunipi.h new file mode 100644 index 00000000..a9ee8982 --- /dev/null +++ b/unipi/devicepluginunipi.h @@ -0,0 +1,100 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2017 Bernhard Trinnes * + * Copyright (C) 2018 Simon Stürz * + * * + * This file is part of nymea. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef DEVICEPLUGINUNIPI_H +#define DEVICEPLUGINUNIPI_H + +#include "plugin/deviceplugin.h" +#include "devicemanager.h" +#include +#include "plugintimer.h" +#include "dimmerswitch.h" + +class DevicePluginUniPi : public DevicePlugin +{ + Q_OBJECT + + Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "devicepluginunipi.json") + Q_INTERFACES(DevicePlugin) + +public: + + explicit DevicePluginUniPi(); + ~DevicePluginUniPi(); + + void init() override; + DeviceManager::DeviceSetupStatus setupDevice(Device *device) override; + void deviceRemoved(Device *device) override; + DeviceManager::DeviceError executeAction(Device *device, const Action &action) override; + DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override; + + +private: + + enum GpioType { + Relay, + DigitalInput, + DigitalOutput, + AnalogInput, + AnalogOutput + }; + + QHash m_usedRelais; + QHash m_usedDigitalOutputs; + QHash m_usedDigitalInputs; + QHash m_usedAnalogOutputs; + QHash m_usedAnalogInputs; + QHash m_usedTemperatureSensors; + QHash m_usedLeds; + + QHash m_dimmerSwitches; + + QList m_relais; + QList m_digitalOutputs; + QList m_digitalInputs; + QList m_analogOutputs; + QList m_analogInputs; + QList m_temperatureSensors; + QList m_leds; + + QWebSocket *m_webSocket = nullptr; + + PluginTimer *m_refreshTimer = nullptr; + + void requestAllData(); + void setOutput(const QString &circuit, bool value); + void connectToEvok(); + +private slots: + void onWebSocketConnected(); + void onWebSocketDisconnected(); + void onWebSocketTextMessageReceived(const QString &message); + void onRefreshTimer(); + + void onDimmerSwitchPressed(); + void onDimmerSwitchLongPressed(); + void onDimmerSwitchDoublePressed(); + void onDimmerSwitchDimValueChanged(int dimValue); +}; + +#endif // DEVICEPLUGINUNIPI_H diff --git a/unipi/devicepluginunipi.json b/unipi/devicepluginunipi.json new file mode 100644 index 00000000..b6db4aec --- /dev/null +++ b/unipi/devicepluginunipi.json @@ -0,0 +1,360 @@ +{ + "displayName": "UniPi", + "name": "UniPi", + "id": "26cba644-35ae-40a6-9c48-924198893a5f", + "paramTypes": [ + { + "id": "5329655d-7e91-4b16-9abf-2abc82bf1b3c", + "name": "port", + "displayName": "Port", + "type": "int", + "defaultValue": "8080" + } + ], + "vendors": [ + { + "displayName": "UniPi", + "name": "unipi", + "id": "c82bfe27-d14d-40bd-b12f-ddba214b5fc5", + "deviceClasses": [ + { + "id": "58f9db7f-fd33-45af-8c98-047b67ae5267", + "name": "relayOutput", + "displayName": "Relay output", + "deviceIcon": "Power", + "createMethods": ["discovery"], + "interfaces": ["power"], + "basicTags": ["Device"], + "paramTypes": [ + { + "id": "7a09e3ad-452c-4bf4-a00c-f8114ed9a7a1", + "name": "number", + "displayName": "Relay number", + "type": "QString" + } + ], + "stateTypes": [ + { + "id": "f9c01e7b-0523-4cac-905a-d5b20028e021", + "name": "power", + "displayName": "Power", + "displayNameEvent": "Relay power changed", + "displayNameAction": "Set relay power", + "type": "bool", + "defaultValue": false, + "writable": true + } + ] + }, + { + "id": "0bec278a-98f1-416b-b496-6d00740f178a", + "name": "digitalInput", + "displayName": "Digital input", + "deviceIcon": "Switch", + "createMethods": ["discovery"], + "interfaces": [ ], + "basicTags": ["Sensor"], + "paramTypes": [ + { + "id": "9c84d9b8-fdc7-41c1-9559-08f061ffc7a6", + "name": "number", + "displayName": "Input number", + "type": "QString" + } + ], + "stateTypes": [ + { + "id": "fa4f2764-b7ff-45e7-993b-b6af1840fd3d", + "name": "inputStatus", + "displayName": "Digital input", + "displayNameEvent": "Digital input changed", + "type": "bool", + "defaultValue": false + } + ] + }, + { + "id": "71e03d00-1b62-412b-b55d-ab90ad2eddff", + "name": "dimmerSwitch", + "displayName": "Dimmer switch", + "deviceIcon": "Switch", + "createMethods": ["discovery"], + "interfaces": ["longpressbutton"], + "basicTags": ["Sensor"], + "paramTypes": [ + { + "id": "2344dedb-3e21-4f59-a016-0fc6233a38ac", + "name": "inputNumber", + "displayName": "Input number", + "type": "QString" + } + ], + "stateTypes": [ + { + "id": "b39a84ff-45c8-4ec1-b3a9-b99aeefc7221", + "name": "status", + "displayName": "Digital input", + "displayNameEvent": "Digital input changed", + "type": "bool", + "defaultValue": false + }, + { + "id": "3e566b7c-11e6-4e97-9d9f-9636c465639e", + "name": "dimValue", + "displayName": "Dim value", + "displayNameEvent": "Dim value changed", + "type": "int", + "defaultValue": 0, + "unit": "Percentage" + } + ], + "eventTypes": [ + { + "id": "44be91cd-cbeb-477b-bb8c-6a6d4f9aaaf3", + "name": "pressed", + "displayName": "Pressed" + }, + { + "id": "a4748afe-dcd6-45ea-8574-7b9c0e925f35", + "name": "longPressed", + "displayName": "Long pressed" + }, + { + "id": "7e7b36f5-b871-43a3-a7f0-6cef1e550ad7", + "name": "doublePressed", + "displayName": "Double pressed" + } + ] + }, + { + "id": "f3a3c5ed-461a-4ca8-930b-df3af821b9e0", + "name": "digitalOutput", + "displayName": "Digital output", + "deviceIcon": "Power", + "createMethods": ["discovery"], + "interfaces": ["power"], + "basicTags": ["Device"], + "paramTypes": [ + { + "id": "c01d5bde-de5d-42c5-b462-79745827875a", + "name": "number", + "displayName": "Output number", + "type": "QString" + } + ], + "stateTypes": [ + { + "id": "470a0e30-a170-47ed-9ed3-c41db919555f", + "name": "power", + "displayName": "Power", + "displayNameAction": "set digital output", + "displayNameEvent": "digital output changed", + "type": "bool", + "defaultValue": false, + "writable": true + } + ] + }, + { + "id": "9094a69f-f475-4050-a345-5ab52cb19774", + "name": "analogOutput", + "displayName": "Analog output", + "deviceIcon": "Power", + "createMethods": ["discovery"], + "interfaces": [ ], + "basicTags": ["Device"], + "paramTypes": [ + { + "id": "46e606cc-67ee-4891-bc39-8fb0565c87da", + "name": "outputNumber", + "displayName": "Analog output number", + "type": "QString" + } + ], + "stateTypes": [ + { + "id": "6d825eb8-6d2a-4ac3-9125-9df8173116c9", + "name": "outputValue", + "displayName": "Analog output", + "displayNameEvent": "Analog output changed", + "displayNameAction": "Set analog output", + "type": "double", + "unit": "Volt", + "minValue": 0.00, + "maxValue": 10.00, + "defaultValue": 0.00, + "writable": true + } + ] + }, + { + "id": "06abd6a4-e655-4243-bc9c-9bd4ef5be2e6", + "name": "analogInput", + "displayName": "Analog Input", + "deviceIcon": "Power", + "createMethods": ["discovery"], + "interfaces": [ ], + "basicTags": ["Sensor"], + "paramTypes": [ + { + "id": "cc6eb664-9fd2-457d-9d0d-0eb9703db4a2", + "name": "inputNumber", + "displayName": "Analog input number", + "type": "QString" + } + ], + "stateTypes": [ + { + "id": "2296f575-cc53-48ef-9086-6a412abfdde5", + "name": "inputValue", + "displayName": "Analog input", + "displayNameEvent": "Analog input changed", + "type": "double", + "unit": "Volt", + "defaultValue": 0.00 + } + ] + }, + { + "id": "eadddc99-ce7d-4169-a2f9-a829fa105ad2", + "name": "blind", + "displayName": "Blind", + "deviceIcon": "Blinds", + "createMethods": ["discovery"], + "interfaces": ["blind"], + "basicTags": ["Actuator"], + "paramTypes": [ + { + "id": "7878ef4b-2395-4995-b17d-b69cb7d280e1", + "name": "outputOpen", + "displayName": "Output open", + "type": "QString" + }, + { + "id": "4aa113d0-294b-403d-9dd1-ad0abe833176", + "name": "outputClose", + "displayName": "Output close", + "type": "QString" + }, + { + "id": "a030f030-4f26-4f33-aadf-ec3c3a5141a7", + "name": "outputTypeOpen", + "displayName": "Output type open", + "type": "QString", + "readOnly": true + }, + { + "id": "fde509c5-8db1-411b-9e71-36997c39ee6b", + "name": "outputTypeClose", + "displayName": "Output type close", + "type": "QString", + "readOnly": true + } + ], + "stateTypes":[ + { + "id": "b8955f33-780a-48a5-9c50-c1bccf09918f", + "name": "status", + "displayName": "Status", + "displayNameEvent": "Status changed", + "type": "QString", + "possibleValues":[ + "opening", + "stopped", + "closing" + ], + "defaultValue": "stopped" + } + ], + "actionTypes":[ + { + "id": "cc8a13ae-2a3d-440a-a127-962fb912e511", + "name": "open", + "displayName": "Open" + }, + { + "id": "19bda8bb-1a78-4f2f-9927-ff543012462e", + "name": "stop", + "displayName": "Stop" + }, + { + "id": "7dc32e36-6a5c-46e1-8507-d9283067ac21", + "name": "close", + "displayName": "Close" + } + ] + }, + { + "id": "34fc941e-3465-4618-acf6-dda9c7242e27", + "name": "light", + "displayName": "Light", + "deviceIcon": "LightBulb", + "createMethods": ["discovery"], + "interfaces": ["light"], + "basicTags": ["Actuator"], + "paramTypes": [ + { + "id": "e1375def-edd6-4886-8e87-73cf3ebc819d", + "name": "output", + "displayName": "Output", + "type": "QString" + }, + { + "id": "5853de25-94c6-4f50-95cd-f1e3ef2ebc59", + "name": "outputType", + "displayName": "Output Type", + "type": "QString", + "readOnly": true + } + ], + "stateTypes":[ + { + "id": "3d0d6abc-87b9-42af-820e-e3bc7bde1743", + "name": "power", + "displayName": "Power", + "displayNameEvent": "Power changed", + "displayNameAction": "Set power", + "type": "bool", + "defaultValue": false, + "writable": true + } + ] + }, + { + "id": "4f0b3cfd-603a-47ec-9719-2db7eeae1143", + "name": "temperatureSensor", + "displayName": "Temperature sensor", + "deviceIcon": "Thermometer", + "createMethods": ["discovery"], + "interfaces": ["temperaturesensor", "connectable"], + "paramTypes": [ + { + "id": "4684cee2-674e-4aa4-823d-096bd49f18ee", + "name": "address", + "displayName": "Address", + "type": "QString" + } + ], + "stateTypes":[ + { + "id": "7641d379-b832-40fc-a29b-7d32bba79236", + "name": "connected", + "displayName": "Connected", + "displayNameEvent": "Connection changed", + "type": "bool", + "defaultValue": false + }, + { + "id": "7fd10e94-f11d-4084-8d03-414fd5592b6a", + "displayName": "Temperature", + "displayNameEvent": "Temperature changed", + "name": "temperature", + "type": "double", + "defaultValue": 0.00 + } + ] + } + ] + } + ] +} diff --git a/unipi/dimmerswitch.cpp b/unipi/dimmerswitch.cpp new file mode 100644 index 00000000..d6440f1f --- /dev/null +++ b/unipi/dimmerswitch.cpp @@ -0,0 +1,90 @@ +#include "dimmerswitch.h" + +DimmerSwitch::DimmerSwitch(QObject *parent) : QObject(parent) +{ + m_longPressedTimer = new QTimer(this); + m_longPressedTimer->setSingleShot(true); + connect(m_longPressedTimer, SIGNAL(timeout()), this, SLOT(onLongPressedTimeout())); + + m_doublePressedTimer = new QTimer(this); + m_doublePressedTimer->setSingleShot(true); + + m_dimmerTimer = new QTimer(this); + connect(m_dimmerTimer, SIGNAL(timeout()), this, SLOT(onDimmerTimeout())); +} + +DimmerSwitch::~DimmerSwitch() +{ + m_longPressedTimer->deleteLater(); + m_doublePressedTimer->deleteLater(); + m_dimmerTimer->deleteLater(); +} + +void DimmerSwitch::setPower(const bool power) +{ + if (m_power == power) { + return; + } + + m_power = power; + if(power){ + m_dimmerTimer->start(250); + m_longPressedTimer->start(2000); + + if (m_doublePressedTimer->isActive()) { + m_doublePressedTimer->stop(); + emit doublePressed(); + } else { + m_doublePressedTimer->start(1000); + emit pressed(); + } + + } else { + m_dimmerTimer->stop(); + m_longPressedTimer->stop(); + } +} + +bool DimmerSwitch::getPower() +{ + return m_power; +} + +void DimmerSwitch::setDimValue(const int dimValue) +{ + m_dimValue = dimValue; + emit dimValueChanged(m_dimValue); +} + +int DimmerSwitch::getDimValue() +{ + return m_dimValue; +} + +void DimmerSwitch::onDimmerTimeout() +{ + if(!m_longPressedTimer->isActive()) { + if (m_countingUp) { + m_dimValue += 5; + if(m_dimValue >= 100) { + m_dimValue = 100; + m_countingUp = false; + } + } else { + m_dimValue -= 5; + if(m_dimValue <= 0) { + m_dimValue = 0; + m_countingUp = true; + } + } + emit dimValueChanged(m_dimValue); + } + +} + +void DimmerSwitch::onLongPressedTimeout() +{ + emit longPressed(); +} + + diff --git a/unipi/dimmerswitch.h b/unipi/dimmerswitch.h new file mode 100644 index 00000000..06c106bb --- /dev/null +++ b/unipi/dimmerswitch.h @@ -0,0 +1,42 @@ +#ifndef DIMMERSWITCH_H +#define DIMMERSWITCH_H + +#include +#include + +class DimmerSwitch : public QObject +{ + Q_OBJECT +public: + explicit DimmerSwitch(QObject *parent = 0); + ~DimmerSwitch(); + + void setPower(const bool power); + bool getPower(); + void setDimValue(const int dimValue); + int getDimValue(); + + QTimer *m_doublePressedTimer = nullptr; + QTimer *m_longPressedTimer = nullptr; + QTimer *m_dimmerTimer = nullptr; + +private: + bool m_power; + int m_dimValue = 0; + bool m_countingUp = true; + bool m_powerWasLow = false; //flag to indicate the power was low within the double pressed time frame + + +signals: + void pressed(); + void longPressed(); + void doublePressed(); + void dimValueChanged(int dimValue); + +private slots: + void onLongPressedTimeout(); + void onDimmerTimeout(); + +}; + +#endif // DIMMERSWITCH_H diff --git a/unipi/translations/26cba644-35ae-40a6-9c48-924198893a5f-en_US.ts b/unipi/translations/26cba644-35ae-40a6-9c48-924198893a5f-en_US.ts new file mode 100644 index 00000000..f7f66d85 --- /dev/null +++ b/unipi/translations/26cba644-35ae-40a6-9c48-924198893a5f-en_US.ts @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/unipi/unipi.pro b/unipi/unipi.pro new file mode 100644 index 00000000..60a45796 --- /dev/null +++ b/unipi/unipi.pro @@ -0,0 +1,13 @@ +include(../plugins.pri) + +TARGET = $$qtLibraryTarget(nymea_devicepluginunipi) + +SOURCES += \ + devicepluginunipi.cpp \ + dimmerswitch.cpp + +HEADERS += \ + devicepluginunipi.h \ + dimmerswitch.h + +