[FL-1783] Power service refactoring (#718)
* settings power: introduce power settings app * power: move power API to separate file * settings power: implement reboot scene * settings power: add power off scene * assets: add crying dolphin, fix Subghz assets names * settings power: fix power off scene GUI * settings power: add battery info scene * power: add cli to application on start hook * power: remove power from main menu * power: move to power service folder * power service: rework power off logic * power: add pubsub events * bt: subscribe to battery level change, update characteristic * application: change order of Settings applications * gui: add bubble element * power: gui improvements * application: rename Notification -> LCD and notifications * Applications: menu order according to documentation and add missing power cli init * settings power: add disconnect USB scene * power cli: notify user to disconnect USB after poweroff * Power: update poweroff message in cli Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
							
								
								
									
										30
									
								
								applications/applications.c
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						| @ -46,12 +46,14 @@ extern void lfrfid_cli_init(); | |||||||
| extern void nfc_cli_init(); | extern void nfc_cli_init(); | ||||||
| extern void storage_cli_init(); | extern void storage_cli_init(); | ||||||
| extern void subghz_cli_init(); | extern void subghz_cli_init(); | ||||||
|  | extern void power_cli_init(); | ||||||
| 
 | 
 | ||||||
| // Settings
 | // Settings
 | ||||||
| extern int32_t notification_settings_app(void* p); | extern int32_t notification_settings_app(void* p); | ||||||
| extern int32_t storage_settings_app(void* p); | extern int32_t storage_settings_app(void* p); | ||||||
| extern int32_t bt_settings_app(void* p); | extern int32_t bt_settings_app(void* p); | ||||||
| extern int32_t about_settings_app(void* p); | extern int32_t about_settings_app(void* p); | ||||||
|  | extern int32_t power_settings_app(void* p); | ||||||
| 
 | 
 | ||||||
| const FlipperApplication FLIPPER_SERVICES[] = { | const FlipperApplication FLIPPER_SERVICES[] = { | ||||||
| /* Services */ | /* Services */ | ||||||
| @ -143,18 +145,14 @@ const size_t FLIPPER_SERVICES_COUNT = sizeof(FLIPPER_SERVICES) / sizeof(FlipperA | |||||||
| // Main menu APP
 | // Main menu APP
 | ||||||
| const FlipperApplication FLIPPER_APPS[] = { | const FlipperApplication FLIPPER_APPS[] = { | ||||||
| 
 | 
 | ||||||
| #ifdef APP_IBUTTON | #ifdef APP_SUBGHZ | ||||||
|     {.app = ibutton_app, .name = "iButton", .stack_size = 2048, .icon = &A_iButton_14}, |     {.app = subghz_app, .name = "Sub-GHz", .stack_size = 2048, .icon = &A_Sub1ghz_14}, | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_NFC | #ifdef APP_NFC | ||||||
|     {.app = nfc_app, .name = "NFC", .stack_size = 4096, .icon = &A_NFC_14}, |     {.app = nfc_app, .name = "NFC", .stack_size = 4096, .icon = &A_NFC_14}, | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_SUBGHZ |  | ||||||
|     {.app = subghz_app, .name = "Sub-GHz", .stack_size = 2048, .icon = &A_Sub1ghz_14}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_LF_RFID | #ifdef APP_LF_RFID | ||||||
|     {.app = lfrfid_app, .name = "125 kHz RFID", .stack_size = 2048, .icon = &A_125khz_14}, |     {.app = lfrfid_app, .name = "125 kHz RFID", .stack_size = 2048, .icon = &A_125khz_14}, | ||||||
| #endif | #endif | ||||||
| @ -163,6 +161,10 @@ const FlipperApplication FLIPPER_APPS[] = { | |||||||
|     {.app = irda_app, .name = "Infrared", .stack_size = 1024 * 3, .icon = &A_Infrared_14}, |     {.app = irda_app, .name = "Infrared", .stack_size = 1024 * 3, .icon = &A_Infrared_14}, | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | #ifdef APP_IBUTTON | ||||||
|  |     {.app = ibutton_app, .name = "iButton", .stack_size = 2048, .icon = &A_iButton_14}, | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| #ifdef APP_GPIO_TEST | #ifdef APP_GPIO_TEST | ||||||
|     {.app = gpio_test_app, .name = "GPIO", .stack_size = 1024, .icon = &A_GPIO_14}, |     {.app = gpio_test_app, .name = "GPIO", .stack_size = 1024, .icon = &A_GPIO_14}, | ||||||
| #endif | #endif | ||||||
| @ -192,6 +194,9 @@ const FlipperOnStartHook FLIPPER_ON_SYSTEM_START[] = { | |||||||
| #ifdef SRV_BT | #ifdef SRV_BT | ||||||
|     bt_cli_init, |     bt_cli_init, | ||||||
| #endif | #endif | ||||||
|  | #ifdef SRV_POWER | ||||||
|  |     power_cli_init, | ||||||
|  | #endif | ||||||
| #ifdef SRV_STORAGE | #ifdef SRV_STORAGE | ||||||
|     storage_cli_init, |     storage_cli_init, | ||||||
| #endif | #endif | ||||||
| @ -258,16 +263,23 @@ const FlipperApplication FLIPPER_ARCHIVE = | |||||||
| 
 | 
 | ||||||
| // Settings menu
 | // Settings menu
 | ||||||
| const FlipperApplication FLIPPER_SETTINGS_APPS[] = { | const FlipperApplication FLIPPER_SETTINGS_APPS[] = { | ||||||
|  | #ifdef SRV_BT | ||||||
|  |     {.app = bt_settings_app, .name = "Bluetooth", .stack_size = 1024, .icon = NULL}, | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| #ifdef SRV_NOTIFICATION | #ifdef SRV_NOTIFICATION | ||||||
|     {.app = notification_settings_app, .name = "Notification", .stack_size = 1024, .icon = NULL}, |     {.app = notification_settings_app, | ||||||
|  |      .name = "LCD and notifications", | ||||||
|  |      .stack_size = 1024, | ||||||
|  |      .icon = NULL}, | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef SRV_STORAGE | #ifdef SRV_STORAGE | ||||||
|     {.app = storage_settings_app, .name = "Storage", .stack_size = 2048, .icon = NULL}, |     {.app = storage_settings_app, .name = "Storage", .stack_size = 2048, .icon = NULL}, | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef SRV_BT | #ifdef SRV_POWER | ||||||
|     {.app = bt_settings_app, .name = "Bluetooth", .stack_size = 1024, .icon = NULL}, |     {.app = power_settings_app, .name = "Power", .stack_size = 1024, .icon = NULL}, | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_ABOUT | #ifdef APP_ABOUT | ||||||
|  | |||||||
| @ -26,6 +26,17 @@ static void bt_pin_code_show_event_handler(Bt* bt, uint32_t pin) { | |||||||
|     string_clear(pin_str); |     string_clear(pin_str); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void bt_battery_level_changed_callback(const void* _event, void* context) { | ||||||
|  |     furi_assert(_event); | ||||||
|  |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     Bt* bt = context; | ||||||
|  |     const PowerEvent* event = _event; | ||||||
|  |     if(event->type == PowerEventTypeBatteryLevelChanged) { | ||||||
|  |         bt_update_battery_level(bt, event->data.battery_level); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| Bt* bt_alloc() { | Bt* bt_alloc() { | ||||||
|     Bt* bt = furi_alloc(sizeof(Bt)); |     Bt* bt = furi_alloc(sizeof(Bt)); | ||||||
|     // Load settings
 |     // Load settings
 | ||||||
| @ -45,6 +56,11 @@ Bt* bt_alloc() { | |||||||
|     bt->dialogs = furi_record_open("dialogs"); |     bt->dialogs = furi_record_open("dialogs"); | ||||||
|     bt->dialog_message = dialog_message_alloc(); |     bt->dialog_message = dialog_message_alloc(); | ||||||
| 
 | 
 | ||||||
|  |     // Power
 | ||||||
|  |     bt->power = furi_record_open("power"); | ||||||
|  |     PubSub* power_pubsub = power_get_pubsub(bt->power); | ||||||
|  |     subscribe_pubsub(power_pubsub, bt_battery_level_changed_callback, bt); | ||||||
|  | 
 | ||||||
|     return bt; |     return bt; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -9,7 +9,8 @@ | |||||||
| #include <gui/view_port.h> | #include <gui/view_port.h> | ||||||
| #include <gui/view.h> | #include <gui/view.h> | ||||||
| 
 | 
 | ||||||
| #include <applications/dialogs/dialogs.h> | #include <dialogs/dialogs.h> | ||||||
|  | #include <power/power_service/power.h> | ||||||
| 
 | 
 | ||||||
| #include "../bt_settings.h" | #include "../bt_settings.h" | ||||||
| 
 | 
 | ||||||
| @ -36,4 +37,5 @@ struct Bt { | |||||||
|     ViewPort* statusbar_view_port; |     ViewPort* statusbar_view_port; | ||||||
|     DialogsApp* dialogs; |     DialogsApp* dialogs; | ||||||
|     DialogMessage* dialog_message; |     DialogMessage* dialog_message; | ||||||
|  |     Power* power; | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -327,6 +327,17 @@ void elements_slightly_rounded_box( | |||||||
|     canvas_draw_rbox(canvas, x, y, width, height, 1); |     canvas_draw_rbox(canvas, x, y, width, height, 1); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void elements_bubble(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height) { | ||||||
|  |     furi_assert(canvas); | ||||||
|  |     canvas_draw_rframe(canvas, x + 4, y, width, height, 3); | ||||||
|  |     uint8_t y_corner = y + height * 2 / 3; | ||||||
|  |     canvas_draw_line(canvas, x, y_corner, x + 4, y_corner - 4); | ||||||
|  |     canvas_draw_line(canvas, x, y_corner, x + 4, y_corner + 4); | ||||||
|  |     canvas_set_color(canvas, ColorWhite); | ||||||
|  |     canvas_draw_line(canvas, x + 4, y_corner - 3, x + 4, y_corner + 3); | ||||||
|  |     canvas_set_color(canvas, ColorBlack); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void elements_string_fit_width(Canvas* canvas, string_t string, uint8_t width) { | void elements_string_fit_width(Canvas* canvas, string_t string, uint8_t width) { | ||||||
|     furi_assert(canvas); |     furi_assert(canvas); | ||||||
|     furi_assert(string); |     furi_assert(string); | ||||||
|  | |||||||
| @ -8,8 +8,7 @@ | |||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| /*
 | /** Draw progress bar.
 | ||||||
|  * Draw progress bar. |  | ||||||
|  * @param x - progress bar position on X axis |  * @param x - progress bar position on X axis | ||||||
|  * @param y - progress bar position on Y axis |  * @param y - progress bar position on Y axis | ||||||
|  * @param width - progress bar width |  * @param width - progress bar width | ||||||
| @ -24,8 +23,7 @@ void elements_progress_bar( | |||||||
|     uint8_t progress, |     uint8_t progress, | ||||||
|     uint8_t total); |     uint8_t total); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Draw scrollbar on canvas at specific position.
 | ||||||
|  * Draw scrollbar on canvas at specific position. |  | ||||||
|  * @param x - scrollbar position on X axis |  * @param x - scrollbar position on X axis | ||||||
|  * @param y - scrollbar position on Y axis |  * @param y - scrollbar position on Y axis | ||||||
|  * @param height - scrollbar height |  * @param height - scrollbar height | ||||||
| @ -40,41 +38,35 @@ void elements_scrollbar_pos( | |||||||
|     uint16_t pos, |     uint16_t pos, | ||||||
|     uint16_t total); |     uint16_t total); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Draw scrollbar on canvas.
 | ||||||
|  * Draw scrollbar on canvas. |  | ||||||
|  * width 3px, height equal to canvas height |  * width 3px, height equal to canvas height | ||||||
|  * @param pos - current element of total elements |  * @param pos - current element of total elements | ||||||
|  * @param total - total elements |  * @param total - total elements | ||||||
|  */ |  */ | ||||||
| void elements_scrollbar(Canvas* canvas, uint16_t pos, uint16_t total); | void elements_scrollbar(Canvas* canvas, uint16_t pos, uint16_t total); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Draw rounded frame
 | ||||||
|  * Draw rounded frame |  | ||||||
|  * @param x, y - top left corner coordinates |  * @param x, y - top left corner coordinates | ||||||
|  * @param width, height - frame width and height |  * @param width, height - frame width and height | ||||||
|  */ |  */ | ||||||
| void elements_frame(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height); | void elements_frame(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Draw button in left corner
 | ||||||
|  * Draw button in left corner |  | ||||||
|  * @param str - button text |  * @param str - button text | ||||||
|  */ |  */ | ||||||
| void elements_button_left(Canvas* canvas, const char* str); | void elements_button_left(Canvas* canvas, const char* str); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Draw button in right corner
 | ||||||
|  * Draw button in right corner |  | ||||||
|  * @param str - button text |  * @param str - button text | ||||||
|  */ |  */ | ||||||
| void elements_button_right(Canvas* canvas, const char* str); | void elements_button_right(Canvas* canvas, const char* str); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Draw button in center
 | ||||||
|  * Draw button in center |  | ||||||
|  * @param str - button text |  * @param str - button text | ||||||
|  */ |  */ | ||||||
| void elements_button_center(Canvas* canvas, const char* str); | void elements_button_center(Canvas* canvas, const char* str); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Draw aligned multiline text
 | ||||||
|  * Draw aligned multiline text |  | ||||||
|  * @param x, y - coordinates based on align param |  * @param x, y - coordinates based on align param | ||||||
|  * @param horizontal, vertical - aligment of multiline text |  * @param horizontal, vertical - aligment of multiline text | ||||||
|  * @param text - string (possible multiline) |  * @param text - string (possible multiline) | ||||||
| @ -87,22 +79,19 @@ void elements_multiline_text_aligned( | |||||||
|     Align vertical, |     Align vertical, | ||||||
|     const char* text); |     const char* text); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Draw multiline text
 | ||||||
|  * Draw multiline text |  | ||||||
|  * @param x, y - top left corner coordinates |  * @param x, y - top left corner coordinates | ||||||
|  * @param text - string (possible multiline) |  * @param text - string (possible multiline) | ||||||
|  */ |  */ | ||||||
| void elements_multiline_text(Canvas* canvas, uint8_t x, uint8_t y, const char* text); | void elements_multiline_text(Canvas* canvas, uint8_t x, uint8_t y, const char* text); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Draw framed multiline text
 | ||||||
|  * Draw framed multiline text |  | ||||||
|  * @param x, y - top left corner coordinates |  * @param x, y - top left corner coordinates | ||||||
|  * @param text - string (possible multiline) |  * @param text - string (possible multiline) | ||||||
|  */ |  */ | ||||||
| void elements_multiline_text_framed(Canvas* canvas, uint8_t x, uint8_t y, const char* text); | void elements_multiline_text_framed(Canvas* canvas, uint8_t x, uint8_t y, const char* text); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Draw slightly rounded frame
 | ||||||
|  * Draw slightly rounded frame |  | ||||||
|  * @param x, y - top left corner coordinates |  * @param x, y - top left corner coordinates | ||||||
|  * @param width, height - size of frame |  * @param width, height - size of frame | ||||||
|  */ |  */ | ||||||
| @ -113,8 +102,7 @@ void elements_slightly_rounded_frame( | |||||||
|     uint8_t width, |     uint8_t width, | ||||||
|     uint8_t height); |     uint8_t height); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Draw slightly rounded box
 | ||||||
|  * Draw slightly rounded box |  | ||||||
|  * @param x, y - top left corner coordinates |  * @param x, y - top left corner coordinates | ||||||
|  * @param width, height - size of box |  * @param width, height - size of box | ||||||
|  */ |  */ | ||||||
| @ -125,8 +113,15 @@ void elements_slightly_rounded_box( | |||||||
|     uint8_t width, |     uint8_t width, | ||||||
|     uint8_t height); |     uint8_t height); | ||||||
| 
 | 
 | ||||||
| /*
 | /** Draw bubble frame for text
 | ||||||
|  * Trim string buffer to fit width in pixels |  * @param x - left x coordinates | ||||||
|  |  * @param y - top y coordinate | ||||||
|  |  * @param width - bubble width | ||||||
|  |  * @param height - bubble height | ||||||
|  |  */ | ||||||
|  | void elements_bubble(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height); | ||||||
|  | 
 | ||||||
|  | /** Trim string buffer to fit width in pixels
 | ||||||
|  * @param string - string to trim |  * @param string - string to trim | ||||||
|  * @param width - max width |  * @param width - max width | ||||||
|  */ |  */ | ||||||
|  | |||||||
| @ -1,278 +0,0 @@ | |||||||
| #include "power.h" |  | ||||||
| #include "power_cli.h" |  | ||||||
| #include "power_views.h" |  | ||||||
| 
 |  | ||||||
| #include <furi.h> |  | ||||||
| #include <furi-hal.h> |  | ||||||
| 
 |  | ||||||
| #include <menu/menu.h> |  | ||||||
| #include <menu/menu_item.h> |  | ||||||
| 
 |  | ||||||
| #include <gui/gui.h> |  | ||||||
| #include <gui/icon_animation.h> |  | ||||||
| #include <gui/view_port.h> |  | ||||||
| #include <gui/view.h> |  | ||||||
| #include <gui/view_dispatcher.h> |  | ||||||
| #include <gui/modules/dialog.h> |  | ||||||
| #include <assets_icons.h> |  | ||||||
| #include <stm32wbxx.h> |  | ||||||
| 
 |  | ||||||
| #include <notification/notification-messages.h> |  | ||||||
| #include <applications/bt/bt_service/bt.h> |  | ||||||
| 
 |  | ||||||
| #define POWER_OFF_TIMEOUT 30 |  | ||||||
| 
 |  | ||||||
| typedef enum { |  | ||||||
|     PowerStateNotCharging, |  | ||||||
|     PowerStateCharging, |  | ||||||
|     PowerStateCharged, |  | ||||||
| } PowerState; |  | ||||||
| 
 |  | ||||||
| struct Power { |  | ||||||
|     ViewDispatcher* view_dispatcher; |  | ||||||
|     View* info_view; |  | ||||||
|     View* off_view; |  | ||||||
|     View* disconnect_view; |  | ||||||
| 
 |  | ||||||
|     ViewPort* battery_view_port; |  | ||||||
| 
 |  | ||||||
|     Dialog* dialog; |  | ||||||
| 
 |  | ||||||
|     ValueMutex* menu_vm; |  | ||||||
|     Cli* cli; |  | ||||||
|     Bt* bt; |  | ||||||
|     MenuItem* menu; |  | ||||||
| 
 |  | ||||||
|     PowerState state; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| void power_draw_battery_callback(Canvas* canvas, void* context) { |  | ||||||
|     furi_assert(context); |  | ||||||
|     Power* power = context; |  | ||||||
|     canvas_draw_icon(canvas, 0, 0, &I_Battery_26x8); |  | ||||||
|     with_view_model( |  | ||||||
|         power->info_view, (PowerInfoModel * model) { |  | ||||||
|             canvas_draw_box(canvas, 2, 2, (float)model->charge / 100 * 20, 4); |  | ||||||
|             return false; |  | ||||||
|         }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| uint32_t power_info_back_callback(void* context) { |  | ||||||
|     return VIEW_NONE; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void power_menu_off_callback(void* context) { |  | ||||||
|     Power* power = context; |  | ||||||
|     power_off(power); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void power_menu_reset_dialog_result(DialogResult result, void* context) { |  | ||||||
|     Power* power = context; |  | ||||||
|     if(result == DialogResultLeft) { |  | ||||||
|         power_reboot(power, PowerBootModeDfu); |  | ||||||
|     } else if(result == DialogResultRight) { |  | ||||||
|         power_reboot(power, PowerBootModeNormal); |  | ||||||
|     } else if(result == DialogResultBack) { |  | ||||||
|         view_dispatcher_switch_to_view(power->view_dispatcher, VIEW_NONE); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void power_menu_reset_callback(void* context) { |  | ||||||
|     Power* power = context; |  | ||||||
|     dialog_set_result_callback(power->dialog, power_menu_reset_dialog_result); |  | ||||||
|     dialog_set_header_text(power->dialog, "Reboot type"); |  | ||||||
|     dialog_set_text(power->dialog, "Reboot where?"); |  | ||||||
|     dialog_set_left_button_text(power->dialog, "DFU"); |  | ||||||
|     dialog_set_right_button_text(power->dialog, "OS"); |  | ||||||
|     view_dispatcher_switch_to_view(power->view_dispatcher, PowerViewDialog); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void power_menu_enable_otg_callback(void* context) { |  | ||||||
|     furi_hal_power_enable_otg(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void power_menu_disable_otg_callback(void* context) { |  | ||||||
|     furi_hal_power_disable_otg(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void power_menu_info_callback(void* context) { |  | ||||||
|     Power* power = context; |  | ||||||
|     view_dispatcher_switch_to_view(power->view_dispatcher, PowerViewInfo); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| Power* power_alloc() { |  | ||||||
|     Power* power = furi_alloc(sizeof(Power)); |  | ||||||
| 
 |  | ||||||
|     power->state = PowerStateNotCharging; |  | ||||||
| 
 |  | ||||||
|     power->menu_vm = furi_record_open("menu"); |  | ||||||
| 
 |  | ||||||
|     power->cli = furi_record_open("cli"); |  | ||||||
|     power_cli_init(power->cli, power); |  | ||||||
| 
 |  | ||||||
|     power->bt = furi_record_open("bt"); |  | ||||||
| 
 |  | ||||||
|     power->menu = menu_item_alloc_menu("Power", icon_animation_alloc(&A_Power_14)); |  | ||||||
|     menu_item_subitem_add( |  | ||||||
|         power->menu, menu_item_alloc_function("Off", NULL, power_menu_off_callback, power)); |  | ||||||
|     menu_item_subitem_add( |  | ||||||
|         power->menu, menu_item_alloc_function("Reboot", NULL, power_menu_reset_callback, power)); |  | ||||||
|     menu_item_subitem_add( |  | ||||||
|         power->menu, |  | ||||||
|         menu_item_alloc_function("Enable OTG", NULL, power_menu_enable_otg_callback, power)); |  | ||||||
|     menu_item_subitem_add( |  | ||||||
|         power->menu, |  | ||||||
|         menu_item_alloc_function("Disable OTG", NULL, power_menu_disable_otg_callback, power)); |  | ||||||
|     menu_item_subitem_add( |  | ||||||
|         power->menu, menu_item_alloc_function("Info", NULL, power_menu_info_callback, power)); |  | ||||||
| 
 |  | ||||||
|     power->view_dispatcher = view_dispatcher_alloc(); |  | ||||||
|     power->info_view = view_alloc(); |  | ||||||
|     view_allocate_model(power->info_view, ViewModelTypeLockFree, sizeof(PowerInfoModel)); |  | ||||||
|     view_set_draw_callback(power->info_view, power_info_draw_callback); |  | ||||||
|     view_set_previous_callback(power->info_view, power_info_back_callback); |  | ||||||
|     view_dispatcher_add_view(power->view_dispatcher, PowerViewInfo, power->info_view); |  | ||||||
| 
 |  | ||||||
|     power->off_view = view_alloc(); |  | ||||||
|     view_allocate_model(power->off_view, ViewModelTypeLockFree, sizeof(PowerOffModel)); |  | ||||||
|     view_set_draw_callback(power->off_view, power_off_draw_callback); |  | ||||||
|     view_dispatcher_add_view(power->view_dispatcher, PowerViewOff, power->off_view); |  | ||||||
| 
 |  | ||||||
|     power->disconnect_view = view_alloc(); |  | ||||||
|     view_set_draw_callback(power->disconnect_view, power_disconnect_draw_callback); |  | ||||||
|     view_dispatcher_add_view(power->view_dispatcher, PowerViewDisconnect, power->disconnect_view); |  | ||||||
| 
 |  | ||||||
|     power->dialog = dialog_alloc(); |  | ||||||
|     dialog_set_context(power->dialog, power); |  | ||||||
|     view_dispatcher_add_view( |  | ||||||
|         power->view_dispatcher, PowerViewDialog, dialog_get_view(power->dialog)); |  | ||||||
| 
 |  | ||||||
|     power->battery_view_port = view_port_alloc(); |  | ||||||
| 
 |  | ||||||
|     view_port_set_width(power->battery_view_port, icon_get_width(&I_Battery_26x8)); |  | ||||||
|     view_port_draw_callback_set(power->battery_view_port, power_draw_battery_callback, power); |  | ||||||
|     return power; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void power_free(Power* power) { |  | ||||||
|     furi_assert(power); |  | ||||||
|     free(power); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void power_off(Power* power) { |  | ||||||
|     furi_assert(power); |  | ||||||
|     furi_hal_power_off(); |  | ||||||
|     view_dispatcher_switch_to_view(power->view_dispatcher, PowerViewDisconnect); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void power_reboot(Power* power, PowerBootMode mode) { |  | ||||||
|     if(mode == PowerBootModeNormal) { |  | ||||||
|         furi_hal_boot_set_mode(FuriHalBootModeNormal); |  | ||||||
|     } else if(mode == PowerBootModeDfu) { |  | ||||||
|         furi_hal_boot_set_mode(FuriHalBootModeDFU); |  | ||||||
|     } |  | ||||||
|     furi_hal_power_reset(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void power_charging_indication_handler(Power* power, NotificationApp* notifications) { |  | ||||||
|     if(furi_hal_power_is_charging()) { |  | ||||||
|         if(furi_hal_power_get_pct() == 100) { |  | ||||||
|             if(power->state != PowerStateCharged) { |  | ||||||
|                 notification_internal_message(notifications, &sequence_charged); |  | ||||||
|                 power->state = PowerStateCharged; |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             if(power->state != PowerStateCharging) { |  | ||||||
|                 notification_internal_message(notifications, &sequence_charging); |  | ||||||
|                 power->state = PowerStateCharging; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(!furi_hal_power_is_charging()) { |  | ||||||
|         if(power->state != PowerStateNotCharging) { |  | ||||||
|             notification_internal_message(notifications, &sequence_not_charging); |  | ||||||
|             power->state = PowerStateNotCharging; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| int32_t power_srv(void* p) { |  | ||||||
|     (void)p; |  | ||||||
|     Power* power = power_alloc(); |  | ||||||
| 
 |  | ||||||
|     NotificationApp* notifications = furi_record_open("notification"); |  | ||||||
|     Gui* gui = furi_record_open("gui"); |  | ||||||
|     gui_add_view_port(gui, power->battery_view_port, GuiLayerStatusBarRight); |  | ||||||
|     view_dispatcher_attach_to_gui(power->view_dispatcher, gui, ViewDispatcherTypeFullscreen); |  | ||||||
| 
 |  | ||||||
|     with_value_mutex( |  | ||||||
|         power->menu_vm, (Menu * menu) { menu_item_add(menu, power->menu); }); |  | ||||||
| 
 |  | ||||||
|     furi_record_create("power", power); |  | ||||||
|     uint8_t battery_level = 0; |  | ||||||
|     uint8_t battery_level_prev = 0; |  | ||||||
|     while(1) { |  | ||||||
|         bool battery_low = false; |  | ||||||
| 
 |  | ||||||
|         with_view_model( |  | ||||||
|             power->info_view, (PowerInfoModel * model) { |  | ||||||
|                 model->charge = furi_hal_power_get_pct(); |  | ||||||
|                 battery_level = model->charge; |  | ||||||
|                 model->health = furi_hal_power_get_bat_health_pct(); |  | ||||||
|                 model->capacity_remaining = furi_hal_power_get_battery_remaining_capacity(); |  | ||||||
|                 model->capacity_full = furi_hal_power_get_battery_full_capacity(); |  | ||||||
|                 model->current_charger = furi_hal_power_get_battery_current(FuriHalPowerICCharger); |  | ||||||
|                 model->current_gauge = furi_hal_power_get_battery_current(FuriHalPowerICFuelGauge); |  | ||||||
|                 model->voltage_charger = furi_hal_power_get_battery_voltage(FuriHalPowerICCharger); |  | ||||||
|                 model->voltage_gauge = furi_hal_power_get_battery_voltage(FuriHalPowerICFuelGauge); |  | ||||||
|                 model->voltage_vbus = furi_hal_power_get_usb_voltage(); |  | ||||||
|                 model->temperature_charger = |  | ||||||
|                     furi_hal_power_get_battery_temperature(FuriHalPowerICCharger); |  | ||||||
|                 model->temperature_gauge = |  | ||||||
|                     furi_hal_power_get_battery_temperature(FuriHalPowerICFuelGauge); |  | ||||||
| 
 |  | ||||||
|                 if(model->charge == 0 && model->voltage_vbus < 4.0f) { |  | ||||||
|                     battery_low = true; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 return true; |  | ||||||
|             }); |  | ||||||
| 
 |  | ||||||
|         with_view_model( |  | ||||||
|             power->off_view, (PowerOffModel * model) { |  | ||||||
|                 if(battery_low) { |  | ||||||
|                     if(model->poweroff_tick == 0) { |  | ||||||
|                         model->poweroff_tick = |  | ||||||
|                             osKernelGetTickCount() + osKernelGetTickFreq() * POWER_OFF_TIMEOUT; |  | ||||||
|                     } else { |  | ||||||
|                         if(osKernelGetTickCount() > model->poweroff_tick) { |  | ||||||
|                             power_off(power); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } else { |  | ||||||
|                     model->poweroff_tick = 0; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 if(model->battery_low != battery_low) { |  | ||||||
|                     model->battery_low = battery_low; |  | ||||||
|                     view_dispatcher_switch_to_view( |  | ||||||
|                         power->view_dispatcher, battery_low ? PowerViewOff : VIEW_NONE); |  | ||||||
|                 } |  | ||||||
|                 return true; |  | ||||||
|             }); |  | ||||||
| 
 |  | ||||||
|         power_charging_indication_handler(power, notifications); |  | ||||||
| 
 |  | ||||||
|         if(battery_level_prev != battery_level) { |  | ||||||
|             battery_level_prev = battery_level; |  | ||||||
|             bt_update_battery_level(power->bt, battery_level); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         view_port_update(power->battery_view_port); |  | ||||||
| 
 |  | ||||||
|         osDelay(1024); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
| @ -1,19 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| typedef struct Power Power; |  | ||||||
| 
 |  | ||||||
| typedef enum { |  | ||||||
|     PowerBootModeNormal, |  | ||||||
|     PowerBootModeDfu, |  | ||||||
| } PowerBootMode; |  | ||||||
| 
 |  | ||||||
| /** Power off device
 |  | ||||||
|  * @param power - Power instance |  | ||||||
|  */ |  | ||||||
| void power_off(Power* power); |  | ||||||
| 
 |  | ||||||
| /** Reboot device
 |  | ||||||
|  * @param power - Power instance |  | ||||||
|  * @param mode - PowerBootMode |  | ||||||
|  */ |  | ||||||
| void power_reboot(Power* power, PowerBootMode mode); |  | ||||||
| @ -1,29 +1,31 @@ | |||||||
| #include "power_cli.h" | #include "power_cli.h" | ||||||
|  | 
 | ||||||
|  | #include <power/power_service/power.h> | ||||||
|  | #include <cli/cli.h> | ||||||
| #include <furi-hal.h> | #include <furi-hal.h> | ||||||
| 
 | 
 | ||||||
| void power_cli_poweroff(Cli* cli, string_t args, void* context) { | void power_cli_poweroff(Cli* cli, string_t args, void* context) { | ||||||
|     Power* power = context; |     power_off(); | ||||||
|     power_off(power); |     printf("It's now safe to disconnect USB from your flipper\r\n"); | ||||||
|  |     while(cli_getc(cli)) { | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void power_cli_reboot(Cli* cli, string_t args, void* context) { | void power_cli_reboot(Cli* cli, string_t args, void* context) { | ||||||
|     Power* power = context; |     power_reboot(PowerBootModeNormal); | ||||||
|     power_reboot(power, PowerBootModeNormal); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void power_cli_dfu(Cli* cli, string_t args, void* context) { | void power_cli_dfu(Cli* cli, string_t args, void* context) { | ||||||
|     Power* power = context; |     power_reboot(PowerBootModeDfu); | ||||||
|     power_reboot(power, PowerBootModeDfu); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void power_cli_factory_reset(Cli* cli, string_t args, void* context) { | void power_cli_factory_reset(Cli* cli, string_t args, void* context) { | ||||||
|     Power* power = context; |  | ||||||
|     printf("All data will be lost. Are you sure (y/n)?\r\n"); |     printf("All data will be lost. Are you sure (y/n)?\r\n"); | ||||||
|     char c = cli_getc(cli); |     char c = cli_getc(cli); | ||||||
|     if(c == 'y' || c == 'Y') { |     if(c == 'y' || c == 'Y') { | ||||||
|         printf("Data will be wiped after reboot.\r\n"); |         printf("Data will be wiped after reboot.\r\n"); | ||||||
|         furi_hal_boot_set_flags(FuriHalBootFlagFactoryReset); |         furi_hal_boot_set_flags(FuriHalBootFlagFactoryReset); | ||||||
|         power_reboot(power, PowerBootModeNormal); |         power_reboot(PowerBootModeNormal); | ||||||
|     } else { |     } else { | ||||||
|         printf("Safe choice.\r\n"); |         printf("Safe choice.\r\n"); | ||||||
|     } |     } | ||||||
| @ -53,13 +55,17 @@ void power_cli_ext(Cli* cli, string_t args, void* context) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void power_cli_init(Cli* cli, Power* power) { | void power_cli_init() { | ||||||
|     cli_add_command(cli, "poweroff", CliCommandFlagParallelSafe, power_cli_poweroff, power); |     Cli* cli = furi_record_open("cli"); | ||||||
|     cli_add_command(cli, "reboot", CliCommandFlagParallelSafe, power_cli_reboot, power); | 
 | ||||||
|  |     cli_add_command(cli, "poweroff", CliCommandFlagParallelSafe, power_cli_poweroff, NULL); | ||||||
|  |     cli_add_command(cli, "reboot", CliCommandFlagParallelSafe, power_cli_reboot, NULL); | ||||||
|     cli_add_command( |     cli_add_command( | ||||||
|         cli, "factory_reset", CliCommandFlagParallelSafe, power_cli_factory_reset, power); |         cli, "factory_reset", CliCommandFlagParallelSafe, power_cli_factory_reset, NULL); | ||||||
|     cli_add_command(cli, "dfu", CliCommandFlagParallelSafe, power_cli_dfu, power); |     cli_add_command(cli, "dfu", CliCommandFlagParallelSafe, power_cli_dfu, NULL); | ||||||
|     cli_add_command(cli, "power_info", CliCommandFlagParallelSafe, power_cli_info, power); |     cli_add_command(cli, "power_info", CliCommandFlagParallelSafe, power_cli_info, NULL); | ||||||
|     cli_add_command(cli, "power_otg", CliCommandFlagParallelSafe, power_cli_otg, power); |     cli_add_command(cli, "power_otg", CliCommandFlagParallelSafe, power_cli_otg, NULL); | ||||||
|     cli_add_command(cli, "power_ext", CliCommandFlagParallelSafe, power_cli_ext, power); |     cli_add_command(cli, "power_ext", CliCommandFlagParallelSafe, power_cli_ext, NULL); | ||||||
|  | 
 | ||||||
|  |     furi_record_close("cli"); | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,6 +1,3 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <cli/cli.h> | void power_cli_init(); | ||||||
| #include "power.h" |  | ||||||
| 
 |  | ||||||
| void power_cli_init(Cli* cli, Power* power); |  | ||||||
|  | |||||||
							
								
								
									
										179
									
								
								applications/power/power_service/power.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						| @ -0,0 +1,179 @@ | |||||||
|  | #include "power_i.h" | ||||||
|  | #include "views/power_off.h" | ||||||
|  | 
 | ||||||
|  | #include <furi.h> | ||||||
|  | #include <furi-hal.h> | ||||||
|  | #include <gui/view_port.h> | ||||||
|  | #include <gui/view.h> | ||||||
|  | 
 | ||||||
|  | #define POWER_OFF_TIMEOUT 90 | ||||||
|  | 
 | ||||||
|  | void power_draw_battery_callback(Canvas* canvas, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Power* power = context; | ||||||
|  |     canvas_draw_icon(canvas, 0, 0, &I_Battery_26x8); | ||||||
|  |     canvas_draw_box(canvas, 2, 2, power->info.charge / 5, 4); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static ViewPort* power_battery_view_port_alloc(Power* power) { | ||||||
|  |     ViewPort* battery_view_port = view_port_alloc(); | ||||||
|  |     view_port_set_width(battery_view_port, icon_get_width(&I_Battery_26x8)); | ||||||
|  |     view_port_draw_callback_set(battery_view_port, power_draw_battery_callback, power); | ||||||
|  |     gui_add_view_port(power->gui, battery_view_port, GuiLayerStatusBarRight); | ||||||
|  |     return battery_view_port; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Power* power_alloc() { | ||||||
|  |     Power* power = furi_alloc(sizeof(Power)); | ||||||
|  | 
 | ||||||
|  |     // Records
 | ||||||
|  |     power->notification = furi_record_open("notification"); | ||||||
|  |     power->gui = furi_record_open("gui"); | ||||||
|  | 
 | ||||||
|  |     // Pubsub
 | ||||||
|  |     init_pubsub(&power->event_pubsub); | ||||||
|  | 
 | ||||||
|  |     // State initialization
 | ||||||
|  |     power->state = PowerStateNotCharging; | ||||||
|  |     power->battery_low = false; | ||||||
|  |     power->power_off_timeout = POWER_OFF_TIMEOUT; | ||||||
|  |     power->info_mtx = osMutexNew(NULL); | ||||||
|  | 
 | ||||||
|  |     // Gui
 | ||||||
|  |     power->view_dispatcher = view_dispatcher_alloc(); | ||||||
|  |     power->power_off = power_off_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         power->view_dispatcher, PowerViewOff, power_off_get_view(power->power_off)); | ||||||
|  |     view_dispatcher_attach_to_gui( | ||||||
|  |         power->view_dispatcher, power->gui, ViewDispatcherTypeFullscreen); | ||||||
|  | 
 | ||||||
|  |     // Battery view port
 | ||||||
|  |     power->battery_view_port = power_battery_view_port_alloc(power); | ||||||
|  | 
 | ||||||
|  |     return power; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void power_free(Power* power) { | ||||||
|  |     furi_assert(power); | ||||||
|  | 
 | ||||||
|  |     // Records
 | ||||||
|  |     furi_record_close("notification"); | ||||||
|  |     furi_record_close("gui"); | ||||||
|  | 
 | ||||||
|  |     // Gui
 | ||||||
|  |     view_dispatcher_remove_view(power->view_dispatcher, PowerViewOff); | ||||||
|  |     power_off_free(power->power_off); | ||||||
|  |     view_port_free(power->battery_view_port); | ||||||
|  | 
 | ||||||
|  |     // State
 | ||||||
|  |     osMutexDelete(power->info_mtx); | ||||||
|  |     free(power); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void power_check_charging_state(Power* power) { | ||||||
|  |     if(furi_hal_power_is_charging()) { | ||||||
|  |         if(power->info.charge == 100) { | ||||||
|  |             if(power->state != PowerStateCharged) { | ||||||
|  |                 notification_internal_message(power->notification, &sequence_charged); | ||||||
|  |                 power->state = PowerStateCharged; | ||||||
|  |                 power->event.type = PowerEventTypeFullyCharged; | ||||||
|  |                 notify_pubsub(&power->event_pubsub, &power->event); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             if(power->state != PowerStateCharging) { | ||||||
|  |                 notification_internal_message(power->notification, &sequence_charging); | ||||||
|  |                 power->state = PowerStateCharging; | ||||||
|  |                 power->event.type = PowerEventTypeStartCharging; | ||||||
|  |                 notify_pubsub(&power->event_pubsub, &power->event); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         if(power->state != PowerStateNotCharging) { | ||||||
|  |             notification_internal_message(power->notification, &sequence_not_charging); | ||||||
|  |             power->state = PowerStateNotCharging; | ||||||
|  |             power->event.type = PowerEventTypeStopCharging; | ||||||
|  |             notify_pubsub(&power->event_pubsub, &power->event); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void power_update_info(Power* power) { | ||||||
|  |     osMutexAcquire(power->info_mtx, osWaitForever); | ||||||
|  |     PowerInfo* info = &power->info; | ||||||
|  | 
 | ||||||
|  |     info->charge = furi_hal_power_get_pct(); | ||||||
|  |     info->health = furi_hal_power_get_bat_health_pct(); | ||||||
|  |     info->capacity_remaining = furi_hal_power_get_battery_remaining_capacity(); | ||||||
|  |     info->capacity_full = furi_hal_power_get_battery_full_capacity(); | ||||||
|  |     info->current_charger = furi_hal_power_get_battery_current(FuriHalPowerICCharger); | ||||||
|  |     info->current_gauge = furi_hal_power_get_battery_current(FuriHalPowerICFuelGauge); | ||||||
|  |     info->voltage_charger = furi_hal_power_get_battery_voltage(FuriHalPowerICCharger); | ||||||
|  |     info->voltage_gauge = furi_hal_power_get_battery_voltage(FuriHalPowerICFuelGauge); | ||||||
|  |     info->voltage_vbus = furi_hal_power_get_usb_voltage(); | ||||||
|  |     info->temperature_charger = furi_hal_power_get_battery_temperature(FuriHalPowerICCharger); | ||||||
|  |     info->temperature_gauge = furi_hal_power_get_battery_temperature(FuriHalPowerICFuelGauge); | ||||||
|  | 
 | ||||||
|  |     osMutexRelease(power->info_mtx); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void power_check_low_battery(Power* power) { | ||||||
|  |     // Check battery charge and vbus voltage
 | ||||||
|  |     if((power->info.charge == 0) && (power->info.voltage_vbus < 4.0f)) { | ||||||
|  |         if(!power->battery_low) { | ||||||
|  |             view_dispatcher_switch_to_view(power->view_dispatcher, PowerViewOff); | ||||||
|  |         } | ||||||
|  |         power->battery_low = true; | ||||||
|  |     } else { | ||||||
|  |         if(power->battery_low) { | ||||||
|  |             view_dispatcher_switch_to_view(power->view_dispatcher, VIEW_NONE); | ||||||
|  |             power->power_off_timeout = POWER_OFF_TIMEOUT; | ||||||
|  |         } | ||||||
|  |         power->battery_low = false; | ||||||
|  |     } | ||||||
|  |     // If battery low, update view and switch off power after timeout
 | ||||||
|  |     if(power->battery_low) { | ||||||
|  |         if(power->power_off_timeout) { | ||||||
|  |             power_off_set_time_left(power->power_off, power->power_off_timeout--); | ||||||
|  |         } else { | ||||||
|  |             power_off(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void power_check_battery_level_change(Power* power) { | ||||||
|  |     if(power->battery_level != power->info.charge) { | ||||||
|  |         power->battery_level = power->info.charge; | ||||||
|  |         power->event.type = PowerEventTypeBatteryLevelChanged; | ||||||
|  |         power->event.data.battery_level = power->battery_level; | ||||||
|  |         notify_pubsub(&power->event_pubsub, &power->event); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int32_t power_srv(void* p) { | ||||||
|  |     (void)p; | ||||||
|  |     Power* power = power_alloc(); | ||||||
|  |     furi_record_create("power", power); | ||||||
|  | 
 | ||||||
|  |     while(1) { | ||||||
|  |         // Update data from gauge and charger
 | ||||||
|  |         power_update_info(power); | ||||||
|  | 
 | ||||||
|  |         // Check low battery level
 | ||||||
|  |         power_check_low_battery(power); | ||||||
|  | 
 | ||||||
|  |         // Check and notify about charging state
 | ||||||
|  |         power_check_charging_state(power); | ||||||
|  | 
 | ||||||
|  |         // Check and notify about battery level change
 | ||||||
|  |         power_check_battery_level_change(power); | ||||||
|  | 
 | ||||||
|  |         // Update battery view port
 | ||||||
|  |         view_port_update(power->battery_view_port); | ||||||
|  | 
 | ||||||
|  |         osDelay(1000); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     power_free(power); | ||||||
|  | 
 | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
							
								
								
									
										65
									
								
								applications/power/power_service/power.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,65 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <furi/pubsub.h> | ||||||
|  | 
 | ||||||
|  | typedef struct Power Power; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     PowerBootModeNormal, | ||||||
|  |     PowerBootModeDfu, | ||||||
|  | } PowerBootMode; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     PowerEventTypeStopCharging, | ||||||
|  |     PowerEventTypeStartCharging, | ||||||
|  |     PowerEventTypeFullyCharged, | ||||||
|  |     PowerEventTypeBatteryLevelChanged, | ||||||
|  | } PowerEventType; | ||||||
|  | 
 | ||||||
|  | typedef union { | ||||||
|  |     uint8_t battery_level; | ||||||
|  | } PowerEventData; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     PowerEventType type; | ||||||
|  |     PowerEventData data; | ||||||
|  | } PowerEvent; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     float current_charger; | ||||||
|  |     float current_gauge; | ||||||
|  | 
 | ||||||
|  |     float voltage_charger; | ||||||
|  |     float voltage_gauge; | ||||||
|  |     float voltage_vbus; | ||||||
|  | 
 | ||||||
|  |     uint32_t capacity_remaining; | ||||||
|  |     uint32_t capacity_full; | ||||||
|  | 
 | ||||||
|  |     float temperature_charger; | ||||||
|  |     float temperature_gauge; | ||||||
|  | 
 | ||||||
|  |     uint8_t charge; | ||||||
|  |     uint8_t health; | ||||||
|  | } PowerInfo; | ||||||
|  | 
 | ||||||
|  | /** Power off device
 | ||||||
|  |  */ | ||||||
|  | void power_off(); | ||||||
|  | 
 | ||||||
|  | /** Reboot device
 | ||||||
|  |  * @param mode - PowerBootMode | ||||||
|  |  */ | ||||||
|  | void power_reboot(PowerBootMode mode); | ||||||
|  | 
 | ||||||
|  | /** Get power info
 | ||||||
|  |  * @param power - Power instance | ||||||
|  |  * @param info - PowerInfo instance | ||||||
|  |  */ | ||||||
|  | void power_get_info(Power* power, PowerInfo* info); | ||||||
|  | 
 | ||||||
|  | /** Get power event pubsub handler
 | ||||||
|  |  * @param power - Power instance | ||||||
|  |  */ | ||||||
|  | PubSub* power_get_pubsub(Power* power); | ||||||
							
								
								
									
										31
									
								
								applications/power/power_service/power_api.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,31 @@ | |||||||
|  | #include "power_i.h" | ||||||
|  | #include <furi.h> | ||||||
|  | #include "furi-hal-power.h" | ||||||
|  | #include "furi-hal-boot.h" | ||||||
|  | 
 | ||||||
|  | void power_off() { | ||||||
|  |     furi_hal_power_off(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void power_reboot(PowerBootMode mode) { | ||||||
|  |     if(mode == PowerBootModeNormal) { | ||||||
|  |         furi_hal_boot_set_mode(FuriHalBootModeNormal); | ||||||
|  |     } else if(mode == PowerBootModeDfu) { | ||||||
|  |         furi_hal_boot_set_mode(FuriHalBootModeDFU); | ||||||
|  |     } | ||||||
|  |     furi_hal_power_reset(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void power_get_info(Power* power, PowerInfo* info) { | ||||||
|  |     furi_assert(power); | ||||||
|  |     furi_assert(info); | ||||||
|  | 
 | ||||||
|  |     osMutexAcquire(power->info_mtx, osWaitForever); | ||||||
|  |     memcpy(info, &power->info, sizeof(power->info)); | ||||||
|  |     osMutexRelease(power->info_mtx); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | PubSub* power_get_pubsub(Power* power) { | ||||||
|  |     furi_assert(power); | ||||||
|  |     return &power->event_pubsub; | ||||||
|  | } | ||||||
							
								
								
									
										37
									
								
								applications/power/power_service/power_i.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						| @ -0,0 +1,37 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "power.h" | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <gui/view_dispatcher.h> | ||||||
|  | #include <gui/gui.h> | ||||||
|  | #include "views/power_off.h" | ||||||
|  | 
 | ||||||
|  | #include <notification/notification-messages.h> | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     PowerStateNotCharging, | ||||||
|  |     PowerStateCharging, | ||||||
|  |     PowerStateCharged, | ||||||
|  | } PowerState; | ||||||
|  | 
 | ||||||
|  | struct Power { | ||||||
|  |     ViewDispatcher* view_dispatcher; | ||||||
|  |     PowerOff* power_off; | ||||||
|  | 
 | ||||||
|  |     ViewPort* battery_view_port; | ||||||
|  |     Gui* gui; | ||||||
|  |     NotificationApp* notification; | ||||||
|  |     PubSub event_pubsub; | ||||||
|  |     PowerEvent event; | ||||||
|  | 
 | ||||||
|  |     PowerState state; | ||||||
|  |     PowerInfo info; | ||||||
|  |     osMutexId_t info_mtx; | ||||||
|  | 
 | ||||||
|  |     bool battery_low; | ||||||
|  |     uint8_t battery_level; | ||||||
|  |     uint8_t power_off_timeout; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef enum { PowerViewOff } PowerView; | ||||||
							
								
								
									
										57
									
								
								applications/power/power_service/views/power_off.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						| @ -0,0 +1,57 @@ | |||||||
|  | #include "power_off.h" | ||||||
|  | #include <furi.h> | ||||||
|  | #include <gui/elements.h> | ||||||
|  | 
 | ||||||
|  | struct PowerOff { | ||||||
|  |     View* view; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     uint32_t time_left_sec; | ||||||
|  | } PowerOffModel; | ||||||
|  | 
 | ||||||
|  | static void power_off_draw_callback(Canvas* canvas, void* _model) { | ||||||
|  |     furi_assert(_model); | ||||||
|  |     PowerOffModel* model = _model; | ||||||
|  |     char buff[32]; | ||||||
|  | 
 | ||||||
|  |     canvas_set_color(canvas, ColorBlack); | ||||||
|  |     canvas_set_font(canvas, FontPrimary); | ||||||
|  |     canvas_draw_str_aligned(canvas, 64, 1, AlignCenter, AlignTop, "Battery low!"); | ||||||
|  |     canvas_draw_icon(canvas, 0, 18, &I_BatteryBody_52x28); | ||||||
|  |     canvas_draw_icon(canvas, 16, 25, &I_FaceNopower_29x14); | ||||||
|  |     elements_bubble(canvas, 54, 17, 70, 30); | ||||||
|  |     canvas_set_font(canvas, FontSecondary); | ||||||
|  |     elements_multiline_text_aligned( | ||||||
|  |         canvas, 70, 23, AlignLeft, AlignTop, "Connect me\n to charger."); | ||||||
|  |     snprintf(buff, sizeof(buff), "Poweroff in %lds.", model->time_left_sec); | ||||||
|  |     canvas_draw_str_aligned(canvas, 64, 60, AlignCenter, AlignBottom, buff); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | PowerOff* power_off_alloc() { | ||||||
|  |     PowerOff* power_off = furi_alloc(sizeof(PowerOff)); | ||||||
|  |     power_off->view = view_alloc(); | ||||||
|  |     view_allocate_model(power_off->view, ViewModelTypeLocking, sizeof(PowerOffModel)); | ||||||
|  |     view_set_draw_callback(power_off->view, power_off_draw_callback); | ||||||
|  |     return power_off; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void power_off_free(PowerOff* power_off) { | ||||||
|  |     furi_assert(power_off); | ||||||
|  |     view_free(power_off->view); | ||||||
|  |     free(power_off); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | View* power_off_get_view(PowerOff* power_off) { | ||||||
|  |     furi_assert(power_off); | ||||||
|  |     return power_off->view; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void power_off_set_time_left(PowerOff* power_off, uint8_t time_left) { | ||||||
|  |     furi_assert(power_off); | ||||||
|  |     with_view_model( | ||||||
|  |         power_off->view, (PowerOffModel * model) { | ||||||
|  |             model->time_left_sec = time_left; | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | } | ||||||
							
								
								
									
										13
									
								
								applications/power/power_service/views/power_off.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,13 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | typedef struct PowerOff PowerOff; | ||||||
|  | 
 | ||||||
|  | #include <gui/view.h> | ||||||
|  | 
 | ||||||
|  | PowerOff* power_off_alloc(); | ||||||
|  | 
 | ||||||
|  | void power_off_free(PowerOff* power_off); | ||||||
|  | 
 | ||||||
|  | View* power_off_get_view(PowerOff* power_off); | ||||||
|  | 
 | ||||||
|  | void power_off_set_time_left(PowerOff* power_off, uint8_t time_left); | ||||||
							
								
								
									
										82
									
								
								applications/power/power_settings_app/power_settings_app.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						| @ -0,0 +1,82 @@ | |||||||
|  | #include "power_settings_app.h" | ||||||
|  | 
 | ||||||
|  | static bool power_settings_custom_event_callback(void* context, uint32_t event) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     PowerSettingsApp* app = context; | ||||||
|  |     return scene_manager_handle_custom_event(app->scene_manager, event); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool power_settings_back_event_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     PowerSettingsApp* app = context; | ||||||
|  |     return scene_manager_handle_back_event(app->scene_manager); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void power_settings_tick_event_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     PowerSettingsApp* app = context; | ||||||
|  |     scene_manager_handle_tick_event(app->scene_manager); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | PowerSettingsApp* power_settings_app_alloc() { | ||||||
|  |     PowerSettingsApp* app = furi_alloc(sizeof(PowerSettingsApp)); | ||||||
|  | 
 | ||||||
|  |     // Records
 | ||||||
|  |     app->gui = furi_record_open("gui"); | ||||||
|  |     app->power = furi_record_open("power"); | ||||||
|  | 
 | ||||||
|  |     // View dispatcher
 | ||||||
|  |     app->view_dispatcher = view_dispatcher_alloc(); | ||||||
|  |     app->scene_manager = scene_manager_alloc(&power_settings_scene_handlers, app); | ||||||
|  |     view_dispatcher_enable_queue(app->view_dispatcher); | ||||||
|  |     view_dispatcher_set_event_callback_context(app->view_dispatcher, app); | ||||||
|  |     view_dispatcher_set_custom_event_callback( | ||||||
|  |         app->view_dispatcher, power_settings_custom_event_callback); | ||||||
|  |     view_dispatcher_set_navigation_event_callback( | ||||||
|  |         app->view_dispatcher, power_settings_back_event_callback); | ||||||
|  |     view_dispatcher_set_tick_event_callback( | ||||||
|  |         app->view_dispatcher, power_settings_tick_event_callback, 2000); | ||||||
|  |     view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); | ||||||
|  | 
 | ||||||
|  |     // Views
 | ||||||
|  |     app->batery_info = battery_info_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         app->view_dispatcher, | ||||||
|  |         PowerSettingsAppViewBatteryInfo, | ||||||
|  |         battery_info_get_view(app->batery_info)); | ||||||
|  |     app->submenu = submenu_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         app->view_dispatcher, PowerSettingsAppViewSubmenu, submenu_get_view(app->submenu)); | ||||||
|  |     app->dialog = dialog_ex_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         app->view_dispatcher, PowerSettingsAppViewDialog, dialog_ex_get_view(app->dialog)); | ||||||
|  | 
 | ||||||
|  |     // Set first scene
 | ||||||
|  |     scene_manager_next_scene(app->scene_manager, PowerSettingsAppSceneStart); | ||||||
|  |     return app; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void power_settings_app_free(PowerSettingsApp* app) { | ||||||
|  |     furi_assert(app); | ||||||
|  |     // Views
 | ||||||
|  |     view_dispatcher_remove_view(app->view_dispatcher, PowerSettingsAppViewBatteryInfo); | ||||||
|  |     battery_info_free(app->batery_info); | ||||||
|  |     view_dispatcher_remove_view(app->view_dispatcher, PowerSettingsAppViewSubmenu); | ||||||
|  |     submenu_free(app->submenu); | ||||||
|  |     view_dispatcher_remove_view(app->view_dispatcher, PowerSettingsAppViewDialog); | ||||||
|  |     dialog_ex_free(app->dialog); | ||||||
|  |     // View dispatcher
 | ||||||
|  |     view_dispatcher_free(app->view_dispatcher); | ||||||
|  |     scene_manager_free(app->scene_manager); | ||||||
|  |     // Records
 | ||||||
|  |     furi_record_close("power"); | ||||||
|  |     furi_record_close("gui"); | ||||||
|  |     free(app); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | extern int32_t power_settings_app(void* p) { | ||||||
|  |     PowerSettingsApp* app = power_settings_app_alloc(); | ||||||
|  |     view_dispatcher_run(app->view_dispatcher); | ||||||
|  |     power_settings_app_free(app); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
							
								
								
									
										31
									
								
								applications/power/power_settings_app/power_settings_app.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,31 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <furi.h> | ||||||
|  | #include <power/power_service/power.h> | ||||||
|  | #include <gui/gui.h> | ||||||
|  | #include <gui/view.h> | ||||||
|  | #include <gui/view_dispatcher.h> | ||||||
|  | #include <gui/scene_manager.h> | ||||||
|  | 
 | ||||||
|  | #include "views/battery_info.h" | ||||||
|  | #include <gui/modules/submenu.h> | ||||||
|  | #include <gui/modules/dialog_ex.h> | ||||||
|  | 
 | ||||||
|  | #include "scenes/power_settings_scene.h" | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     Power* power; | ||||||
|  |     Gui* gui; | ||||||
|  |     SceneManager* scene_manager; | ||||||
|  |     ViewDispatcher* view_dispatcher; | ||||||
|  |     BatteryInfo* batery_info; | ||||||
|  |     Submenu* submenu; | ||||||
|  |     DialogEx* dialog; | ||||||
|  |     PowerInfo info; | ||||||
|  | } PowerSettingsApp; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     PowerSettingsAppViewBatteryInfo, | ||||||
|  |     PowerSettingsAppViewSubmenu, | ||||||
|  |     PowerSettingsAppViewDialog, | ||||||
|  | } PowerSettingsAppView; | ||||||
| @ -0,0 +1,17 @@ | |||||||
|  | #include "../power_settings_app.h" | ||||||
|  | 
 | ||||||
|  | void power_settings_scene_usb_disconnect_on_enter(void* context) { | ||||||
|  |     PowerSettingsApp* app = context; | ||||||
|  | 
 | ||||||
|  |     dialog_ex_set_header( | ||||||
|  |         app->dialog, "Disconnect USB for safe\nshutdown", 64, 26, AlignCenter, AlignTop); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(app->view_dispatcher, PowerSettingsAppViewDialog); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool power_settings_scene_usb_disconnect_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void power_settings_scene_usb_disconnect_on_exit(void* context) { | ||||||
|  | } | ||||||
| @ -0,0 +1,30 @@ | |||||||
|  | #include "power_settings_scene.h" | ||||||
|  | 
 | ||||||
|  | // Generate scene on_enter handlers array
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, | ||||||
|  | void (*const power_settings_on_enter_handlers[])(void*) = { | ||||||
|  | #include "power_settings_scene_config.h" | ||||||
|  | }; | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | // Generate scene on_event handlers array
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, | ||||||
|  | bool (*const power_settings_on_event_handlers[])(void* context, SceneManagerEvent event) = { | ||||||
|  | #include "power_settings_scene_config.h" | ||||||
|  | }; | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | // Generate scene on_exit handlers array
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, | ||||||
|  | void (*const power_settings_on_exit_handlers[])(void* context) = { | ||||||
|  | #include "power_settings_scene_config.h" | ||||||
|  | }; | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | // Initialize scene handlers configuration structure
 | ||||||
|  | const SceneManagerHandlers power_settings_scene_handlers = { | ||||||
|  |     .on_enter_handlers = power_settings_on_enter_handlers, | ||||||
|  |     .on_event_handlers = power_settings_on_event_handlers, | ||||||
|  |     .on_exit_handlers = power_settings_on_exit_handlers, | ||||||
|  |     .scene_num = PowerSettingsAppSceneNum, | ||||||
|  | }; | ||||||
| @ -0,0 +1,29 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/scene_manager.h> | ||||||
|  | 
 | ||||||
|  | // Generate scene id and total number
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) PowerSettingsAppScene##id, | ||||||
|  | typedef enum { | ||||||
|  | #include "power_settings_scene_config.h" | ||||||
|  |     PowerSettingsAppSceneNum, | ||||||
|  | } PowerSettingsAppScene; | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | extern const SceneManagerHandlers power_settings_scene_handlers; | ||||||
|  | 
 | ||||||
|  | // Generate scene on_enter handlers declaration
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); | ||||||
|  | #include "power_settings_scene_config.h" | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | // Generate scene on_event handlers declaration
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) \ | ||||||
|  |     bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); | ||||||
|  | #include "power_settings_scene_config.h" | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | // Generate scene on_exit handlers declaration
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); | ||||||
|  | #include "power_settings_scene_config.h" | ||||||
|  | #undef ADD_SCENE | ||||||
| @ -0,0 +1,34 @@ | |||||||
|  | #include "../power_settings_app.h" | ||||||
|  | 
 | ||||||
|  | static void power_settings_scene_battery_info_update_model(PowerSettingsApp* app) { | ||||||
|  |     power_get_info(app->power, &app->info); | ||||||
|  |     BatteryInfoModel battery_info_data = { | ||||||
|  |         .vbus_voltage = app->info.voltage_vbus, | ||||||
|  |         .gauge_voltage = app->info.voltage_gauge, | ||||||
|  |         .gauge_current = app->info.current_gauge, | ||||||
|  |         .gauge_temperature = app->info.temperature_gauge, | ||||||
|  |         .charge = app->info.charge, | ||||||
|  |         .health = app->info.health, | ||||||
|  |     }; | ||||||
|  |     battery_info_set_data(app->batery_info, &battery_info_data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void power_settings_scene_battery_info_on_enter(void* context) { | ||||||
|  |     PowerSettingsApp* app = context; | ||||||
|  |     power_settings_scene_battery_info_update_model(app); | ||||||
|  |     view_dispatcher_switch_to_view(app->view_dispatcher, PowerSettingsAppViewBatteryInfo); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool power_settings_scene_battery_info_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     PowerSettingsApp* app = context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeTick) { | ||||||
|  |         power_settings_scene_battery_info_update_model(app); | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void power_settings_scene_battery_info_on_exit(void* context) { | ||||||
|  | } | ||||||
| @ -0,0 +1,5 @@ | |||||||
|  | ADD_SCENE(power_settings, start, Start) | ||||||
|  | ADD_SCENE(power_settings, battery_info, BatteryInfo) | ||||||
|  | ADD_SCENE(power_settings, reboot, Reboot) | ||||||
|  | ADD_SCENE(power_settings, power_off, PowerOff) | ||||||
|  | ADD_SCENE(power_settings, usb_disconnect, UsbDisconnect) | ||||||
| @ -0,0 +1,48 @@ | |||||||
|  | #include "../power_settings_app.h" | ||||||
|  | 
 | ||||||
|  | void power_settings_scene_power_off_dialog_callback(DialogExResult result, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     PowerSettingsApp* app = context; | ||||||
|  |     view_dispatcher_send_custom_event(app->view_dispatcher, result); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void power_settings_scene_power_off_on_enter(void* context) { | ||||||
|  |     PowerSettingsApp* app = context; | ||||||
|  |     DialogEx* dialog = app->dialog; | ||||||
|  | 
 | ||||||
|  |     dialog_ex_set_header(dialog, "Turn off Device?", 64, 2, AlignCenter, AlignTop); | ||||||
|  |     dialog_ex_set_text( | ||||||
|  |         dialog, "   I will be\nwaiting for\n you here...", 78, 16, AlignLeft, AlignTop); | ||||||
|  |     dialog_ex_set_icon(dialog, 21, 13, &I_Cry_dolph_55x52); | ||||||
|  |     dialog_ex_set_left_button_text(dialog, "Back"); | ||||||
|  |     dialog_ex_set_right_button_text(dialog, "OFF"); | ||||||
|  |     dialog_ex_set_result_callback(dialog, power_settings_scene_power_off_dialog_callback); | ||||||
|  |     dialog_ex_set_context(dialog, app); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(app->view_dispatcher, PowerSettingsAppViewDialog); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool power_settings_scene_power_off_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     PowerSettingsApp* app = context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == DialogExResultLeft) { | ||||||
|  |             scene_manager_previous_scene(app->scene_manager); | ||||||
|  |         } else if(event.event == DialogExResultRight) { | ||||||
|  |             power_off(); | ||||||
|  |             // Check if USB is connected
 | ||||||
|  |             power_get_info(app->power, &app->info); | ||||||
|  |             if(app->info.voltage_vbus > 4.0f) { | ||||||
|  |                 scene_manager_next_scene(app->scene_manager, PowerSettingsAppSceneUsbDisconnect); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void power_settings_scene_power_off_on_exit(void* context) { | ||||||
|  |     PowerSettingsApp* app = context; | ||||||
|  |     dialog_ex_clean(app->dialog); | ||||||
|  | } | ||||||
							
								
								
									
										52
									
								
								applications/power/power_settings_app/scenes/power_settings_scene_reboot.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						| @ -0,0 +1,52 @@ | |||||||
|  | #include "../power_settings_app.h" | ||||||
|  | 
 | ||||||
|  | enum PowerSettingsRebootSubmenuIndex { | ||||||
|  |     PowerSettingsRebootSubmenuIndexDfu, | ||||||
|  |     PowerSettingsRebootSubmenuIndexOs, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void power_settings_scene_reboot_submenu_callback(void* context, uint32_t index) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     PowerSettingsApp* app = context; | ||||||
|  |     view_dispatcher_send_custom_event(app->view_dispatcher, index); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void power_settings_scene_reboot_on_enter(void* context) { | ||||||
|  |     PowerSettingsApp* app = context; | ||||||
|  |     Submenu* submenu = app->submenu; | ||||||
|  | 
 | ||||||
|  |     submenu_set_header(submenu, "Reboot type"); | ||||||
|  |     submenu_add_item( | ||||||
|  |         submenu, | ||||||
|  |         "Firmware upgrade", | ||||||
|  |         PowerSettingsRebootSubmenuIndexDfu, | ||||||
|  |         power_settings_scene_reboot_submenu_callback, | ||||||
|  |         app); | ||||||
|  |     submenu_add_item( | ||||||
|  |         submenu, | ||||||
|  |         "Flipper OS", | ||||||
|  |         PowerSettingsRebootSubmenuIndexOs, | ||||||
|  |         power_settings_scene_reboot_submenu_callback, | ||||||
|  |         app); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(app->view_dispatcher, PowerSettingsAppViewSubmenu); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool power_settings_scene_reboot_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == PowerSettingsRebootSubmenuIndexDfu) { | ||||||
|  |             power_reboot(PowerBootModeDfu); | ||||||
|  |         } else if(event.event == PowerSettingsRebootSubmenuIndexOs) { | ||||||
|  |             power_reboot(PowerBootModeNormal); | ||||||
|  |         } | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void power_settings_scene_reboot_on_exit(void* context) { | ||||||
|  |     PowerSettingsApp* app = context; | ||||||
|  |     submenu_clean(app->submenu); | ||||||
|  | } | ||||||
							
								
								
									
										64
									
								
								applications/power/power_settings_app/scenes/power_settings_scene_start.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						| @ -0,0 +1,64 @@ | |||||||
|  | #include "../power_settings_app.h" | ||||||
|  | 
 | ||||||
|  | enum PowerSettingsSubmenuIndex { | ||||||
|  |     PowerSettingsSubmenuIndexBatteryInfo, | ||||||
|  |     PowerSettingsSubmenuIndexReboot, | ||||||
|  |     PowerSettingsSubmenuIndexOff, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void power_settings_scene_start_submenu_callback(void* context, uint32_t index) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     PowerSettingsApp* app = context; | ||||||
|  |     view_dispatcher_send_custom_event(app->view_dispatcher, index); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void power_settings_scene_start_on_enter(void* context) { | ||||||
|  |     PowerSettingsApp* app = context; | ||||||
|  |     Submenu* submenu = app->submenu; | ||||||
|  | 
 | ||||||
|  |     submenu_add_item( | ||||||
|  |         submenu, | ||||||
|  |         "Battery info", | ||||||
|  |         PowerSettingsSubmenuIndexBatteryInfo, | ||||||
|  |         power_settings_scene_start_submenu_callback, | ||||||
|  |         app); | ||||||
|  |     submenu_add_item( | ||||||
|  |         submenu, | ||||||
|  |         "Reboot", | ||||||
|  |         PowerSettingsSubmenuIndexReboot, | ||||||
|  |         power_settings_scene_start_submenu_callback, | ||||||
|  |         app); | ||||||
|  |     submenu_add_item( | ||||||
|  |         submenu, | ||||||
|  |         "Power OFF", | ||||||
|  |         PowerSettingsSubmenuIndexOff, | ||||||
|  |         power_settings_scene_start_submenu_callback, | ||||||
|  |         app); | ||||||
|  |     submenu_set_selected_item( | ||||||
|  |         submenu, scene_manager_get_scene_state(app->scene_manager, PowerSettingsAppSceneStart)); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(app->view_dispatcher, PowerSettingsAppViewSubmenu); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool power_settings_scene_start_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     PowerSettingsApp* app = context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == PowerSettingsSubmenuIndexBatteryInfo) { | ||||||
|  |             scene_manager_next_scene(app->scene_manager, PowerSettingsAppSceneBatteryInfo); | ||||||
|  |         } else if(event.event == PowerSettingsSubmenuIndexReboot) { | ||||||
|  |             scene_manager_next_scene(app->scene_manager, PowerSettingsAppSceneReboot); | ||||||
|  |         } else if(event.event == PowerSettingsSubmenuIndexOff) { | ||||||
|  |             scene_manager_next_scene(app->scene_manager, PowerSettingsAppScenePowerOff); | ||||||
|  |         } | ||||||
|  |         scene_manager_set_scene_state(app->scene_manager, PowerSettingsAppSceneStart, event.event); | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void power_settings_scene_start_on_exit(void* context) { | ||||||
|  |     PowerSettingsApp* app = context; | ||||||
|  |     submenu_clean(app->submenu); | ||||||
|  | } | ||||||
							
								
								
									
										126
									
								
								applications/power/power_settings_app/views/battery_info.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,126 @@ | |||||||
|  | #include "battery_info.h" | ||||||
|  | #include <furi.h> | ||||||
|  | #include <gui/elements.h> | ||||||
|  | 
 | ||||||
|  | struct BatteryInfo { | ||||||
|  |     View* view; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void draw_stat(Canvas* canvas, int x, int y, const Icon* icon, char* val) { | ||||||
|  |     canvas_draw_frame(canvas, x - 7, y + 7, 30, 13); | ||||||
|  |     canvas_draw_icon(canvas, x, y, icon); | ||||||
|  |     canvas_set_color(canvas, ColorWhite); | ||||||
|  |     canvas_draw_box(canvas, x - 4, y + 16, 24, 6); | ||||||
|  |     canvas_set_color(canvas, ColorBlack); | ||||||
|  |     canvas_draw_str_aligned(canvas, x + 8, y + 22, AlignCenter, AlignBottom, val); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { | ||||||
|  |     char emote[20] = {}; | ||||||
|  |     char header[20] = {}; | ||||||
|  |     char value[20] = {}; | ||||||
|  | 
 | ||||||
|  |     int32_t drain_current = data->gauge_current * (-1000); | ||||||
|  |     uint32_t charge_current = data->gauge_current * 1000; | ||||||
|  | 
 | ||||||
|  |     // Draw battery
 | ||||||
|  |     canvas_draw_icon(canvas, x, y, &I_BatteryBody_52x28); | ||||||
|  |     if(charge_current > 0) { | ||||||
|  |         canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceCharging_29x14); | ||||||
|  |     } else if(drain_current > 100) { | ||||||
|  |         canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceConfused_29x14); | ||||||
|  |     } else if(data->charge < 10) { | ||||||
|  |         canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNopower_29x14); | ||||||
|  |     } else { | ||||||
|  |         canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNormal_29x14); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Draw bubble
 | ||||||
|  |     elements_bubble(canvas, 53, 0, 71, 39); | ||||||
|  | 
 | ||||||
|  |     // Set text
 | ||||||
|  |     if(charge_current > 0) { | ||||||
|  |         snprintf(emote, sizeof(emote), "%s", "Yummy!"); | ||||||
|  |         snprintf(header, sizeof(header), "%s", "Charging at"); | ||||||
|  |         snprintf( | ||||||
|  |             value, | ||||||
|  |             sizeof(value), | ||||||
|  |             "%ld.%ldV   %ldmA", | ||||||
|  |             (uint32_t)(data->vbus_voltage), | ||||||
|  |             (uint32_t)(data->vbus_voltage * 10) % 10, | ||||||
|  |             charge_current); | ||||||
|  |     } else if(drain_current > 0) { | ||||||
|  |         snprintf(emote, sizeof(emote), "%s", drain_current > 100 ? "Oh no!" : "Om-nom-nom!"); | ||||||
|  |         snprintf(header, sizeof(header), "%s", "Consumption is"); | ||||||
|  |         snprintf( | ||||||
|  |             value, sizeof(value), "%ld %s", drain_current, drain_current > 100 ? "mA!" : "mA"); | ||||||
|  |     } else if(charge_current != 0 || drain_current != 0) { | ||||||
|  |         snprintf(header, 20, "..."); | ||||||
|  |     } else { | ||||||
|  |         snprintf(header, sizeof(header), "Charged!"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     canvas_draw_str_aligned(canvas, 92, y + 3, AlignCenter, AlignCenter, emote); | ||||||
|  |     canvas_draw_str_aligned(canvas, 92, y + 15, AlignCenter, AlignCenter, header); | ||||||
|  |     canvas_draw_str_aligned(canvas, 92, y + 27, AlignCenter, AlignCenter, value); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void battery_info_draw_callback(Canvas* canvas, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     BatteryInfoModel* model = context; | ||||||
|  | 
 | ||||||
|  |     canvas_clear(canvas); | ||||||
|  |     canvas_set_color(canvas, ColorBlack); | ||||||
|  |     draw_battery(canvas, model, 0, 5); | ||||||
|  | 
 | ||||||
|  |     char batt_level[10]; | ||||||
|  |     char temperature[10]; | ||||||
|  |     char voltage[10]; | ||||||
|  |     char health[10]; | ||||||
|  | 
 | ||||||
|  |     snprintf(batt_level, sizeof(batt_level), "%ld%%", (uint32_t)model->charge); | ||||||
|  |     snprintf(temperature, sizeof(temperature), "%ld C", (uint32_t)model->gauge_temperature); | ||||||
|  |     snprintf( | ||||||
|  |         voltage, | ||||||
|  |         sizeof(voltage), | ||||||
|  |         "%ld.%01ld V", | ||||||
|  |         (uint32_t)model->gauge_voltage, | ||||||
|  |         (uint32_t)(model->gauge_voltage * 10) % 10); | ||||||
|  |     snprintf(health, sizeof(health), "%d%%", model->health); | ||||||
|  | 
 | ||||||
|  |     draw_stat(canvas, 8, 42, &I_Battery_16x16, batt_level); | ||||||
|  |     draw_stat(canvas, 40, 42, &I_Temperature_16x16, temperature); | ||||||
|  |     draw_stat(canvas, 72, 42, &I_Voltage_16x16, voltage); | ||||||
|  |     draw_stat(canvas, 104, 42, &I_Health_16x16, health); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | BatteryInfo* battery_info_alloc() { | ||||||
|  |     BatteryInfo* battery_info = furi_alloc(sizeof(BatteryInfo)); | ||||||
|  |     battery_info->view = view_alloc(); | ||||||
|  |     view_set_context(battery_info->view, battery_info); | ||||||
|  |     view_allocate_model(battery_info->view, ViewModelTypeLocking, sizeof(BatteryInfoModel)); | ||||||
|  |     view_set_draw_callback(battery_info->view, battery_info_draw_callback); | ||||||
|  | 
 | ||||||
|  |     return battery_info; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void battery_info_free(BatteryInfo* battery_info) { | ||||||
|  |     furi_assert(battery_info); | ||||||
|  |     view_free(battery_info->view); | ||||||
|  |     free(battery_info); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | View* battery_info_get_view(BatteryInfo* battery_info) { | ||||||
|  |     furi_assert(battery_info); | ||||||
|  |     return battery_info->view; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void battery_info_set_data(BatteryInfo* battery_info, BatteryInfoModel* data) { | ||||||
|  |     furi_assert(battery_info); | ||||||
|  |     furi_assert(data); | ||||||
|  |     with_view_model( | ||||||
|  |         battery_info->view, (BatteryInfoModel * model) { | ||||||
|  |             memcpy(model, data, sizeof(BatteryInfoModel)); | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | } | ||||||
							
								
								
									
										22
									
								
								applications/power/power_settings_app/views/battery_info.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,22 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/view.h> | ||||||
|  | 
 | ||||||
|  | typedef struct BatteryInfo BatteryInfo; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     float vbus_voltage; | ||||||
|  |     float gauge_voltage; | ||||||
|  |     float gauge_current; | ||||||
|  |     float gauge_temperature; | ||||||
|  |     uint8_t charge; | ||||||
|  |     uint8_t health; | ||||||
|  | } BatteryInfoModel; | ||||||
|  | 
 | ||||||
|  | BatteryInfo* battery_info_alloc(); | ||||||
|  | 
 | ||||||
|  | void battery_info_free(BatteryInfo* battery_info); | ||||||
|  | 
 | ||||||
|  | View* battery_info_get_view(BatteryInfo* battery_info); | ||||||
|  | 
 | ||||||
|  | void battery_info_set_data(BatteryInfo* battery_info, BatteryInfoModel* data); | ||||||
| @ -1,131 +0,0 @@ | |||||||
| #include "power_views.h" |  | ||||||
| #include <gui/elements.h> |  | ||||||
| 
 |  | ||||||
| static void draw_stat(Canvas* canvas, int x, int y, const Icon* icon, char* val) { |  | ||||||
|     canvas_draw_frame(canvas, x - 7, y + 7, 30, 13); |  | ||||||
|     canvas_draw_icon(canvas, x, y, icon); |  | ||||||
|     canvas_set_color(canvas, ColorWhite); |  | ||||||
|     canvas_draw_box(canvas, x - 4, y + 16, 24, 6); |  | ||||||
|     canvas_set_color(canvas, ColorBlack); |  | ||||||
|     canvas_draw_str_aligned(canvas, x + 8, y + 22, AlignCenter, AlignBottom, val); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static void draw_battery(Canvas* canvas, PowerInfoModel* data, int x, int y) { |  | ||||||
|     char emote[20]; |  | ||||||
|     char header[20]; |  | ||||||
|     char value[20]; |  | ||||||
| 
 |  | ||||||
|     int32_t drain_current = -data->current_gauge * 1000; |  | ||||||
|     uint32_t charge_current = data->current_gauge * 1000; |  | ||||||
|     // battery
 |  | ||||||
|     canvas_draw_icon(canvas, x, y, &I_BatteryBody_52x28); |  | ||||||
|     if(charge_current > 0) { |  | ||||||
|         canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceCharging_29x14); |  | ||||||
|     } else if(drain_current > 100) { |  | ||||||
|         canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceConfused_29x14); |  | ||||||
|     } else if(data->charge < 10) { |  | ||||||
|         canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNopower_29x14); |  | ||||||
|     } else { |  | ||||||
|         canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNormal_29x14); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     //bubble
 |  | ||||||
|     canvas_draw_frame(canvas, 57, 0, 71, 39); |  | ||||||
|     canvas_draw_line(canvas, 53, 23, 57, 19); |  | ||||||
|     canvas_draw_line(canvas, 53, 23, 57, 27); |  | ||||||
|     canvas_set_color(canvas, ColorWhite); |  | ||||||
|     canvas_draw_box(canvas, 57, 0, 2, 2); |  | ||||||
|     canvas_draw_box(canvas, 57, 37, 2, 2); |  | ||||||
|     canvas_draw_box(canvas, 126, 0, 2, 2); |  | ||||||
|     canvas_draw_box(canvas, 126, 37, 2, 2); |  | ||||||
|     canvas_draw_line(canvas, 57, 20, 57, 26); |  | ||||||
|     canvas_set_color(canvas, ColorBlack); |  | ||||||
|     canvas_draw_dot(canvas, 58, 1); |  | ||||||
|     canvas_draw_dot(canvas, 58, 37); |  | ||||||
|     canvas_draw_dot(canvas, 126, 1); |  | ||||||
|     canvas_draw_dot(canvas, 126, 37); |  | ||||||
| 
 |  | ||||||
|     // text
 |  | ||||||
|     if(charge_current > 0) { |  | ||||||
|         snprintf(emote, sizeof(emote), "%s", "Yummy!"); |  | ||||||
|         snprintf(header, sizeof(header), "%s", "Charging at"); |  | ||||||
|         snprintf( |  | ||||||
|             value, |  | ||||||
|             sizeof(value), |  | ||||||
|             "%ld.%ldV   %ldmA", |  | ||||||
|             (uint32_t)(data->voltage_vbus), |  | ||||||
|             (uint32_t)(data->voltage_vbus * 10) % 10, |  | ||||||
|             charge_current); |  | ||||||
|     } else if(drain_current > 0) { |  | ||||||
|         snprintf(emote, sizeof(emote), "%s", drain_current > 100 ? "Oh no!" : "Om-nom-nom!"); |  | ||||||
|         snprintf(header, sizeof(header), "%s", "Consumption is"); |  | ||||||
|         snprintf( |  | ||||||
|             value, sizeof(value), "%ld %s", drain_current, drain_current > 100 ? "mA!" : "mA"); |  | ||||||
|     } else if(charge_current != 0 || drain_current != 0) { |  | ||||||
|         snprintf(header, 20, "%s", "..."); |  | ||||||
|         memset(value, 0, sizeof(value)); |  | ||||||
|         memset(emote, 0, sizeof(emote)); |  | ||||||
|     } else { |  | ||||||
|         snprintf(header, sizeof(header), "%s", "Charged!"); |  | ||||||
|         memset(value, 0, sizeof(value)); |  | ||||||
|         memset(emote, 0, sizeof(emote)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     canvas_draw_str_aligned(canvas, 92, y + 3, AlignCenter, AlignCenter, emote); |  | ||||||
|     canvas_draw_str_aligned(canvas, 92, y + 15, AlignCenter, AlignCenter, header); |  | ||||||
|     canvas_draw_str_aligned(canvas, 92, y + 27, AlignCenter, AlignCenter, value); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| void power_info_draw_callback(Canvas* canvas, void* context) { |  | ||||||
|     furi_assert(context); |  | ||||||
|     PowerInfoModel* data = context; |  | ||||||
| 
 |  | ||||||
|     canvas_clear(canvas); |  | ||||||
|     canvas_set_color(canvas, ColorBlack); |  | ||||||
|     draw_battery(canvas, data, 0, 5); |  | ||||||
| 
 |  | ||||||
|     char batt_level[10]; |  | ||||||
|     char temperature[10]; |  | ||||||
|     char voltage[10]; |  | ||||||
|     char health[10]; |  | ||||||
| 
 |  | ||||||
|     snprintf(batt_level, sizeof(batt_level), "%ld%s", (uint32_t)data->charge, "%"); |  | ||||||
|     snprintf(temperature, sizeof(temperature), "%ld %s", (uint32_t)data->temperature_gauge, "C"); |  | ||||||
|     snprintf( |  | ||||||
|         voltage, |  | ||||||
|         sizeof(voltage), |  | ||||||
|         "%ld.%01ld V", |  | ||||||
|         (uint32_t)data->voltage_gauge, |  | ||||||
|         (uint32_t)(data->voltage_gauge * 10) % 10); |  | ||||||
|     snprintf(health, sizeof(health), "%d%s", data->health, "%"); |  | ||||||
| 
 |  | ||||||
|     draw_stat(canvas, 8, 42, &I_Battery_16x16, batt_level); |  | ||||||
|     draw_stat(canvas, 40, 42, &I_Temperature_16x16, temperature); |  | ||||||
|     draw_stat(canvas, 72, 42, &I_Voltage_16x16, voltage); |  | ||||||
|     draw_stat(canvas, 104, 42, &I_Health_16x16, health); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void power_off_draw_callback(Canvas* canvas, void* context) { |  | ||||||
|     furi_assert(context); |  | ||||||
|     PowerOffModel* model = context; |  | ||||||
| 
 |  | ||||||
|     canvas_set_color(canvas, ColorBlack); |  | ||||||
|     canvas_set_font(canvas, FontPrimary); |  | ||||||
|     canvas_draw_str(canvas, 2, 15, "!!! Low Battery !!!"); |  | ||||||
| 
 |  | ||||||
|     char buffer[64]; |  | ||||||
|     canvas_set_font(canvas, FontSecondary); |  | ||||||
|     canvas_draw_str(canvas, 5, 30, "Connect to charger"); |  | ||||||
|     snprintf( |  | ||||||
|         buffer, |  | ||||||
|         64, |  | ||||||
|         "Or poweroff in %lds", |  | ||||||
|         (model->poweroff_tick - osKernelGetTickCount()) / osKernelGetTickFreq()); |  | ||||||
|     canvas_draw_str(canvas, 5, 42, buffer); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void power_disconnect_draw_callback(Canvas* canvas, void* context) { |  | ||||||
|     canvas_set_font(canvas, FontPrimary); |  | ||||||
|     elements_multiline_text_aligned( |  | ||||||
|         canvas, 64, 32, AlignCenter, AlignCenter, "It's now safe to turn off\nyour flipper"); |  | ||||||
| } |  | ||||||
| @ -1,38 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include <stdint.h> |  | ||||||
| #include <stdbool.h> |  | ||||||
| #include <furi.h> |  | ||||||
| #include <gui/canvas.h> |  | ||||||
| #include <gui/view.h> |  | ||||||
| 
 |  | ||||||
| typedef enum { PowerViewInfo, PowerViewDialog, PowerViewOff, PowerViewDisconnect } PowerView; |  | ||||||
| 
 |  | ||||||
| typedef struct { |  | ||||||
|     float current_charger; |  | ||||||
|     float current_gauge; |  | ||||||
| 
 |  | ||||||
|     float voltage_charger; |  | ||||||
|     float voltage_gauge; |  | ||||||
|     float voltage_vbus; |  | ||||||
| 
 |  | ||||||
|     uint32_t capacity_remaining; |  | ||||||
|     uint32_t capacity_full; |  | ||||||
| 
 |  | ||||||
|     float temperature_charger; |  | ||||||
|     float temperature_gauge; |  | ||||||
| 
 |  | ||||||
|     uint8_t charge; |  | ||||||
|     uint8_t health; |  | ||||||
| } PowerInfoModel; |  | ||||||
| 
 |  | ||||||
| typedef struct { |  | ||||||
|     uint32_t poweroff_tick; |  | ||||||
|     bool battery_low; |  | ||||||
| } PowerOffModel; |  | ||||||
| 
 |  | ||||||
| void power_info_draw_callback(Canvas* canvas, void* context); |  | ||||||
| 
 |  | ||||||
| void power_off_draw_callback(Canvas* canvas, void* context); |  | ||||||
| 
 |  | ||||||
| void power_disconnect_draw_callback(Canvas* canvas, void* context); |  | ||||||
| @ -5,64 +5,64 @@ extern const Icon I_Certification1_103x23; | |||||||
| extern const Icon I_Certification2_119x30; | extern const Icon I_Certification2_119x30; | ||||||
| extern const Icon A_WatchingTV_128x64; | extern const Icon A_WatchingTV_128x64; | ||||||
| extern const Icon A_Wink_128x64; | extern const Icon A_Wink_128x64; | ||||||
| extern const Icon I_125_10px; |  | ||||||
| extern const Icon I_ble_10px; |  | ||||||
| extern const Icon I_dir_10px; | extern const Icon I_dir_10px; | ||||||
| extern const Icon I_ibutt_10px; |  | ||||||
| extern const Icon I_ir_10px; |  | ||||||
| extern const Icon I_Nfc_10px; | extern const Icon I_Nfc_10px; | ||||||
| extern const Icon I_sub1_10px; | extern const Icon I_sub1_10px; | ||||||
|  | extern const Icon I_ir_10px; | ||||||
|  | extern const Icon I_ibutt_10px; | ||||||
| extern const Icon I_unknown_10px; | extern const Icon I_unknown_10px; | ||||||
| extern const Icon I_ButtonCenter_7x7; | extern const Icon I_ble_10px; | ||||||
| extern const Icon I_ButtonLeftSmall_3x5; | extern const Icon I_125_10px; | ||||||
| extern const Icon I_ButtonLeft_4x7; |  | ||||||
| extern const Icon I_ButtonRightSmall_3x5; | extern const Icon I_ButtonRightSmall_3x5; | ||||||
| extern const Icon I_ButtonRight_4x7; | extern const Icon I_ButtonLeft_4x7; | ||||||
|  | extern const Icon I_ButtonLeftSmall_3x5; | ||||||
| extern const Icon I_DFU_128x50; | extern const Icon I_DFU_128x50; | ||||||
| extern const Icon I_Warning_30x23; | extern const Icon I_Warning_30x23; | ||||||
| extern const Icon I_DolphinFirstStart0_70x53; | extern const Icon I_ButtonRight_4x7; | ||||||
| extern const Icon I_DolphinFirstStart1_59x53; | extern const Icon I_ButtonCenter_7x7; | ||||||
| extern const Icon I_DolphinFirstStart2_59x51; |  | ||||||
| extern const Icon I_DolphinFirstStart3_57x48; |  | ||||||
| extern const Icon I_DolphinFirstStart4_67x53; |  | ||||||
| extern const Icon I_DolphinFirstStart5_54x49; |  | ||||||
| extern const Icon I_DolphinFirstStart6_58x54; |  | ||||||
| extern const Icon I_DolphinFirstStart7_61x51; |  | ||||||
| extern const Icon I_DolphinFirstStart8_56x51; |  | ||||||
| extern const Icon I_DolphinOkay_41x43; | extern const Icon I_DolphinOkay_41x43; | ||||||
|  | extern const Icon I_DolphinFirstStart4_67x53; | ||||||
|  | extern const Icon I_DolphinFirstStart2_59x51; | ||||||
|  | extern const Icon I_DolphinFirstStart5_54x49; | ||||||
|  | extern const Icon I_DolphinFirstStart0_70x53; | ||||||
|  | extern const Icon I_DolphinFirstStart6_58x54; | ||||||
|  | extern const Icon I_DolphinFirstStart1_59x53; | ||||||
|  | extern const Icon I_DolphinFirstStart8_56x51; | ||||||
|  | extern const Icon I_DolphinFirstStart7_61x51; | ||||||
| extern const Icon I_Flipper_young_80x60; | extern const Icon I_Flipper_young_80x60; | ||||||
| extern const Icon I_DoorLeft_70x55; | extern const Icon I_DolphinFirstStart3_57x48; | ||||||
|  | extern const Icon I_PassportBottom_128x17; | ||||||
| extern const Icon I_DoorLeft_8x56; | extern const Icon I_DoorLeft_8x56; | ||||||
| extern const Icon I_DoorLocked_10x56; | extern const Icon I_DoorLocked_10x56; | ||||||
| extern const Icon I_DoorRight_70x55; |  | ||||||
| extern const Icon I_DoorRight_8x56; | extern const Icon I_DoorRight_8x56; | ||||||
| extern const Icon I_LockPopup_100x49; | extern const Icon I_DoorLeft_70x55; | ||||||
| extern const Icon I_PassportBottom_128x17; |  | ||||||
| extern const Icon I_PassportLeft_6x47; | extern const Icon I_PassportLeft_6x47; | ||||||
| extern const Icon I_Back_15x10; | extern const Icon I_DoorRight_70x55; | ||||||
|  | extern const Icon I_LockPopup_100x49; | ||||||
|  | extern const Icon I_Mute_25x27; | ||||||
|  | extern const Icon I_IrdaArrowUp_4x8; | ||||||
|  | extern const Icon I_Up_hvr_25x27; | ||||||
|  | extern const Icon I_Mute_hvr_25x27; | ||||||
|  | extern const Icon I_Vol_down_25x27; | ||||||
| extern const Icon I_Down_25x27; | extern const Icon I_Down_25x27; | ||||||
|  | extern const Icon I_Power_hvr_25x27; | ||||||
|  | extern const Icon I_IrdaLearnShort_128x31; | ||||||
|  | extern const Icon I_IrdaArrowDown_4x8; | ||||||
|  | extern const Icon I_Vol_down_hvr_25x27; | ||||||
|  | extern const Icon I_IrdaLearn_128x64; | ||||||
| extern const Icon I_Down_hvr_25x27; | extern const Icon I_Down_hvr_25x27; | ||||||
| extern const Icon I_Fill_marker_7x7; | extern const Icon I_Fill_marker_7x7; | ||||||
| extern const Icon I_IrdaArrowDown_4x8; |  | ||||||
| extern const Icon I_IrdaArrowUp_4x8; |  | ||||||
| extern const Icon I_IrdaLearnShort_128x31; |  | ||||||
| extern const Icon I_IrdaLearn_128x64; |  | ||||||
| extern const Icon I_IrdaSendShort_128x34; |  | ||||||
| extern const Icon I_IrdaSend_128x64; |  | ||||||
| extern const Icon I_Mute_25x27; |  | ||||||
| extern const Icon I_Mute_hvr_25x27; |  | ||||||
| extern const Icon I_Power_25x27; | extern const Icon I_Power_25x27; | ||||||
| extern const Icon I_Power_hvr_25x27; |  | ||||||
| extern const Icon I_Up_25x27; |  | ||||||
| extern const Icon I_Up_hvr_25x27; |  | ||||||
| extern const Icon I_Vol_down_25x27; |  | ||||||
| extern const Icon I_Vol_down_hvr_25x27; |  | ||||||
| extern const Icon I_Vol_up_25x27; | extern const Icon I_Vol_up_25x27; | ||||||
|  | extern const Icon I_Up_25x27; | ||||||
|  | extern const Icon I_Back_15x10; | ||||||
|  | extern const Icon I_IrdaSend_128x64; | ||||||
|  | extern const Icon I_IrdaSendShort_128x34; | ||||||
| extern const Icon I_Vol_up_hvr_25x27; | extern const Icon I_Vol_up_hvr_25x27; | ||||||
| extern const Icon I_KeyBackspaceSelected_16x9; |  | ||||||
| extern const Icon I_KeyBackspace_16x9; |  | ||||||
| extern const Icon I_KeySaveSelected_24x11; |  | ||||||
| extern const Icon I_KeySave_24x11; | extern const Icon I_KeySave_24x11; | ||||||
|  | extern const Icon I_KeyBackspaceSelected_16x9; | ||||||
|  | extern const Icon I_KeySaveSelected_24x11; | ||||||
|  | extern const Icon I_KeyBackspace_16x9; | ||||||
| extern const Icon A_125khz_14; | extern const Icon A_125khz_14; | ||||||
| extern const Icon A_Bluetooth_14; | extern const Icon A_Bluetooth_14; | ||||||
| extern const Icon A_Debug_14; | extern const Icon A_Debug_14; | ||||||
| @ -81,41 +81,42 @@ extern const Icon A_U2F_14; | |||||||
| extern const Icon A_iButton_14; | extern const Icon A_iButton_14; | ||||||
| extern const Icon I_Detailed_chip_17x13; | extern const Icon I_Detailed_chip_17x13; | ||||||
| extern const Icon I_Medium_chip_22x21; | extern const Icon I_Medium_chip_22x21; | ||||||
| extern const Icon I_BatteryBody_52x28; | extern const Icon I_Health_16x16; | ||||||
| extern const Icon I_Battery_16x16; |  | ||||||
| extern const Icon I_FaceCharging_29x14; | extern const Icon I_FaceCharging_29x14; | ||||||
| extern const Icon I_FaceConfused_29x14; | extern const Icon I_BatteryBody_52x28; | ||||||
|  | extern const Icon I_Voltage_16x16; | ||||||
|  | extern const Icon I_Temperature_16x16; | ||||||
| extern const Icon I_FaceNopower_29x14; | extern const Icon I_FaceNopower_29x14; | ||||||
| extern const Icon I_FaceNormal_29x14; | extern const Icon I_FaceNormal_29x14; | ||||||
| extern const Icon I_Health_16x16; | extern const Icon I_Battery_16x16; | ||||||
| extern const Icon I_Temperature_16x16; | extern const Icon I_FaceConfused_29x14; | ||||||
| extern const Icon I_Voltage_16x16; |  | ||||||
| extern const Icon I_RFIDBigChip_37x36; |  | ||||||
| extern const Icon I_RFIDDolphinReceive_97x61; |  | ||||||
| extern const Icon I_RFIDDolphinSend_97x61; |  | ||||||
| extern const Icon I_RFIDDolphinSuccess_108x57; | extern const Icon I_RFIDDolphinSuccess_108x57; | ||||||
| extern const Icon I_SDError_43x35; | extern const Icon I_RFIDBigChip_37x36; | ||||||
|  | extern const Icon I_RFIDDolphinSend_97x61; | ||||||
|  | extern const Icon I_RFIDDolphinReceive_97x61; | ||||||
| extern const Icon I_SDQuestion_35x43; | extern const Icon I_SDQuestion_35x43; | ||||||
| extern const Icon I_Background_128x11; | extern const Icon I_SDError_43x35; | ||||||
| extern const Icon I_Background_128x8; | extern const Icon I_Cry_dolph_55x52; | ||||||
| extern const Icon I_BadUsb_9x8; | extern const Icon I_BadUsb_9x8; | ||||||
| extern const Icon I_Battery_19x8; |  | ||||||
| extern const Icon I_Battery_26x8; |  | ||||||
| extern const Icon I_Bluetooth_5x8; |  | ||||||
| extern const Icon I_Lock_8x8; |  | ||||||
| extern const Icon I_PlaceholderL_11x13; |  | ||||||
| extern const Icon I_PlaceholderR_30x13; | extern const Icon I_PlaceholderR_30x13; | ||||||
| extern const Icon I_SDcardFail_11x8; | extern const Icon I_Background_128x8; | ||||||
|  | extern const Icon I_Lock_8x8; | ||||||
|  | extern const Icon I_Battery_26x8; | ||||||
|  | extern const Icon I_PlaceholderL_11x13; | ||||||
|  | extern const Icon I_Battery_19x8; | ||||||
| extern const Icon I_SDcardMounted_11x8; | extern const Icon I_SDcardMounted_11x8; | ||||||
|  | extern const Icon I_SDcardFail_11x8; | ||||||
| extern const Icon I_USBConnected_15x8; | extern const Icon I_USBConnected_15x8; | ||||||
| extern const Icon I_Lock_7x8; | extern const Icon I_Bluetooth_5x8; | ||||||
| extern const Icon I_Quest_7x8; | extern const Icon I_Background_128x11; | ||||||
| extern const Icon I_Scanning_123x52; | extern const Icon I_Scanning_123x52; | ||||||
|  | extern const Icon I_Quest_7x8; | ||||||
| extern const Icon I_Unlock_7x8; | extern const Icon I_Unlock_7x8; | ||||||
| extern const Icon I_DolphinExcited_64x63; | extern const Icon I_Lock_7x8; | ||||||
| extern const Icon I_DolphinMafia_115x62; | extern const Icon I_DolphinMafia_115x62; | ||||||
| extern const Icon I_DolphinNice_96x59; | extern const Icon I_DolphinExcited_64x63; | ||||||
| extern const Icon I_DolphinWait_61x59; |  | ||||||
| extern const Icon I_iButtonDolphinSuccess_109x60; | extern const Icon I_iButtonDolphinSuccess_109x60; | ||||||
| extern const Icon I_iButtonDolphinVerySuccess_108x52; | extern const Icon I_iButtonDolphinVerySuccess_108x52; | ||||||
| extern const Icon I_iButtonKey_49x44; | extern const Icon I_iButtonKey_49x44; | ||||||
|  | extern const Icon I_DolphinNice_96x59; | ||||||
|  | extern const Icon I_DolphinWait_61x59; | ||||||
|  | |||||||
							
								
								
									
										
											BIN
										
									
								
								assets/icons/Settings/Cry_dolph_55x52.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.8 KiB | 
| Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB | 
| Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB | 
| Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB | 
 gornekich
						gornekich