diff --git a/debian/control b/debian/control index 5415aa05..5b83a7cd 100644 --- a/debian/control +++ b/debian/control @@ -725,7 +725,7 @@ Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, nymea-plugins-translations, -Description: nymea.io plugin for remote ssh connection +Description: nymea.io plugin for UniPi devices 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. @@ -749,6 +749,21 @@ Description: nymea.io plugin for remote ssh connection . This package will install the nymea.io plugin for unipi devices +Package: nymea-plugin-serialportcommander +Architecture: any +Depends: ${shlibs:Depends}, + ${misc:Depends}, + libqt5serialport5, + nymea-plugins-translations, +Description: nymea.io plugin to send and receive strings over a serial port + 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 serial ports + Package: nymea-plugins-translations Section: misc @@ -816,6 +831,7 @@ Depends: nymea-plugin-boblight, nymea-plugin-mqttclient, nymea-plugin-remotessh, nymea-plugin-unipi, + nymea-plugin-serialportcommander, 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/nymea-plugin-serialportcommander.install.in b/debian/nymea-plugin-serialportcommander.install.in new file mode 100644 index 00000000..b5a25a6c --- /dev/null +++ b/debian/nymea-plugin-serialportcommander.install.in @@ -0,0 +1 @@ +usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_devicepluginserialportcommander.so diff --git a/nymea-plugins.pro b/nymea-plugins.pro index 7e7689f6..6e31b8c3 100644 --- a/nymea-plugins.pro +++ b/nymea-plugins.pro @@ -36,6 +36,7 @@ PLUGIN_DIRS = \ pushbullet \ remotessh \ senic \ + serialportcommander \ simulation \ snapd \ tasmota \ diff --git a/serialportcommander/devicepluginserialportcommander.cpp b/serialportcommander/devicepluginserialportcommander.cpp new file mode 100644 index 00000000..20d7fed2 --- /dev/null +++ b/serialportcommander/devicepluginserialportcommander.cpp @@ -0,0 +1,219 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2019 Bernhard Trinnes * + * * + * 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 * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/*! + \page serialportcommander.html + \title Serial Port Commander + \brief Plug-In to send and receive strings over a serial port. + + \ingroup plugins + \ingroup nymea-plugins + + \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/serialportcommander/devicepluginserialportcommander.json +*/ + +#include "devicepluginserialportcommander.h" +#include "plugininfo.h" + +DevicePluginSerialPortCommander::DevicePluginSerialPortCommander() +{ +} + +DeviceManager::DeviceSetupStatus DevicePluginSerialPortCommander::setupDevice(Device *device) +{ + if (device->deviceClassId() == serialPortCommanderDeviceClassId) { + QString interface = device->paramValue(serialPortCommanderDeviceSerialPortParamTypeId).toString(); + + if (!m_usedInterfaces.contains(interface)) { + + QSerialPort *serialPort = new QSerialPort(interface, this); + if(!serialPort) + return DeviceManager::DeviceSetupStatusFailure; + + serialPort->setBaudRate(device->paramValue(serialPortCommanderDeviceBaudRateParamTypeId).toInt()); + serialPort->setDataBits(QSerialPort::DataBits(device->paramValue(serialPortCommanderDeviceDataBitsParamTypeId).toInt())); + + if (device->paramValue(serialPortCommanderDeviceParityParamTypeId).toString().contains("Even")) { + serialPort->setParity(QSerialPort::Parity::EvenParity ); + } else if (device->paramValue(serialPortCommanderDeviceParityParamTypeId).toString().contains("Odd")) { + serialPort->setParity(QSerialPort::Parity::OddParity ); + } else if (device->paramValue(serialPortCommanderDeviceParityParamTypeId).toString().contains("Space")) { + serialPort->setParity(QSerialPort::Parity::SpaceParity ); + } else if (device->paramValue(serialPortCommanderDeviceParityParamTypeId).toString().contains("Mark")) { + serialPort->setParity(QSerialPort::Parity::MarkParity ); + } else { + serialPort->setParity(QSerialPort::Parity::NoParity); + } + + serialPort->setStopBits(QSerialPort::StopBits(device->paramValue(serialPortCommanderDeviceStopBitsParamTypeId).toInt())); + + if (device->paramValue(serialPortCommanderDeviceFlowControlParamTypeId).toString().contains("Hardware")) { + serialPort->setFlowControl(QSerialPort::FlowControl::HardwareControl); + } else if (device->paramValue(serialPortCommanderDeviceFlowControlParamTypeId).toString().contains("Software")) { + serialPort->setFlowControl(QSerialPort::FlowControl::SoftwareControl); + } else { + serialPort->setFlowControl(QSerialPort::FlowControl::NoFlowControl); + } + + if (!serialPort->open(QIODevice::ReadWrite)) { + qCWarning(dcSerialPortCommander()) << "Could not open serial port" << interface << serialPort->errorString(); + return DeviceManager::DeviceSetupStatusFailure; + } + + connect(serialPort, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(onSerialError(QSerialPort::SerialPortError))); + connect(serialPort, SIGNAL(readyRead()), this, SLOT(onReadyRead())); + connect(serialPort, SIGNAL(baudRateChanged(qint32, QSerialPort::Direction)), this, SLOT(onBaudRateChanged(qint32, QSerialPort::Direction))); + connect(serialPort, SIGNAL(parityChanged(QSerialPort::Parity)), this, SLOT(onParityChanged(QSerialPort::Parity))); + connect(serialPort, SIGNAL(dataBitsChanged(QSerialPort::DataBits)), this, SLOT(onDataBitsChanged(QSerialPort::DataBits))); + connect(serialPort, SIGNAL(stopBitsChanged(QSerialPort::StopBits)), this, SLOT(onStopBitsChanged(QSerialPort::StopBits))); + connect(serialPort, SIGNAL(flowControlChanged(QSerialPort::FlowControl)), this, SLOT(onFlowControlChanged(QSerialPort::FlowControl))); + qCDebug(dcSerialPortCommander()) << "Setup successfully serial port" << interface; + m_usedInterfaces.append(interface); + m_serialPorts.insert(device, serialPort); + } else { + return DeviceManager::DeviceSetupStatusFailure; + } + return DeviceManager::DeviceSetupStatusSuccess; + } + return DeviceManager::DeviceSetupStatusFailure; +} + + +DeviceManager::DeviceError DevicePluginSerialPortCommander::discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) +{ + Q_UNUSED(params) + // Create the list of available serial interfaces + QList deviceDescriptors; + + Q_FOREACH(QSerialPortInfo port, QSerialPortInfo::availablePorts()) { + if (m_usedInterfaces.contains(port.portName())){ + //device already in use + qCDebug(dcSerialPortCommander()) << "Found serial port that is already used:" << port.portName(); + } else { + //Serial port is not yet used, create now a new one + qCDebug(dcSerialPortCommander()) << "Found serial port:" << port.portName(); + QString description = port.manufacturer() + " " + port.description(); + DeviceDescriptor descriptor(deviceClassId, port.portName(), description); + ParamList parameters; + parameters.append(Param(serialPortCommanderDeviceSerialPortParamTypeId, port.portName())); + descriptor.setParams(parameters); + deviceDescriptors.append(descriptor); + } + } + emit devicesDiscovered(deviceClassId, deviceDescriptors); + return DeviceManager::DeviceErrorAsync; +} + + +DeviceManager::DeviceError DevicePluginSerialPortCommander::executeAction(Device *device, const Action &action) +{ + if (device->deviceClassId() == serialPortCommanderDeviceClassId ) { + + if (action.actionTypeId() == serialPortCommanderTriggerActionTypeId) { + + QSerialPort *serialPort = m_serialPorts.value(device); + serialPort->write(action.param(serialPortCommanderTriggerActionOutputDataParamTypeId).value().toByteArray()); + + return DeviceManager::DeviceErrorNoError; + } + return DeviceManager::DeviceErrorActionTypeNotFound; + } + return DeviceManager::DeviceErrorDeviceClassNotFound; +} + + +void DevicePluginSerialPortCommander::deviceRemoved(Device *device) +{ + if (device->deviceClassId() == serialPortCommanderDeviceClassId) { + + m_usedInterfaces.removeAll(device->paramValue(serialPortCommanderDeviceSerialPortParamTypeId).toString()); + QSerialPort *serialPort = m_serialPorts.take(device); + serialPort->close(); + serialPort->deleteLater(); + } +} + +void DevicePluginSerialPortCommander::onReadyRead() +{ + QSerialPort *serialPort = static_cast(sender()); + Device *device = m_serialPorts.key(serialPort); + + QByteArray data; + while (!serialPort->atEnd()) { + data = serialPort->read(100); + } + qDebug(dcSerialPortCommander()) << "Message received" << data; + + Event event(serialPortCommanderTriggeredEventTypeId, device->id()); + ParamList parameters; + parameters.append(Param(serialPortCommanderTriggeredEventInputDataParamTypeId, data)); + event.setParams(parameters); + emitEvent(event); +} + +void DevicePluginSerialPortCommander::onSerialError(QSerialPort::SerialPortError error) +{ + qCWarning(dcSerialPortCommander) << "Serial Port error happened:" << error; +} + +void DevicePluginSerialPortCommander::onBaudRateChanged(qint32 baudRate, QSerialPort::Direction direction) +{ + Q_UNUSED(direction) + QSerialPort *serialPort = static_cast(sender()); + Device *device = m_serialPorts.key(serialPort); + device->setParamValue(serialPortCommanderDeviceBaudRateParamTypeId, baudRate); +} + +void DevicePluginSerialPortCommander::onParityChanged(QSerialPort::Parity parity) +{ + QSerialPort *serialPort = static_cast(sender()); + Device *device = m_serialPorts.key(serialPort); + device->setParamValue(serialPortCommanderDeviceParityParamTypeId, parity); +} + +void DevicePluginSerialPortCommander::onDataBitsChanged(QSerialPort::DataBits dataBits) +{ + QSerialPort *serialPort = static_cast(sender()); + Device *device = m_serialPorts.key(serialPort); + device->setParamValue(serialPortCommanderDeviceDataBitsParamTypeId, dataBits); +} + +void DevicePluginSerialPortCommander::onStopBitsChanged(QSerialPort::StopBits stopBits) +{ + QSerialPort *serialPort = static_cast(sender()); + Device *device = m_serialPorts.key(serialPort); + device->setParamValue(serialPortCommanderDeviceStopBitsParamTypeId, stopBits); +} + +void DevicePluginSerialPortCommander::onFlowControlChanged(QSerialPort::FlowControl flowControl) +{ + QSerialPort *serialPort = static_cast(sender()); + Device *device = m_serialPorts.key(serialPort); + device->setParamValue(serialPortCommanderDeviceFlowControlParamTypeId, flowControl); +} + diff --git a/serialportcommander/devicepluginserialportcommander.h b/serialportcommander/devicepluginserialportcommander.h new file mode 100644 index 00000000..d1e2b45e --- /dev/null +++ b/serialportcommander/devicepluginserialportcommander.h @@ -0,0 +1,63 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2019 Bernhard Trinnes * + * * + * 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 DEVICEPLUGINSERIALPORTCOMMANDER_H +#define DEVICEPLUGINSERIALPORTCOMMANDER_H + +#include "plugin/deviceplugin.h" +#include "devicemanager.h" +#include +#include + +class DevicePluginSerialPortCommander : public DevicePlugin +{ + Q_OBJECT + + Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "devicepluginserialportcommander.json") + Q_INTERFACES(DevicePlugin) + +public: + explicit DevicePluginSerialPortCommander(); + + DeviceManager::DeviceSetupStatus setupDevice(Device *device) override; + void deviceRemoved(Device *device) override; + DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms); + DeviceManager::DeviceError executeAction(Device *device, const Action &action) override; + +private: + QHash m_serialPorts; + QList m_usedInterfaces; + +private slots: + void onReadyRead(); + void onSerialError(QSerialPort::SerialPortError error); + void onBaudRateChanged(qint32 baudRate, QSerialPort::Direction direction); + void onParityChanged(QSerialPort::Parity parity); + void onDataBitsChanged(QSerialPort::DataBits dataBits); + void onStopBitsChanged(QSerialPort::StopBits stopBits); + void onFlowControlChanged(QSerialPort::FlowControl flowControl); + +signals: + +}; + +#endif // DEVICEPLUGINSERIALPORTCOMMANDER_H diff --git a/serialportcommander/devicepluginserialportcommander.json b/serialportcommander/devicepluginserialportcommander.json new file mode 100644 index 00000000..891696a2 --- /dev/null +++ b/serialportcommander/devicepluginserialportcommander.json @@ -0,0 +1,117 @@ +{ + "displayName": "Serial port commander", + "name": "SerialPortCommander", + "id": "fe93a12e-36f4-4015-8019-26b659817773", + "vendors": [ + { + "name": "guh", + "displayName": "guh GmbH", + "id": "2062d64d-3232-433c-88bc-0d33c0ba2ba6", + "deviceClasses": [ + + { + "id": "540566d8-a2a6-4ce2-9a1e-a66a989e6199", + "name": "serialPortCommander", + "displayName": "Serial port commander", + "deviceIcon": "Network", + "createMethods": ["user", "discovery"], + "interfaces": ["outputtrigger", "inputtrigger"], + "basicTags": [ + "Device" + ], + "paramTypes": [ + { + "id": "ed49f7d8-ab18-4c37-9b80-1004b75dcb91", + "name": "serialPort", + "displayName": "Serial port", + "type": "QString", + "inputType": "TextLine", + "defaultValue": "ttyAMA0" + }, + { + "id": "45dfc828-f238-4263-89a3-9b35cf5dea39", + "name": "baudRate", + "displayName": "Baud rate", + "type": "int", + "defaultValue": 9600 + }, + { + "id": "add4f7fb-1be9-4944-a420-3355b20174f9", + "name": "dataBits", + "displayName": "Data bits", + "type": "int", + "defaultValue": 8 + }, + { + "id": "4ea8bcdf-d4c5-45a4-a54f-f10ac3f08a78", + "name": "stopBits", + "displayName": "Stop bits", + "type": "int", + "defaultValue": 0 + }, + { + "id": "7e5d197f-0224-4c6f-8e86-0e7c867da5f1", + "name": "flowControl", + "displayName": "Flow control", + "type": "QString", + "inputType": "TextLine", + "allowedValues": [ + "No Flow Control", + "Hardware Control", + "Software Control" + ], + "defaultValue": "No Flow Control" + }, + { + "id": "72de1b08-2a27-49c5-90e0-8788c3ea1da3", + "name": "parity", + "displayName": "Parity", + "type": "QString", + "inputType": "TextLine", + "allowedValues": [ + "No Parity", + "Even Parity", + "Odd Parity", + "Space Parity", + "Mark Parity" + ], + "defaultValue": "No Parity" + } + ], + "actionTypes": [ + { + "id": "0b22c4d1-f5f6-4a93-aa93-660d27bf8f71", + "name": "trigger", + "displayName": "Trigger", + "paramTypes": [ + { + "id": "a27ecedc-424e-49ce-8956-9dbca2feac02", + "name": "outputData", + "displayName": "Data", + "type": "QString", + "inputType": "TextArea" + } + ] + } + ], + "eventTypes": [ + { + "id": "32087633-616c-45a7-85af-4f1695c22359", + "name": "triggered", + "displayName": "Data received", + "paramTypes": [ + { + "id": "b98fdacc-59d7-41c4-b790-1fdca50dfb22", + "name": "inputData", + "displayName": "Received Data", + "type": "QString", + "defaultValue": "" + } + ] + } + ] + } + ] + } + ] +} diff --git a/serialportcommander/serialportcommander.pro b/serialportcommander/serialportcommander.pro new file mode 100644 index 00000000..ffee06b0 --- /dev/null +++ b/serialportcommander/serialportcommander.pro @@ -0,0 +1,12 @@ +include(../plugins.pri) + +QT += serialport + +TARGET = $$qtLibraryTarget(nymea_devicepluginserialportcommander) + +SOURCES += \ + devicepluginserialportcommander.cpp \ + + +HEADERS += \ + devicepluginserialportcommander.h \