diff --git a/debian/control b/debian/control index 109179e7..b83c4a60 100644 --- a/debian/control +++ b/debian/control @@ -550,6 +550,21 @@ Description: nymea.io plugin for wemo This package will install the nymea.io plugin for wemo +Package: nymea-plugin-ws2812fx +Architecture: any +Depends: ${shlibs:Depends}, + ${misc:Depends}, + nymea-plugins-translations, +Description: nymea.io plugin for ws2812fx + 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 ws2812fx + + Package: nymea-plugin-tcpcommander Architecture: any Depends: ${shlibs:Depends}, diff --git a/debian/nymea-plugin-ws2812fx.install.in b/debian/nymea-plugin-ws2812fx.install.in new file mode 100644 index 00000000..8a70387a --- /dev/null +++ b/debian/nymea-plugin-ws2812fx.install.in @@ -0,0 +1 @@ +usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_devicepluginws2812fx.so diff --git a/nymea-plugins.pro b/nymea-plugins.pro index 8985227c..b3ded9b2 100644 --- a/nymea-plugins.pro +++ b/nymea-plugins.pro @@ -44,6 +44,7 @@ PLUGIN_DIRS = \ unitec \ wakeonlan \ wemo \ + ws2812fx \ message(============================================) diff --git a/ws2812fx/README.md b/ws2812fx/README.md new file mode 100644 index 00000000..b516a2eb --- /dev/null +++ b/ws2812fx/README.md @@ -0,0 +1,7 @@ +# WS2812FX + +Plug-In for the WS2812FX project, a WS2812 effect library for Arduino and ESP8266. +Flash your microcontroller with the provided "serial_control" example. Connect the microcontroller +over USB to the nymea device. + +See also: https://github.com/kitesurfer1404/WS2812FX diff --git a/ws2812fx/devicepluginws2812fx.cpp b/ws2812fx/devicepluginws2812fx.cpp new file mode 100644 index 00000000..e40fd5c1 --- /dev/null +++ b/ws2812fx/devicepluginws2812fx.cpp @@ -0,0 +1,426 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * 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 ws2812fx.html + \title WS2812FX Control + \brief Plug-In to control WS2812FX over USB + + \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/ws2812fx/devicepluginws2812fx.json +*/ +#include +#include "devicepluginws2812fx.h" +#include "plugininfo.h" + +DevicePluginWs2812fx ::DevicePluginWs2812fx () +{ +} + +Device::DeviceSetupStatus DevicePluginWs2812fx::setupDevice(Device *device) +{ + if (device->deviceClassId() == ws2812fxDeviceClassId) { + QString interface = device->paramValue(ws2812fxDeviceSerialPortParamTypeId).toString(); + + if (!m_usedInterfaces.contains(interface)) { + + QSerialPort *serialPort = new QSerialPort(interface, this); + if(!serialPort) + return Device::DeviceSetupStatusFailure; + + serialPort->setBaudRate(115200); + serialPort->setDataBits(QSerialPort::DataBits::Data8); + serialPort->setParity(QSerialPort::Parity::NoParity); + serialPort->setStopBits(QSerialPort::StopBits::OneStop); + serialPort->setFlowControl(QSerialPort::FlowControl::NoFlowControl); + + if (!serialPort->open(QIODevice::ReadWrite)) { + qCWarning(dcWs2812fx()) << "Could not open serial port" << interface << serialPort->errorString(); + serialPort->deleteLater(); + return Device::DeviceSetupStatusFailure; + } + + connect(serialPort, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(onSerialError(QSerialPort::SerialPortError))); + connect(serialPort, SIGNAL(readyRead()), this, SLOT(onReadyRead())); + + qCDebug(dcWs2812fx()) << "Setup successfully serial port" << interface; + device->setStateValue(ws2812fxConnectedStateTypeId, true); + m_usedInterfaces.append(interface); + m_serialPorts.insert(device, serialPort); + + if(!m_reconnectTimer) { + m_reconnectTimer = new QTimer(this); + m_reconnectTimer->setSingleShot(true); + m_reconnectTimer->setInterval(5000); + + connect(m_reconnectTimer, &QTimer::timeout, this, &DevicePluginWs2812fx::onReconnectTimer); + } + } else { + return Device::DeviceSetupStatusFailure; + } + return Device::DeviceSetupStatusSuccess; + } + return Device::DeviceSetupStatusFailure; +} + + +Device::DeviceError DevicePluginWs2812fx::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()) { + + qCDebug(dcWs2812fx()) << "Found serial port:" << port.portName(); + QString description = port.manufacturer() + " " + port.description(); + DeviceDescriptor descriptor(deviceClassId, port.portName(), description); + foreach (Device *existingDevice, myDevices().filterByParam(ws2812fxDeviceSerialPortParamTypeId, port.portName())) { + descriptor.setDeviceId(existingDevice->id()); + } + ParamList parameters; + parameters.append(Param(ws2812fxDeviceSerialPortParamTypeId, port.portName())); + descriptor.setParams(parameters); + deviceDescriptors.append(descriptor); + } + emit devicesDiscovered(deviceClassId, deviceDescriptors); + return Device::DeviceErrorAsync; +} + + +Device::DeviceError DevicePluginWs2812fx::executeAction(Device *device, const Action &action) +{ + if (device->deviceClassId() == ws2812fxDeviceClassId ) { + + QByteArray command; + if (action.actionTypeId() == ws2812fxPowerActionTypeId) { + command.append("b "); + if (action.param(ws2812fxPowerActionPowerParamTypeId).value().toBool()) { + command.append("30"); + } else { + command.append("0"); + } + command.append("\r\n"); + return sendCommand(device, action.id(), command, CommandType::Brightness); + } + + if (action.actionTypeId() == ws2812fxBrightnessActionTypeId) { + + command.append("b "); + command.append(action.param(ws2812fxBrightnessActionBrightnessParamTypeId).value().toString()); + command.append("\r\n"); + return sendCommand(device, action.id(), command, CommandType::Brightness); + } + + if (action.actionTypeId() == ws2812fxSpeedActionTypeId) { + + command.append("s "); + command.append(action.param(ws2812fxSpeedActionSpeedParamTypeId).value().toString()); + command.append("\r\n"); + return sendCommand(device, action.id(), command, CommandType::Speed); + } + + if (action.actionTypeId() == ws2812fxColorActionTypeId) { + + QColor color; + color= action.param(ws2812fxColorActionColorParamTypeId).value().value(); + command.append("c "); + command.append(QString(color.name()).remove("#")); + command.append("\r\n"); + return sendCommand(device, action.id(), command, CommandType::Color); + } + + if (action.actionTypeId() == ws2812fxColorTemperatureActionTypeId) { + + // minValue 153, maxValue 500 + QColor color; + color.setRgb(255, 255, static_cast((255.00-(((action.param(ws2812fxColorTemperatureActionColorTemperatureParamTypeId).value().toDouble()-153.00)/347.00))*255.00))); + device->setStateValue(ws2812fxColorTemperatureStateTypeId, action.param(ws2812fxColorTemperatureActionColorTemperatureParamTypeId).value()); + command.append("c "); + command.append(QString(color.name()).remove("#")); + command.append("\r\n"); + return sendCommand(device, action.id(), command, CommandType::Color); + } + + if (action.actionTypeId() == ws2812fxEffectModeActionTypeId) { + + QString effectMode = action.param(ws2812fxEffectModeActionEffectModeParamTypeId).value().toString(); + command.append("m "); + if (effectMode == "Static") { + command.append(QString::number(FX_MODE_STATIC)); + } else if (effectMode == "Blink") { + command.append(QString::number(FX_MODE_BLINK)); + } else if (effectMode == "Color Wipe") { + command.append(QString::number(FX_MODE_COLOR_WIPE)); + } else if (effectMode == "Color Wipe Inverse") { + command.append(QString::number(FX_MODE_COLOR_WIPE_INV)); + } else if (effectMode == "Color Wipe Reverse") { + command.append(QString::number(FX_MODE_COLOR_WIPE_REV)); + } else if (effectMode == "Color Wipe Reverse Inverse") { + command.append(QString::number(FX_MODE_COLOR_WIPE_REV_INV)); + } else if (effectMode == "Color Wipe Random") { + command.append(QString::number(FX_MODE_COLOR_WIPE_RANDOM)); + } else if (effectMode == "Random Color") { + command.append(QString::number(FX_MODE_RANDOM_COLOR)); + } else if (effectMode == "Single Dynamic") { + command.append(QString::number(FX_MODE_SINGLE_DYNAMIC)); + } else if (effectMode == "Multi Dynamic") { + command.append(QString::number(FX_MODE_MULTI_DYNAMIC)); + } else if (effectMode == "Rainbow") { + command.append(QString::number(FX_MODE_RAINBOW)); + } else if (effectMode == "Rainbow Cycle") { + command.append(QString::number(FX_MODE_RAINBOW_CYCLE)); + } else if (effectMode == "Scan") { + command.append(QString::number(FX_MODE_SCAN)); + } else if (effectMode == "Dual Scan") { + command.append(QString::number(FX_MODE_DUAL_SCAN)); + } else if (effectMode == "Fade") { + command.append(QString::number(FX_MODE_FADE)); + } else if (effectMode == "Theater Chase") { + command.append(QString::number(FX_MODE_THEATER_CHASE)); + } else if (effectMode == "Theater Chase Rainbow") { + command.append(QString::number(FX_MODE_THEATER_CHASE_RAINBOW)); + } else if (effectMode == "Running Lights") { + command.append(QString::number(FX_MODE_RUNNING_LIGHTS)); + } else if (effectMode == "Twinkle") { + command.append(QString::number(FX_MODE_TWINKLE)); + } else if (effectMode == "Twinkle Random") { + command.append(QString::number(FX_MODE_TWINKLE_RANDOM)); + } else if (effectMode == "Twinkle Fade") { + command.append(QString::number(FX_MODE_TWINKLE_FADE)); + } else if (effectMode == "Twinkle Fade Random") { + command.append(QString::number(FX_MODE_TWINKLE_FADE_RANDOM)); + } else if (effectMode == "Sparkle") { + command.append(QString::number(FX_MODE_SPARKLE)); + } else if (effectMode == "Flash Sparkle") { + command.append(QString::number(FX_MODE_FLASH_SPARKLE)); + } else if (effectMode == "Hyper Sparkle") { + command.append(QString::number(FX_MODE_HYPER_SPARKLE)); + } else if (effectMode == "Strobe") { + command.append(QString::number(FX_MODE_STROBE)); + } else if (effectMode == "Strobe Rainbow") { + command.append(QString::number(FX_MODE_STROBE_RAINBOW)); + } else if (effectMode == "Multi Strobe") { + command.append(QString::number(FX_MODE_MULTI_STROBE)); + } else if (effectMode == "Blink Rainbow") { + command.append(QString::number(FX_MODE_BLINK_RAINBOW)); + } else if (effectMode == "Chase White") { + command.append(QString::number(FX_MODE_CHASE_WHITE)); + } else if (effectMode == "Chase Color") { + command.append(QString::number(FX_MODE_CHASE_COLOR)); + } else if (effectMode == "Chase Random") { + command.append(QString::number(FX_MODE_CHASE_RANDOM)); + } else if (effectMode == "Chase Flash") { + command.append(QString::number(FX_MODE_CHASE_FLASH)); + } else if (effectMode == "Chase Flash Random") { + command.append(QString::number(FX_MODE_CHASE_FLASH_RANDOM)); + } else if (effectMode == "Chase Rainbow White") { + command.append(QString::number(FX_MODE_CHASE_RAINBOW_WHITE)); + } else if (effectMode == "Chase Blackout") { + command.append(QString::number(FX_MODE_CHASE_BLACKOUT)); + } else if (effectMode == "Chase Blackout Rainbow") { + command.append(QString::number(FX_MODE_CHASE_BLACKOUT_RAINBOW)); + } else if (effectMode == "Color Sweep Random") { + command.append(QString::number(FX_MODE_COLOR_SWEEP_RANDOM)); + } else if (effectMode == "Running Color") { + command.append(QString::number(FX_MODE_RUNNING_COLOR)); + } else if (effectMode == "Running Red Blue") { + command.append(QString::number(FX_MODE_RUNNING_RED_BLUE)); + } else if (effectMode == "Running Random") { + command.append(QString::number(FX_MODE_RUNNING_RANDOM)); + }else if (effectMode == "Larson Scanner") { + command.append(QString::number(FX_MODE_LARSON_SCANNER)); + }else if (effectMode == "Comet") { + command.append(QString::number(FX_MODE_COMET)); + }else if (effectMode == "Fireworks") { + command.append(QString::number(FX_MODE_FIREWORKS)); + }else if (effectMode == "Fireworks Random") { + command.append(QString::number(FX_MODE_FIREWORKS_RANDOM)); + }else if (effectMode == "Merry Christmas") { + command.append(QString::number(FX_MODE_MERRY_CHRISTMAS)); + }else if (effectMode == "Fire Flicker") { + command.append(QString::number(FX_MODE_FIRE_FLICKER)); + }else if (effectMode == "Fire Flicker (soft)") { + command.append(QString::number(FX_MODE_FIRE_FLICKER_SOFT)); + }else if (effectMode == "Fire Flicker (intense)") { + command.append(QString::number(FX_MODE_FIRE_FLICKER_INTENSE)); + }else if (effectMode == "Circus Combustus") { + command.append(QString::number(FX_MODE_CIRCUS_COMBUSTUS)); + }else if (effectMode == "Halloween") { + command.append(QString::number(FX_MODE_HALLOWEEN)); + }else if (effectMode == "Bicolor Chase") { + command.append(QString::number(FX_MODE_BICOLOR_CHASE)); + }else if (effectMode == "Tricolor Chase") { + command.append(QString::number(FX_MODE_TRICOLOR_CHASE)); + }else if (effectMode == "ICU") { + command.append(QString::number(FX_MODE_ICU)); + }else if (effectMode == "Custom 0") { + command.append(QString::number(FX_MODE_CUSTOM_0)); + }else if (effectMode == "Custom 1") { + command.append(QString::number(FX_MODE_CUSTOM_1)); + }else if (effectMode == "Custom 2") { + command.append(QString::number(FX_MODE_CUSTOM_2)); + }else if (effectMode == "Custom 3") { + command.append(QString::number(FX_MODE_CUSTOM_3)); + } + command.append("\r\n"); + return sendCommand(device, action.id(), command, CommandType::Mode); + } + return Device::DeviceErrorActionTypeNotFound; + } + return Device::DeviceErrorDeviceClassNotFound; +} + + +void DevicePluginWs2812fx::deviceRemoved(Device *device) +{ + if (device->deviceClassId() == ws2812fxDeviceClassId) { + + m_usedInterfaces.removeAll(device->paramValue(ws2812fxDeviceSerialPortParamTypeId).toString()); + QSerialPort *serialPort = m_serialPorts.take(device); + serialPort->flush(); + serialPort->close(); + serialPort->deleteLater(); + } + + if (myDevices().empty()) { + m_reconnectTimer->stop(); + m_reconnectTimer->deleteLater(); + } +} + +void DevicePluginWs2812fx::onReadyRead() +{ + QSerialPort *serialPort = static_cast(sender()); + Device *device = m_serialPorts.key(serialPort); + + QByteArray data; + while (serialPort->canReadLine()) { + data = serialPort->readLine(); + qDebug(dcWs2812fx()) << "Message received" << data; + + if (data.contains("mode")) { + if (m_pendingActions.contains(CommandType::Mode)) { + emit actionExecutionFinished(m_pendingActions.value(CommandType::Mode), Device::DeviceErrorNoError); + } + QString mode = data.split('-').at(1); + mode.remove(0, 1); + mode.remove("\r\n"); + qDebug(dcWs2812fx()) << "set mode to:" << mode; + device->setStateValue(ws2812fxEffectModeStateTypeId, mode); + } + if (data.contains("brightness")) { + if (m_pendingActions.contains(CommandType::Brightness)) { + emit actionExecutionFinished(m_pendingActions.value(CommandType::Brightness), Device::DeviceErrorNoError); + } + QString rawBrightness = data.split(':').at(1); + rawBrightness.remove(" "); + rawBrightness.remove("\r\n"); + int brightness = rawBrightness.toInt(); + + qDebug(dcWs2812fx()) << "set brightness to:" << brightness; + device->setStateValue(ws2812fxBrightnessStateTypeId, brightness); + if (brightness == 0) { + device->setStateValue(ws2812fxPowerStateTypeId, false); + } else { + device->setStateValue(ws2812fxPowerStateTypeId, true); + } + } + if (data.contains("speed")) { + if (m_pendingActions.contains(CommandType::Speed)) { + emit actionExecutionFinished(m_pendingActions.value(CommandType::Speed), Device::DeviceErrorNoError); + } + QString rawSpeed = data.split(':').at(1); + rawSpeed.remove(" "); + rawSpeed.remove("\r\n"); + int speed = data.split(':').at(1).toInt(); + + qDebug(dcWs2812fx()) << "set speed to:" << speed; + device->setStateValue(ws2812fxSpeedStateTypeId, speed); + } + if (data.contains("color")) { + if (m_pendingActions.contains(CommandType::Color)) { + emit actionExecutionFinished(m_pendingActions.value(CommandType::Color), Device::DeviceErrorNoError); + } + QString rawColor = data.split(':').at(1); + rawColor.remove(" "); + rawColor.remove("0x"); + rawColor.remove("\r\n"); + rawColor.prepend("#"); + qDebug(dcWs2812fx()) << "set color to:" << rawColor; + device->setStateValue(ws2812fxColorStateTypeId, rawColor); + } + } +} + +void DevicePluginWs2812fx::onReconnectTimer() +{ + foreach(Device *device, myDevices()) { + if (!device->stateValue(ws2812fxConnectedStateTypeId).toBool()) { + QSerialPort *serialPort = m_serialPorts.value(device); + if (serialPort) { + if (serialPort->open(QSerialPort::ReadWrite)) { + device->setStateValue(ws2812fxConnectedStateTypeId, true); + } else { + device->setStateValue(ws2812fxConnectedStateTypeId, false); + m_reconnectTimer->start(); + } + } + } + } + +} + +void DevicePluginWs2812fx::onSerialError(QSerialPort::SerialPortError error) +{ + QSerialPort *serialPort = static_cast(sender()); + Device *device = m_serialPorts.key(serialPort); + + if (error != QSerialPort::NoError && serialPort->isOpen()) { + qCCritical(dcWs2812fx()) << "Serial port error:" << error << serialPort->errorString(); + m_reconnectTimer->start(); + serialPort->close(); + device->setStateValue(ws2812fxConnectedStateTypeId, false); + } +} + +Device::DeviceError DevicePluginWs2812fx::sendCommand(Device* device, ActionId actionId, const QByteArray &command, CommandType commandType) +{ + qDebug(dcWs2812fx()) << "Sending command" << command; + QSerialPort *serialPort = m_serialPorts.value(device); + if (!serialPort) + return Device::DeviceErrorDeviceNotFound; + if (serialPort->write(command) != command.length()) { + qCWarning(dcWs2812fx) << "Error writing to serial port"; + return Device::DeviceErrorHardwareFailure; + } + m_pendingActions.insert(commandType, actionId); + return Device::DeviceErrorAsync; +} diff --git a/ws2812fx/devicepluginws2812fx.h b/ws2812fx/devicepluginws2812fx.h new file mode 100644 index 00000000..159d6d90 --- /dev/null +++ b/ws2812fx/devicepluginws2812fx.h @@ -0,0 +1,133 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * 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 DEVICEPLUGINWS2812FX_H +#define DEVICEPLUGINWS2812FX_H + +#define FX_MODE_STATIC 0 +#define FX_MODE_BLINK 1 +#define FX_MODE_BREATH 2 +#define FX_MODE_COLOR_WIPE 3 +#define FX_MODE_COLOR_WIPE_INV 4 +#define FX_MODE_COLOR_WIPE_REV 5 +#define FX_MODE_COLOR_WIPE_REV_INV 6 +#define FX_MODE_COLOR_WIPE_RANDOM 7 +#define FX_MODE_RANDOM_COLOR 8 +#define FX_MODE_SINGLE_DYNAMIC 9 +#define FX_MODE_MULTI_DYNAMIC 10 +#define FX_MODE_RAINBOW 11 +#define FX_MODE_RAINBOW_CYCLE 12 +#define FX_MODE_SCAN 13 +#define FX_MODE_DUAL_SCAN 14 +#define FX_MODE_FADE 15 +#define FX_MODE_THEATER_CHASE 16 +#define FX_MODE_THEATER_CHASE_RAINBOW 17 +#define FX_MODE_RUNNING_LIGHTS 18 +#define FX_MODE_TWINKLE 19 +#define FX_MODE_TWINKLE_RANDOM 20 +#define FX_MODE_TWINKLE_FADE 21 +#define FX_MODE_TWINKLE_FADE_RANDOM 22 +#define FX_MODE_SPARKLE 23 +#define FX_MODE_FLASH_SPARKLE 24 +#define FX_MODE_HYPER_SPARKLE 25 +#define FX_MODE_STROBE 26 +#define FX_MODE_STROBE_RAINBOW 27 +#define FX_MODE_MULTI_STROBE 28 +#define FX_MODE_BLINK_RAINBOW 29 +#define FX_MODE_CHASE_WHITE 30 +#define FX_MODE_CHASE_COLOR 31 +#define FX_MODE_CHASE_RANDOM 32 +#define FX_MODE_CHASE_RAINBOW 33 +#define FX_MODE_CHASE_FLASH 34 +#define FX_MODE_CHASE_FLASH_RANDOM 35 +#define FX_MODE_CHASE_RAINBOW_WHITE 36 +#define FX_MODE_CHASE_BLACKOUT 37 +#define FX_MODE_CHASE_BLACKOUT_RAINBOW 38 +#define FX_MODE_COLOR_SWEEP_RANDOM 39 +#define FX_MODE_RUNNING_COLOR 40 +#define FX_MODE_RUNNING_RED_BLUE 41 +#define FX_MODE_RUNNING_RANDOM 42 +#define FX_MODE_LARSON_SCANNER 43 +#define FX_MODE_COMET 44 +#define FX_MODE_FIREWORKS 45 +#define FX_MODE_FIREWORKS_RANDOM 46 +#define FX_MODE_MERRY_CHRISTMAS 47 +#define FX_MODE_FIRE_FLICKER 48 +#define FX_MODE_FIRE_FLICKER_SOFT 49 +#define FX_MODE_FIRE_FLICKER_INTENSE 50 +#define FX_MODE_CIRCUS_COMBUSTUS 51 +#define FX_MODE_HALLOWEEN 52 +#define FX_MODE_BICOLOR_CHASE 53 +#define FX_MODE_TRICOLOR_CHASE 54 +#define FX_MODE_ICU 55 +#define FX_MODE_CUSTOM 56 // keep this for backward compatiblity +#define FX_MODE_CUSTOM_0 56 // custom modes need to go at the end +#define FX_MODE_CUSTOM_1 57 +#define FX_MODE_CUSTOM_2 58 +#define FX_MODE_CUSTOM_3 59 + +#include "devices/deviceplugin.h" + +#include +#include +#include + +class DevicePluginWs2812fx : public DevicePlugin +{ + Q_OBJECT + + Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "devicepluginws2812fx.json") + Q_INTERFACES(DevicePlugin) + +public: + explicit DevicePluginWs2812fx(); + + Device::DeviceSetupStatus setupDevice(Device *device) override; + void deviceRemoved(Device *device) override; + Device::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override; + Device::DeviceError executeAction(Device *device, const Action &action) override; + +private: + enum CommandType { + Color, + Speed, + Brightness, + Mode + }; + + QHash m_serialPorts; + QList m_usedInterfaces; + QHash m_pendingActions; + + QTimer *m_reconnectTimer = nullptr; + Device::DeviceError sendCommand(Device *device, ActionId actionId, const QByteArray &command, CommandType commandType); + +private slots: + void onReadyRead(); + void onReconnectTimer(); + void onSerialError(QSerialPort::SerialPortError error); + +signals: + +}; + +#endif // DEVICEPLUGINWS2812FX_H diff --git a/ws2812fx/devicepluginws2812fx.json b/ws2812fx/devicepluginws2812fx.json new file mode 100644 index 00000000..90624c35 --- /dev/null +++ b/ws2812fx/devicepluginws2812fx.json @@ -0,0 +1,172 @@ +{ + "displayName": "WS2812FX", + "name": "ws2812fx", + "id": "5a3c8ca5-4ef7-47a3-bf3f-c257b9f11ef0", + "vendors": [ + { + "name": "nymea", + "displayName": "nymea", + "id": "2062d64d-3232-433c-88bc-0d33c0ba2ba6", + "deviceClasses": [ + { + "id": "24364e36-b199-4e35-b468-c44da58c009c", + "name": "ws2812fx", + "displayName": "WS2812FX", + "createMethods": ["user", "discovery"], + "interfaces": ["colorlight", "connectable"], + "paramTypes": [ + { + "id": "ed49f7d8-ab18-4c37-9b80-1004b75dcb91", + "name": "serialPort", + "displayName": "Serial port", + "type": "QString", + "inputType": "TextLine", + "defaultValue": "ttyAMC0" + } + ], + "stateTypes": [ + { + "id": "ee2c35ca-4236-43aa-aa7c-a5639c6dcf86", + "name": "connected", + "displayName": "reachable", + "displayNameEvent": "reachable changed", + "defaultValue": false, + "type": "bool" + }, + { + "id": "00db76b4-86b3-4a3f-b4e8-ceded6891282", + "name": "power", + "displayName": "power", + "displayNameEvent": "power changed", + "displayNameAction": "Set power", + "type": "bool", + "defaultValue": false, + "writable": true + }, + { + "id": "3de39d75-a775-43f1-b439-0fe326b4ba51", + "name": "colorTemperature", + "displayName": "color temperature", + "displayNameEvent": "color temperature changed", + "displayNameAction": "Set color temperature", + "type": "int", + "unit": "Mired", + "defaultValue": 170, + "minValue": 153, + "maxValue": 500, + "writable": true + }, + { + "id": "8490bab0-1fdc-4acc-81e4-e7cc91d9ce1d", + "name": "color", + "displayName": "color", + "displayNameEvent": "color changed", + "displayNameAction": "Set color", + "type": "QColor", + "defaultValue": "#000000", + "writable": true + }, + { + "id": "05f2a002-6ce5-4811-bd79-44f15e981f5a", + "name": "brightness", + "displayName": "brightness", + "displayNameEvent": "brightness changed", + "displayNameAction": "Set brigtness", + "type": "int", + "unit": "Percentage", + "defaultValue": 0, + "minValue": 0, + "maxValue": 100, + "writable": true + }, + { + "id": "f1a37e73-1154-4f05-9444-d07566723c5a", + "name": "speed", + "displayName": "speed", + "displayNameEvent": "speed changed", + "displayNameAction": "Set speed", + "type": "int", + "unit": "Percentage", + "defaultValue": 0, + "minValue": 0, + "maxValue": 100, + "writable": true + }, + { + "id": "ebfb4bc6-b1dc-4a5c-a495-077cba4fec1a", + "name": "effectMode", + "displayName": "Effect mode", + "displayNameEvent": "Effect mode changed", + "displayNameAction": "Set effect mode", + "type": "QString", + "defaultValue": "Static", + "possibleValues": [ + "Static", + "Blink", + "Breath", + "Color Wipe", + "Color Wipe Inverse", + "Color Wipe Reverse", + "Color Wipe Reverse Inverse", + "Color Wipe Random ", + "Random Color", + "Single Dynamic", + "Multi Dynamic", + "Rainbow", + "Rainbow Cycle", + "Scan", + "Dual Scan", + "Fade", + "Theater Chase", + "Theater Chase Rainbow", + "Running Lights", + "Twinkle", + "Twinkle Random", + "Twinkle Fade", + "Twinkle Fade Random", + "Sparkle", + "Flash Sparkle", + "Hyper Sparkle", + "Strobe", + "Strobe Rainbow", + "Multi Strobe", + "Blink Rainbow", + "Chase White", + "Chase Color", + "Chase Random", + "Chase Rainbow", + "Chase Flash", + "Chase Flash Random", + "Chase Rainbow White", + "Chase Blackout", + "Chase Blackout Rainbow", + "Color Sweep Random", + "Running Color", + "Running Red Blue", + "Running Random", + "Larson Scanner", + "Comet", + "Fireworks", + "Fireworks Random", + "Merry Christmas", + "Fire Flicker", + "Fire Flicker (soft)", + "Fire Flicker (intense)", + "Circus Combustus", + "Halloween", + "Bicolor Chase", + "Tricolor Chase", + "ICU", + "Custom 0", + "Custom 1", + "Custom 2", + "Custom 3" + ], + "writable": true + } + ] + } + ] + } + ] +} diff --git a/ws2812fx/translations/5a3c8ca5-4ef7-47a3-bf3f-c257b9f11ef0-en_US.ts b/ws2812fx/translations/5a3c8ca5-4ef7-47a3-bf3f-c257b9f11ef0-en_US.ts new file mode 100644 index 00000000..dcd02922 --- /dev/null +++ b/ws2812fx/translations/5a3c8ca5-4ef7-47a3-bf3f-c257b9f11ef0-en_US.ts @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ws2812fx/ws2812fx.pro b/ws2812fx/ws2812fx.pro new file mode 100644 index 00000000..3fe77d5b --- /dev/null +++ b/ws2812fx/ws2812fx.pro @@ -0,0 +1,12 @@ +include(../plugins.pri) + +QT += serialport + +TARGET = $$qtLibraryTarget(nymea_devicepluginws2812fx) + +SOURCES += \ + devicepluginws2812fx.cpp \ + + +HEADERS += \ + devicepluginws2812fx.h \