Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ClusterM/flipperzero-firmware.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgornekich <n.gorbadey@gmail.com>2021-09-24 19:28:02 +0300
committerGitHub <noreply@github.com>2021-09-24 19:28:02 +0300
commitd3b58f732f71fc30819ee674bf5a05fdc3222c82 (patch)
tree2e7a07a9e459cb21861b69ebc91abfeb741fe4b9 /applications/power
parentc64052b4912f56554835504c3c5f112380ce20dc (diff)
[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>
Diffstat (limited to 'applications/power')
-rw-r--r--applications/power/power.c278
-rw-r--r--applications/power/power.h19
-rw-r--r--applications/power/power_cli.c38
-rw-r--r--applications/power/power_cli.h5
-rwxr-xr-xapplications/power/power_service/power.c179
-rw-r--r--applications/power/power_service/power.h65
-rw-r--r--applications/power/power_service/power_api.c31
-rwxr-xr-xapplications/power/power_service/power_i.h37
-rwxr-xr-xapplications/power/power_service/views/power_off.c57
-rw-r--r--applications/power/power_service/views/power_off.h13
-rwxr-xr-xapplications/power/power_settings_app/power_settings_app.c82
-rw-r--r--applications/power/power_settings_app/power_settings_app.h31
-rwxr-xr-xapplications/power/power_settings_app/scenes/power_settinfs_scene_usb_disconnect.c17
-rw-r--r--applications/power/power_settings_app/scenes/power_settings_scene.c30
-rw-r--r--applications/power/power_settings_app/scenes/power_settings_scene.h29
-rwxr-xr-xapplications/power/power_settings_app/scenes/power_settings_scene_battery_info.c34
-rwxr-xr-xapplications/power/power_settings_app/scenes/power_settings_scene_config.h5
-rwxr-xr-xapplications/power/power_settings_app/scenes/power_settings_scene_power_off.c48
-rwxr-xr-xapplications/power/power_settings_app/scenes/power_settings_scene_reboot.c52
-rwxr-xr-xapplications/power/power_settings_app/scenes/power_settings_scene_start.c64
-rw-r--r--applications/power/power_settings_app/views/battery_info.c126
-rw-r--r--applications/power/power_settings_app/views/battery_info.h22
-rw-r--r--applications/power/power_views.c131
-rw-r--r--applications/power/power_views.h38
24 files changed, 945 insertions, 486 deletions
diff --git a/applications/power/power.c b/applications/power/power.c
deleted file mode 100644
index 6fc4077a..00000000
--- a/applications/power/power.c
+++ /dev/null
@@ -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;
-}
diff --git a/applications/power/power.h b/applications/power/power.h
deleted file mode 100644
index 9829eb4f..00000000
--- a/applications/power/power.h
+++ /dev/null
@@ -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);
diff --git a/applications/power/power_cli.c b/applications/power/power_cli.c
index c39cafde..6ac955e6 100644
--- a/applications/power/power_cli.c
+++ b/applications/power/power_cli.c
@@ -1,29 +1,31 @@
#include "power_cli.h"
+
+#include <power/power_service/power.h>
+#include <cli/cli.h>
#include <furi-hal.h>
void power_cli_poweroff(Cli* cli, string_t args, void* context) {
- Power* power = context;
- power_off(power);
+ power_off();
+ 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) {
- Power* power = context;
- power_reboot(power, PowerBootModeNormal);
+ power_reboot(PowerBootModeNormal);
}
void power_cli_dfu(Cli* cli, string_t args, void* context) {
- Power* power = context;
- power_reboot(power, PowerBootModeDfu);
+ power_reboot(PowerBootModeDfu);
}
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");
char c = cli_getc(cli);
if(c == 'y' || c == 'Y') {
printf("Data will be wiped after reboot.\r\n");
furi_hal_boot_set_flags(FuriHalBootFlagFactoryReset);
- power_reboot(power, PowerBootModeNormal);
+ power_reboot(PowerBootModeNormal);
} else {
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) {
- cli_add_command(cli, "poweroff", CliCommandFlagParallelSafe, power_cli_poweroff, power);
- cli_add_command(cli, "reboot", CliCommandFlagParallelSafe, power_cli_reboot, power);
+void power_cli_init() {
+ Cli* cli = furi_record_open("cli");
+
+ cli_add_command(cli, "poweroff", CliCommandFlagParallelSafe, power_cli_poweroff, NULL);
+ cli_add_command(cli, "reboot", CliCommandFlagParallelSafe, power_cli_reboot, NULL);
cli_add_command(
- cli, "factory_reset", CliCommandFlagParallelSafe, power_cli_factory_reset, power);
- cli_add_command(cli, "dfu", CliCommandFlagParallelSafe, power_cli_dfu, power);
- cli_add_command(cli, "power_info", CliCommandFlagParallelSafe, power_cli_info, power);
- cli_add_command(cli, "power_otg", CliCommandFlagParallelSafe, power_cli_otg, power);
- cli_add_command(cli, "power_ext", CliCommandFlagParallelSafe, power_cli_ext, power);
+ cli, "factory_reset", CliCommandFlagParallelSafe, power_cli_factory_reset, NULL);
+ cli_add_command(cli, "dfu", CliCommandFlagParallelSafe, power_cli_dfu, NULL);
+ cli_add_command(cli, "power_info", CliCommandFlagParallelSafe, power_cli_info, NULL);
+ cli_add_command(cli, "power_otg", CliCommandFlagParallelSafe, power_cli_otg, NULL);
+ cli_add_command(cli, "power_ext", CliCommandFlagParallelSafe, power_cli_ext, NULL);
+
+ furi_record_close("cli");
}
diff --git a/applications/power/power_cli.h b/applications/power/power_cli.h
index 5d8f208c..f92db05f 100644
--- a/applications/power/power_cli.h
+++ b/applications/power/power_cli.h
@@ -1,6 +1,3 @@
#pragma once
-#include <cli/cli.h>
-#include "power.h"
-
-void power_cli_init(Cli* cli, Power* power); \ No newline at end of file
+void power_cli_init();
diff --git a/applications/power/power_service/power.c b/applications/power/power_service/power.c
new file mode 100755
index 00000000..db791a20
--- /dev/null
+++ b/applications/power/power_service/power.c
@@ -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;
+}
diff --git a/applications/power/power_service/power.h b/applications/power/power_service/power.h
new file mode 100644
index 00000000..d3dec410
--- /dev/null
+++ b/applications/power/power_service/power.h
@@ -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);
diff --git a/applications/power/power_service/power_api.c b/applications/power/power_service/power_api.c
new file mode 100644
index 00000000..388466d6
--- /dev/null
+++ b/applications/power/power_service/power_api.c
@@ -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;
+}
diff --git a/applications/power/power_service/power_i.h b/applications/power/power_service/power_i.h
new file mode 100755
index 00000000..35bba2d8
--- /dev/null
+++ b/applications/power/power_service/power_i.h
@@ -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;
diff --git a/applications/power/power_service/views/power_off.c b/applications/power/power_service/views/power_off.c
new file mode 100755
index 00000000..73abc16b
--- /dev/null
+++ b/applications/power/power_service/views/power_off.c
@@ -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;
+ });
+}
diff --git a/applications/power/power_service/views/power_off.h b/applications/power/power_service/views/power_off.h
new file mode 100644
index 00000000..2e2e91f7
--- /dev/null
+++ b/applications/power/power_service/views/power_off.h
@@ -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);
diff --git a/applications/power/power_settings_app/power_settings_app.c b/applications/power/power_settings_app/power_settings_app.c
new file mode 100755
index 00000000..69adcc9e
--- /dev/null
+++ b/applications/power/power_settings_app/power_settings_app.c
@@ -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;
+}
diff --git a/applications/power/power_settings_app/power_settings_app.h b/applications/power/power_settings_app/power_settings_app.h
new file mode 100644
index 00000000..8429b54b
--- /dev/null
+++ b/applications/power/power_settings_app/power_settings_app.h
@@ -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;
diff --git a/applications/power/power_settings_app/scenes/power_settinfs_scene_usb_disconnect.c b/applications/power/power_settings_app/scenes/power_settinfs_scene_usb_disconnect.c
new file mode 100755
index 00000000..1a76edcb
--- /dev/null
+++ b/applications/power/power_settings_app/scenes/power_settinfs_scene_usb_disconnect.c
@@ -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) {
+}
diff --git a/applications/power/power_settings_app/scenes/power_settings_scene.c b/applications/power/power_settings_app/scenes/power_settings_scene.c
new file mode 100644
index 00000000..306bdbb6
--- /dev/null
+++ b/applications/power/power_settings_app/scenes/power_settings_scene.c
@@ -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,
+};
diff --git a/applications/power/power_settings_app/scenes/power_settings_scene.h b/applications/power/power_settings_app/scenes/power_settings_scene.h
new file mode 100644
index 00000000..ebc0e3d0
--- /dev/null
+++ b/applications/power/power_settings_app/scenes/power_settings_scene.h
@@ -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
diff --git a/applications/power/power_settings_app/scenes/power_settings_scene_battery_info.c b/applications/power/power_settings_app/scenes/power_settings_scene_battery_info.c
new file mode 100755
index 00000000..4dd04f8c
--- /dev/null
+++ b/applications/power/power_settings_app/scenes/power_settings_scene_battery_info.c
@@ -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) {
+}
diff --git a/applications/power/power_settings_app/scenes/power_settings_scene_config.h b/applications/power/power_settings_app/scenes/power_settings_scene_config.h
new file mode 100755
index 00000000..569e907a
--- /dev/null
+++ b/applications/power/power_settings_app/scenes/power_settings_scene_config.h
@@ -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)
diff --git a/applications/power/power_settings_app/scenes/power_settings_scene_power_off.c b/applications/power/power_settings_app/scenes/power_settings_scene_power_off.c
new file mode 100755
index 00000000..84a7910a
--- /dev/null
+++ b/applications/power/power_settings_app/scenes/power_settings_scene_power_off.c
@@ -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);
+}
diff --git a/applications/power/power_settings_app/scenes/power_settings_scene_reboot.c b/applications/power/power_settings_app/scenes/power_settings_scene_reboot.c
new file mode 100755
index 00000000..d6ec2d04
--- /dev/null
+++ b/applications/power/power_settings_app/scenes/power_settings_scene_reboot.c
@@ -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);
+}
diff --git a/applications/power/power_settings_app/scenes/power_settings_scene_start.c b/applications/power/power_settings_app/scenes/power_settings_scene_start.c
new file mode 100755
index 00000000..c2ae256f
--- /dev/null
+++ b/applications/power/power_settings_app/scenes/power_settings_scene_start.c
@@ -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);
+}
diff --git a/applications/power/power_settings_app/views/battery_info.c b/applications/power/power_settings_app/views/battery_info.c
new file mode 100644
index 00000000..599aefee
--- /dev/null
+++ b/applications/power/power_settings_app/views/battery_info.c
@@ -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;
+ });
+}
diff --git a/applications/power/power_settings_app/views/battery_info.h b/applications/power/power_settings_app/views/battery_info.h
new file mode 100644
index 00000000..e62aa44f
--- /dev/null
+++ b/applications/power/power_settings_app/views/battery_info.h
@@ -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);
diff --git a/applications/power/power_views.c b/applications/power/power_views.c
deleted file mode 100644
index c6248880..00000000
--- a/applications/power/power_views.c
+++ /dev/null
@@ -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");
-}
diff --git a/applications/power/power_views.h b/applications/power/power_views.h
deleted file mode 100644
index 2aaa114f..00000000
--- a/applications/power/power_views.h
+++ /dev/null
@@ -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); \ No newline at end of file