Skip to content

Commit e61b6cd

Browse files
Implement direct control for v2 protocol (Canvas and Shapes), clean up some formatting with v1 to match.
1 parent 7276d25 commit e61b6cd

File tree

3 files changed

+107
-46
lines changed

3 files changed

+107
-46
lines changed

Controllers/NanoleafController/NanoleafController.cpp

Lines changed: 86 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -159,30 +159,77 @@ void NanoleafController::UpdateLEDs(std::vector<RGBColor>& colors)
159159

160160
if(model == NANOLEAF_LIGHT_PANELS_MODEL)
161161
{
162+
/*---------------------------------------------------------*\
163+
| Protocol V1 - https://forum.nanoleaf.me/docs |
164+
| |
165+
| Size Description |
166+
| --------------------------------------------------------- |
167+
| 1 nPanels Number of panels |
168+
| |
169+
| 1 panelId ID of panel |
170+
| 1 nFrames Number of frames (always 1) |
171+
| 1 R Red channel |
172+
| 1 G Green channel |
173+
| 1 B Blue channel |
174+
| 1 W White channel (ignored) |
175+
| 1 transitionTime Transition time (x 100ms) |
176+
\*---------------------------------------------------------*/
162177
uint8_t size = panel_ids.size();
163178

164-
uint8_t* message = (uint8_t*)malloc(size*7+6+1);
179+
uint8_t* message = (uint8_t*)malloc((size * 7) + 1);
165180

166-
message[0] = (uint8_t)size;
181+
message[0] = (uint8_t)size; /* nPanels */
167182

168183
for(int i = 0; i < size; i++)
169184
{
170-
message[( 7 * i) + 0 + 1] = (uint8_t)panel_ids[i];
171-
message[( 7 * i) + 1 + 1] = (uint8_t)1;
172-
message[( 7 * i) + 2 + 1] = (uint8_t)RGBGetRValue(colors[i]);
173-
message[( 7 * i) + 3 + 1] = (uint8_t)RGBGetGValue(colors[i]);
174-
message[( 7 * i) + 4 + 1] = (uint8_t)RGBGetBValue(colors[i]);
175-
message[( 7 * i) + 5 + 1] = (uint8_t)0;
176-
message[( 7 * i) + 6 + 1] = (uint8_t)0;
185+
message[(7 * i) + 0 + 1] = (uint8_t)panel_ids[i]; /* panelId */
186+
message[(7 * i) + 1 + 1] = (uint8_t)1; /* nFrames */
187+
message[(7 * i) + 2 + 1] = (uint8_t)RGBGetRValue(colors[i]); /* R */
188+
message[(7 * i) + 3 + 1] = (uint8_t)RGBGetGValue(colors[i]); /* G */
189+
message[(7 * i) + 4 + 1] = (uint8_t)RGBGetBValue(colors[i]); /* B */
190+
message[(7 * i) + 5 + 1] = (uint8_t)0; /* W */
191+
message[(7 * i) + 6 + 1] = (uint8_t)0; /* transitionTime */
177192
}
178193

179-
external_control_socket.udp_write(reinterpret_cast<char*>(message), size*7+6+1);
194+
external_control_socket.udp_write((char*)message, (size * 7) + 1);
180195
}
181-
else if(model == NANOLEAF_CANVAS_MODEL)
196+
else if((model == NANOLEAF_CANVAS_MODEL)
197+
|| (model == NANOLEAF_SHAPES_MODEL))
182198
{
183199
/*---------------------------------------------------------*\
184-
| Insert V2 protocol implementation here. |
200+
| Protocol V2 - https://forum.nanoleaf.me/docs |
201+
| |
202+
| Size Description |
203+
| --------------------------------------------------------- |
204+
| 2 nPanels Number of panels |
205+
| |
206+
| 2 panelId ID of panel |
207+
| 1 R Red channel |
208+
| 1 G Green channel |
209+
| 1 B Blue channel |
210+
| 1 W White channel (ignored) |
211+
| 2 transitionTime Transition time (x 100ms) |
185212
\*---------------------------------------------------------*/
213+
uint8_t size = panel_ids.size();
214+
215+
uint8_t* message = (uint8_t*)malloc((size * 8) + 2);
216+
217+
message[0] = (uint8_t)(size >> 8); /* nPanels H */
218+
message[1] = (uint8_t)(size & 0xFF); /* nPanels L */
219+
220+
for(int i = 0; i < size; i++)
221+
{
222+
message[(8 * i) + 0 + 2] = (uint8_t)(panel_ids[i] >> 8); /* panelId H */
223+
message[(8 * i) + 1 + 2] = (uint8_t)(panel_ids[i] & 0xFF); /* panelId L */
224+
message[(8 * i) + 2 + 2] = (uint8_t)RGBGetRValue(colors[i]); /* R */
225+
message[(8 * i) + 3 + 2] = (uint8_t)RGBGetGValue(colors[i]); /* G */
226+
message[(8 * i) + 4 + 2] = (uint8_t)RGBGetBValue(colors[i]); /* B */
227+
message[(8 * i) + 5 + 2] = (uint8_t)0; /* W */
228+
message[(8 * i) + 6 + 2] = (uint8_t)0; /* transitionTime H */
229+
message[(8 * i) + 7 + 2] = (uint8_t)0; /* transitionTime L */
230+
}
231+
232+
external_control_socket.udp_write((char *)message, (size * 8) + 2);
186233
}
187234
}
188235

@@ -192,21 +239,40 @@ void NanoleafController::StartExternalControl()
192239
request["write"]["command"] = "display";
193240
request["write"]["animType"] = "extControl";
194241

242+
/*-------------------------------------------------------------*\
243+
| Determine whether to use v1 or v2 extControl protocol based |
244+
| on model string |
245+
\*-------------------------------------------------------------*/
195246
if(model == NANOLEAF_LIGHT_PANELS_MODEL)
196247
{
248+
/*---------------------------------------------------------*\
249+
| Protocol v1 returns IP and port for UDP communication |
250+
\*---------------------------------------------------------*/
197251
request["write"]["extControlVersion"] = "v1";
252+
253+
json response;
254+
if((APIRequest("PUT", location, "/api/v1/"+auth_token+"/effects", &request, &response) / 100) == 2)
255+
{
256+
external_control_socket.udp_client(response["streamControlIpAddr"].get<std::string>().c_str(), std::to_string(response["streamControlPort"].get<int>()).c_str());
257+
258+
selectedEffect = NANOLEAF_DIRECT_MODE_EFFECT_NAME;
259+
}
198260
}
199-
else if(model == NANOLEAF_CANVAS_MODEL)
261+
else if((model == NANOLEAF_CANVAS_MODEL)
262+
|| (model == NANOLEAF_SHAPES_MODEL))
200263
{
264+
/*---------------------------------------------------------*\
265+
| Protocol v2 does not return anything, use device IP and |
266+
| port 60222 |
267+
\*---------------------------------------------------------*/
201268
request["write"]["extControlVersion"] = "v2";
202-
}
203269

204-
json response;
205-
if((APIRequest("PUT", location, "/api/v1/"+auth_token+"/effects", &request, &response) / 100) == 2)
206-
{
207-
external_control_socket.udp_client(response["streamControlIpAddr"].get<std::string>().c_str(), std::to_string(response["streamControlPort"].get<int>()).c_str());
270+
if((APIRequest("PUT", location, "/api/v1/"+auth_token+"/effects", &request) / 100) == 2)
271+
{
272+
external_control_socket.udp_client(address.c_str(), "60222");
208273

209-
selectedEffect = NANOLEAF_DIRECT_MODE_EFFECT_NAME;
274+
selectedEffect = NANOLEAF_DIRECT_MODE_EFFECT_NAME;
275+
}
210276
}
211277
}
212278

@@ -280,4 +346,4 @@ std::string NanoleafController::GetSelectedEffect()
280346
int NanoleafController::GetBrightness()
281347
{
282348
return brightness;
283-
};
349+
};

Controllers/NanoleafController/NanoleafController.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#define NANOLEAF_DIRECT_MODE_EFFECT_NAME "*Dynamic*"
1515
#define NANOLEAF_LIGHT_PANELS_MODEL "NL22"
1616
#define NANOLEAF_CANVAS_MODEL "NL29"
17+
#define NANOLEAF_SHAPES_MODEL "NL42"
1718

1819
class NanoleafController
1920
{

Controllers/NanoleafController/RGBController_Nanoleaf.cpp

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -25,28 +25,25 @@ RGBController_Nanoleaf::RGBController_Nanoleaf(std::string a_address, int a_port
2525
type = DEVICE_TYPE_LIGHT;
2626

2727
/*-------------------------------------------------------------*\
28-
| Direct mode currently only supported for Nanoleaf Panels. |
28+
| Direct mode uses external control protocol. |
2929
\*-------------------------------------------------------------*/
30-
if(controller.GetModel() == NANOLEAF_LIGHT_PANELS_MODEL)
31-
{
32-
mode Direct;
33-
Direct.name = "Direct";
34-
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR;
35-
Direct.color_mode = MODE_COLORS_PER_LED;
36-
modes.push_back(Direct);
30+
mode Direct;
31+
Direct.name = "Direct";
32+
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR;
33+
Direct.color_mode = MODE_COLORS_PER_LED;
34+
modes.push_back(Direct);
3735

38-
/*---------------------------------------------------------*\
39-
| Set this effect as current if the name is selected. |
40-
\*---------------------------------------------------------*/
41-
if(controller.GetSelectedEffect() == NANOLEAF_DIRECT_MODE_EFFECT_NAME)
42-
{
43-
/*-----------------------------------------------------*\
44-
| If the direct mode is active, we need to call this |
45-
| method to open the socket. |
46-
\*-----------------------------------------------------*/
47-
controller.StartExternalControl();
48-
active_mode = 0;
49-
}
36+
/*---------------------------------------------------------*\
37+
| Set this effect as current if the name is selected. |
38+
\*---------------------------------------------------------*/
39+
if(controller.GetSelectedEffect() == NANOLEAF_DIRECT_MODE_EFFECT_NAME)
40+
{
41+
/*-----------------------------------------------------*\
42+
| If the direct mode is active, we need to call this |
43+
| method to open the socket. |
44+
\*-----------------------------------------------------*/
45+
controller.StartExternalControl();
46+
active_mode = 0;
5047
}
5148

5249
/*-------------------------------------------------------------*\
@@ -89,7 +86,7 @@ void RGBController_Nanoleaf::SetupZones()
8986
for(std::vector<int>::const_iterator it = controller.GetPanelIds().begin(); it != controller.GetPanelIds().end(); ++it)
9087
{
9188
led new_led;
92-
new_led.name = std::to_string(*it);
89+
new_led.name = std::to_string(*it);
9390
leds.push_back(new_led);
9491
}
9592

@@ -107,10 +104,7 @@ void RGBController_Nanoleaf::ResizeZone(int /*zone*/, int /*new_size*/)
107104

108105
void RGBController_Nanoleaf::DeviceUpdateLEDs()
109106
{
110-
if(controller.GetModel() == NANOLEAF_LIGHT_PANELS_MODEL)
111-
{
112-
controller.UpdateLEDs(colors);
113-
}
107+
controller.UpdateLEDs(colors);
114108
}
115109

116110
void RGBController_Nanoleaf::UpdateZoneLEDs(int /*zone*/)
@@ -133,7 +127,7 @@ void RGBController_Nanoleaf::DeviceUpdateMode()
133127
/*---------------------------------------------------------*\
134128
| Mode 0 is reserved for Direct mode |
135129
\*---------------------------------------------------------*/
136-
if(active_mode == 0 && controller.GetModel() == NANOLEAF_LIGHT_PANELS_MODEL)
130+
if(active_mode == 0)
137131
{
138132
controller.StartExternalControl();
139133
}

0 commit comments

Comments
 (0)