• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OpenLightingProject / ola / 26690148907

30 May 2026 05:21PM UTC coverage: 44.862%. Remained the same
26690148907

Pull #1738

github

web-flow
Merge 408ef0208 into 185b10f22
Pull Request #1738: Support Nanoleaf v2 protocol, closes #1730

8557 of 19863 branches covered (43.08%)

20 of 37 new or added lines in 3 files covered. (54.05%)

1 existing line in 1 file now uncovered.

22116 of 49298 relevant lines covered (44.86%)

48.79 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

74.32
/plugins/nanoleaf/NanoleafNode.cpp
1
/*
2
 * This program is free software; you can redistribute it and/or modify
3
 * it under the terms of the GNU General Public License as published by
4
 * the Free Software Foundation; either version 2 of the License, or
5
 * (at your option) any later version.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU Library General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
15
 *
16
 * NanoleafNode.cpp
17
 * A Nanoleaf node
18
 * Copyright (C) 2017 Peter Newman
19
 */
20

21
#include <math.h>
22

23
#include <algorithm>
24
#include <memory>
25
#include <vector>
26

27
#include "ola/Constants.h"
28
#include "ola/Logging.h"
29
#include "ola/network/SocketAddress.h"
30
#include "ola/util/Utils.h"
31
#include "plugins/nanoleaf/NanoleafNode.h"
32

33
namespace ola {
34
namespace plugin {
35
namespace nanoleaf {
36

37
using ola::network::IPV4SocketAddress;
38
using ola::network::UDPSocket;
39
using ola::utils::TruncateUInt16Low;
40
using std::auto_ptr;
41
using std::vector;
42

43
/*
44
 * Create a new Nanoleaf node.
45
 * @param ss a SelectServerInterface to use
46
 * @param socket a UDPSocket or Null. Ownership is transferred.
47
 */
48
NanoleafNode::NanoleafNode(ola::io::SelectServerInterface *ss,
3✔
49
                           std::vector<uint16_t> panels,
50
                           ola::network::UDPSocketInterface *socket,
51
                           NanoleafVersion version)
3✔
52
    : m_running(false),
3✔
53
      m_ss(ss),
3✔
54
      m_panels(panels),
3✔
55
      m_version(version),
3✔
56
      m_output_stream(&m_output_queue),
3✔
57
      m_socket(socket) {
3✔
58
}
3✔
59

60

61

62
/*
63
 * Cleanup.
64
 */
65
NanoleafNode::~NanoleafNode() {
3✔
66
  Stop();
3✔
67
}
3✔
68

69

70
/*
71
 * Start this node.
72
 */
73
bool NanoleafNode::Start() {
3✔
74
  if (m_running) {
3✔
75
    return false;
76
  }
77

78
  if (!InitNetwork()) {
3✔
79
    return false;
80
  }
81
  m_running = true;
3✔
82
  return true;
3✔
83
}
84

85

86
/*
87
 * Stop this node.
88
 */
89
bool NanoleafNode::Stop() {
6✔
90
  if (!m_running) {
6✔
91
    return false;
92
  }
93

94
  m_ss->RemoveReadDescriptor(m_socket.get());
3✔
95
  m_socket.reset();
3✔
96
  m_running = false;
3✔
97
  return true;
3✔
98
}
99

100

101
/*
102
 * Send some DMX data
103
 */
104
bool NanoleafNode::SendDMX(const IPV4SocketAddress &target,
3✔
105
                           const DmxBuffer &buffer) {
106
  if (!buffer.Size()) {
3✔
107
    OLA_DEBUG << "Not sending 0 length packet";
×
108
    return true;
×
109
  } else if (buffer.Size() < NANOLEAF_SLOTS_PER_PANEL) {
3✔
110
    OLA_INFO << "Insufficient DMX data, required " << NANOLEAF_SLOTS_PER_PANEL
×
111
             << ", got " << buffer.Size();
×
112
  }
113

114
  // Although the field is now a uint16_t in some cases, we're still limited to
115
  // 170 panels in one DMX universe unless we added the opportunity to address
116
  // them so they overlapped
117
  uint16_t panel_count = std::min(
3✔
118
      static_cast<uint16_t>(m_panels.size()),
3✔
119
      static_cast<uint16_t>(floor(buffer.Size() / NANOLEAF_SLOTS_PER_PANEL)));
3✔
120

121
  m_output_queue.Clear();
3✔
122
  if (m_version == VERSION_V1) {
3✔
123
    // This needs truncating as v1 only supports up to 255 panels but we use a
124
    // uint16_t so we can use the same setup for v2
125
    m_output_stream << TruncateUInt16Low(panel_count);
1✔
126
    for (uint16_t i = 0; i < panel_count; i++) {
3✔
127
      // This needs truncating as v1 only supports panel IDs up to 255 but we
128
      // use a uint16_t so we can use the same setup for v2
129
      m_output_stream << TruncateUInt16Low(m_panels[i])
2✔
130
                      << NANOLEAF_FRAME_COUNT_V1;
2✔
131
      m_output_stream.Write(buffer.GetRaw() + (i * NANOLEAF_SLOTS_PER_PANEL),
2✔
132
                            NANOLEAF_SLOTS_PER_PANEL);
133
      m_output_stream << NANOLEAF_WHITE_LEVEL << NANOLEAF_TRANSITION_TIME_V1;
2✔
134
    }
135
  } else if (m_version == VERSION_V2) {
2✔
136
    // This is just 16 bit in v2
137
    m_output_stream << panel_count;
2✔
138
    for (uint16_t i = 0; i < panel_count; i++) {
8✔
139
      // This is just 16 bit in v2
140
      m_output_stream << m_panels[i];
6✔
141
      // No frame count in v2
142
      // TODO(Peter): Check this doesn't flip the bytes)
143
      m_output_stream.Write(buffer.GetRaw() + (i * NANOLEAF_SLOTS_PER_PANEL),
6✔
144
                            NANOLEAF_SLOTS_PER_PANEL);
145
      // Transition time is a uint16_t in v2
146
      m_output_stream << NANOLEAF_WHITE_LEVEL << NANOLEAF_TRANSITION_TIME_V2;
6✔
147
    }
148
  } else {
NEW
149
    OLA_WARN << "Unknown Nanoleaf protocol version " << m_version;
×
150
  }
151

152
  // m_output_queue.Dump(&std::cerr);
153

154
  bool ok = m_socket->SendTo(&m_output_queue, target);
3✔
155
  if (!ok) {
3✔
156
    OLA_WARN << "Failed to send Nanoleaf packet";
×
157
  }
158

159
  if (!m_output_queue.Empty()) {
3✔
160
    OLA_WARN << "Failed to send complete Nanoleaf packet";
×
161
    m_output_queue.Clear();
×
162
  }
163
  return ok;
164
}
165

166

167
/*
168
 * Called when there is data on this socket. Right now we discard all packets.
169
 */
170
void NanoleafNode::SocketReady() {
×
171
  uint8_t packet[1500];
×
172
  ssize_t packet_size = sizeof(packet);
×
173
  ola::network::IPV4SocketAddress source;
×
174

175
  if (!m_socket->RecvFrom(reinterpret_cast<uint8_t*>(&packet),
×
176
                          &packet_size,
177
                          &source)) {
178
    return;
×
179
  }
180

181
  OLA_INFO << "Received Nanoleaf packet from " << source << ", discarding";
×
182
}
×
183

184

185
/*
186
 * Setup the networking components.
187
 */
188
bool NanoleafNode::InitNetwork() {
3✔
189
  std::auto_ptr<ola::network::UDPSocketInterface> socket(m_socket.release());
3✔
190

191
  if (!socket.get()) {
3✔
192
    socket.reset(new UDPSocket());
×
193
  }
194

195
  if (!socket->Init()) {
3✔
196
    OLA_WARN << "Socket init failed";
×
197
    return false;
×
198
  }
199

200
  // if (!socket->Bind(IPV4SocketAddress(IPV4Address::WildCard(),
201
  //                   NANOLEAF_PORT))) {
202
  //   return false;
203
  // }
204

205
  // Do we need to call this if we don't bind?
206
  socket->SetOnData(NewCallback(this, &NanoleafNode::SocketReady));
3✔
207
  m_ss->AddReadDescriptor(socket.get());
3✔
208
  m_socket.reset(socket.release());
3✔
209
  return true;
3✔
210
}
3✔
211
}  // namespace nanoleaf
212
}  // namespace plugin
213
}  // namespace ola
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc