diff --git a/applications/main/momentum_app/scenes/momentum_app_scene_interface_statusbar.c b/applications/main/momentum_app/scenes/momentum_app_scene_interface_statusbar.c index 555e3c954..6035b12c6 100644 --- a/applications/main/momentum_app/scenes/momentum_app_scene_interface_statusbar.c +++ b/applications/main/momentum_app/scenes/momentum_app_scene_interface_statusbar.c @@ -28,8 +28,6 @@ static void momentum_app_scene_interface_statusbar_battery_icon_changed(Variable variable_item_set_current_value_text(item, battery_icon_names[index]); momentum_settings.battery_icon = index; app->save_settings = true; - power_set_battery_icon_enabled(furi_record_open(RECORD_POWER), index != BatteryIconOff); - furi_record_close(RECORD_POWER); } static void momentum_app_scene_interface_statusbar_statusbar_clock_changed(VariableItem* item) { diff --git a/applications/services/power/power_service/power.c b/applications/services/power/power_service/power.c index 74217c354..a15728bc1 100644 --- a/applications/services/power/power_service/power.c +++ b/applications/services/power/power_service/power.c @@ -4,17 +4,18 @@ #include #include +#include +#include + #define TAG "Power" -#define POWER_OFF_TIMEOUT 90 +#define POWER_OFF_TIMEOUT_S (90U) +#define POWER_POLL_PERIOD_MS (1000UL) -void power_set_battery_icon_enabled(Power* power, bool is_enabled) { - furi_assert(power); +#define POWER_VBUS_LOW_THRESHOLD (4.0f) +#define POWER_HEALTH_LOW_THRESHOLD (70U) - view_port_enabled_set(power->battery_view_port, is_enabled); -} - -void power_draw_battery_callback(Canvas* canvas, void* context) { +static void power_draw_battery_callback(Canvas* canvas, void* context) { furi_assert(context); Power* power = context; BatteryIcon battery_icon = momentum_settings.battery_icon; @@ -237,6 +238,7 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { } canvas_set_bitmap_mode(canvas, false); } + } else { canvas_draw_box(canvas, 8, 3, 8, 2); } @@ -246,12 +248,12 @@ 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_25x8)); 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; } static void power_start_auto_shutdown_timer(Power* power) { - furi_timer_start(power->auto_shutdown_timer, furi_ms_to_ticks(power->shutdown_idle_delay_ms)); + furi_timer_start( + power->auto_shutdown_timer, furi_ms_to_ticks(power->settings.shutdown_idle_delay_ms)); } static void power_stop_auto_shutdown_timer(Power* power) { @@ -271,7 +273,7 @@ static void power_auto_shutdown_callback(const void* value, void* context) { } static void power_auto_shutdown_arm(Power* power) { - if(power->shutdown_idle_delay_ms) { + if(power->settings.shutdown_idle_delay_ms) { if(power->input_events_subscription == NULL) { power->input_events_subscription = furi_pubsub_subscribe( power->input_events_pubsub, power_auto_shutdown_callback, power); @@ -302,14 +304,31 @@ static void power_loader_callback(const void* message, void* context) { const LoaderEvent* event = message; if(event->type == LoaderEventTypeApplicationBeforeLoad) { + power->app_running = true; power_auto_shutdown_inhibit(power); } else if( event->type == LoaderEventTypeApplicationLoadFailed || event->type == LoaderEventTypeApplicationStopped) { + power->app_running = false; power_auto_shutdown_arm(power); } } +static void power_storage_callback(const void* message, void* context) { + furi_assert(context); + Power* power = context; + const StorageEvent* event = message; + + if(event->type == StorageEventTypeCardMount) { + PowerMessage msg = { + .type = PowerMessageTypeReloadSettings, + }; + + furi_check( + furi_message_queue_put(power->message_queue, &msg, FuriWaitForever) == FuriStatusOk); + } +} + static void power_auto_shutdown_timer_callback(void* context) { furi_assert(context); Power* power = context; @@ -317,121 +336,66 @@ static void power_auto_shutdown_timer_callback(void* context) { power_off(power); } -static void power_shutdown_time_changed_callback(const void* event, void* context) { - furi_assert(event); - furi_assert(context); - Power* power = context; - power->shutdown_idle_delay_ms = *(uint32_t*)event; - if(power->shutdown_idle_delay_ms) { +static void power_apply_settings(Power* power) { + if(power->settings.shutdown_idle_delay_ms && !power->app_running) { power_auto_shutdown_arm(power); } else if(power_is_running_auto_shutdown_timer(power)) { power_auto_shutdown_inhibit(power); } } -Power* power_alloc(void) { - Power* power = malloc(sizeof(Power)); +static bool power_update_info(Power* power) { + const PowerInfo info = { + .is_charging = furi_hal_power_is_charging(), + .gauge_is_ok = furi_hal_power_gauge_is_ok(), + .is_shutdown_requested = furi_hal_power_is_shutdown_requested(), + .charge = furi_hal_power_get_pct(), + .health = furi_hal_power_get_bat_health_pct(), + .capacity_remaining = furi_hal_power_get_battery_remaining_capacity(), + .capacity_full = furi_hal_power_get_battery_full_capacity(), + .current_charger = furi_hal_power_get_battery_current(FuriHalPowerICCharger), + .current_gauge = furi_hal_power_get_battery_current(FuriHalPowerICFuelGauge), + .voltage_battery_charge_limit = furi_hal_power_get_battery_charge_voltage_limit(), + .voltage_charger = furi_hal_power_get_battery_voltage(FuriHalPowerICCharger), + .voltage_gauge = furi_hal_power_get_battery_voltage(FuriHalPowerICFuelGauge), + .voltage_vbus = furi_hal_power_get_usb_voltage(), + .temperature_charger = furi_hal_power_get_battery_temperature(FuriHalPowerICCharger), + .temperature_gauge = furi_hal_power_get_battery_temperature(FuriHalPowerICFuelGauge), + }; - // Records - power->notification = furi_record_open(RECORD_NOTIFICATION); - power->gui = furi_record_open(RECORD_GUI); - - // Pubsub - power->event_pubsub = furi_pubsub_alloc(); - power->settings_events = furi_pubsub_alloc(); - power->loader = furi_record_open(RECORD_LOADER); - power->input_events_pubsub = furi_record_open(RECORD_INPUT_EVENTS); - power->input_events_subscription = NULL; - power->ascii_events_pubsub = furi_record_open(RECORD_ASCII_EVENTS); - power->ascii_events_subscription = NULL; - power->app_start_stop_subscription = - furi_pubsub_subscribe(loader_get_pubsub(power->loader), power_loader_callback, power); - power->settings_events_subscription = - furi_pubsub_subscribe(power->settings_events, power_shutdown_time_changed_callback, power); - - // State initialization - power->state = PowerStateNotCharging; - power->battery_low = false; - power->power_off_timeout = POWER_OFF_TIMEOUT; - power->api_mtx = furi_mutex_alloc(FuriMutexTypeNormal); - - // 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)); - power->power_unplug_usb = power_unplug_usb_alloc(); - view_dispatcher_add_view( - power->view_dispatcher, - PowerViewUnplugUsb, - power_unplug_usb_get_view(power->power_unplug_usb)); - view_dispatcher_attach_to_gui(power->view_dispatcher, power->gui, ViewDispatcherTypeDesktop); - - // Battery view port - power->battery_view_port = power_battery_view_port_alloc(power); - power_set_battery_icon_enabled(power, momentum_settings.battery_icon != BatteryIconOff); - power->show_low_bat_level_message = true; - - //Auto shutdown timer - power->auto_shutdown_timer = - furi_timer_alloc(power_auto_shutdown_timer_callback, FuriTimerTypeOnce, power); - - return power; + const bool need_refresh = (power->info.charge != info.charge) || + (power->info.is_charging != info.is_charging); + power->info = info; + return need_refresh; } static void power_check_charging_state(Power* power) { + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + if(furi_hal_power_is_charging()) { if((power->info.charge == 100) || (furi_hal_power_is_charging_done())) { if(power->state != PowerStateCharged) { - notification_internal_message(power->notification, &sequence_charged); + notification_internal_message(notification, &sequence_charged); power->state = PowerStateCharged; power->event.type = PowerEventTypeFullyCharged; furi_pubsub_publish(power->event_pubsub, &power->event); } - } else { - if(power->state != PowerStateCharging) { - notification_internal_message(power->notification, &sequence_charging); - power->state = PowerStateCharging; - power->event.type = PowerEventTypeStartCharging; - furi_pubsub_publish(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; + + } else if(power->state != PowerStateCharging) { + notification_internal_message(notification, &sequence_charging); + power->state = PowerStateCharging; + power->event.type = PowerEventTypeStartCharging; furi_pubsub_publish(power->event_pubsub, &power->event); } + + } else if(power->state != PowerStateNotCharging) { + notification_internal_message(notification, &sequence_not_charging); + power->state = PowerStateNotCharging; + power->event.type = PowerEventTypeStopCharging; + furi_pubsub_publish(power->event_pubsub, &power->event); } -} -static bool power_update_info(Power* power) { - PowerInfo info; - - info.is_charging = furi_hal_power_is_charging(); - info.gauge_is_ok = furi_hal_power_gauge_is_ok(); - info.is_shutdown_requested = furi_hal_power_is_shutdown_requested(); - 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_battery_charge_limit = furi_hal_power_get_battery_charge_voltage_limit(); - 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); - - furi_mutex_acquire(power->api_mtx, FuriWaitForever); - bool need_refresh = power->info.charge != info.charge; - need_refresh |= power->info.is_charging != info.is_charging; - power->info = info; - furi_mutex_release(power->api_mtx); - - return need_refresh; + furi_record_close(RECORD_NOTIFICATION); } static void power_check_low_battery(Power* power) { @@ -440,40 +404,41 @@ static void power_check_low_battery(Power* power) { } // Check battery charge and vbus voltage - if((power->info.is_shutdown_requested) && (power->info.voltage_vbus < 4.0f) && - power->show_low_bat_level_message) { + if((power->info.is_shutdown_requested) && + (power->info.voltage_vbus < POWER_VBUS_LOW_THRESHOLD) && power->show_battery_low_warning) { if(!power->battery_low) { - view_dispatcher_send_to_front(power->view_dispatcher); - view_dispatcher_switch_to_view(power->view_dispatcher, PowerViewOff); + view_holder_send_to_front(power->view_holder); + view_holder_set_view(power->view_holder, power_off_get_view(power->view_power_off)); } 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; + // view_dispatcher_switch_to_view(power->view_dispatcher, VIEW_NONE); + view_holder_set_view(power->view_holder, NULL); + power->power_off_timeout = POWER_OFF_TIMEOUT_S; } power->battery_low = false; } // If battery low, update view and switch off power after timeout if(power->battery_low) { - PowerOffResponse response = power_off_get_response(power->power_off); + PowerOffResponse response = power_off_get_response(power->view_power_off); if(response == PowerOffResponseDefault) { if(power->power_off_timeout) { - power_off_set_time_left(power->power_off, power->power_off_timeout--); + power_off_set_time_left(power->view_power_off, power->power_off_timeout--); } else { power_off(power); } } else if(response == PowerOffResponseOk) { power_off(power); } else if(response == PowerOffResponseHide) { - view_dispatcher_switch_to_view(power->view_dispatcher, VIEW_NONE); + view_holder_set_view(power->view_holder, NULL); if(power->power_off_timeout) { - power_off_set_time_left(power->power_off, power->power_off_timeout--); + power_off_set_time_left(power->view_power_off, power->power_off_timeout--); } else { power_off(power); } } else if(response == PowerOffResponseCancel) { - view_dispatcher_switch_to_view(power->view_dispatcher, VIEW_NONE); + view_holder_set_view(power->view_holder, NULL); } } } @@ -490,20 +455,172 @@ static void power_check_battery_level_change(Power* power) { static void power_check_charge_cap(Power* power) { uint32_t cap = momentum_settings.charge_cap; if(power->info.charge >= cap && cap < 100) { - if(!power->info.is_charge_capped) { // Suppress charging if charge reaches custom cap - power->info.is_charge_capped = true; + if(!power->is_charge_capped) { // Suppress charging if charge reaches custom cap + power->is_charge_capped = true; furi_hal_power_suppress_charge_enter(); } } else { - if(power->info.is_charge_capped) { // Start charging again if charge below custom cap - power->info.is_charge_capped = false; + if(power->is_charge_capped) { // Start charging again if charge below custom cap + power->is_charge_capped = false; furi_hal_power_suppress_charge_exit(); } } } -void power_trigger_ui_update(Power* power) { - view_port_update(power->battery_view_port); +static void power_handle_shutdown(Power* power) { + furi_hal_power_off(); + // Notify user if USB is plugged + view_holder_send_to_front(power->view_holder); + view_holder_set_view( + power->view_holder, power_unplug_usb_get_view(power->view_power_unplug_usb)); + furi_delay_ms(100); + furi_halt("Disconnect USB for safe shutdown"); +} + +static void power_handle_reboot(PowerBootMode mode) { + if(mode == PowerBootModeNormal) { + update_operation_disarm(); + } else if(mode == PowerBootModeDfu) { + furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeDfu); + } else if(mode == PowerBootModeUpdateStart) { + furi_hal_rtc_set_boot_mode(FuriHalRtcBootModePreUpdate); + } else { + furi_crash(); + } + + furi_hal_power_reset(); +} + +static bool power_message_callback(FuriEventLoopObject* object, void* context) { + furi_assert(context); + Power* power = context; + + furi_assert(object == power->message_queue); + + PowerMessage msg; + furi_check(furi_message_queue_get(power->message_queue, &msg, 0) == FuriStatusOk); + + switch(msg.type) { + case PowerMessageTypeShutdown: + power_handle_shutdown(power); + break; + case PowerMessageTypeReboot: + power_handle_reboot(msg.boot_mode); + break; + case PowerMessageTypeGetInfo: + *msg.power_info = power->info; + break; + case PowerMessageTypeIsBatteryHealthy: + *msg.bool_param = power->info.health > POWER_HEALTH_LOW_THRESHOLD; + break; + case PowerMessageTypeShowBatteryLowWarning: + power->show_battery_low_warning = *msg.bool_param; + break; + case PowerMessageTypeGetSettings: + furi_assert(msg.lock); + *msg.settings = power->settings; + break; + case PowerMessageTypeSetSettings: + furi_assert(msg.lock); + power->settings = *msg.csettings; + power_apply_settings(power); + power_settings_save(&power->settings); + break; + case PowerMessageTypeReloadSettings: + power_settings_load(&power->settings); + power_apply_settings(power); + break; + default: + furi_crash(); + } + + if(msg.lock) { + api_lock_unlock(msg.lock); + } + + return true; +} + +static void power_tick_callback(void* context) { + furi_assert(context); + Power* power = context; + + // Update data from gauge and charger + const bool need_refresh = 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); + // Check charge cap, compare with user setting and (un)suppress charging + power_check_charge_cap(power); + // Update battery view port + view_port_enabled_set( + power->battery_view_port, momentum_settings.battery_icon != BatteryIconOff); + if(need_refresh) { + view_port_update(power->battery_view_port); + } + // Check OTG status and disable it in case of fault + if(furi_hal_power_is_otg_enabled()) { + furi_hal_power_check_otg_status(); + } +} + +static void power_init_settings(Power* power) { + Storage* storage = furi_record_open(RECORD_STORAGE); + furi_pubsub_subscribe(storage_get_pubsub(storage), power_storage_callback, power); + + if(storage_sd_status(storage) != FSE_OK) { + FURI_LOG_D(TAG, "SD Card not ready, skipping settings"); + return; + } + + power_settings_load(&power->settings); + power_apply_settings(power); + furi_record_close(RECORD_STORAGE); +} + +static Power* power_alloc(void) { + Power* power = malloc(sizeof(Power)); + // Pubsub + power->event_pubsub = furi_pubsub_alloc(); + // State initialization + power->power_off_timeout = POWER_OFF_TIMEOUT_S; + power->show_battery_low_warning = true; + // Gui + Gui* gui = furi_record_open(RECORD_GUI); + + // Auto shutdown on idle + Loader* loader = furi_record_open(RECORD_LOADER); + furi_pubsub_subscribe(loader_get_pubsub(loader), power_loader_callback, power); + power->input_events_pubsub = furi_record_open(RECORD_INPUT_EVENTS); + power->ascii_events_pubsub = furi_record_open(RECORD_ASCII_EVENTS); + power->auto_shutdown_timer = + furi_timer_alloc(power_auto_shutdown_timer_callback, FuriTimerTypeOnce, power); + power->app_running = loader_is_locked(loader); + + power->view_holder = view_holder_alloc(); + power->view_power_off = power_off_alloc(); + power->view_power_unplug_usb = power_unplug_usb_alloc(); + + view_holder_attach_to_gui(power->view_holder, gui); + // Battery view port + power->battery_view_port = power_battery_view_port_alloc(power); + gui_add_view_port(gui, power->battery_view_port, GuiLayerStatusBarRight); + // Event loop + power->event_loop = furi_event_loop_alloc(); + power->message_queue = furi_message_queue_alloc(4, sizeof(PowerMessage)); + + furi_event_loop_subscribe_message_queue( + power->event_loop, + power->message_queue, + FuriEventLoopEventIn, + power_message_callback, + power); + furi_event_loop_tick_set(power->event_loop, 1000, power_tick_callback, power); + + return power; } int32_t power_srv(void* p) { @@ -517,44 +634,11 @@ int32_t power_srv(void* p) { } Power* power = power_alloc(); - if(!LOAD_POWER_SETTINGS(&power->shutdown_idle_delay_ms)) { - power->shutdown_idle_delay_ms = 0; - } - power_auto_shutdown_arm(power); + power_init_settings(power); power_update_info(power); - power->info.is_charge_capped = false; // default false + furi_record_create(RECORD_POWER, power); - - while(1) { - // Update data from gauge and charger - bool need_refresh = 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); - - // Check charge cap, compare with user setting and suppress/unsuppress charging - power_check_charge_cap(power); - - // Update battery view port - if(need_refresh) { - view_port_update(power->battery_view_port); - } - - // Check OTG status and disable it in case of fault - if(furi_hal_power_is_otg_enabled()) { - furi_hal_power_check_otg_status(); - } - - furi_delay_ms(1000); - } - - furi_crash("That was unexpected"); + furi_event_loop_run(power->event_loop); return 0; } diff --git a/applications/services/power/power_service/power.h b/applications/services/power/power_service/power.h index 5fe320315..0168a8656 100644 --- a/applications/services/power/power_service/power.h +++ b/applications/services/power/power_service/power.h @@ -56,8 +56,6 @@ typedef struct { uint8_t charge; uint8_t health; - - bool is_charge_capped; } PowerInfo; /** Power off device @@ -85,14 +83,6 @@ void power_get_info(Power* power, PowerInfo* info); */ FuriPubSub* power_get_pubsub(Power* power); -/** Get power settings events pubsub handler - * - * @param power Power instance - * - * @return FuriPubSub instance - */ -FuriPubSub* power_get_settings_events_pubsub(Power* power); - /** Check battery health * * @return true if battery is healthy @@ -106,19 +96,6 @@ bool power_is_battery_healthy(Power* power); */ void power_enable_low_battery_level_notification(Power* power, bool enable); -/** Trigger UI update for changing battery layout - * - * @param power Power instance - */ -void power_trigger_ui_update(Power* power); - -/** Enable or disable battery icon - * - * @param power Power instance - * @param is_enabled Show battery or not - */ -void power_set_battery_icon_enabled(Power* power, bool is_enabled); - #ifdef __cplusplus } #endif diff --git a/applications/services/power/power_service/power_api.c b/applications/services/power/power_service/power_api.c index d2b4c4e5b..9c17747af 100644 --- a/applications/services/power/power_service/power_api.c +++ b/applications/services/power/power_service/power_api.c @@ -1,41 +1,39 @@ #include "power_i.h" -#include -#include -#include - void power_off(Power* power) { furi_check(power); - furi_hal_power_off(); - // Notify user if USB is plugged - view_dispatcher_send_to_front(power->view_dispatcher); - view_dispatcher_switch_to_view(power->view_dispatcher, PowerViewUnplugUsb); - furi_delay_ms(100); - furi_halt("Disconnect USB for safe shutdown"); + PowerMessage msg = { + .type = PowerMessageTypeShutdown, + }; + + furi_check( + furi_message_queue_put(power->message_queue, &msg, FuriWaitForever) == FuriStatusOk); } -void power_reboot(PowerBootMode mode) { - if(mode == PowerBootModeNormal) { - update_operation_disarm(); - } else if(mode == PowerBootModeDfu) { - furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeDfu); - } else if(mode == PowerBootModeUpdateStart) { - furi_hal_rtc_set_boot_mode(FuriHalRtcBootModePreUpdate); - } else { - furi_crash(); - } +void power_reboot(Power* power, PowerBootMode mode) { + PowerMessage msg = { + .type = PowerMessageTypeReboot, + .boot_mode = mode, + }; - furi_hal_power_reset(); + furi_check( + furi_message_queue_put(power->message_queue, &msg, FuriWaitForever) == FuriStatusOk); } void power_get_info(Power* power, PowerInfo* info) { furi_check(power); furi_check(info); - furi_mutex_acquire(power->api_mtx, FuriWaitForever); - memcpy(info, &power->info, sizeof(power->info)); - furi_mutex_release(power->api_mtx); + PowerMessage msg = { + .type = PowerMessageTypeGetInfo, + .power_info = info, + .lock = api_lock_alloc_locked(), + }; + + furi_check( + furi_message_queue_put(power->message_queue, &msg, FuriWaitForever) == FuriStatusOk); + api_lock_wait_unlock_and_free(msg.lock); } FuriPubSub* power_get_pubsub(Power* power) { @@ -43,23 +41,66 @@ FuriPubSub* power_get_pubsub(Power* power) { return power->event_pubsub; } -FuriPubSub* power_get_settings_events_pubsub(Power* power) { - furi_assert(power); - return power->settings_events; -} - bool power_is_battery_healthy(Power* power) { furi_check(power); - bool is_healthy = false; - furi_mutex_acquire(power->api_mtx, FuriWaitForever); - is_healthy = power->info.health > POWER_BATTERY_HEALTHY_LEVEL; - furi_mutex_release(power->api_mtx); - return is_healthy; + + bool ret = false; + + PowerMessage msg = { + .type = PowerMessageTypeIsBatteryHealthy, + .lock = api_lock_alloc_locked(), + .bool_param = &ret, + }; + + furi_check( + furi_message_queue_put(power->message_queue, &msg, FuriWaitForever) == FuriStatusOk); + api_lock_wait_unlock_and_free(msg.lock); + + return ret; } void power_enable_low_battery_level_notification(Power* power, bool enable) { furi_check(power); - furi_mutex_acquire(power->api_mtx, FuriWaitForever); - power->show_low_bat_level_message = enable; - furi_mutex_release(power->api_mtx); + + PowerMessage msg = { + .type = PowerMessageTypeShowBatteryLowWarning, + .bool_param = &enable, + }; + + furi_check( + furi_message_queue_put(power->message_queue, &msg, FuriWaitForever) == FuriStatusOk); +} + +/* + * Private API for the Settings app + */ + +void power_get_settings(Power* power, PowerSettings* settings) { + furi_assert(power); + furi_assert(settings); + + PowerMessage msg = { + .type = PowerMessageTypeGetSettings, + .settings = settings, + .lock = api_lock_alloc_locked(), + }; + + furi_check( + furi_message_queue_put(power->message_queue, &msg, FuriWaitForever) == FuriStatusOk); + api_lock_wait_unlock_and_free(msg.lock); +} + +void power_set_settings(Power* power, const PowerSettings* settings) { + furi_assert(power); + furi_assert(settings); + + PowerMessage msg = { + .type = PowerMessageTypeSetSettings, + .csettings = settings, + .lock = api_lock_alloc_locked(), + }; + + furi_check( + furi_message_queue_put(power->message_queue, &msg, FuriWaitForever) == FuriStatusOk); + api_lock_wait_unlock_and_free(msg.lock); } diff --git a/applications/services/power/power_service/power_i.h b/applications/services/power/power_service/power_i.h index faecae05e..03bf395ce 100644 --- a/applications/services/power/power_service/power_i.h +++ b/applications/services/power/power_service/power_i.h @@ -10,9 +10,10 @@ #include #include "views/power_off.h" -#include #include "views/power_unplug_usb.h" +#include + typedef enum { PowerStateNotCharging, PowerStateCharging, @@ -38,16 +39,14 @@ struct Power { uint8_t battery_level; uint8_t power_off_timeout; - FuriPubSub* settings_events; FuriPubSub* input_events_pubsub; - FuriPubSubSubscription* input_events_subscription; FuriPubSub* ascii_events_pubsub; + FuriPubSubSubscription* input_events_subscription; FuriPubSubSubscription* ascii_events_subscription; - FuriPubSubSubscription* app_start_stop_subscription; - FuriPubSubSubscription* settings_events_subscription; - uint32_t shutdown_idle_delay_ms; FuriTimer* auto_shutdown_timer; - Loader* loader; + PowerSettings settings; + bool is_charge_capped; + bool app_running; }; typedef enum { @@ -61,6 +60,10 @@ typedef enum { PowerMessageTypeGetInfo, PowerMessageTypeIsBatteryHealthy, PowerMessageTypeShowBatteryLowWarning, + + PowerMessageTypeGetSettings, + PowerMessageTypeSetSettings, + PowerMessageTypeReloadSettings, } PowerMessageType; typedef struct { @@ -69,6 +72,9 @@ typedef struct { PowerBootMode boot_mode; PowerInfo* power_info; bool* bool_param; + + PowerSettings* settings; + const PowerSettings* csettings; }; FuriApiLock lock; } PowerMessage; diff --git a/applications/services/power/power_service/power_settings_api_i.h b/applications/services/power/power_service/power_settings_api_i.h new file mode 100644 index 000000000..ed6c2c34b --- /dev/null +++ b/applications/services/power/power_service/power_settings_api_i.h @@ -0,0 +1,8 @@ +#pragma once + +#include "power.h" +#include "../power_settings.h" + +void power_get_settings(Power* power, PowerSettings* settings); + +void power_set_settings(Power* power, const PowerSettings* settings); diff --git a/applications/services/power/power_settings.c b/applications/services/power/power_settings.c index b9d5b6dc7..926f48e78 100644 --- a/applications/services/power/power_settings.c +++ b/applications/services/power/power_settings.c @@ -1,12 +1,42 @@ #include "power_settings.h" #include "power_settings_filename.h" -bool SAVE_POWER_SETTINGS(uint32_t* x) { - return saved_struct_save( - POWER_SETTINGS_PATH, x, sizeof(uint32_t), POWER_SETTINGS_MAGIC, POWER_SETTINGS_VER); +#include +#include + +#define TAG "PowerSettings" + +#define POWER_SETTINGS_VER (1) +#define POWER_SETTINGS_MAGIC (0x21) + +void power_settings_load(PowerSettings* settings) { + furi_assert(settings); + + const bool success = saved_struct_load( + POWER_SETTINGS_PATH, + settings, + sizeof(PowerSettings), + POWER_SETTINGS_MAGIC, + POWER_SETTINGS_VER); + + if(!success) { + FURI_LOG_W(TAG, "Failed to load file, using defaults"); + memset(settings, 0, sizeof(PowerSettings)); + // power_settings_save(settings); + } } -bool LOAD_POWER_SETTINGS(uint32_t* x) { - return saved_struct_load( - POWER_SETTINGS_PATH, x, sizeof(uint32_t), POWER_SETTINGS_MAGIC, POWER_SETTINGS_VER); +void power_settings_save(const PowerSettings* settings) { + furi_assert(settings); + + const bool success = saved_struct_save( + POWER_SETTINGS_PATH, + settings, + sizeof(PowerSettings), + POWER_SETTINGS_MAGIC, + POWER_SETTINGS_VER); + + if(!success) { + FURI_LOG_E(TAG, "Failed to save file"); + } } diff --git a/applications/services/power/power_settings.h b/applications/services/power/power_settings.h index 2377df970..4f94b9b69 100644 --- a/applications/services/power/power_settings.h +++ b/applications/services/power/power_settings.h @@ -1,18 +1,10 @@ -#include -#include +#pragma once -#define POWER_SETTINGS_VER (1) +#include -#define POWER_SETTINGS_MAGIC (0x21) +typedef struct { + uint32_t shutdown_idle_delay_ms; +} PowerSettings; -#ifdef __cplusplus -extern "C" { -#endif - -bool SAVE_POWER_SETTINGS(uint32_t* x); - -bool LOAD_POWER_SETTINGS(uint32_t* x); - -#ifdef __cplusplus -} -#endif +void power_settings_load(PowerSettings* settings); +void power_settings_save(const PowerSettings* settings); diff --git a/applications/settings/power_settings_app/mock_imports/mock_power_settings.c b/applications/settings/power_settings_app/mock_imports/mock_power_api.c similarity index 74% rename from applications/settings/power_settings_app/mock_imports/mock_power_settings.c rename to applications/settings/power_settings_app/mock_imports/mock_power_api.c index b30affaab..ef69fc9ca 100644 --- a/applications/settings/power_settings_app/mock_imports/mock_power_settings.c +++ b/applications/settings/power_settings_app/mock_imports/mock_power_api.c @@ -3,5 +3,5 @@ // Instead of copying the file, can (ab)use the preprocessor to insert the source code here // Then, we still use the Header from original code as if nothing happened -// LOAD_POWER_SETTINGS(), SAVE_POWER_SETTINGS() -#include +// power_get_settings(), power_set_settings() +#include diff --git a/applications/settings/power_settings_app/power_settings_app.c b/applications/settings/power_settings_app/power_settings_app.c index 718fe0ef0..834c03476 100644 --- a/applications/settings/power_settings_app/power_settings_app.c +++ b/applications/settings/power_settings_app/power_settings_app.c @@ -28,9 +28,6 @@ PowerSettingsApp* power_settings_app_alloc(uint32_t first_scene, ViewDispatcherT app->gui = furi_record_open(RECORD_GUI); app->power = furi_record_open(RECORD_POWER); - //PubSub - app->settings_events = power_get_settings_events_pubsub(app->power); - // View dispatcher app->view_dispatcher = view_dispatcher_alloc(); app->scene_manager = scene_manager_alloc(&power_settings_scene_handlers, app); @@ -64,6 +61,8 @@ PowerSettingsApp* power_settings_app_alloc(uint32_t first_scene, ViewDispatcherT PowerSettingsAppViewVariableItemList, variable_item_list_get_view(app->variable_item_list)); + power_get_settings(app->power, &app->settings); + // Set first scene scene_manager_next_scene(app->scene_manager, first_scene); return app; @@ -71,6 +70,7 @@ PowerSettingsApp* power_settings_app_alloc(uint32_t first_scene, ViewDispatcherT void power_settings_app_free(PowerSettingsApp* app) { furi_assert(app); + power_set_settings(app->power, &app->settings); // Views view_dispatcher_remove_view(app->view_dispatcher, PowerSettingsAppViewBatteryInfo); battery_info_free(app->battery_info); diff --git a/applications/settings/power_settings_app/power_settings_app.h b/applications/settings/power_settings_app/power_settings_app.h index 65d4fd20c..0b8425a7b 100644 --- a/applications/settings/power_settings_app/power_settings_app.h +++ b/applications/settings/power_settings_app/power_settings_app.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -13,21 +14,21 @@ #include #include -#include #include "scenes/power_settings_scene.h" typedef struct { + PowerSettings settings; + Power* power; Gui* gui; SceneManager* scene_manager; ViewDispatcher* view_dispatcher; BatteryInfo* battery_info; Submenu* submenu; + VariableItemList* variable_item_list; DialogEx* dialog; PowerInfo info; - VariableItemList* variable_item_list; - uint32_t shutdown_idle_delay_ms; - FuriPubSub* settings_events; + bool about_battery; } PowerSettingsApp; diff --git a/applications/settings/power_settings_app/scenes/power_settings_scene_config.h b/applications/settings/power_settings_app/scenes/power_settings_scene_config.h index 1b4436f14..f57071b9b 100644 --- a/applications/settings/power_settings_app/scenes/power_settings_scene_config.h +++ b/applications/settings/power_settings_app/scenes/power_settings_scene_config.h @@ -3,4 +3,3 @@ ADD_SCENE(power_settings, battery_info, BatteryInfo) ADD_SCENE(power_settings, reboot, Reboot) ADD_SCENE(power_settings, reboot_confirm, RebootConfirm) ADD_SCENE(power_settings, power_off, PowerOff) -ADD_SCENE(power_settings, shutdown_idle, ShutdownIdle) diff --git a/applications/settings/power_settings_app/scenes/power_settings_scene_shutdown_idle.c b/applications/settings/power_settings_app/scenes/power_settings_scene_shutdown_idle.c deleted file mode 100644 index a04f9fdf2..000000000 --- a/applications/settings/power_settings_app/scenes/power_settings_scene_shutdown_idle.c +++ /dev/null @@ -1,68 +0,0 @@ -#include "../power_settings_app.h" -#include - -#define SHUTDOWN_IDLE_DELAY_COUNT 9 -#define SCENE_EVENT_SELECT_SHUTDOWN_IDLE_DELAY 0 - -const char* const shutdown_idle_delay_text[SHUTDOWN_IDLE_DELAY_COUNT] = - {"OFF", "15min", "30min", "1h", "2h", "6h", "12h", "24h", "48h"}; - -const uint32_t shutdown_idle_delay_value[SHUTDOWN_IDLE_DELAY_COUNT] = - {0, 900000, 1800000, 3600000, 7200000, 21600000, 43200000, 86400000, 172800000}; - -static void power_settings_scene_shutodwn_idle_callback(void* context, uint32_t index) { - PowerSettingsApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, index); -} - -static void power_settings_scene_start_shutdown_idle_delay_changed(VariableItem* item) { - PowerSettingsApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, shutdown_idle_delay_text[index]); - app->shutdown_idle_delay_ms = shutdown_idle_delay_value[index]; -} - -void power_settings_scene_shutdown_idle_on_enter(void* context) { - PowerSettingsApp* app = context; - LOAD_POWER_SETTINGS(&app->shutdown_idle_delay_ms); - VariableItemList* variable_item_list = app->variable_item_list; - VariableItem* item; - uint8_t value_index; - - item = variable_item_list_add( - variable_item_list, - "Set Time", - SHUTDOWN_IDLE_DELAY_COUNT, - power_settings_scene_start_shutdown_idle_delay_changed, - app); - - variable_item_list_set_enter_callback( - variable_item_list, power_settings_scene_shutodwn_idle_callback, app); - - value_index = value_index_uint32( - app->shutdown_idle_delay_ms, shutdown_idle_delay_value, SHUTDOWN_IDLE_DELAY_COUNT); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, shutdown_idle_delay_text[value_index]); - - view_dispatcher_switch_to_view(app->view_dispatcher, PowerSettingsAppViewVariableItemList); -} - -bool power_settings_scene_shutdown_idle_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SCENE_EVENT_SELECT_SHUTDOWN_IDLE_DELAY) { - consumed = true; - } - } - return consumed; -} - -void power_settings_scene_shutdown_idle_on_exit(void* context) { - PowerSettingsApp* app = context; - SAVE_POWER_SETTINGS(&app->shutdown_idle_delay_ms); - furi_pubsub_publish(app->settings_events, &app->shutdown_idle_delay_ms); - variable_item_list_reset(app->variable_item_list); -} diff --git a/applications/settings/power_settings_app/scenes/power_settings_scene_start.c b/applications/settings/power_settings_app/scenes/power_settings_scene_start.c index 47c5b85c3..7c51dde42 100644 --- a/applications/settings/power_settings_app/scenes/power_settings_scene_start.c +++ b/applications/settings/power_settings_app/scenes/power_settings_scene_start.c @@ -1,5 +1,7 @@ #include "../power_settings_app.h" +#include + enum PowerSettingsSubmenuIndex { PowerSettingsSubmenuIndexBatteryInfo, PowerSettingsSubmenuIndexReboot, @@ -7,6 +9,29 @@ enum PowerSettingsSubmenuIndex { PowerSettingsSubmenuShutdownIdle }; +#define SHUTDOWN_IDLE_DELAY_COUNT 9 +const char* const shutdown_idle_delay_text[SHUTDOWN_IDLE_DELAY_COUNT] = { + "OFF", + "15min", + "30min", + "1h", + "2h", + "6h", + "12h", + "24h", + "48h", +}; +const uint32_t shutdown_idle_delay_value[SHUTDOWN_IDLE_DELAY_COUNT] = + {0, 900000, 1800000, 3600000, 7200000, 21600000, 43200000, 86400000, 172800000}; + +static void power_settings_scene_start_auto_lock_delay_changed(VariableItem* item) { + PowerSettingsApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, shutdown_idle_delay_text[index]); + app->settings.shutdown_idle_delay_ms = shutdown_idle_delay_value[index]; +} + static void power_settings_scene_start_submenu_callback(void* context, uint32_t index) { furi_assert(context); PowerSettingsApp* app = context; @@ -15,34 +40,36 @@ static void power_settings_scene_start_submenu_callback(void* context, uint32_t void power_settings_scene_start_on_enter(void* context) { PowerSettingsApp* app = context; - Submenu* submenu = app->submenu; + VariableItemList* variable_item_list = app->variable_item_list; - 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_add_item( - submenu, + VariableItem* item; + uint8_t value_index; + + variable_item_list_add(variable_item_list, "Battery Info", 1, NULL, NULL); + + variable_item_list_add(variable_item_list, "Reboot", 1, NULL, NULL); + + variable_item_list_add(variable_item_list, "Power OFF", 1, NULL, NULL); + + item = variable_item_list_add( + variable_item_list, "Shutdown on Idle", - PowerSettingsSubmenuShutdownIdle, - power_settings_scene_start_submenu_callback, + SHUTDOWN_IDLE_DELAY_COUNT, + power_settings_scene_start_auto_lock_delay_changed, app); - submenu_set_selected_item( - submenu, scene_manager_get_scene_state(app->scene_manager, PowerSettingsAppSceneStart)); + value_index = value_index_uint32( + app->settings.shutdown_idle_delay_ms, + shutdown_idle_delay_value, + SHUTDOWN_IDLE_DELAY_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, shutdown_idle_delay_text[value_index]); + + variable_item_list_set_selected_item( + variable_item_list, + scene_manager_get_scene_state(app->scene_manager, PowerSettingsAppSceneStart)); + + variable_item_list_set_enter_callback( + variable_item_list, power_settings_scene_start_submenu_callback, app); view_dispatcher_switch_to_view(app->view_dispatcher, PowerSettingsAppViewSubmenu); } @@ -59,7 +86,6 @@ bool power_settings_scene_start_on_event(void* context, SceneManagerEvent event) } else if(event.event == PowerSettingsSubmenuIndexOff) { scene_manager_next_scene(app->scene_manager, PowerSettingsAppScenePowerOff); } else if(event.event == PowerSettingsSubmenuShutdownIdle) { - scene_manager_next_scene(app->scene_manager, PowerSettingsAppSceneShutdownIdle); } scene_manager_set_scene_state(app->scene_manager, PowerSettingsAppSceneStart, event.event); consumed = true; diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 980d5903d..938447f39 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -2996,12 +2996,9 @@ Function,-,pow10f,float,float Function,+,power_enable_low_battery_level_notification,void,"Power*, _Bool" Function,+,power_get_info,void,"Power*, PowerInfo*" Function,+,power_get_pubsub,FuriPubSub*,Power* -Function,+,power_get_settings_events_pubsub,FuriPubSub*,Power* Function,+,power_is_battery_healthy,_Bool,Power* Function,+,power_off,void,Power* Function,+,power_reboot,void,"Power*, PowerBootMode" -Function,+,power_set_battery_icon_enabled,void,"Power*, _Bool" -Function,-,power_trigger_ui_update,void,Power* Function,+,powf,float,"float, float" Function,-,powl,long double,"long double, long double" Function,+,pretty_format_bytes_hex_canonical,void,"FuriString*, size_t, const char*, const uint8_t*, size_t"