diff options
author | gornekich <n.gorbadey@gmail.com> | 2021-09-24 19:28:02 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-24 19:28:02 +0300 |
commit | d3b58f732f71fc30819ee674bf5a05fdc3222c82 (patch) | |
tree | 2e7a07a9e459cb21861b69ebc91abfeb741fe4b9 /applications/power | |
parent | c64052b4912f56554835504c3c5f112380ce20dc (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')
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 |