Lamp-Da 0.1
A compact lantern project
Loading...
Searching...
No Matches
default_behavior.hpp
Go to the documentation of this file.
1
5#ifndef DEFAULT_BEHAVIOR_MANAGER_HPP
6#define DEFAULT_BEHAVIOR_MANAGER_HPP
7
8//
9// note: this code is included as-is by:
10// - src/modes/user/indexable_behavior.hpp
11// - src/modes/user/simple_behavior.hpp
12//
13
14#include "src/system/component/time_handling.h"
16
17#include <cstdint>
18namespace lampda::user {
19
20//
21// These are defined in src/modes/user/{flavor}_behavior.hpp
22//
23
24bool button_clicked_default(const uint8_t);
25
26bool button_hold_default(const uint8_t, const bool, const uint32_t);
27
28//
29// These callbacks are the same for all lamp flavors*
30//
31
32// *meaning: LampTy is the one doing the "polyfill" between flavors
33
35{
36 auto manager = get_context();
37
38 // initialize the lamp object
39 manager.lamp.startup();
40
41 // callbacks
42 manager.power_on_sequence();
43}
44
46{
47 // callbacks
48 auto manager = get_context();
49 manager.power_off_sequence();
50
51 // clear lamp on power-off
52 manager.lamp.clear();
53 manager.lamp.show_now();
54
55 // (no-op) internal symbol used during build
56 ensure_build_canary();
57}
58
59void brightness_update(const brightness_t brightness)
60{
61 auto manager = get_context();
62
63 // dont handle invalid commands
64 if (brightness <= ::lampda::brightness::absoluteMaximumBrightness)
65 {
66 // force update of the internal references
67 manager.lamp.align_internal_to_system_brightness();
68
69 // set brightness for underlying object (w/o re-entry in update_brightness)
70 manager.lamp.setBrightness(brightness, true, true);
71
72 // callbacks
73 manager.brightness_update(brightness);
74 }
75 else
76 {
77 // this call could be just a max brightness update
78 manager.lamp.enforce_internal_brightness_limits();
79 }
80}
81
82void sunset_timer_update(const float progress)
83{
84 auto manager = get_context();
85
86 // callbacks
87 manager.sunset_update(progress);
88}
89
91{
92 auto manager = get_context();
93 manager.write_parameters();
94}
95
97{
98 auto manager = get_context();
99 manager.read_parameters();
100}
101
102bool button_start_click_default(const uint8_t clicks) { return false; }
103
104bool button_start_hold_default(const uint8_t clicks, const bool isEndOfHoldEvent, const uint32_t holdDuration)
105{
106 switch (clicks)
107 {
108 case 1:
109 {
110 // 1+hold (2s): activate bluetooth advertising
111 // activate bluetooth !
112 auto manager = get_context();
113
114 // ramp can activate bluetooth :
115 // - if it is not enabled yet
116 // - if it is enabled, but the bounded peer already exists (force open connection)
117
118 // Start pairing mode
119 const bool hasPairedDeviceAndNoPairing = hal::bluetooth::is_bounded() and
122 if (hasPairedDeviceAndNoPairing)
123 {
124 if (manager.overlay_animate_ramp(
125 holdDuration, 1000, modes::colors::PaletteGradient<modes::colors::Blue, modes::colors::Blue>))
126 {
127 // Start BLE but with paired device authorization only
129
130 bsp::lampda_print("Enable BLE for paired device");
131 }
132 }
133 // else: not bounded, or already started bounded connection
134 else
135 {
136 // Start pairing mode
137 const bool isNotCurrentlyPairing =
140 if (isNotCurrentlyPairing and
141 manager.overlay_animate_ramp(
142 holdDuration, 3000, modes::colors::PaletteGradient<modes::colors::Blue, modes::colors::Blue>))
143 {
144 // Start pairing mode
146
147 bsp::lampda_print("Enable BLE pairing mode");
148 }
149 }
150 return true;
151 }
152 case 5:
153 {
154 if (not isEndOfHoldEvent and holdDuration > 0)
155 {
156 // sunset timer !
157 auto manager = get_context();
158 if (manager.overlay_animate_ramp(
159 holdDuration, 1000, modes::colors::PaletteGradient<modes::colors::White, modes::colors::Red>))
160 {
161 // update the sunset timing
162 manager.state.isSunsetTimingPending = 2;
163 }
164
165 return true;
166 }
167 break;
168 }
169 }
170
171 return false;
172}
173
174bool button_clicked_usermode(const uint8_t clicks)
175{
176 auto manager = get_context();
177 return manager.custom_click(clicks);
178}
179
180bool button_hold_usermode(const uint8_t clicks, const bool isEndOfHoldEvent, const uint32_t holdDuration)
181{
182 auto manager = get_context();
183 return manager.custom_hold(clicks, isEndOfHoldEvent, holdDuration);
184}
185
186void loop()
187{
188 auto manager = get_context();
189 manager.loop();
190
191 // signal display update every loop
192 manager.lamp.signal_display();
193}
194
196{
197#ifdef LMBD_LAMP_TYPE__INDEXABLE
199 return true;
200#else
201 auto manager = get_context();
202 return manager.should_spawn_thread();
203#endif
204}
205
207{
208 auto manager = get_context();
209 manager.lamp.show();
210
211 if (manager.should_spawn_thread())
212 manager.user_thread();
213}
214
216namespace default_behaviors {
217
219bool button_clicked(const uint8_t clicks)
220{
221 auto manager = get_context();
222
223 switch (clicks)
224 {
225 case 6: // 6 clicks: jump to first mode of first category
226 {
227 // exit fav group if needed
228 manager.exit_favorite_group(manager.state.beforeFavoriteActiveIndex);
229
230 // return to first state
231 manager.set_active_group(0);
232 manager.set_active_mode(0);
233 // ramps will be updated on their own
234
235 // Blip for state change
236 manager.blip(250);
237
238 return true;
239 }
240 }
241
242 // nothing
243 return false;
244}
245
247bool button_hold(const uint8_t clicks, const bool isEndOfHoldEvent, const uint32_t holdDuration)
248{
249 auto manager = get_context();
250 auto& rampHandler = manager.state.rampHandler;
251
252 switch (clicks)
253 {
254 case 3: // 3 click+hold: configure custom ramp
255 // no ramps in favorite group
256 if (not manager.state.isInFavoriteMockGroup)
257 {
258 rampHandler.update_ramp(manager.get_active_custom_ramp(), holdDuration, [&](uint8_t rampValue) {
259 manager.custom_ramp_update(rampValue);
260 manager.set_active_custom_ramp(rampValue);
261 });
262 return true;
263 }
264 break;
265
266 // 5 click+hold: Add 5 minutes to sunset timer
267 case 5:
268 {
269 if (not isEndOfHoldEvent and holdDuration > 0 and logic::sunset::is_enabled())
270 {
271 // sunset timer !
272 auto manager = get_context();
273 if (manager.overlay_animate_ramp(
274 holdDuration, 1000, modes::colors::PaletteGradient<modes::colors::White, modes::colors::Red>))
275 {
276 // update the sunset timing
277 manager.state.isSunsetTimingPending = 2;
278 }
279 }
280 break;
281 }
282
283 case 13: // 13 clicks + hold: reset the stored parameters
284 {
285 if (not isEndOfHoldEvent and holdDuration > 0)
286 {
287 auto manager = get_context();
288 if (manager.overlay_animate_ramp(
289 holdDuration, 5000, modes::colors::PaletteGradient<modes::colors::Red, modes::colors::Red>))
290 {
291 bsp::lampda_print("clearing user parameters");
292
293 // reset the file system and memory
295 }
296 }
297 break;
298 }
299
300 case 20: // 20 clicks + hold: reset the whole system and stored parameters
301 {
302 if (not isEndOfHoldEvent and holdDuration > 0)
303 {
304 auto manager = get_context();
305 if (manager.overlay_animate_ramp(
306 holdDuration, 5000, modes::colors::PaletteGradient<modes::colors::Red, modes::colors::Red>))
307 {
308 // reset the file system and memory
309 bsp::lampda_print("clearing the whole file format");
311
312 // shutdown the lamp
313 logic::behavior::internal::handle_shutdown_state();
314 }
315 }
316 break;
317 }
318 }
319
320 return false;
321}
322
323namespace __private {
324
326void handle_brightness_control(const brightness_t requiredbrightness)
327{
328 // ignore if not on
330 return;
331
333 // update brightness
334 const brightness_t desiredBrightness =
335 min<brightness_t>(::lampda::brightness::absoluteMaximumBrightness, requiredbrightness);
336 // Update the system brightness for real, not the temporary. We want the changes to be saved
337 logic::brightness::update_brightness(desiredBrightness);
338 // update saved brightness
341
342 // and change the sunset timer if needed
344}
345
347void handle_on_off_command(const bool shouldBeOn)
348{
349 // turn on
350 if (shouldBeOn)
351 {
354 }
355 // turn off
356 else
357 {
360 }
361}
362
367void handle_ramp_command(const uint8_t ramp)
368{
369 // ignore if not on
371 return;
372
373 auto manager = get_context();
374
375 // update ramp value in modes
376 manager.custom_ramp_update(ramp, 1000);
377 // override user choice
378 manager.set_active_custom_ramp(ramp);
379}
380
385{
386 if (not time.is_valid())
387 {
388 bsp::lampda_print("Refusing set_time command %d %dh %dm %ds. Invalid values.",
389 time.dayOfTheWeek,
390 time.hour,
391 time.minutes,
392 time.seconds);
393 return;
394 }
395 const bool isValid = component::time::set_real_time(time);
397 "set time %d %dh %dm %ds (validity: %d)", time.dayOfTheWeek, time.hour, time.minutes, time.seconds, isValid);
398}
399
404{
405 if (not time.is_valid())
406 {
407 bsp::lampda_print("Refusing sunset command %d %dh %dm %ds. Invalid values.",
408 time.dayOfTheWeek,
409 time.hour,
410 time.minutes,
411 time.seconds);
412 return;
413 }
414 const auto& realTime = component::time::get_real_time();
415 if (not realTime.is_valid())
416 {
417 bsp::lampda_print("Refusing sunset command %d %dh %dm %ds : Time is not synchronized yet.",
418 time.dayOfTheWeek,
419 time.hour,
420 time.minutes,
421 time.seconds);
422 return;
423 }
424
425 const uint32_t internalLampActionTime = component::time::get_platform_time_from_target_time(time);
426 if (internalLampActionTime <= 0)
427 {
428 bsp::lampda_print("Refusing sunset command %d %dh %dm %ds: Target time is incoherent",
429 time.dayOfTheWeek,
430 time.hour,
431 time.minutes,
432 time.seconds);
433 return;
434 }
435
436 // lamp will turn on if not already turned on
439
440 logic::sunset::set_deadline(internalLampActionTime);
441}
442
443void handle_mode_control(const uint8_t groupIndex, const uint8_t modeIndex)
444{
445 // lamp will turn on if not already turned on
448
449 auto manager = get_context();
450
451 if (groupIndex >= manager.get_groups_count())
452 {
453 bsp::lampda_print("Group id %d is greater than max group id %d", groupIndex, manager.get_groups_count());
454 return;
455 }
456 manager.set_active_group(groupIndex);
457
458 if (modeIndex >= manager.get_modes_count())
459 {
460 bsp::lampda_print("Mode id %d is greater than max mode id %d", modeIndex, manager.get_modes_count());
461 return;
462 }
463 manager.set_active_mode(modeIndex);
464 manager.blip(250);
465}
466
467} // namespace __private
468
469bool handle_user_command(const common::UserCommand& command)
470{
471 switch (command.get_type())
472 {
474 {
475 uint8_t ramp;
476 if (command.parse_ramp(ramp))
477 __private::handle_ramp_command(ramp);
478 else
479 bsp::lampda_print("Failed to parse set_user_ramp command");
480 return true;
481 }
483 {
484 brightness_t brgt;
485 if (command.parse_brightness(brgt))
486 __private::handle_brightness_control(brgt);
487 else
488 bsp::lampda_print("Failed to parse brightness command");
489 return true;
490 }
492 {
493 uint8_t groupId;
494 uint8_t modeId;
495 if (command.parse_set_mode(groupId, modeId))
496 __private::handle_mode_control(groupId, modeId);
497 else
498 bsp::lampda_print("Failed to parse set_mode command");
499 return true;
500 }
502 {
503 bool shouldTurnOn;
504 if (command.parse_turn_onoff(shouldTurnOn))
505 __private::handle_on_off_command(shouldTurnOn);
506 else
507 bsp::lampda_print("Failed to parse onoff command");
508 return true;
509 }
511 {
512 component::time::RealTime time;
513 if (command.parse_set_real_time_command(time))
514 __private::handle_set_time_command(time);
515 else
516 bsp::lampda_print("Failed to parse set_real_time command");
517 return true;
518 }
520 {
521 component::time::RealTime time;
522 if (command.parse_set_sunset_to_time_command(time))
523 __private::handle_sunset_to_time_command(time);
524 else
525 bsp::lampda_print("Failed to parse set_sunset_time command");
526 return true;
527 }
528
529 default:
530 break;
531 }
532 return false;
533}
534
535} // namespace default_behaviors
536
537} // namespace lampda::user
538
539#endif
Interface for the platform specific bluetooth.
@ SetSunsetToTime
set the sunset to a target real time
@ SetRealTime
set the system real time
@ SetUserRamp
set the user ramp value
void clear(const Type type)
Clear an alert.
Definition: alerts.cpp:902
void handle_sunset_to_time_command(const component::time::RealTime &time)
Handle the timing command.
Definition: default_behavior.hpp:403
void handle_on_off_command(const bool shouldBeOn)
handle the On or Off command
Definition: default_behavior.hpp:347
void handle_brightness_control(const brightness_t requiredbrightness)
handle the brightness command
Definition: default_behavior.hpp:326
void handle_set_time_command(const component::time::RealTime &time)
Handle the set time command.
Definition: default_behavior.hpp:384
void handle_ramp_command(const uint8_t ramp)
Handle the ramp command.
Definition: default_behavior.hpp:367
bool format()
Delete the user file.
Definition: filesystem.cpp:338
void clear_internal_fs()
hard clean of the whole filesystem, you will loose all stored data.
Definition: filesystem.cpp:48
void lampda_print(const char *format,...)
C linkage to print functions.
Definition: text_out.cpp:227
uint32_t get_platform_time_from_target_time(const RealTime &time)
Given a real time, return the expected platform time in seconds at wich it will be reached.
Definition: time_handling.cpp:92
RealTime get_real_time()
Return a real time value, only valid if it was set first.
Definition: time_handling.cpp:90
bool set_real_time(const RealTime &realTime)
Set the real time in the lamp, only valid until the next shutdown.
Definition: time_handling.cpp:75
bool is_open_to_all()
Return true if the device is advertising, and all can connect.
bool is_advertising()
Return true is the bluetooth is visible by other devices.
void start_advertising(bool allowUnknownConnections)
start the advertising sequence (with a timeout)
bool is_connected()
Return true if a bluetooth user is connected.
bool is_bounded()
Return true if the bluetooth connection is bounded to a pair.
void set_power_on()
set system state to "output on"
Definition: behavior.cpp:125
void set_power_off()
set system state to "output off". Can be ignored
Definition: behavior.cpp:126
bool is_in_output_state()
return true if the system is in output mode
Definition: behavior.cpp:139
void update_brightness(const brightness_t newBrightness, const bool shouldCallUserBrightnessCallback)
update the internal brightness values
Definition: brightness_handle.cpp:66
void update_saved_brightness()
Update the saved brightness value with the current brightness.
Definition: brightness_handle.cpp:58
void lock_brightness_update(bool shouldLock)
Lock the hability of the sunset timer to control the brightness.
Definition: sunset_timer.cpp:247
void set_deadline(const uint32_t timeshutdown_s)
Set the sunset timer to a known clock deadline, replacing existing timers. Start the timer,...
Definition: sunset_timer.cpp:130
void bump_timer()
signal to the timer that some time may be added. Only add time if the timer is in the fadeout phase
Definition: sunset_timer.cpp:206
bool is_enabled()
Indicate if the sunset is set.
Definition: sunset_timer.cpp:24
bool button_hold(const uint8_t clicks, const bool isEndOfHoldEvent, const uint32_t holdDuration)
must be called by the lampda::user::button_hold_default
Definition: default_behavior.hpp:247
bool button_clicked(const uint8_t clicks)
must be called by the lampda::user::button_clicked_default
Definition: default_behavior.hpp:219
Contains code handling custom user mode functions for indexable strips.
Definition: default_behavior.hpp:18
bool button_clicked_default(const uint8_t)
Called to handle button click events for default user mode behaviors.
Definition: indexable_behavior.hpp:18
bool button_hold_default(const uint8_t, const bool, const uint32_t)
Called to handle button click+hold events for user mode behaviors.
Definition: indexable_behavior.hpp:157
void brightness_update(const brightness_t brightness)
Called when the system changes the LED strip brightness.
Definition: default_behavior.hpp:59
void user_thread()
Called at each tick of the secondary thread.
Definition: default_behavior.hpp:206
bool button_hold_usermode(const uint8_t clicks, const bool isEndOfHoldEvent, const uint32_t holdDuration)
Called to handle button click+hold events if "usermode UI" is on.
Definition: default_behavior.hpp:180
bool button_start_hold_default(const uint8_t clicks, const bool isEndOfHoldEvent, const uint32_t holdDuration)
Called to handle button hold on lamp start.
Definition: default_behavior.hpp:104
void read_parameters()
Called when system wants to read parameters from filesystem.
Definition: default_behavior.hpp:96
void power_on_sequence()
Called when the system powers on (must be non blocking function!)
Definition: default_behavior.hpp:34
void sunset_timer_update(const float progress)
Called when the sunset timer progresses [0; 1].
Definition: default_behavior.hpp:82
bool button_start_click_default(const uint8_t clicks)
Called to handle button click on lamp start.
Definition: default_behavior.hpp:102
bool should_spawn_thread()
Determine if a second user_thread() loop thread should be spawned.
Definition: default_behavior.hpp:195
void write_parameters()
Called when system wants to write parameters to filesystem.
Definition: default_behavior.hpp:90
void power_off_sequence()
Called when the system powers off (must be non blocking function!)
Definition: default_behavior.hpp:45
void loop()
Called at each tick of the main loop.
Definition: default_behavior.hpp:186
bool button_clicked_usermode(const uint8_t clicks)
Called to handle button click events if "usermode UI" is active.
Definition: default_behavior.hpp:174
uint16_t brightness_t
Define the type of the brightness parameters.
Definition: constants.h:147
Store a real time reference.
Definition: time_handling.h:18
uint8_t minutes
minutes, in the [0; 59]
Definition: time_handling.h:30
uint8_t dayOfTheWeek
[0; 6] the index of the day of the week. Monday is 0, sunday is 6
Definition: time_handling.h:28
uint8_t hour
hour of the day, in the [0; 23]
Definition: time_handling.h:29
uint8_t seconds
seconds, in the [0; 59]
Definition: time_handling.h:31