notification_app.c 20 KB

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