diff --git a/common/protocol/Ola.proto b/common/protocol/Ola.proto index 47747090c8..c7aa81dc44 100644 --- a/common/protocol/Ola.proto +++ b/common/protocol/Ola.proto @@ -67,6 +67,7 @@ enum PluginIds { OLA_PLUGIN_OPENPIXELCONTROL = 21; OLA_PLUGIN_GPIO = 22; OLA_PLUGIN_SPIDMX = 23; + OLA_PLUGIN_NANOLEAF = 10000; /* * To obtain a new plugin ID, open a ticket at diff --git a/configure.ac b/configure.ac index e9fb4e560a..611b99f4b9 100644 --- a/configure.ac +++ b/configure.ac @@ -825,6 +825,7 @@ PLUGIN_SUPPORT(gpio, USE_GPIO) PLUGIN_SUPPORT(karate, USE_KARATE) PLUGIN_SUPPORT(kinet, USE_KINET) PLUGIN_SUPPORT(milinst, USE_MILINST) +PLUGIN_SUPPORT(nanoleaf, USE_NANOLEAF) PLUGIN_SUPPORT(opendmx, USE_OPENDMX) PLUGIN_SUPPORT(openpixelcontrol, USE_OPENPIXELCONTROL) PLUGIN_SUPPORT(osc, USE_OSC, [$have_liblo]) diff --git a/include/ola/io/BigEndianStream.h b/include/ola/io/BigEndianStream.h index 098b8ef571..0edc27077d 100644 --- a/include/ola/io/BigEndianStream.h +++ b/include/ola/io/BigEndianStream.h @@ -22,6 +22,7 @@ #ifndef INCLUDE_OLA_IO_BIGENDIANSTREAM_H_ #define INCLUDE_OLA_IO_BIGENDIANSTREAM_H_ +#include #include #include #include @@ -67,8 +68,7 @@ class BigEndianInputStreamAdaptor: public BigEndianInputStreamInterface { return ok; } - BigEndianInputStreamAdaptor(const BigEndianInputStreamAdaptor&); - BigEndianInputStreamAdaptor& operator=(const BigEndianInputStreamAdaptor&); + DISALLOW_COPY_AND_ASSIGN(BigEndianInputStreamAdaptor); }; @@ -99,8 +99,7 @@ class BigEndianInputStream: public BigEndianInputStreamInterface { InputStream m_input_stream; BigEndianInputStreamAdaptor m_adaptor; - BigEndianInputStream(const BigEndianInputStream&); - BigEndianInputStream& operator=(const BigEndianInputStream&); + DISALLOW_COPY_AND_ASSIGN(BigEndianInputStream); }; @@ -157,9 +156,7 @@ class BigEndianOutputStreamAdaptor: public BigEndianOutputStreamInterface { return *this; } - BigEndianOutputStreamAdaptor(const BigEndianOutputStreamAdaptor&); - BigEndianOutputStreamAdaptor& operator=( - const BigEndianOutputStreamAdaptor&); + DISALLOW_COPY_AND_ASSIGN(BigEndianOutputStreamAdaptor); }; @@ -196,8 +193,7 @@ class BigEndianOutputStream: public BigEndianOutputStreamInterface { return *this; } - BigEndianOutputStream(const BigEndianOutputStream&); - BigEndianOutputStream& operator=(const BigEndianOutputStream&); + DISALLOW_COPY_AND_ASSIGN(BigEndianOutputStream); }; } // namespace io } // namespace ola diff --git a/include/ola/io/IOQueue.h b/include/ola/io/IOQueue.h index 18adfdfda0..3b9673e283 100644 --- a/include/ola/io/IOQueue.h +++ b/include/ola/io/IOQueue.h @@ -21,6 +21,7 @@ #ifndef INCLUDE_OLA_IO_IOQUEUE_H_ #define INCLUDE_OLA_IO_IOQUEUE_H_ +#include #include #include #include @@ -91,8 +92,7 @@ class IOQueue: public InputBufferInterface, void AppendBlock(); // no copying / assignment for now - IOQueue(const IOQueue&); - IOQueue& operator=(const IOQueue&); + DISALLOW_COPY_AND_ASSIGN(IOQueue); }; } // namespace io } // namespace ola diff --git a/include/ola/io/IOStack.h b/include/ola/io/IOStack.h index 5c0e65ed3e..cfa88f258d 100644 --- a/include/ola/io/IOStack.h +++ b/include/ola/io/IOStack.h @@ -21,6 +21,7 @@ #ifndef INCLUDE_OLA_IO_IOSTACK_H_ #define INCLUDE_OLA_IO_IOSTACK_H_ +#include #include #include #include @@ -82,8 +83,7 @@ class IOStack: public IOVecInterface, void PrependBlock(); // no copying / assignment for now - IOStack(const IOStack&); - IOStack& operator=(const IOStack&); + DISALLOW_COPY_AND_ASSIGN(IOStack); }; } // namespace io } // namespace ola diff --git a/include/ola/io/InputStream.h b/include/ola/io/InputStream.h index 78d70c5ae9..7c98e4f1fa 100644 --- a/include/ola/io/InputStream.h +++ b/include/ola/io/InputStream.h @@ -22,6 +22,7 @@ #define INCLUDE_OLA_IO_INPUTSTREAM_H_ #include +#include #include #include @@ -87,8 +88,7 @@ class InputStream: public InputStreamInterface { return length == sizeof(*val); } - InputStream(const InputStream&); - InputStream& operator=(const InputStream&); + DISALLOW_COPY_AND_ASSIGN(InputStream); }; } // namespace io } // namespace ola diff --git a/include/ola/io/MemoryBuffer.h b/include/ola/io/MemoryBuffer.h index 506ed2dc31..8e1d566507 100644 --- a/include/ola/io/MemoryBuffer.h +++ b/include/ola/io/MemoryBuffer.h @@ -21,6 +21,7 @@ #ifndef INCLUDE_OLA_IO_MEMORYBUFFER_H_ #define INCLUDE_OLA_IO_MEMORYBUFFER_H_ +#include #include #include #include @@ -63,8 +64,7 @@ class MemoryBuffer: public InputBufferInterface { const unsigned int m_size; unsigned int m_cursor; - MemoryBuffer(const MemoryBuffer&); - MemoryBuffer& operator=(const MemoryBuffer&); + DISALLOW_COPY_AND_ASSIGN(MemoryBuffer); }; } // namespace io } // namespace ola diff --git a/include/ola/io/OutputStream.h b/include/ola/io/OutputStream.h index bf6f6b1887..f8a4182a80 100644 --- a/include/ola/io/OutputStream.h +++ b/include/ola/io/OutputStream.h @@ -22,6 +22,7 @@ #define INCLUDE_OLA_IO_OUTPUTSTREAM_H_ #include +#include #include namespace ola { @@ -80,8 +81,7 @@ class OutputStream: public OutputStreamInterface { return *this; } - OutputStream(const OutputStream&); - OutputStream& operator=(const OutputStream&); + DISALLOW_COPY_AND_ASSIGN(OutputStream); }; } // namespace io } // namespace ola diff --git a/include/ola/network/Socket.h b/include/ola/network/Socket.h index b0955566c6..6f3f2972f0 100644 --- a/include/ola/network/Socket.h +++ b/include/ola/network/Socket.h @@ -39,7 +39,7 @@ namespace network { /** * @brief The interface for UDPSockets. * - * This only supports IPv4 sockets. Its an Interface so we can mock it out for + * This only supports IPv4 sockets. Its an %Interface so we can mock it out for * testing. */ class UDPSocketInterface: public ola::io::BidirectionalFileDescriptor { diff --git a/libs/acn/RootSender.h b/libs/acn/RootSender.h index b583c41d13..31926ef8f0 100644 --- a/libs/acn/RootSender.h +++ b/libs/acn/RootSender.h @@ -22,6 +22,7 @@ #define LIBS_ACN_ROOTSENDER_H_ #include "ola/acn/CID.h" +#include "ola/base/Macro.h" #include "libs/acn/PDU.h" #include "libs/acn/RootPDU.h" #include "libs/acn/Transport.h" @@ -59,8 +60,7 @@ class RootSender { PDUBlock m_root_block; RootPDU m_root_pdu; - RootSender(const RootSender&); - RootSender& operator=(const RootSender&); + DISALLOW_COPY_AND_ASSIGN(RootSender); }; } // namespace acn } // namespace ola diff --git a/libs/acn/UDPTransport.h b/libs/acn/UDPTransport.h index 5619ae88e2..17eb7c1a5d 100644 --- a/libs/acn/UDPTransport.h +++ b/libs/acn/UDPTransport.h @@ -22,6 +22,7 @@ #define LIBS_ACN_UDPTRANSPORT_H_ #include "ola/acn/ACNPort.h" +#include "ola/base/Macro.h" #include "ola/network/IPV4Address.h" #include "ola/network/Socket.h" #include "libs/acn/PDU.h" @@ -51,8 +52,7 @@ class OutgoingUDPTransport: public OutgoingTransport { class OutgoingUDPTransportImpl *m_impl; ola::network::IPV4SocketAddress m_destination; - OutgoingUDPTransport(const OutgoingUDPTransport&); - OutgoingUDPTransport& operator=(const OutgoingUDPTransport&); + DISALLOW_COPY_AND_ASSIGN(OutgoingUDPTransport); }; diff --git a/olad/DynamicPluginLoader.cpp b/olad/DynamicPluginLoader.cpp index 462ac21311..d5b11e60ec 100644 --- a/olad/DynamicPluginLoader.cpp +++ b/olad/DynamicPluginLoader.cpp @@ -59,6 +59,10 @@ #include "plugins/milinst/MilInstPlugin.h" #endif // USE_MILINST +#ifdef USE_NANOLEAF +#include "plugins/nanoleaf/NanoleafPlugin.h" +#endif // USE_NANOLEAF + #ifdef USE_OPENDMX #include "plugins/opendmx/OpenDmxPlugin.h" #endif // USE_OPENDMX @@ -177,6 +181,11 @@ void DynamicPluginLoader::PopulatePlugins() { new ola::plugin::milinst::MilInstPlugin(m_plugin_adaptor)); #endif // USE_MILINST +#ifdef USE_NANOLEAF + m_plugins.push_back( + new ola::plugin::nanoleaf::NanoleafPlugin(m_plugin_adaptor)); +#endif // USE_NANOLEAF + #ifdef USE_OPENDMX m_plugins.push_back( new ola::plugin::opendmx::OpenDmxPlugin(m_plugin_adaptor)); diff --git a/plugins/Makefile.mk b/plugins/Makefile.mk index 412a9f3d09..b298128a1e 100644 --- a/plugins/Makefile.mk +++ b/plugins/Makefile.mk @@ -6,6 +6,7 @@ include plugins/gpio/Makefile.mk include plugins/karate/Makefile.mk include plugins/kinet/Makefile.mk include plugins/milinst/Makefile.mk +include plugins/nanoleaf/Makefile.mk include plugins/opendmx/Makefile.mk include plugins/openpixelcontrol/Makefile.mk include plugins/osc/Makefile.mk diff --git a/plugins/espnet/EspNetNode.h b/plugins/espnet/EspNetNode.h index de0b96dc7f..06879675b2 100644 --- a/plugins/espnet/EspNetNode.h +++ b/plugins/espnet/EspNetNode.h @@ -25,6 +25,7 @@ #include #include "ola/Callback.h" #include "ola/DmxBuffer.h" +#include "ola/base/Macro.h" #include "ola/network/IPV4Address.h" #include "ola/network/Interface.h" #include "ola/network/Socket.h" @@ -81,8 +82,6 @@ class EspNetNode { Callback0 *closure; } universe_handler; - EspNetNode(const EspNetNode&); - EspNetNode& operator=(const EspNetNode&); bool InitNetwork(); void HandlePoll(const espnet_poll_t &poll, ssize_t length, const ola::network::IPV4Address &source); @@ -130,6 +129,8 @@ class EspNetNode { static const uint8_t DATA_PAIRS = 2; static const uint8_t DATA_RLE = 4; static const uint8_t START_CODE = 0; + + DISALLOW_COPY_AND_ASSIGN(EspNetNode); }; } // namespace espnet } // namespace plugin diff --git a/plugins/kinet/KiNetNode.h b/plugins/kinet/KiNetNode.h index d7b9db5119..76251bdb15 100644 --- a/plugins/kinet/KiNetNode.h +++ b/plugins/kinet/KiNetNode.h @@ -24,6 +24,7 @@ #include #include "ola/DmxBuffer.h" +#include "ola/base/Macro.h" #include "ola/io/BigEndianStream.h" #include "ola/io/IOQueue.h" #include "ola/io/SelectServerInterface.h" @@ -56,9 +57,6 @@ class KiNetNode { ola::network::Interface m_interface; std::auto_ptr m_socket; - KiNetNode(const KiNetNode&); - KiNetNode& operator=(const KiNetNode&); - void SocketReady(); void PopulatePacketHeader(uint16_t msg_type); bool InitNetwork(); @@ -67,6 +65,8 @@ class KiNetNode { static const uint32_t KINET_MAGIC_NUMBER = 0x0401dc4a; static const uint16_t KINET_VERSION_ONE = 0x0100; static const uint16_t KINET_DMX_MSG = 0x0101; + + DISALLOW_COPY_AND_ASSIGN(KiNetNode); }; } // namespace kinet } // namespace plugin diff --git a/plugins/nanoleaf/Makefile.mk b/plugins/nanoleaf/Makefile.mk new file mode 100644 index 0000000000..29c95c8fb8 --- /dev/null +++ b/plugins/nanoleaf/Makefile.mk @@ -0,0 +1,39 @@ +# LIBRARIES +################################################## +if USE_NANOLEAF +# This is a library which isn't coupled to olad +noinst_LTLIBRARIES += plugins/nanoleaf/libolananoleafnode.la +plugins_nanoleaf_libolananoleafnode_la_SOURCES = plugins/nanoleaf/NanoleafNode.cpp \ + plugins/nanoleaf/NanoleafNode.h +plugins_nanoleaf_libolananoleafnode_la_LIBADD = common/libolacommon.la + +lib_LTLIBRARIES += plugins/nanoleaf/libolananoleaf.la + +# Plugin description is generated from README.md +built_sources += plugins/nanoleaf/NanoleafPluginDescription.h +nodist_plugins_nanoleaf_libolananoleaf_la_SOURCES = \ + plugins/nanoleaf/NanoleafPluginDescription.h +plugins/nanoleaf/NanoleafPluginDescription.h: plugins/nanoleaf/README.md plugins/nanoleaf/Makefile.mk plugins/convert_README_to_header.sh + sh $(top_srcdir)/plugins/convert_README_to_header.sh $(top_srcdir)/plugins/nanoleaf $(top_builddir)/plugins/nanoleaf/NanoleafPluginDescription.h + +plugins_nanoleaf_libolananoleaf_la_SOURCES = \ + plugins/nanoleaf/NanoleafPlugin.cpp \ + plugins/nanoleaf/NanoleafPlugin.h \ + plugins/nanoleaf/NanoleafDevice.cpp \ + plugins/nanoleaf/NanoleafDevice.h \ + plugins/nanoleaf/NanoleafPort.h +plugins_nanoleaf_libolananoleaf_la_LIBADD = \ + olad/plugin_api/libolaserverplugininterface.la \ + plugins/nanoleaf/libolananoleafnode.la + +# TESTS +################################################## +test_programs += plugins/nanoleaf/NanoleafTester + +plugins_nanoleaf_NanoleafTester_SOURCES = plugins/nanoleaf/NanoleafNodeTest.cpp +plugins_nanoleaf_NanoleafTester_CXXFLAGS = $(COMMON_TESTING_FLAGS) +plugins_nanoleaf_NanoleafTester_LDADD = $(COMMON_TESTING_LIBS) \ + plugins/nanoleaf/libolananoleafnode.la +endif + +EXTRA_DIST += plugins/nanoleaf/README.md diff --git a/plugins/nanoleaf/NanoleafDevice.cpp b/plugins/nanoleaf/NanoleafDevice.cpp new file mode 100644 index 0000000000..fb51a12af7 --- /dev/null +++ b/plugins/nanoleaf/NanoleafDevice.cpp @@ -0,0 +1,155 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * NanoleafDevice.cpp + * A Nanoleaf Device. + * Copyright (C) 2017 Peter Newman + */ + +#include +#include +#include +#include + +#include "ola/Callback.h" +#include "ola/Logging.h" +#include "ola/StringUtils.h" +#include "ola/network/IPV4Address.h" +#include "ola/network/InterfacePicker.h" +#include "ola/network/NetworkUtils.h" +#include "ola/network/SocketAddress.h" +#include "olad/PluginAdaptor.h" +#include "olad/Port.h" +#include "olad/Preferences.h" +#include "plugins/nanoleaf/NanoleafDevice.h" +#include "plugins/nanoleaf/NanoleafPort.h" + +namespace ola { +namespace plugin { +namespace nanoleaf { + +using ola::network::IPV4Address; +using ola::network::IPV4SocketAddress; +using std::auto_ptr; +using std::string; +using std::vector; + +/* + * Create a new Nanoleaf Device + */ +NanoleafDevice::NanoleafDevice( + AbstractPlugin *owner, + Preferences *preferences, + PluginAdaptor *plugin_adaptor, + const ola::network::IPV4Address &controller) + : Device(owner, "Nanoleaf Device"), + m_node(NULL), + m_preferences(preferences), + m_plugin_adaptor(plugin_adaptor), + m_controller(controller) { + SetDefaults(); +} + + +/* + * Start this device + * @return true on success, false on failure + */ +bool NanoleafDevice::StartHook() { + vector panels; + vector panel_list; + StringSplit(m_preferences->GetValue(PanelsKey()), &panel_list, ","); + vector::const_iterator iter = panel_list.begin(); + for (; iter != panel_list.end(); ++iter) { + if (iter->empty()) { + continue; + } + + uint8_t panel; + if (!StringToInt(*iter, &panel)) { + OLA_WARN << "Invalid value for panel: " << *iter; + return false; + } + panels.push_back(panel); + } + + if (panels.empty()) { + OLA_WARN << "No panels found"; + m_node = NULL; + return false; + } + + m_node = new NanoleafNode(m_plugin_adaptor, panels); + + if (!m_node->Start()) { + delete m_node; + m_node = NULL; + return false; + } + + uint16_t ip_port; + if (!StringToInt(m_preferences->GetValue(IPPortKey()), &ip_port)) { + ip_port = DEFAULT_STREAMING_PORT; + } + IPV4SocketAddress socket_address = IPV4SocketAddress(m_controller, ip_port); + AddPort(new NanoleafOutputPort(this, socket_address, m_node, 0)); + return true; +} + + +string NanoleafDevice::DeviceId() const { + return m_controller.ToString(); +} + + +string NanoleafDevice::IPPortKey() const { + return m_controller.ToString() + "-port"; +} + + +string NanoleafDevice::PanelsKey() const { + return m_controller.ToString() + "-panels"; +} + + +void NanoleafDevice::SetDefaults() { + // Set device options + m_preferences->SetDefaultValue(PanelsKey(), StringValidator(), ""); + m_preferences->SetDefaultValue( + IPPortKey(), + UIntValidator(1, std::numeric_limits::max()), + DEFAULT_STREAMING_PORT); + m_preferences->Save(); +} + + +/** + * Stop this device. This is called before the ports are deleted + */ +void NanoleafDevice::PrePortStop() { + m_node->Stop(); +} + + +/* + * Stop this device + */ +void NanoleafDevice::PostPortStop() { + delete m_node; + m_node = NULL; +} +} // namespace nanoleaf +} // namespace plugin +} // namespace ola diff --git a/plugins/nanoleaf/NanoleafDevice.h b/plugins/nanoleaf/NanoleafDevice.h new file mode 100644 index 0000000000..4d63ab28af --- /dev/null +++ b/plugins/nanoleaf/NanoleafDevice.h @@ -0,0 +1,63 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * NanoleafDevice.h + * Interface for the Nanoleaf device + * Copyright (C) 2017 Peter Newman + */ + +#ifndef PLUGINS_NANOLEAF_NANOLEAFDEVICE_H_ +#define PLUGINS_NANOLEAF_NANOLEAFDEVICE_H_ + +#include +#include + +#include "ola/network/IPV4Address.h" +#include "olad/Device.h" + +namespace ola { +namespace plugin { +namespace nanoleaf { + +class NanoleafDevice: public ola::Device { + public: + NanoleafDevice(AbstractPlugin *owner, + class Preferences *preferences, + class PluginAdaptor *plugin_adaptor, + const ola::network::IPV4Address &controller); + + std::string DeviceId() const; + + protected: + bool StartHook(); + void PrePortStop(); + void PostPortStop(); + + private: + class NanoleafNode *m_node; + class Preferences *m_preferences; + class PluginAdaptor *m_plugin_adaptor; + const ola::network::IPV4Address m_controller; + + void SetDefaults(); + std::string IPPortKey() const; + std::string PanelsKey() const; + + static const uint16_t DEFAULT_STREAMING_PORT = 60221; +}; +} // namespace nanoleaf +} // namespace plugin +} // namespace ola +#endif // PLUGINS_NANOLEAF_NANOLEAFDEVICE_H_ diff --git a/plugins/nanoleaf/NanoleafNode.cpp b/plugins/nanoleaf/NanoleafNode.cpp new file mode 100644 index 0000000000..b421c5528d --- /dev/null +++ b/plugins/nanoleaf/NanoleafNode.cpp @@ -0,0 +1,184 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * NanoleafNode.cpp + * A Nanoleaf node + * Copyright (C) 2017 Peter Newman + */ + +#include + +#include +#include +#include + +#include "ola/Constants.h" +#include "ola/Logging.h" +#include "ola/network/SocketAddress.h" +#include "plugins/nanoleaf/NanoleafNode.h" + +namespace ola { +namespace plugin { +namespace nanoleaf { + +using ola::network::IPV4SocketAddress; +using ola::network::UDPSocket; +using std::auto_ptr; +using std::vector; + +/* + * Create a new Nanoleaf node. + * @param ss a SelectServerInterface to use + * @param socket a UDPSocket or Null. Ownership is transferred. + */ +NanoleafNode::NanoleafNode(ola::io::SelectServerInterface *ss, + std::vector panels, + ola::network::UDPSocketInterface *socket) + : m_running(false), + m_ss(ss), + m_panels(panels), + m_output_stream(&m_output_queue), + m_socket(socket) { +} + + + +/* + * Cleanup. + */ +NanoleafNode::~NanoleafNode() { + Stop(); +} + + +/* + * Start this node. + */ +bool NanoleafNode::Start() { + if (m_running) { + return false; + } + + if (!InitNetwork()) { + return false; + } + m_running = true; + return true; +} + + +/* + * Stop this node. + */ +bool NanoleafNode::Stop() { + if (!m_running) { + return false; + } + + m_ss->RemoveReadDescriptor(m_socket.get()); + m_socket.reset(); + m_running = false; + return true; +} + + +/* + * Send some DMX data + */ +bool NanoleafNode::SendDMX(const IPV4SocketAddress &target, + const DmxBuffer &buffer) { + if (!buffer.Size()) { + OLA_DEBUG << "Not sending 0 length packet"; + return true; + } else if (buffer.Size() < NANOLEAF_SLOTS_PER_PANEL) { + OLA_INFO << "Insufficient DMX data, required " << NANOLEAF_SLOTS_PER_PANEL + << ", got " << buffer.Size(); + } + + uint8_t panel_count = std::min( + static_cast(m_panels.size()), + static_cast(floor(buffer.Size() / NANOLEAF_SLOTS_PER_PANEL))); + + m_output_queue.Clear(); + m_output_stream << panel_count; + for (uint8_t i = 0; i < panel_count; i++) { + m_output_stream << m_panels[i] << NANOLEAF_FRAME_COUNT; + m_output_stream.Write(buffer.GetRaw() + (i * NANOLEAF_SLOTS_PER_PANEL), + NANOLEAF_SLOTS_PER_PANEL); + m_output_stream << NANOLEAF_WHITE_LEVEL << NANOLEAF_TRANSITION_TIME; + } + + // m_output_queue.Dump(&std::cerr); + + bool ok = m_socket->SendTo(&m_output_queue, target); + if (!ok) { + OLA_WARN << "Failed to send Nanoleaf packet"; + } + + if (!m_output_queue.Empty()) { + OLA_WARN << "Failed to send complete Nanoleaf packet"; + m_output_queue.Clear(); + } + return ok; +} + + +/* + * Called when there is data on this socket. Right now we discard all packets. + */ +void NanoleafNode::SocketReady() { + uint8_t packet[1500]; + ssize_t packet_size = sizeof(packet); + ola::network::IPV4SocketAddress source; + + if (!m_socket->RecvFrom(reinterpret_cast(&packet), + &packet_size, + &source)) { + return; + } + + OLA_INFO << "Received Nanoleaf packet from " << source << ", discarding"; +} + + +/* + * Setup the networking components. + */ +bool NanoleafNode::InitNetwork() { + std::auto_ptr socket(m_socket.release()); + + if (!socket.get()) { + socket.reset(new UDPSocket()); + } + + if (!socket->Init()) { + OLA_WARN << "Socket init failed"; + return false; + } + + // if (!socket->Bind(IPV4SocketAddress(IPV4Address::WildCard(), + // NANOLEAF_PORT))) { + // return false; + // } + + // Do we need to call this if we don't bind? + socket->SetOnData(NewCallback(this, &NanoleafNode::SocketReady)); + m_ss->AddReadDescriptor(socket.get()); + m_socket.reset(socket.release()); + return true; +} +} // namespace nanoleaf +} // namespace plugin +} // namespace ola diff --git a/plugins/nanoleaf/NanoleafNode.h b/plugins/nanoleaf/NanoleafNode.h new file mode 100644 index 0000000000..dc5208e94c --- /dev/null +++ b/plugins/nanoleaf/NanoleafNode.h @@ -0,0 +1,76 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * NanoleafNode.h + * Header file for the NanoleafNode class. + * Copyright (C) 2017 Peter Newman + */ + +#ifndef PLUGINS_NANOLEAF_NANOLEAFNODE_H_ +#define PLUGINS_NANOLEAF_NANOLEAFNODE_H_ + +#include +#include + +#include "ola/DmxBuffer.h" +#include "ola/base/Macro.h" +#include "ola/io/OutputStream.h" +#include "ola/io/IOQueue.h" +#include "ola/io/SelectServerInterface.h" +#include "ola/network/Interface.h" +#include "ola/network/SocketAddress.h" +#include "ola/network/Socket.h" + +namespace ola { +namespace plugin { +namespace nanoleaf { + +class NanoleafNode { + public: + NanoleafNode(ola::io::SelectServerInterface *ss, + std::vector panels, + ola::network::UDPSocketInterface *socket = NULL); + virtual ~NanoleafNode(); + + bool Start(); + bool Stop(); + + // The following apply to Input Ports (those which send data) + bool SendDMX(const ola::network::IPV4SocketAddress &target, + const ola::DmxBuffer &buffer); + + private: + bool m_running; + ola::io::SelectServerInterface *m_ss; + std::vector m_panels; + ola::io::IOQueue m_output_queue; + ola::io::OutputStream m_output_stream; + ola::network::Interface m_interface; + std::auto_ptr m_socket; + + void SocketReady(); + bool InitNetwork(); + + static const uint8_t NANOLEAF_FRAME_COUNT = 0x01; + static const uint8_t NANOLEAF_WHITE_LEVEL = 0x00; + static const uint8_t NANOLEAF_TRANSITION_TIME = 0x01; + static const uint8_t NANOLEAF_SLOTS_PER_PANEL = 3; + + DISALLOW_COPY_AND_ASSIGN(NanoleafNode); +}; +} // namespace nanoleaf +} // namespace plugin +} // namespace ola +#endif // PLUGINS_NANOLEAF_NANOLEAFNODE_H_ diff --git a/plugins/nanoleaf/NanoleafNodeTest.cpp b/plugins/nanoleaf/NanoleafNodeTest.cpp new file mode 100644 index 0000000000..b39b899bad --- /dev/null +++ b/plugins/nanoleaf/NanoleafNodeTest.cpp @@ -0,0 +1,99 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * NanoleafNodeTest.cpp + * Test fixture for the NanoleafNode class + * Copyright (C) 2017 Peter Newman + */ + +#include + +#include + +#include "ola/DmxBuffer.h" +#include "ola/Logging.h" +#include "ola/io/SelectServer.h" +#include "ola/network/IPV4Address.h" +#include "ola/network/SocketAddress.h" +#include "ola/testing/MockUDPSocket.h" +#include "plugins/nanoleaf/NanoleafNode.h" +#include "ola/testing/TestUtils.h" + +using ola::DmxBuffer; +using ola::network::IPV4Address; +using ola::network::IPV4SocketAddress; +using ola::plugin::nanoleaf::NanoleafNode; +using ola::testing::MockUDPSocket; +using std::vector; + + +class NanoleafNodeTest: public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(NanoleafNodeTest); + CPPUNIT_TEST(testSendDMX); + CPPUNIT_TEST_SUITE_END(); + + public: + NanoleafNodeTest() + : CppUnit::TestFixture(), + ss(NULL), + m_socket(new MockUDPSocket()) { + } + void setUp(); + + void testSendDMX(); + + private: + ola::io::SelectServer ss; + IPV4Address target_ip; + MockUDPSocket *m_socket; + + static const uint16_t NANOLEAF_PORT = 60221; +}; + + +CPPUNIT_TEST_SUITE_REGISTRATION(NanoleafNodeTest); + +void NanoleafNodeTest::setUp() { + ola::InitLogging(ola::OLA_LOG_INFO, ola::OLA_LOG_STDERR); + ola::network::IPV4Address::FromString("10.0.0.10", &target_ip); +} + +/** + * Check sending DMX works. + */ +void NanoleafNodeTest::testSendDMX() { + vector panels; + panels.push_back(0x10); + panels.push_back(0x20); + + NanoleafNode node(&ss, panels, m_socket); + OLA_ASSERT_TRUE(node.Start()); + + const uint8_t expected_data[] = { + 0x02, + 0x10, 0x01, 1, 5, 8, 0x00, 0x01, + 0x20, 0x01, 10, 14, 45, 0x00, 0x01 + }; + + m_socket->AddExpectedData(expected_data, sizeof(expected_data), target_ip, + NANOLEAF_PORT); + + DmxBuffer buffer; + buffer.SetFromString("1,5,8,10,14,45"); + OLA_ASSERT_TRUE(node.SendDMX(IPV4SocketAddress(target_ip, NANOLEAF_PORT), + buffer)); + m_socket->Verify(); + OLA_ASSERT(node.Stop()); +} diff --git a/plugins/nanoleaf/NanoleafPlugin.cpp b/plugins/nanoleaf/NanoleafPlugin.cpp new file mode 100644 index 0000000000..db129c6fbe --- /dev/null +++ b/plugins/nanoleaf/NanoleafPlugin.cpp @@ -0,0 +1,134 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * NanoleafPlugin.cpp + * The Nanoleaf plugin for ola + * Copyright (C) 2017 Peter Newman + */ + +#include +#include + +#include "ola/Logging.h" +#include "ola/network/IPV4Address.h" +#include "olad/PluginAdaptor.h" +#include "olad/Preferences.h" +#include "plugins/nanoleaf/NanoleafDevice.h" +#include "plugins/nanoleaf/NanoleafPlugin.h" +#include "plugins/nanoleaf/NanoleafPluginDescription.h" + + +namespace ola { +namespace plugin { +namespace nanoleaf { + +using ola::network::IPV4Address; +using std::string; +using std::vector; + +const char NanoleafPlugin::CONTROLLER_KEY[] = "controller"; +const char NanoleafPlugin::PLUGIN_NAME[] = "Nanoleaf"; +const char NanoleafPlugin::PLUGIN_PREFIX[] = "nanoleaf"; + +NanoleafPlugin::NanoleafPlugin(PluginAdaptor *plugin_adaptor) + : Plugin(plugin_adaptor) { +} + +NanoleafPlugin::~NanoleafPlugin() {} + +/* + * Start the plugin. + */ +bool NanoleafPlugin::StartHook() { + vector controllers_strings = m_preferences->GetMultipleValue( + CONTROLLER_KEY); + vector::const_iterator iter = controllers_strings.begin(); + + for (; iter != controllers_strings.end(); ++iter) { + if (iter->empty()) { + continue; + } + IPV4Address target; + if (IPV4Address::FromString(*iter, &target)) { + NanoleafDevice *device = new NanoleafDevice(this, + m_preferences, + m_plugin_adaptor, + target); + + if (!device) { + continue; + } + + if (!device->Start()) { + delete device; + continue; + } + m_devices.push_back(device); + m_plugin_adaptor->RegisterDevice(device); + } else { + OLA_WARN << "Invalid controller IP address : " << *iter; + } + } + // TODO(Peter): Check we got at least one good device? + return true; +} + + +/* + * Stop the plugin + * @return true on success, false on failure + */ +bool NanoleafPlugin::StopHook() { + vector::iterator iter = m_devices.begin(); + bool ok = true; + for (; iter != m_devices.end(); ++iter) { + m_plugin_adaptor->UnregisterDevice(*iter); + ok &= (*iter)->Stop(); + delete *iter; + } + return ok; +} + + +/* + * Return the description for this plugin. + * @return a string description of the plugin + */ +string NanoleafPlugin::Description() const { + return plugin_description; +} + + +/* + * Set default preferences. + */ +bool NanoleafPlugin::SetDefaultPreferences() { + bool save = false; + + if (!m_preferences) { + return false; + } + + save |= m_preferences->SetDefaultValue(CONTROLLER_KEY, + StringValidator(true), ""); + + if (save) { + m_preferences->Save(); + } + return true; +} +} // namespace nanoleaf +} // namespace plugin +} // namespace ola diff --git a/plugins/nanoleaf/NanoleafPlugin.h b/plugins/nanoleaf/NanoleafPlugin.h new file mode 100644 index 0000000000..86fc2f9261 --- /dev/null +++ b/plugins/nanoleaf/NanoleafPlugin.h @@ -0,0 +1,58 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * NanoleafPlugin.h + * The Nanoleaf plugin for ola + * Copyright (C) 2017 Peter Newman + */ + +#ifndef PLUGINS_NANOLEAF_NANOLEAFPLUGIN_H_ +#define PLUGINS_NANOLEAF_NANOLEAFPLUGIN_H_ + +#include +#include +#include +#include "olad/Plugin.h" +#include "ola/plugin_id.h" + +namespace ola { +namespace plugin { +namespace nanoleaf { + +class NanoleafPlugin : public Plugin { + public: + explicit NanoleafPlugin(PluginAdaptor *plugin_adaptor); + ~NanoleafPlugin(); + + std::string Name() const { return PLUGIN_NAME; } + ola_plugin_id Id() const { return OLA_PLUGIN_NANOLEAF; } + std::string Description() const; + std::string PluginPrefix() const { return PLUGIN_PREFIX; } + + private: + std::vector m_devices; + + bool StartHook(); + bool StopHook(); + bool SetDefaultPreferences(); + + static const char PLUGIN_NAME[]; + static const char PLUGIN_PREFIX[]; + static const char CONTROLLER_KEY[]; +}; +} // namespace nanoleaf +} // namespace plugin +} // namespace ola +#endif // PLUGINS_NANOLEAF_NANOLEAFPLUGIN_H_ diff --git a/plugins/nanoleaf/NanoleafPort.h b/plugins/nanoleaf/NanoleafPort.h new file mode 100644 index 0000000000..3abff5a596 --- /dev/null +++ b/plugins/nanoleaf/NanoleafPort.h @@ -0,0 +1,60 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * NanoleafPort.h + * The Nanoleaf plugin for ola + * Copyright (C) 2017 Peter Newman + */ + +#ifndef PLUGINS_NANOLEAF_NANOLEAFPORT_H_ +#define PLUGINS_NANOLEAF_NANOLEAFPORT_H_ + +#include +#include "ola/network/SocketAddress.h" +#include "olad/Port.h" +#include "plugins/nanoleaf/NanoleafDevice.h" +#include "plugins/nanoleaf/NanoleafNode.h" + +namespace ola { +namespace plugin { +namespace nanoleaf { + +class NanoleafOutputPort: public BasicOutputPort { + public: + NanoleafOutputPort(NanoleafDevice *device, + const ola::network::IPV4SocketAddress &target, + NanoleafNode *node, + unsigned int port_id) + : BasicOutputPort(device, port_id), + m_node(node), + m_target(target) { + } + + bool WriteDMX(const DmxBuffer &buffer, OLA_UNUSED uint8_t priority) { + return m_node->SendDMX(m_target, buffer); + } + + std::string Description() const { + return "Controller: " + m_target.Host().ToString(); + } + + private: + NanoleafNode *m_node; + const ola::network::IPV4SocketAddress m_target; +}; +} // namespace nanoleaf +} // namespace plugin +} // namespace ola +#endif // PLUGINS_NANOLEAF_NANOLEAFPORT_H_ diff --git a/plugins/nanoleaf/README.md b/plugins/nanoleaf/README.md new file mode 100644 index 0000000000..e994c030de --- /dev/null +++ b/plugins/nanoleaf/README.md @@ -0,0 +1,28 @@ +Nanoleaf Plugin +============ + +This plugin creates a device per IP, each with one output port. Each port +represents a Nanoleaf Aurora controller, with up to 30 panels attached. +This plugin uses the streaming external control effects functionality of +the Aurora OpenAPI. + +Currently only the [Nanoleaf +Aurora](https://nanoleaf.me/en/consumer-led-lighting/products/ +smarter-series/nanoleaf-light-panels-smarter-kit/) +is supported. + + +## Config file: `ola-nanoleaf.conf` + +`controller = ` +The IP of the controller to send to. You can communicate with more than +one controller by adding multiple `controller =` lines + +### Per Device Settings + +`-port = ` +The port to stream to on the controller, range is 1 - 65535. + +`-panels = [int]` +The list of panel IDs to control, each panel is mapped to three DMX512 slots +(for Red, Green and Blue). diff --git a/plugins/osc/OSCNode.h b/plugins/osc/OSCNode.h index 850cfbe41e..2a2347d2df 100644 --- a/plugins/osc/OSCNode.h +++ b/plugins/osc/OSCNode.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -133,8 +134,7 @@ class OSCNode { lo_address liblo_address; private: - NodeOSCTarget(const NodeOSCTarget&); - NodeOSCTarget& operator=(const NodeOSCTarget&); + DISALLOW_COPY_AND_ASSIGN(NodeOSCTarget); }; typedef std::vector OSCTargetVector; diff --git a/tools/e133/DesignatedControllerConnection.cpp b/tools/e133/DesignatedControllerConnection.cpp index c1b8d0c273..5daae899fa 100644 --- a/tools/e133/DesignatedControllerConnection.cpp +++ b/tools/e133/DesignatedControllerConnection.cpp @@ -24,6 +24,7 @@ #include "ola/Callback.h" #include "ola/Logging.h" #include "ola/acn/ACNVectors.h" +#include "ola/base/Macro.h" #include "ola/io/SelectServerInterface.h" #include "ola/network/HealthCheckedConnection.h" #include "ola/stl/STLUtils.h" @@ -72,8 +73,7 @@ class OutstandingMessage { bool m_message_sent; auto_ptr m_rdm_response; - OutstandingMessage(const OutstandingMessage&); - OutstandingMessage& operator=(const OutstandingMessage&); + DISALLOW_COPY_AND_ASSIGN(OutstandingMessage); }; diff --git a/tools/e133/DesignatedControllerConnection.h b/tools/e133/DesignatedControllerConnection.h index d565223229..7993b70e12 100644 --- a/tools/e133/DesignatedControllerConnection.h +++ b/tools/e133/DesignatedControllerConnection.h @@ -24,6 +24,7 @@ #include #include +#include "ola/base/Macro.h" #include "ola/e133/MessageBuilder.h" #include "ola/io/NonBlockingSender.h" #include "ola/io/SelectServerInterface.h" @@ -108,8 +109,6 @@ class DesignatedControllerConnection { static const unsigned int MAX_QUEUE_SIZE; - DesignatedControllerConnection(const DesignatedControllerConnection&); - DesignatedControllerConnection& operator=( - const DesignatedControllerConnection&); + DISALLOW_COPY_AND_ASSIGN(DesignatedControllerConnection); }; #endif // TOOLS_E133_DESIGNATEDCONTROLLERCONNECTION_H_ diff --git a/tools/e133/DeviceManagerImpl.cpp b/tools/e133/DeviceManagerImpl.cpp index 9b8011a54a..86ace1d99c 100644 --- a/tools/e133/DeviceManagerImpl.cpp +++ b/tools/e133/DeviceManagerImpl.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -88,8 +89,7 @@ class DeviceState { bool am_designated_controller; private: - DeviceState(const DeviceState&); - DeviceState& operator=(const DeviceState&); + DISALLOW_COPY_AND_ASSIGN(DeviceState); }; diff --git a/tools/e133/SimpleE133Node.h b/tools/e133/SimpleE133Node.h index e50617b0e7..df27f26bbe 100644 --- a/tools/e133/SimpleE133Node.h +++ b/tools/e133/SimpleE133Node.h @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -93,7 +94,6 @@ class SimpleE133Node { void DumpTCPStats(); void SendUnsolicited(); - SimpleE133Node(const SimpleE133Node&); - SimpleE133Node operator=(const SimpleE133Node&); + DISALLOW_COPY_AND_ASSIGN(SimpleE133Node); }; #endif // TOOLS_E133_SIMPLEE133NODE_H_ diff --git a/tools/rdm/Makefile.mk b/tools/rdm/Makefile.mk index 6b3b66a89c..e186b75422 100644 --- a/tools/rdm/Makefile.mk +++ b/tools/rdm/Makefile.mk @@ -82,8 +82,7 @@ dist_rdmtestsexec_SCRIPTS = \ tools/rdm/rdm_responder_test.py \ tools/rdm/rdm_test_server.py -dist_noinst_SCRIPTS = \ - tools/rdm/list_rdm_tests.py +dist_noinst_SCRIPTS += tools/rdm/list_rdm_tests.py # Data files for the RDM Test Server tools_rdm_testserver_staticdir = $(datadir)/ola/rdm-server