Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatic shutdown on idle #1647

Open
wants to merge 35 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
6dc39ba
Merge pull request #1 from flipperdevices/dev
SHxKenzuto Aug 15, 2022
cf053b4
Merge remote-tracking branch 'upstream/dev' into dev
SHxKenzuto Aug 15, 2022
409151f
Merge remote-tracking branch 'upstream/dev' into dev
SHxKenzuto Aug 22, 2022
af8d236
added "Shutdown on Idle" setting under power settings
SHxKenzuto Aug 23, 2022
024326e
Merge remote-tracking branch 'upstream/dev' into dev
SHxKenzuto Aug 23, 2022
94b2d25
Merge remote-tracking branch 'origin/dev' into shutdown_idle
SHxKenzuto Aug 23, 2022
44e3842
fix issues Shutdown on idle
SHxKenzuto Aug 23, 2022
40cabd5
Merge remote-tracking branch 'upstream/dev' into shutdown_idle
SHxKenzuto Sep 19, 2022
6d83a3e
Merge branch 'dev' into shutdown_idle
skotopes Sep 28, 2022
a3ade92
Power: free variable_item_list after removing from view dispatcher
skotopes Sep 28, 2022
d3c1ff5
Merge remote-tracking branch 'upstream/dev' into shutdown_idle
SHxKenzuto Oct 10, 2022
82f2354
event based check for auto shutdown timer setting
SHxKenzuto Oct 10, 2022
12515b2
Merge remote-tracking branch 'upstream/dev' into shutdown_idle
SHxKenzuto Oct 21, 2022
dfe7828
Merge branch 'dev' into shutdown_idle
skotopes Oct 28, 2022
3a5d70b
Merge branch 'dev' into shutdown_idle
SHxKenzuto Nov 4, 2022
f07424f
added 2 hours interval option in shutdown on idle submenu
SHxKenzuto Nov 4, 2022
76c2ccd
Merge branch 'dev-upstream' into shutdown_idle
SHxKenzuto Dec 9, 2022
400558a
Shutdown on Idle: auto-shutdown timer arm/inhibit hooked to loader ev…
SHxKenzuto Dec 9, 2022
bfc563e
Merge remote-tracking branch 'upstream/dev' into shutdown_idle
SHxKenzuto Dec 9, 2022
4ed6950
Merge branch 'dev' into shutdown_idle
skotopes Dec 10, 2022
461aca0
Remove merge artifact
skotopes Dec 10, 2022
1cb5d6f
Merge branch 'dev' into shutdown_idle
skotopes Dec 10, 2022
b931ef7
Merge branch 'dev' into shutdown_idle
skotopes Dec 12, 2022
d8db432
Shutdown on Idle: moved timer allocation before callbacks subscriptions
SHxKenzuto Dec 18, 2022
d65fd6f
Shutdown on Idle: optimized loader callback subscription
SHxKenzuto Dec 18, 2022
d75e6a1
grouped furi_record_open instructions
SHxKenzuto Dec 18, 2022
8f46234
Merge remote-tracking branch 'upstream/dev' into shutdown_idle
SHxKenzuto Dec 18, 2022
3aa4229
changed power_off instruction to furi_hal_power_off
SHxKenzuto Dec 19, 2022
345c654
Merge remote-tracking branch 'origin/dev' into shutdown_idle
skotopes Feb 26, 2023
104e34d
Merge branch 'dev' into shutdown_idle
skotopes Mar 9, 2023
b69b4f4
Merge remote-tracking branch 'origin/dev' into shutdown_idle
skotopes Sep 1, 2023
4ab846b
Sync api symbols
skotopes Sep 1, 2023
9a86178
Merge branch 'dev' into shutdown_idle
skotopes Sep 4, 2023
0dbc1b6
Merge branch 'dev' into shutdown_idle
hedger Sep 5, 2023
4fdb8f0
Merge remote-tracking branch 'origin/dev' into shutdown_idle
skotopes Jun 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 116 additions & 1 deletion applications/services/power/power_service/power.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#define POWER_OFF_TIMEOUT 90
#define TAG "Power"

static void power_loader_callback(const void*, void*);

void power_draw_battery_callback(Canvas* canvas, void* context) {
furi_assert(context);
Power* power = context;
Expand Down Expand Up @@ -49,15 +51,105 @@ static ViewPort* power_battery_view_port_alloc(Power* power) {
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));
}

static void power_stop_auto_shutdown_timer(Power* power) {
furi_timer_stop(power->auto_shutdown_timer);
}

static uint32_t power_is_running_auto_shutdown_timer(Power* power) {
return furi_timer_is_running(power->auto_shutdown_timer);
}

static void power_input_event_callback(const void* value, void* context) {
furi_assert(value);
furi_assert(context);
const InputEvent* event = value;
Power* power = context;
if(event->type == InputTypePress) {
power_start_auto_shutdown_timer(power);
}
}

static void power_auto_shutdown_arm(Power* power) {
if(power->shutdown_idle_delay_ms) {
if(power->input_events_subscription == NULL) {
power->input_events_subscription = furi_pubsub_subscribe(
power->input_events_pubsub, power_input_event_callback, power);
}
if(power->app_start_stop_subscription == NULL) {
power->app_start_stop_subscription = furi_pubsub_subscribe(
loader_get_pubsub(power->loader), power_loader_callback, power);
}
power_start_auto_shutdown_timer(power);
}
}

static void power_auto_shutdown_inhibit(Power* power) {
power_stop_auto_shutdown_timer(power);
if(power->input_events_subscription) {
furi_pubsub_unsubscribe(power->input_events_pubsub, power->input_events_subscription);
power->input_events_subscription = NULL;
}
}

static void power_loader_callback(const void* message, void* context) {
furi_assert(context);
Power* power = context;
const LoaderEvent* event = message;

if(event->type == LoaderEventTypeApplicationStarted) {
power_auto_shutdown_inhibit(power);
} else if(event->type == LoaderEventTypeApplicationStopped) {
power_auto_shutdown_arm(power);
}
}

static void power_auto_shutdown_timer_callback(void* context) {
furi_assert(context);
Power* power = context;
power_auto_shutdown_inhibit(power);
furi_hal_power_off();
}

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) {
power_auto_shutdown_arm(power);
} else if(power_is_running_auto_shutdown_timer(power)) {
if(power->app_start_stop_subscription) {
furi_pubsub_unsubscribe(
loader_get_pubsub(power->loader), power->app_start_stop_subscription);
}
power_auto_shutdown_inhibit(power);
}
}

Power* power_alloc() {
Power* power = malloc(sizeof(Power));

//Auto shutdown timer
power->auto_shutdown_timer =
furi_timer_alloc(power_auto_shutdown_timer_callback, FuriTimerTypeOnce, power);

// Records
power->notification = furi_record_open(RECORD_NOTIFICATION);
power->gui = furi_record_open(RECORD_GUI);
power->loader = furi_record_open(RECORD_LOADER);
power->input_events_pubsub = furi_record_open(RECORD_INPUT_EVENTS);

// Pubsub
power->event_pubsub = furi_pubsub_alloc();
power->settings_events = furi_pubsub_alloc();
power->input_events_subscription = NULL;
power->app_start_stop_subscription = NULL;
power->settings_events_subscription =
furi_pubsub_subscribe(power->settings_events, power_shutdown_time_changed_callback, power);

// State initialization
power->state = PowerStateNotCharging;
Expand Down Expand Up @@ -100,11 +192,29 @@ void power_free(Power* power) {
furi_mutex_free(power->api_mtx);

// FuriPubSub
furi_pubsub_unsubscribe(power->settings_events, power->settings_events_subscription);
if(power->input_events_subscription) {
furi_pubsub_unsubscribe(power->input_events_pubsub, power->input_events_subscription);
power->input_events_subscription = NULL;
}
if(power->app_start_stop_subscription) {
furi_pubsub_unsubscribe(
loader_get_pubsub(power->loader), power->app_start_stop_subscription);
}

furi_pubsub_free(power->event_pubsub);
furi_pubsub_free(power->settings_events);
power->loader = NULL;
power->input_events_pubsub = NULL;

//Auto shutdown timer
furi_timer_free(power->auto_shutdown_timer);

// Records
furi_record_close(RECORD_NOTIFICATION);
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_LOADER);
furi_record_close(RECORD_INPUT_EVENTS);

free(power);
}
Expand Down Expand Up @@ -225,6 +335,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;
SAVE_POWER_SETTINGS(&power->shutdown_idle_delay_ms);
}
power_auto_shutdown_arm(power);
power_update_info(power);
furi_record_create(RECORD_POWER, power);

Expand All @@ -251,7 +366,7 @@ int32_t power_srv(void* p) {

furi_delay_ms(1000);
}

power_auto_shutdown_inhibit(power);
power_free(power);

return 0;
Expand Down
8 changes: 8 additions & 0 deletions applications/services/power/power_service/power.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,14 @@ 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
Expand Down
5 changes: 5 additions & 0 deletions applications/services/power/power_service/power_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ 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_assert(power);
bool is_healthy = false;
Expand Down
10 changes: 10 additions & 0 deletions applications/services/power/power_service/power_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
#include <gui/view_dispatcher.h>
#include <gui/gui.h>
#include <assets_icons.h>
#include <loader/loader.h>

#include <gui/modules/popup.h>
#include "views/power_off.h"
#include <power/power_settings.h>
#include "views/power_unplug_usb.h"

#include <notification/notification_messages.h>
Expand All @@ -30,6 +32,11 @@ struct Power {
Gui* gui;
NotificationApp* notification;
FuriPubSub* event_pubsub;
FuriPubSub* settings_events;
FuriPubSub* input_events_pubsub;
FuriPubSubSubscription* input_events_subscription;
FuriPubSubSubscription* app_start_stop_subscription;
FuriPubSubSubscription* settings_events_subscription;
PowerEvent event;

PowerState state;
Expand All @@ -40,6 +47,9 @@ struct Power {
uint8_t battery_level;
uint8_t power_off_timeout;

uint32_t shutdown_idle_delay_ms;
FuriTimer* auto_shutdown_timer;
Loader* loader;
FuriMutex* api_mtx;
};

Expand Down
16 changes: 16 additions & 0 deletions applications/services/power/power_settings.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#include <toolbox/saved_struct.h>
#include <storage/storage.h>
#include "power_settings_filename.h"

#define POWER_SETTINGS_VER (1)

#define POWER_SETTINGS_PATH INT_PATH(POWER_SETTINGS_FILE_NAME)
#define POWER_SETTINGS_MAGIC (0x21)

#define SAVE_POWER_SETTINGS(x) \
saved_struct_save( \
POWER_SETTINGS_PATH, (x), sizeof(uint32_t), POWER_SETTINGS_MAGIC, POWER_SETTINGS_VER)

#define LOAD_POWER_SETTINGS(x) \
saved_struct_load( \
POWER_SETTINGS_PATH, (x), sizeof(uint32_t), POWER_SETTINGS_MAGIC, POWER_SETTINGS_VER)
3 changes: 3 additions & 0 deletions applications/services/power/power_settings_filename.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#pragma once

#define POWER_SETTINGS_FILE_NAME ".power.settings"
16 changes: 16 additions & 0 deletions applications/settings/power_settings_app/power_settings_app.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ PowerSettingsApp* power_settings_app_alloc(uint32_t first_scene) {
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);
Expand All @@ -45,11 +48,16 @@ PowerSettingsApp* power_settings_app_alloc(uint32_t first_scene) {
PowerSettingsAppViewBatteryInfo,
battery_info_get_view(app->batery_info));
app->submenu = submenu_alloc();
app->variable_item_list = variable_item_list_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));
view_dispatcher_add_view(
app->view_dispatcher,
PowerSettingsAppViewVariableItemList,
variable_item_list_get_view(app->variable_item_list));

// Set first scene
scene_manager_next_scene(app->scene_manager, first_scene);
Expand All @@ -61,16 +69,24 @@ void power_settings_app_free(PowerSettingsApp* 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_remove_view(app->view_dispatcher, PowerSettingsAppViewVariableItemList);
variable_item_list_free(app->variable_item_list);

// View dispatcher
view_dispatcher_free(app->view_dispatcher);
scene_manager_free(app->scene_manager);

// Records
furi_record_close(RECORD_POWER);
furi_record_close(RECORD_GUI);

free(app);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
#include "views/battery_info.h"
#include <gui/modules/submenu.h>
#include <gui/modules/dialog_ex.h>
#include <gui/modules/variable_item_list.h>

#include <power/power_settings.h>
#include "scenes/power_settings_scene.h"

typedef struct {
Expand All @@ -23,10 +25,14 @@ typedef struct {
Submenu* submenu;
DialogEx* dialog;
PowerInfo info;
VariableItemList* variable_item_list;
uint32_t shutdown_idle_delay_ms;
FuriPubSub* settings_events;
} PowerSettingsApp;

typedef enum {
PowerSettingsAppViewBatteryInfo,
PowerSettingsAppViewSubmenu,
PowerSettingsAppViewDialog,
PowerSettingsAppViewVariableItemList
} PowerSettingsAppView;
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ 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, shutdown_idle, ShutdownIdle)
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#include "../power_settings_app.h"
#include <lib/toolbox/value_index.h>

#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);
skotopes marked this conversation as resolved.
Show resolved Hide resolved
furi_pubsub_publish(app->settings_events, &app->shutdown_idle_delay_ms);
variable_item_list_reset(app->variable_item_list);
}
Loading