1
// off-mode.c: "Off" mode for Anduril.
2
// Copyright (C) 2017-2023 Selene ToyKeeper
3
// SPDX-License-Identifier: GPL-3.0-or-later
9
#ifdef USE_SUNSET_TIMER
10
#include "sunset-timer.h"
13
// set level smooth maybe
14
void off_state_set_level(uint8_t level);
17
uint8_t off_state(Event event, uint16_t arg) {
19
// turn emitter off when entering state
20
if (event == EV_enter_state) {
22
off_state_set_level(0);
23
#ifdef USE_SMOOTH_STEPS
24
// don't go to sleep while animating
25
arg |= smooth_steps_in_progress;
28
#if NUM_CHANNEL_MODES > 1
29
// reset to ramp mode's channel when light turns off
30
channel_mode = cfg.channel_mode;
32
#ifdef USE_INDICATOR_LED
33
// redundant, sleep tick does the same thing
34
//indicator_led_update(cfg.indicator_led_mode & 0x03, 0);
35
#elif defined(USE_AUX_RGB_LEDS)
36
// redundant, sleep tick does the same thing
37
//rgb_led_update(cfg.rgb_led_off_mode, 0);
39
#ifdef USE_SUNSET_TIMER
40
sunset_timer = 0; // needs a reset in case previous timer was aborted
42
// sleep while off (lower power use)
43
// (unless delay requested; give the ADC some time to catch up)
44
if (! arg) { go_to_standby = 1; }
48
// go back to sleep eventually if we got bumped but didn't leave "off" state
49
else if (event == EV_tick) {
50
if (arg > HOLD_TIMEOUT
51
#ifdef USE_SMOOTH_STEPS
52
&& (! smooth_steps_in_progress)
56
#ifdef USE_INDICATOR_LED
57
// redundant, sleep tick does the same thing
58
//indicator_led_update(cfg.indicator_led_mode & 0x03, arg);
59
#elif defined(USE_AUX_RGB_LEDS)
60
// redundant, sleep tick does the same thing
61
//rgb_led_update(cfg.rgb_led_off_mode, arg);
67
#if defined(TICK_DURING_STANDBY)
68
// blink the indicator LED, maybe
69
else if (event == EV_sleep_tick) {
70
if (ticks_since_on < 255) ticks_since_on ++;
71
#ifdef USE_MANUAL_MEMORY_TIMER
72
// reset to manual memory level when timer expires
73
if (cfg.manual_memory &&
74
(arg >= (cfg.manual_memory_timer * SLEEP_TICKS_PER_MINUTE))) {
75
manual_memory_restore();
78
#ifdef USE_INDICATOR_LED
79
indicator_led_update(cfg.indicator_led_mode & 0x03, arg);
80
#elif defined(USE_AUX_RGB_LEDS)
81
rgb_led_update(cfg.rgb_led_off_mode, arg);
85
// lock the light after being off for N minutes
86
uint16_t ticks = cfg.autolock_time * SLEEP_TICKS_PER_MINUTE;
87
if ((cfg.autolock_time > 0) && (arg > ticks)) {
88
set_state(lockout_state, 0);
90
#endif // ifdef USE_AUTOLOCK
95
#if (B_TIMING_ON == B_PRESS_T)
96
// hold (initially): go to lowest level (floor), but allow abort for regular click
97
else if (event == EV_click1_press) {
98
off_state_set_level(nearest_level(1));
101
#endif // B_TIMING_ON == B_PRESS_T
103
// hold: go to lowest level
104
else if (event == EV_click1_hold) {
105
#if (B_TIMING_ON == B_PRESS_T)
106
#ifdef MOON_TIMING_HINT
108
// let the user know they can let go now to stay at moon
112
#else // B_RELEASE_T or B_TIMEOUT_T
113
off_state_set_level(nearest_level(1));
115
#ifdef USE_RAMP_AFTER_MOON_CONFIG
116
if (cfg.dont_ramp_after_moon) {
117
return EVENT_HANDLED;
120
// don't start ramping immediately;
121
// give the user time to release at moon level
122
//if (arg >= HOLD_TIMEOUT) { // smaller
123
if (arg >= (!cfg.ramp_style) * HOLD_TIMEOUT) { // more consistent
124
set_state(steady_state, 1);
126
return EVENT_HANDLED;
129
// hold, release quickly: go to lowest level (floor)
130
else if (event == EV_click1_hold_release) {
131
set_state(steady_state, 1);
132
return EVENT_HANDLED;
135
#if (B_TIMING_ON != B_TIMEOUT_T)
136
// 1 click (before timeout): go to memorized level, but allow abort for double click
137
else if (event == EV_click1_release) {
138
#if defined(USE_MANUAL_MEMORY) && !defined(USE_MANUAL_MEMORY_TIMER)
139
// this clause probably isn't used by any configs any more
140
// but is included just in case someone configures it this way
141
if (cfg.manual_memory) {
142
manual_memory_restore();
145
off_state_set_level(nearest_level(memorized_level));
146
return EVENT_HANDLED;
148
#endif // if (B_TIMING_ON != B_TIMEOUT_T)
150
// 1 click: regular mode
151
else if (event == EV_1click) {
152
#if (B_TIMING_ON != B_TIMEOUT_T)
153
set_state(steady_state, memorized_level);
155
// FIXME: B_TIMEOUT_T breaks manual_memory and manual_memory_timer
156
// (need to duplicate manual mem logic here, probably)
157
set_state(steady_state, memorized_level);
159
return EVENT_HANDLED;
162
// click, hold: momentary at ceiling or turbo
163
else if (event == EV_click2_hold) {
164
ticks_since_on = 0; // momentary turbo is definitely "on"
165
uint8_t turbo_level; // how bright is "turbo"?
167
#if defined(USE_2C_STYLE_CONFIG) // user can choose 2C behavior
168
uint8_t style_2c = cfg.ramp_2c_style;
170
// simple UI has its own turbo config
171
if (cfg.simple_ui_active) style_2c = cfg.ramp_2c_style_simple;
175
if (0 == style_2c) turbo_level = nearest_level(MAX_LEVEL);
176
else turbo_level = MAX_LEVEL;
178
// simple UI: ceiling
179
// full UI: full power
181
if (cfg.simple_ui_active) turbo_level = nearest_level(MAX_LEVEL);
184
turbo_level = MAX_LEVEL;
187
off_state_set_level(turbo_level);
188
return EVENT_HANDLED;
190
else if (event == EV_click2_hold_release) {
191
off_state_set_level(0);
192
return EVENT_HANDLED;
195
// 2 clicks: highest mode (ceiling)
196
else if (event == EV_2clicks) {
197
set_state(steady_state, MAX_LEVEL);
198
return EVENT_HANDLED;
201
// 3 clicks (initial press): off, to prep for later events
202
else if (event == EV_click3_press) {
203
#ifdef USE_SMOOTH_STEPS
204
// immediately cancel any animations in progress
205
smooth_steps_in_progress = 0;
207
off_state_set_level(0);
208
return EVENT_HANDLED;
212
// 3 clicks: battcheck mode / blinky mode group 1
213
else if (event == EV_3clicks) {
214
set_state(battcheck_state, 0);
215
return EVENT_HANDLED;
219
#ifdef USE_LOCKOUT_MODE
220
// 4 clicks: soft lockout
221
else if (event == EV_4clicks) {
223
set_state(lockout_state, 0);
224
return EVENT_HANDLED;
228
#if defined(USE_FACTORY_RESET) && defined(USE_SOFT_FACTORY_RESET)
229
// 13 clicks and hold the last click: invoke factory reset (reboot)
230
else if (event == EV_click13_hold) {
232
return EVENT_HANDLED;
236
#ifdef USE_VERSION_CHECK
237
// 15+ clicks: show the version number
238
else if (event == EV_15clicks) {
239
set_state(version_check_state, 0);
240
return EVENT_HANDLED;
245
// 10 clicks, but hold last click: turn simple UI off (or configure it)
246
else if ((event == EV_click10_hold) && (!arg)) {
247
if (cfg.simple_ui_active) { // turn off simple UI
249
cfg.simple_ui_active = 0;
252
else { // configure simple UI ramp
253
push_state(simple_ui_config_state, 0);
255
return EVENT_HANDLED;
258
////////// Every action below here is blocked in the (non-Extended) Simple UI //////////
260
#ifndef USE_EXTENDED_SIMPLE_UI
261
if (cfg.simple_ui_active) {
262
return EVENT_NOT_HANDLED;
264
#endif // ifndef USE_EXTENDED_SIMPLE_UI
265
#endif // ifdef USE_SIMPLE_UI
267
// click, click, long-click: strobe mode
268
#ifdef USE_STROBE_STATE
269
else if (event == EV_click3_hold) {
270
set_state(strobe_state, 0);
271
return EVENT_HANDLED;
273
#elif defined(USE_BORING_STROBE_STATE)
274
else if (event == EV_click3_hold) {
275
set_state(boring_strobe_state, 0);
276
return EVENT_HANDLED;
280
#ifdef USE_INDICATOR_LED
281
// 7 clicks: change indicator LED mode
282
else if (event == EV_7clicks) {
283
uint8_t mode = (cfg.indicator_led_mode & 3) + 1;
284
#ifdef TICK_DURING_STANDBY
289
#ifdef INDICATOR_LED_SKIP_LOW
290
if (mode == 1) { mode ++; }
292
cfg.indicator_led_mode = (cfg.indicator_led_mode & 0b11111100) | mode;
293
// redundant, sleep tick does the same thing
294
//indicator_led_update(cfg.indicator_led_mode & 0x03, arg);
296
return EVENT_HANDLED;
298
#elif defined(USE_AUX_RGB_LEDS)
299
// 7 clicks: change RGB aux LED pattern
300
else if (event == EV_7clicks) {
301
uint8_t mode = (cfg.rgb_led_off_mode >> 4) + 1;
302
mode = mode % RGB_LED_NUM_PATTERNS;
303
cfg.rgb_led_off_mode = (mode << 4) | (cfg.rgb_led_off_mode & 0x0f);
304
rgb_led_update(cfg.rgb_led_off_mode, 0);
307
return EVENT_HANDLED;
309
// 7 clicks (hold last): change RGB aux LED color
310
else if (event == EV_click7_hold) {
311
setting_rgb_mode_now = 1;
312
if (0 == (arg & 0x3f)) {
313
uint8_t mode = (cfg.rgb_led_off_mode & 0x0f) + 1;
314
mode = mode % RGB_LED_NUM_COLORS;
315
cfg.rgb_led_off_mode = mode | (cfg.rgb_led_off_mode & 0xf0);
318
rgb_led_update(cfg.rgb_led_off_mode, arg);
319
return EVENT_HANDLED;
321
else if (event == EV_click7_hold_release) {
322
setting_rgb_mode_now = 0;
324
return EVENT_HANDLED;
326
#endif // end 7 clicks
328
////////// Every action below here is blocked in the Extended Simple UI //////////
331
#ifdef USE_EXTENDED_SIMPLE_UI
332
if (cfg.simple_ui_active) {
333
return EVENT_NOT_HANDLED;
335
#endif // ifdef USE_EXTENDED_SIMPLE_UI
337
// 10 clicks: enable simple UI
338
else if (event == EV_10clicks) {
340
cfg.simple_ui_active = 1;
342
return EVENT_HANDLED;
344
#endif // ifdef USE_SIMPLE_UI
346
#ifdef USE_MOMENTARY_MODE
347
// 5 clicks: momentary mode
348
else if (event == EV_5clicks) {
350
set_state(momentary_state, 0);
351
return EVENT_HANDLED;
355
#ifdef USE_TACTICAL_MODE
356
// 6 clicks: tactical mode
357
else if (event == EV_6clicks) {
359
set_state(tactical_state, 0);
360
return EVENT_HANDLED;
364
#ifdef USE_GLOBALS_CONFIG
365
// 9 clicks, but hold last click: configure misc global settings
366
else if ((event == EV_click9_hold) && (!arg)) {
367
push_state(globals_config_state, 0);
368
return EVENT_HANDLED;
372
return EVENT_NOT_HANDLED;
376
void off_state_set_level(uint8_t level) {
377
// this pattern gets used a few times, so reduce duplication
378
#ifdef USE_SMOOTH_STEPS
379
if (cfg.smooth_steps_style) set_level_smooth(level, 8);