notification_app.c 17 KB

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