notification_app.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. #include <furi_hal_light.h>
  2. #include <furi.h>
  3. #include <furi_hal.h>
  4. #include <storage/storage.h>
  5. #include <input/input.h>
  6. #include "notification.h"
  7. #include "notification_messages.h"
  8. #include "notification_app.h"
  9. #define TAG "NotificationSrv"
  10. static const uint8_t minimal_delay = 100;
  11. static const uint8_t led_off_values[NOTIFICATION_LED_COUNT] = {0x00, 0x00, 0x00};
  12. static const uint8_t reset_red_mask = 1 << 0;
  13. static const uint8_t reset_green_mask = 1 << 1;
  14. static const uint8_t reset_blue_mask = 1 << 2;
  15. static const uint8_t reset_vibro_mask = 1 << 3;
  16. static const uint8_t reset_sound_mask = 1 << 4;
  17. static const uint8_t reset_display_mask = 1 << 5;
  18. static const uint8_t reset_blink_mask = 1 << 6;
  19. void notification_vibro_on(bool force);
  20. void notification_vibro_off();
  21. void notification_sound_on(float freq, float volume, bool force);
  22. void notification_sound_off();
  23. uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8_t value);
  24. uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value);
  25. uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app);
  26. void notification_message_save_settings(NotificationApp* app) {
  27. NotificationAppMessage m = {
  28. .type = SaveSettingsMessage, .back_event = furi_event_flag_alloc()};
  29. furi_check(furi_message_queue_put(app->queue, &m, FuriWaitForever) == FuriStatusOk);
  30. furi_event_flag_wait(
  31. m.back_event, NOTIFICATION_EVENT_COMPLETE, FuriFlagWaitAny, FuriWaitForever);
  32. furi_event_flag_free(m.back_event);
  33. };
  34. // internal layer
  35. void notification_apply_internal_led_layer(NotificationLedLayer* layer, uint8_t layer_value) {
  36. furi_assert(layer);
  37. furi_assert(layer->index < LayerMAX);
  38. // set value
  39. layer->value[LayerInternal] = layer_value;
  40. // apply if current layer is internal
  41. if(layer->index == LayerInternal) {
  42. furi_hal_light_set(layer->light, layer->value[LayerInternal]);
  43. }
  44. }
  45. bool notification_is_any_led_layer_internal_and_not_empty(NotificationApp* app) {
  46. bool result = false;
  47. if((app->led[0].index == LayerInternal) || (app->led[1].index == LayerInternal) ||
  48. (app->led[2].index == LayerInternal)) {
  49. if((app->led[0].value[LayerInternal] != 0x00) ||
  50. (app->led[1].value[LayerInternal] != 0x00) ||
  51. (app->led[2].value[LayerInternal] != 0x00)) {
  52. result = true;
  53. }
  54. }
  55. return result;
  56. }
  57. // notification layer
  58. void notification_apply_notification_led_layer(
  59. NotificationLedLayer* layer,
  60. const uint8_t layer_value) {
  61. furi_assert(layer);
  62. furi_assert(layer->index < LayerMAX);
  63. // set value
  64. layer->index = LayerNotification;
  65. // set layer
  66. layer->value[LayerNotification] = layer_value;
  67. // apply
  68. furi_hal_light_set(layer->light, layer->value[LayerNotification]);
  69. }
  70. void notification_reset_notification_led_layer(NotificationLedLayer* layer) {
  71. furi_assert(layer);
  72. furi_assert(layer->index < LayerMAX);
  73. // set value
  74. layer->value[LayerNotification] = 0;
  75. // set layer
  76. layer->index = LayerInternal;
  77. // apply
  78. furi_hal_light_set(layer->light, layer->value[LayerInternal]);
  79. }
  80. void notification_reset_notification_layer(NotificationApp* app, uint8_t reset_mask) {
  81. if(reset_mask & reset_blink_mask) {
  82. furi_hal_light_blink_stop();
  83. }
  84. if(reset_mask & reset_red_mask) {
  85. notification_reset_notification_led_layer(&app->led[0]);
  86. }
  87. if(reset_mask & reset_green_mask) {
  88. notification_reset_notification_led_layer(&app->led[1]);
  89. }
  90. if(reset_mask & reset_blue_mask) {
  91. notification_reset_notification_led_layer(&app->led[2]);
  92. }
  93. if(reset_mask & reset_vibro_mask) {
  94. notification_vibro_off();
  95. }
  96. if(reset_mask & reset_sound_mask) {
  97. notification_sound_off();
  98. }
  99. if(reset_mask & reset_display_mask) {
  100. furi_timer_start(app->display_timer, notification_settings_display_off_delay_ticks(app));
  101. }
  102. }
  103. static void notification_apply_notification_leds(NotificationApp* app, const uint8_t* values) {
  104. for(uint8_t i = 0; i < NOTIFICATION_LED_COUNT; i++) {
  105. notification_apply_notification_led_layer(
  106. &app->led[i], notification_settings_get_rgb_led_brightness(app, values[i]));
  107. }
  108. }
  109. // settings
  110. uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8_t value) {
  111. return (value * app->settings.display_brightness);
  112. }
  113. uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value) {
  114. return (value * app->settings.led_brightness);
  115. }
  116. uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app) {
  117. return (
  118. (float)(app->settings.display_off_delay_ms) /
  119. (1000.0f / furi_kernel_get_tick_frequency()));
  120. }
  121. // generics
  122. void notification_vibro_on(bool force) {
  123. if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode) || force) {
  124. furi_hal_vibro_on(true);
  125. }
  126. }
  127. void notification_vibro_off() {
  128. furi_hal_vibro_on(false);
  129. }
  130. void notification_sound_on(float freq, float volume, bool force) {
  131. if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode) || force) {
  132. if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) {
  133. furi_hal_speaker_start(freq, volume);
  134. }
  135. }
  136. }
  137. void notification_sound_off() {
  138. if(furi_hal_speaker_is_mine()) {
  139. furi_hal_speaker_stop();
  140. furi_hal_speaker_release();
  141. }
  142. }
  143. // display timer
  144. static void notification_display_timer(void* ctx) {
  145. furi_assert(ctx);
  146. NotificationApp* app = ctx;
  147. notification_message(app, &sequence_display_backlight_off);
  148. }
  149. // message processing
  150. void notification_process_notification_message(
  151. NotificationApp* app,
  152. NotificationAppMessage* message) {
  153. uint32_t notification_message_index = 0;
  154. bool force_volume = false;
  155. bool force_vibro = false;
  156. const NotificationMessage* notification_message;
  157. notification_message = (*message->sequence)[notification_message_index];
  158. bool led_active = false;
  159. uint8_t led_values[NOTIFICATION_LED_COUNT] = {0x00, 0x00, 0x00};
  160. bool reset_notifications = true;
  161. float speaker_volume_setting = app->settings.speaker_volume;
  162. bool vibro_setting = app->settings.vibro_on;
  163. float display_brightness_setting = app->settings.display_brightness;
  164. uint8_t reset_mask = 0;
  165. while(notification_message != NULL) {
  166. switch(notification_message->type) {
  167. case NotificationMessageTypeLedDisplayBacklight:
  168. // if on - switch on and start timer
  169. // if off - switch off and stop timer
  170. // on timer - switch off
  171. if(notification_message->data.led.value > 0x00) {
  172. notification_apply_notification_led_layer(
  173. &app->display,
  174. notification_message->data.led.value * display_brightness_setting);
  175. } else {
  176. notification_reset_notification_led_layer(&app->display);
  177. if(furi_timer_is_running(app->display_timer)) {
  178. furi_timer_stop(app->display_timer);
  179. }
  180. }
  181. reset_mask |= reset_display_mask;
  182. break;
  183. case NotificationMessageTypeLedDisplayBacklightEnforceOn:
  184. furi_assert(app->display_led_lock < UINT8_MAX);
  185. app->display_led_lock++;
  186. if(app->display_led_lock == 1) {
  187. notification_apply_internal_led_layer(
  188. &app->display,
  189. notification_message->data.led.value * display_brightness_setting);
  190. }
  191. break;
  192. case NotificationMessageTypeLedDisplayBacklightEnforceAuto:
  193. furi_assert(app->display_led_lock > 0);
  194. app->display_led_lock--;
  195. if(app->display_led_lock == 0) {
  196. notification_apply_internal_led_layer(
  197. &app->display,
  198. notification_message->data.led.value * display_brightness_setting);
  199. }
  200. break;
  201. case NotificationMessageTypeLedRed:
  202. // store and send on delay or after seq
  203. led_active = true;
  204. led_values[0] = notification_message->data.led.value;
  205. app->led[0].value_last[LayerNotification] = led_values[0];
  206. reset_mask |= reset_red_mask;
  207. break;
  208. case NotificationMessageTypeLedGreen:
  209. // store and send on delay or after seq
  210. led_active = true;
  211. led_values[1] = notification_message->data.led.value;
  212. app->led[1].value_last[LayerNotification] = led_values[1];
  213. reset_mask |= reset_green_mask;
  214. break;
  215. case NotificationMessageTypeLedBlue:
  216. // store and send on delay or after seq
  217. led_active = true;
  218. led_values[2] = notification_message->data.led.value;
  219. app->led[2].value_last[LayerNotification] = led_values[2];
  220. reset_mask |= reset_blue_mask;
  221. break;
  222. case NotificationMessageTypeLedBlinkStart:
  223. // store and send on delay or after seq
  224. led_active = true;
  225. furi_hal_light_blink_start(
  226. notification_message->data.led_blink.color,
  227. app->settings.led_brightness * 255,
  228. notification_message->data.led_blink.on_time,
  229. notification_message->data.led_blink.period);
  230. reset_mask |= reset_blink_mask;
  231. reset_mask |= reset_red_mask;
  232. reset_mask |= reset_green_mask;
  233. reset_mask |= reset_blue_mask;
  234. break;
  235. case NotificationMessageTypeLedBlinkColor:
  236. led_active = true;
  237. furi_hal_light_blink_set_color(notification_message->data.led_blink.color);
  238. break;
  239. case NotificationMessageTypeLedBlinkStop:
  240. furi_hal_light_blink_stop();
  241. reset_mask &= ~reset_blink_mask;
  242. reset_mask |= reset_red_mask;
  243. reset_mask |= reset_green_mask;
  244. reset_mask |= reset_blue_mask;
  245. break;
  246. case NotificationMessageTypeVibro:
  247. if(notification_message->data.vibro.on) {
  248. if(vibro_setting) notification_vibro_on(force_vibro);
  249. } else {
  250. notification_vibro_off();
  251. }
  252. reset_mask |= reset_vibro_mask;
  253. break;
  254. case NotificationMessageTypeSoundOn:
  255. notification_sound_on(
  256. notification_message->data.sound.frequency,
  257. notification_message->data.sound.volume * speaker_volume_setting,
  258. force_volume);
  259. reset_mask |= reset_sound_mask;
  260. break;
  261. case NotificationMessageTypeSoundOff:
  262. notification_sound_off();
  263. reset_mask |= reset_sound_mask;
  264. break;
  265. case NotificationMessageTypeDelay:
  266. if(led_active) {
  267. if(notification_is_any_led_layer_internal_and_not_empty(app)) {
  268. notification_apply_notification_leds(app, led_off_values);
  269. furi_delay_ms(minimal_delay);
  270. }
  271. led_active = false;
  272. notification_apply_notification_leds(app, led_values);
  273. reset_mask |= reset_red_mask;
  274. reset_mask |= reset_green_mask;
  275. reset_mask |= reset_blue_mask;
  276. }
  277. furi_delay_ms(notification_message->data.delay.length);
  278. break;
  279. case NotificationMessageTypeDoNotReset:
  280. reset_notifications = false;
  281. break;
  282. case NotificationMessageTypeForceSpeakerVolumeSetting:
  283. speaker_volume_setting = notification_message->data.forced_settings.speaker_volume;
  284. force_volume = true;
  285. break;
  286. case NotificationMessageTypeForceVibroSetting:
  287. vibro_setting = notification_message->data.forced_settings.vibro;
  288. force_vibro = true;
  289. break;
  290. case NotificationMessageTypeForceDisplayBrightnessSetting:
  291. display_brightness_setting =
  292. notification_message->data.forced_settings.display_brightness;
  293. break;
  294. case NotificationMessageTypeLedBrightnessSettingApply:
  295. led_active = true;
  296. for(uint8_t i = 0; i < NOTIFICATION_LED_COUNT; i++) {
  297. led_values[i] = app->led[i].value_last[LayerNotification];
  298. }
  299. reset_mask |= reset_red_mask;
  300. reset_mask |= reset_green_mask;
  301. reset_mask |= reset_blue_mask;
  302. break;
  303. }
  304. notification_message_index++;
  305. notification_message = (*message->sequence)[notification_message_index];
  306. };
  307. // send and do minimal delay
  308. if(led_active) {
  309. bool need_minimal_delay = false;
  310. if(notification_is_any_led_layer_internal_and_not_empty(app)) {
  311. need_minimal_delay = true;
  312. }
  313. notification_apply_notification_leds(app, led_values);
  314. reset_mask |= reset_red_mask;
  315. reset_mask |= reset_green_mask;
  316. reset_mask |= reset_blue_mask;
  317. if((need_minimal_delay) && (reset_notifications)) {
  318. notification_apply_notification_leds(app, led_off_values);
  319. furi_delay_ms(minimal_delay);
  320. }
  321. }
  322. if(reset_notifications) {
  323. notification_reset_notification_layer(app, reset_mask);
  324. }
  325. }
  326. void notification_process_internal_message(NotificationApp* app, NotificationAppMessage* message) {
  327. uint32_t notification_message_index = 0;
  328. const NotificationMessage* notification_message;
  329. notification_message = (*message->sequence)[notification_message_index];
  330. while(notification_message != NULL) {
  331. switch(notification_message->type) {
  332. case NotificationMessageTypeLedDisplayBacklight:
  333. notification_apply_internal_led_layer(
  334. &app->display,
  335. notification_settings_get_display_brightness(
  336. app, notification_message->data.led.value));
  337. break;
  338. case NotificationMessageTypeLedRed:
  339. app->led[0].value_last[LayerInternal] = notification_message->data.led.value;
  340. notification_apply_internal_led_layer(
  341. &app->led[0],
  342. notification_settings_get_rgb_led_brightness(
  343. app, notification_message->data.led.value));
  344. break;
  345. case NotificationMessageTypeLedGreen:
  346. app->led[1].value_last[LayerInternal] = notification_message->data.led.value;
  347. notification_apply_internal_led_layer(
  348. &app->led[1],
  349. notification_settings_get_rgb_led_brightness(
  350. app, notification_message->data.led.value));
  351. break;
  352. case NotificationMessageTypeLedBlue:
  353. app->led[2].value_last[LayerInternal] = notification_message->data.led.value;
  354. notification_apply_internal_led_layer(
  355. &app->led[2],
  356. notification_settings_get_rgb_led_brightness(
  357. app, notification_message->data.led.value));
  358. break;
  359. case NotificationMessageTypeLedBrightnessSettingApply:
  360. for(uint8_t i = 0; i < NOTIFICATION_LED_COUNT; i++) {
  361. uint8_t new_val = notification_settings_get_rgb_led_brightness(
  362. app, app->led[i].value_last[LayerInternal]);
  363. notification_apply_internal_led_layer(&app->led[i], new_val);
  364. }
  365. break;
  366. default:
  367. break;
  368. }
  369. notification_message_index++;
  370. notification_message = (*message->sequence)[notification_message_index];
  371. }
  372. }
  373. static bool notification_load_settings(NotificationApp* app) {
  374. NotificationSettings settings;
  375. File* file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
  376. const size_t settings_size = sizeof(NotificationSettings);
  377. FURI_LOG_I(TAG, "loading settings from \"%s\"", NOTIFICATION_SETTINGS_PATH);
  378. bool fs_result =
  379. storage_file_open(file, NOTIFICATION_SETTINGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
  380. if(fs_result) {
  381. uint16_t bytes_count = storage_file_read(file, &settings, settings_size);
  382. if(bytes_count != settings_size) {
  383. fs_result = false;
  384. }
  385. }
  386. if(fs_result) {
  387. FURI_LOG_I(TAG, "load success");
  388. if(settings.version != NOTIFICATION_SETTINGS_VERSION) {
  389. FURI_LOG_E(
  390. TAG, "version(%d != %d) mismatch", settings.version, NOTIFICATION_SETTINGS_VERSION);
  391. } else {
  392. furi_kernel_lock();
  393. memcpy(&app->settings, &settings, settings_size);
  394. furi_kernel_unlock();
  395. }
  396. } else {
  397. FURI_LOG_E(TAG, "load failed, %s", storage_file_get_error_desc(file));
  398. }
  399. storage_file_close(file);
  400. storage_file_free(file);
  401. furi_record_close(RECORD_STORAGE);
  402. return fs_result;
  403. };
  404. static bool notification_save_settings(NotificationApp* app) {
  405. NotificationSettings settings;
  406. File* file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
  407. const size_t settings_size = sizeof(NotificationSettings);
  408. FURI_LOG_I(TAG, "saving settings to \"%s\"", NOTIFICATION_SETTINGS_PATH);
  409. furi_kernel_lock();
  410. memcpy(&settings, &app->settings, settings_size);
  411. furi_kernel_unlock();
  412. bool fs_result =
  413. storage_file_open(file, NOTIFICATION_SETTINGS_PATH, FSAM_WRITE, FSOM_CREATE_ALWAYS);
  414. if(fs_result) {
  415. uint16_t bytes_count = storage_file_write(file, &settings, settings_size);
  416. if(bytes_count != settings_size) {
  417. fs_result = false;
  418. }
  419. }
  420. if(fs_result) {
  421. FURI_LOG_I(TAG, "save success");
  422. } else {
  423. FURI_LOG_E(TAG, "save failed, %s", storage_file_get_error_desc(file));
  424. }
  425. storage_file_close(file);
  426. storage_file_free(file);
  427. furi_record_close(RECORD_STORAGE);
  428. return fs_result;
  429. };
  430. static void input_event_callback(const void* value, void* context) {
  431. furi_assert(value);
  432. furi_assert(context);
  433. NotificationApp* app = context;
  434. notification_message(app, &sequence_display_backlight_on);
  435. }
  436. // App alloc
  437. static NotificationApp* notification_app_alloc() {
  438. NotificationApp* app = malloc(sizeof(NotificationApp));
  439. app->queue = furi_message_queue_alloc(8, sizeof(NotificationAppMessage));
  440. app->display_timer = furi_timer_alloc(notification_display_timer, FuriTimerTypeOnce, app);
  441. app->settings.speaker_volume = 1.0f;
  442. app->settings.display_brightness = 1.0f;
  443. app->settings.led_brightness = 1.0f;
  444. app->settings.display_off_delay_ms = 30000;
  445. app->settings.vibro_on = true;
  446. app->display.value[LayerInternal] = 0x00;
  447. app->display.value[LayerNotification] = 0x00;
  448. app->display.index = LayerInternal;
  449. app->display.light = LightBacklight;
  450. app->led[0].value[LayerInternal] = 0x00;
  451. app->led[0].value[LayerNotification] = 0x00;
  452. app->led[0].index = LayerInternal;
  453. app->led[0].light = LightRed;
  454. app->led[1].value[LayerInternal] = 0x00;
  455. app->led[1].value[LayerNotification] = 0x00;
  456. app->led[1].index = LayerInternal;
  457. app->led[1].light = LightGreen;
  458. app->led[2].value[LayerInternal] = 0x00;
  459. app->led[2].value[LayerNotification] = 0x00;
  460. app->led[2].index = LayerInternal;
  461. app->led[2].light = LightBlue;
  462. app->settings.version = NOTIFICATION_SETTINGS_VERSION;
  463. // display backlight control
  464. app->event_record = furi_record_open(RECORD_INPUT_EVENTS);
  465. furi_pubsub_subscribe(app->event_record, input_event_callback, app);
  466. notification_message(app, &sequence_display_backlight_on);
  467. return app;
  468. };
  469. // App
  470. int32_t notification_srv(void* p) {
  471. UNUSED(p);
  472. NotificationApp* app = notification_app_alloc();
  473. if(!notification_load_settings(app)) {
  474. notification_save_settings(app);
  475. }
  476. notification_vibro_off();
  477. notification_sound_off();
  478. notification_apply_internal_led_layer(&app->display, 0x00);
  479. notification_apply_internal_led_layer(&app->led[0], 0x00);
  480. notification_apply_internal_led_layer(&app->led[1], 0x00);
  481. notification_apply_internal_led_layer(&app->led[2], 0x00);
  482. furi_record_create(RECORD_NOTIFICATION, app);
  483. NotificationAppMessage message;
  484. while(1) {
  485. furi_check(furi_message_queue_get(app->queue, &message, FuriWaitForever) == FuriStatusOk);
  486. switch(message.type) {
  487. case NotificationLayerMessage:
  488. notification_process_notification_message(app, &message);
  489. break;
  490. case InternalLayerMessage:
  491. notification_process_internal_message(app, &message);
  492. break;
  493. case SaveSettingsMessage:
  494. notification_save_settings(app);
  495. break;
  496. }
  497. if(message.back_event != NULL) {
  498. furi_event_flag_set(message.back_event, NOTIFICATION_EVENT_COMPLETE);
  499. }
  500. }
  501. return 0;
  502. };