feat(battery): Estimated time remaining

Adds a new token %time% that will display an
estimate of the remaining time until fully
charged/discharged
This commit is contained in:
Michael Carlberg 2016-11-19 15:42:31 +01:00
parent 30c1cb3d50
commit 38f9ba81cd
5 changed files with 236 additions and 126 deletions

View File

@ -59,27 +59,35 @@ option(ENABLE_DAMAGE_EXT "Enable Damage X extension" OFF)
set(SETTING_ALSA_SOUNDCARD "default" set(SETTING_ALSA_SOUNDCARD "default"
CACHE STRING "Name of the ALSA soundcard driver") CACHE STRING "Name of the ALSA soundcard driver")
set(SETTING_CONNECTION_TEST_IP "8.8.8.8"
CACHE STRING "Address to ping when testing network connection")
set(SETTING_PATH_BACKLIGHT_VAL "/sys/class/backlight/%card%/brightness"
CACHE STRING "Path to file containing the current backlight value")
set(SETTING_PATH_BACKLIGHT_MAX "/sys/class/backlight/%card%/max_brightness"
CACHE STRING "Path to file containing the maximum backlight value")
set(SETTING_PATH_BATTERY_CAPACITY "/sys/class/power_supply/%battery%/capacity"
CACHE STRING "Path to file containing the current battery capacity")
set(SETTING_PATH_ADAPTER_STATUS "/sys/class/power_supply/%adapter%/online"
CACHE STRING "Path to file containing the current adapter status")
set(SETTING_BSPWM_SOCKET_PATH "/tmp/bspwm_0_0-socket" set(SETTING_BSPWM_SOCKET_PATH "/tmp/bspwm_0_0-socket"
CACHE STRING "Path to bspwm socket") CACHE STRING "Path to bspwm socket")
set(SETTING_BSPWM_STATUS_PREFIX "W" set(SETTING_BSPWM_STATUS_PREFIX "W"
CACHE STRING "Prefix prepended to the bspwm status line") CACHE STRING "Prefix prepended to the bspwm status line")
set(SETTING_CONNECTION_TEST_IP "8.8.8.8"
CACHE STRING "Address to ping when testing network connection")
set(SETTING_PATH_ADAPTER_STATUS "/sys/class/power_supply/%adapter%/online"
CACHE STRING "Path to file containing the current adapter status")
set(SETTING_PATH_BACKLIGHT_MAX "/sys/class/backlight/%card%/max_brightness"
CACHE STRING "Path to file containing the maximum backlight value")
set(SETTING_PATH_BACKLIGHT_VAL "/sys/class/backlight/%card%/brightness"
CACHE STRING "Path to file containing the current backlight value")
set(SETTING_PATH_BATTERY_CAPACITY "/sys/class/power_supply/%battery%/charge_now"
CACHE STRING "Path to file containing the current battery capacity")
set(SETTING_PATH_BATTERY_CAPACITY_MAX "/sys/class/power_supply/%battery%/charge_full"
CACHE STRING "Path to file containing the current battery capacity")
set(SETTING_PATH_BATTERY_CAPACITY_PERC "/sys/class/power_supply/%battery%/capacity"
CACHE STRING "Path to file containing the current battery capacity in percentage")
set(SETTING_PATH_BATTERY_RATE "/sys/class/power_supply/%battery%/current_now"
CACHE STRING "Path to file containing the current battery rate")
set(SETTING_PATH_BATTERY_VOLTAGE "/sys/class/power_supply/%battery%/voltage_now"
CACHE STRING "Path to file containing the current battery voltage")
set(SETTING_PATH_CPU_INFO "/proc/stat" set(SETTING_PATH_CPU_INFO "/proc/stat"
CACHE STRING "Path to file containing cpu info") CACHE STRING "Path to file containing cpu info")
set(SETTING_PATH_MEMORY_INFO "/proc/meminfo" set(SETTING_PATH_MEMORY_INFO "/proc/meminfo"
CACHE STRING "Path to file containing memory info") CACHE STRING "Path to file containing memory info")
set(SETTING_PATH_TEMPERATURE_INFO "/sys/class/thermal/thermal_zone%zone%/temp"
CACHE STRING "Path to file containing the current temperature")
set(SETTING_PATH_MESSAGING_FIFO "/tmp/polybar_mqueue.%pid%" set(SETTING_PATH_MESSAGING_FIFO "/tmp/polybar_mqueue.%pid%"
CACHE STRING "Path to file containing the current temperature") CACHE STRING "Path to file containing the current temperature")
set(SETTING_PATH_TEMPERATURE_INFO "/sys/class/thermal/thermal_zone%zone%/temp"
CACHE STRING "Path to file containing the current temperature")
# }}} # }}}

View File

@ -32,18 +32,35 @@ else()
message(STATUS " Linking C++ library: system default") message(STATUS " Linking C++ library: system default")
endif() endif()
message(STATUS "---------------------------") message(STATUS "--------------------------------------------")
message(STATUS " Build testsuite ${BUILD_TESTS}") message(STATUS " Build testsuite ${BUILD_TESTS}")
message(STATUS " Enable debug logger ${DEBUG_LOGGER}") message(STATUS " Enable debug logger ${DEBUG_LOGGER}")
message(STATUS " Enable extra tracing ${VERBOSE_TRACELOG}") message(STATUS " Enable extra tracing ${VERBOSE_TRACELOG}")
message(STATUS " Enable ccache support ${ENABLE_CCACHE}") message(STATUS " Enable ccache support ${ENABLE_CCACHE}")
message(STATUS "---------------------------") message(STATUS "--------------------------------------------")
message(STATUS " Enable alsa support ${ENABLE_ALSA}") message(STATUS " Enable alsa support ${ENABLE_ALSA}")
message(STATUS " Enable i3 support ${ENABLE_I3}") message(STATUS " Enable i3 support ${ENABLE_I3}")
message(STATUS " Enable mpd support ${ENABLE_MPD}") message(STATUS " Enable mpd support ${ENABLE_MPD}")
message(STATUS " Enable network support ${ENABLE_NETWORK}") message(STATUS " Enable network support ${ENABLE_NETWORK}")
message(STATUS "---------------------------") message(STATUS "--------------------------------------------")
message(STATUS " Enable X RandR ${ENABLE_RANDR_EXT}") message(STATUS " Enable X RandR ${ENABLE_RANDR_EXT}")
message(STATUS " Enable X Render ${ENABLE_RENDER_EXT}") message(STATUS " Enable X Render ${ENABLE_RENDER_EXT}")
message(STATUS " Enable X Damage ${ENABLE_DAMAGE_EXT}") message(STATUS " Enable X Damage ${ENABLE_DAMAGE_EXT}")
message(STATUS "---------------------------") message(STATUS "--------------------------------------------")
message(STATUS " ALSA_SOUNDCARD ${SETTING_ALSA_SOUNDCARD}")
message(STATUS " BSPWM_SOCKET_PATH ${SETTING_BSPWM_SOCKET_PATH}")
message(STATUS " BSPWM_STATUS_PREFIX ${SETTING_BSPWM_STATUS_PREFIX}")
message(STATUS " CONNECTION_TEST_IP ${SETTING_CONNECTION_TEST_IP}")
message(STATUS " PATH_ADAPTER_STATUS ${SETTING_PATH_ADAPTER_STATUS}")
message(STATUS " PATH_BACKLIGHT_VAL ${SETTING_PATH_BACKLIGHT_VAL}")
message(STATUS " PATH_BACKLIGHT_MAX ${SETTING_PATH_BACKLIGHT_MAX}")
message(STATUS " PATH_BATTERY_CAPACITY ${SETTING_PATH_BATTERY_CAPACITY}")
message(STATUS " PATH_BATTERY_CAPACITY_MAX ${SETTING_PATH_BATTERY_CAPACITY_MAX}")
message(STATUS " PATH_BATTERY_CAPACITY_PERC ${SETTING_PATH_BATTERY_CAPACITY_PERC}")
message(STATUS " PATH_BATTERY_VOLTAGE ${SETTING_PATH_BATTERY_VOLTAGE}")
message(STATUS " PATH_BATTERY_RATE ${SETTING_PATH_BATTERY_RATE}")
message(STATUS " PATH_CPU_INFO ${SETTING_PATH_CPU_INFO}")
message(STATUS " PATH_MEMORY_INFO ${SETTING_PATH_MEMORY_INFO}")
message(STATUS " PATH_MESSAGING_FIFO ${SETTING_PATH_MESSAGING_FIFO}")
message(STATUS " PATH_TEMPERATURE_INFO ${SETTING_PATH_TEMPERATURE_INFO}")
message(STATUS "--------------------------------------------")

View File

@ -28,18 +28,23 @@
#endif #endif
#define BUILDER_SPACE_TOKEN "%__" #define BUILDER_SPACE_TOKEN "%__"
#define ALSA_SOUNDCARD "@SETTING_ALSA_SOUNDCARD@" #define ALSA_SOUNDCARD "@SETTING_ALSA_SOUNDCARD@"
#define CONNECTION_TEST_IP "@SETTING_CONNECTION_TEST_IP@"
#define PATH_BACKLIGHT_VAL "@SETTING_PATH_BACKLIGHT_VAL@"
#define PATH_BACKLIGHT_MAX "@SETTING_PATH_BACKLIGHT_MAX@"
#define PATH_BATTERY_CAPACITY "@SETTING_PATH_BATTERY_CAPACITY@"
#define PATH_ADAPTER_STATUS "@SETTING_PATH_ADAPTER_STATUS@"
#define BSPWM_SOCKET_PATH "@SETTING_BSPWM_SOCKET_PATH@" #define BSPWM_SOCKET_PATH "@SETTING_BSPWM_SOCKET_PATH@"
#define BSPWM_STATUS_PREFIX "@SETTING_BSPWM_STATUS_PREFIX@" #define BSPWM_STATUS_PREFIX "@SETTING_BSPWM_STATUS_PREFIX@"
#define CONNECTION_TEST_IP "@SETTING_CONNECTION_TEST_IP@"
#define PATH_ADAPTER_STATUS "@SETTING_PATH_ADAPTER_STATUS@"
#define PATH_BACKLIGHT_MAX "@SETTING_PATH_BACKLIGHT_MAX@"
#define PATH_BACKLIGHT_VAL "@SETTING_PATH_BACKLIGHT_VAL@"
#define PATH_BATTERY_CAPACITY "@SETTING_PATH_BATTERY_CAPACITY@"
#define PATH_BATTERY_CAPACITY_MAX "@SETTING_PATH_BATTERY_CAPACITY_MAX@"
#define PATH_BATTERY_CAPACITY_PERC "@SETTING_PATH_BATTERY_CAPACITY_PERC@"
#define PATH_BATTERY_RATE "@SETTING_PATH_BATTERY_RATE@"
#define PATH_BATTERY_VOLTAGE "@SETTING_PATH_BATTERY_VOLTAGE@"
#define PATH_CPU_INFO "@SETTING_PATH_CPU_INFO@" #define PATH_CPU_INFO "@SETTING_PATH_CPU_INFO@"
#define PATH_MEMORY_INFO "@SETTING_PATH_MEMORY_INFO@" #define PATH_MEMORY_INFO "@SETTING_PATH_MEMORY_INFO@"
#define PATH_TEMPERATURE_INFO "@SETTING_PATH_TEMPERATURE_INFO@"
#define PATH_MESSAGING_FIFO "@SETTING_PATH_MESSAGING_FIFO@" #define PATH_MESSAGING_FIFO "@SETTING_PATH_MESSAGING_FIFO@"
#define PATH_TEMPERATURE_INFO "@SETTING_PATH_TEMPERATURE_INFO@"
auto print_build_info = []() { auto print_build_info = []() {
// clang-format off // clang-format off
@ -51,18 +56,23 @@ auto print_build_info = []() {
<< (ENABLE_MPD ? "+" : "-") << "mpd " << (ENABLE_MPD ? "+" : "-") << "mpd "
<< (ENABLE_NETWORK ? "+" : "-") << "network " << (ENABLE_NETWORK ? "+" : "-") << "network "
<< "\n\n" << "\n\n"
<< "ALSA_SOUNDCARD " << ALSA_SOUNDCARD << "\n" << "ALSA_SOUNDCARD " << ALSA_SOUNDCARD << "\n"
<< "BSPWM_SOCKET_PATH " << BSPWM_SOCKET_PATH << "\n" << "BSPWM_SOCKET_PATH " << BSPWM_SOCKET_PATH << "\n"
<< "BSPWM_STATUS_PREFIX " << BSPWM_STATUS_PREFIX << "\n" << "BSPWM_STATUS_PREFIX " << BSPWM_STATUS_PREFIX << "\n"
<< "BUILDER_SPACE_TOKEN " << BUILDER_SPACE_TOKEN << "\n" << "BUILDER_SPACE_TOKEN " << BUILDER_SPACE_TOKEN << "\n"
<< "CONNECTION_TEST_IP " << CONNECTION_TEST_IP << "\n" << "CONNECTION_TEST_IP " << CONNECTION_TEST_IP << "\n"
<< "PATH_ADAPTER_STATUS " << PATH_ADAPTER_STATUS << "\n" << "PATH_ADAPTER_STATUS " << PATH_ADAPTER_STATUS << "\n"
<< "PATH_BACKLIGHT_MAX " << PATH_BACKLIGHT_MAX << "\n" << "PATH_BACKLIGHT_MAX " << PATH_BACKLIGHT_MAX << "\n"
<< "PATH_BACKLIGHT_VAL " << PATH_BACKLIGHT_VAL << "\n" << "PATH_BACKLIGHT_VAL " << PATH_BACKLIGHT_VAL << "\n"
<< "PATH_BATTERY_CAPACITY " << PATH_BATTERY_CAPACITY << "\n" << "PATH_BATTERY_CAPACITY " << PATH_BATTERY_CAPACITY << "\n"
<< "PATH_CPU_INFO " << PATH_CPU_INFO << "\n" << "PATH_BATTERY_CAPACITY " << PATH_BATTERY_CAPACITY << "\n"
<< "PATH_MEMORY_INFO " << PATH_MEMORY_INFO << "\n" << "PATH_BATTERY_CAPACITY_MAX " << PATH_BATTERY_CAPACITY_MAX << "\n"
<< "PATH_TEMPERATURE_INFO " << PATH_TEMPERATURE_INFO << "\n"; << "PATH_BATTERY_CAPACITY_PERC " << PATH_BATTERY_CAPACITY_PERC << "\n"
<< "PATH_BATTERY_RATE " << PATH_BATTERY_RATE << "\n"
<< "PATH_BATTERY_VOLTAGE " << PATH_BATTERY_VOLTAGE << "\n"
<< "PATH_CPU_INFO " << PATH_CPU_INFO << "\n"
<< "PATH_MEMORY_INFO " << PATH_MEMORY_INFO << "\n"
<< "PATH_TEMPERATURE_INFO " << PATH_TEMPERATURE_INFO << "\n";
// clang-format on // clang-format on
}; };

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "common.hpp"
#include "config.hpp" #include "config.hpp"
#include "drawtypes/animation.hpp" #include "drawtypes/animation.hpp"
#include "drawtypes/label.hpp" #include "drawtypes/label.hpp"
@ -13,7 +14,23 @@
POLYBAR_NS POLYBAR_NS
namespace modules { namespace modules {
enum class battery_state { NONE = 0, UNKNOWN, CHARGING, DISCHARGING, FULL }; enum class battery_state {
NONE = 0,
CHARGING,
DISCHARGING,
FULL,
};
enum class battery_value {
NONE = 0,
ADAPTER,
CAPACITY,
CAPACITY_MAX,
CAPACITY_PERC,
VOLTAGE,
RATE,
};
class battery_module : public inotify_module<battery_module> { class battery_module : public inotify_module<battery_module> {
public: public:
using inotify_module::inotify_module; using inotify_module::inotify_module;
@ -29,6 +46,7 @@ namespace modules {
protected: protected:
int current_percentage(); int current_percentage();
battery_state current_state(); battery_state current_state();
string current_time();
void subthread(); void subthread();
private: private:
@ -43,6 +61,8 @@ namespace modules {
static constexpr auto TAG_LABEL_DISCHARGING = "<label-discharging>"; static constexpr auto TAG_LABEL_DISCHARGING = "<label-discharging>";
static constexpr auto TAG_LABEL_FULL = "<label-full>"; static constexpr auto TAG_LABEL_FULL = "<label-full>";
static const int SKIP_N_UNCHANGED{3};
animation_t m_animation_charging; animation_t m_animation_charging;
ramp_t m_ramp_capacity; ramp_t m_ramp_capacity;
progressbar_t m_bar_capacity; progressbar_t m_bar_capacity;
@ -50,18 +70,14 @@ namespace modules {
label_t m_label_discharging; label_t m_label_discharging;
label_t m_label_full; label_t m_label_full;
string m_battery; battery_state m_state{battery_state::DISCHARGING};
string m_adapter; map<battery_value, string> m_valuepath;
string m_path_capacity; std::atomic<int> m_percentage{0};
string m_path_adapter; int m_fullat{100};
battery_state m_state = battery_state::UNKNOWN;
std::atomic_int m_percentage{0};
interval_t m_interval; interval_t m_interval;
chrono::system_clock::time_point m_lastpoll; chrono::system_clock::time_point m_lastpoll;
string m_timeformat;
int m_fullat = 100; int m_unchanged{SKIP_N_UNCHANGED};
}; };
} }

View File

@ -4,41 +4,40 @@
POLYBAR_NS POLYBAR_NS
namespace modules { namespace modules {
/**
* Bootstrap module by setting up required components
*/
void battery_module::setup() { void battery_module::setup() {
// Load configuration values {{{ // Load configuration values
auto battery = m_conf.get<string>(name(), "battery", "BAT0");
auto adapter = m_conf.get<string>(name(), "adapter", "ADP1");
m_valuepath[battery_value::ADAPTER] = string_util::replace(PATH_ADAPTER_STATUS, "%adapter%", adapter);
m_valuepath[battery_value::CAPACITY] = string_util::replace(PATH_BATTERY_CAPACITY, "%battery%", battery);
m_valuepath[battery_value::CAPACITY_MAX] = string_util::replace(PATH_BATTERY_CAPACITY_MAX, "%battery%", battery);
m_valuepath[battery_value::CAPACITY_PERC] = string_util::replace(PATH_BATTERY_CAPACITY_PERC, "%battery%", battery);
m_valuepath[battery_value::VOLTAGE] = string_util::replace(PATH_BATTERY_VOLTAGE, "%battery%", battery);
m_valuepath[battery_value::RATE] = string_util::replace(PATH_BATTERY_RATE, "%battery%", battery);
for (auto&& file : m_valuepath) {
if (!file_util::exists(file.second))
throw module_error("The file '" + file.second + "' does not exist");
}
m_battery = m_conf.get<string>(name(), "battery", "BAT0");
m_adapter = m_conf.get<string>(name(), "adapter", "ADP1");
m_fullat = m_conf.get<int>(name(), "full-at", 100); m_fullat = m_conf.get<int>(name(), "full-at", 100);
m_interval = interval_t{m_conf.get<float>(name(), "poll-interval", 5.0f)}; m_interval = interval_t{m_conf.get<float>(name(), "poll-interval", 5.0f)};
m_lastpoll = chrono::system_clock::now(); m_lastpoll = chrono::system_clock::now();
m_path_capacity = string_util::replace(PATH_BATTERY_CAPACITY, "%battery%", m_battery); // Load state and capacity level
m_path_adapter = string_util::replace(PATH_ADAPTER_STATUS, "%adapter%", m_adapter);
// }}}
// Validate paths {{{
if (!file_util::exists(m_path_capacity))
throw module_error("The file '" + m_path_capacity + "' does not exist");
if (!file_util::exists(m_path_adapter))
throw module_error("The file '" + m_path_adapter + "' does not exist");
// }}}
// Load state and capacity level {{{
m_percentage = current_percentage(); m_percentage = current_percentage();
m_state = current_state(); m_state = current_state();
// }}} // Add formats and elements
// Add formats and elements {{{
m_formatter->add(FORMAT_CHARGING, TAG_LABEL_CHARGING, m_formatter->add(FORMAT_CHARGING, TAG_LABEL_CHARGING,
{TAG_BAR_CAPACITY, TAG_RAMP_CAPACITY, TAG_ANIMATION_CHARGING, TAG_LABEL_CHARGING}); {TAG_BAR_CAPACITY, TAG_RAMP_CAPACITY, TAG_ANIMATION_CHARGING, TAG_LABEL_CHARGING});
m_formatter->add(FORMAT_DISCHARGING, TAG_LABEL_DISCHARGING,
{TAG_BAR_CAPACITY, TAG_RAMP_CAPACITY, TAG_LABEL_DISCHARGING});
m_formatter->add( m_formatter->add(
FORMAT_FULL, TAG_LABEL_FULL, {TAG_BAR_CAPACITY, TAG_RAMP_CAPACITY, TAG_LABEL_FULL}); FORMAT_DISCHARGING, TAG_LABEL_DISCHARGING, {TAG_BAR_CAPACITY, TAG_RAMP_CAPACITY, TAG_LABEL_DISCHARGING});
m_formatter->add(FORMAT_FULL, TAG_LABEL_FULL, {TAG_BAR_CAPACITY, TAG_RAMP_CAPACITY, TAG_LABEL_FULL});
if (m_formatter->has(TAG_ANIMATION_CHARGING, FORMAT_CHARGING)) if (m_formatter->has(TAG_ANIMATION_CHARGING, FORMAT_CHARGING))
m_animation_charging = load_animation(m_conf, name(), TAG_ANIMATION_CHARGING); m_animation_charging = load_animation(m_conf, name(), TAG_ANIMATION_CHARGING);
@ -46,51 +45,67 @@ namespace modules {
m_bar_capacity = load_progressbar(m_bar, m_conf, name(), TAG_BAR_CAPACITY); m_bar_capacity = load_progressbar(m_bar, m_conf, name(), TAG_BAR_CAPACITY);
if (m_formatter->has(TAG_RAMP_CAPACITY)) if (m_formatter->has(TAG_RAMP_CAPACITY))
m_ramp_capacity = load_ramp(m_conf, name(), TAG_RAMP_CAPACITY); m_ramp_capacity = load_ramp(m_conf, name(), TAG_RAMP_CAPACITY);
if (m_formatter->has(TAG_LABEL_CHARGING, FORMAT_CHARGING)) { if (m_formatter->has(TAG_LABEL_CHARGING, FORMAT_CHARGING))
m_label_charging = load_optional_label(m_conf, name(), TAG_LABEL_CHARGING, "%percentage%"); m_label_charging = load_optional_label(m_conf, name(), TAG_LABEL_CHARGING, "%percentage%");
} if (m_formatter->has(TAG_LABEL_DISCHARGING, FORMAT_DISCHARGING))
if (m_formatter->has(TAG_LABEL_DISCHARGING, FORMAT_DISCHARGING)) { m_label_discharging = load_optional_label(m_conf, name(), TAG_LABEL_DISCHARGING, "%percentage%");
m_label_discharging = if (m_formatter->has(TAG_LABEL_FULL, FORMAT_FULL))
load_optional_label(m_conf, name(), TAG_LABEL_DISCHARGING, "%percentage%");
}
if (m_formatter->has(TAG_LABEL_FULL, FORMAT_FULL)) {
m_label_full = load_optional_label(m_conf, name(), TAG_LABEL_FULL, "%percentage%"); m_label_full = load_optional_label(m_conf, name(), TAG_LABEL_FULL, "%percentage%");
// Create inotify watches
watch(m_valuepath[battery_value::CAPACITY_PERC], IN_ACCESS);
watch(m_valuepath[battery_value::ADAPTER], IN_ACCESS);
// Setup time if token is used
if (m_label_charging->has_token("%time%") || m_label_discharging->has_token("%time%")) {
if (!m_bar.locale.empty())
setlocale(LC_TIME, m_bar.locale.c_str());
m_timeformat = m_conf.get<string>(name(), "time-format", "%H:%M:%S");
} }
// }}}
// Create inotify watches {{{
watch(m_path_capacity, IN_ACCESS);
watch(m_path_adapter, IN_ACCESS);
// }}}
} }
/**
* Dispatch the subthread used to update the
* charging animation when the module is started
*/
void battery_module::start() { void battery_module::start() {
inotify_module::start(); inotify_module::start();
m_threads.emplace_back(thread(&battery_module::subthread, this)); m_threads.emplace_back(thread(&battery_module::subthread, this));
} }
/**
* Release wake lock when stopping the module
*/
void battery_module::teardown() { void battery_module::teardown() {
wakeup(); wakeup();
} }
/**
* Idle between polling inotify watches for events.
*
* If the defined interval has been reached, trigger a manual
* poll in case the inotify events aren't fired.
*
* This fallback is needed because some systems won't
* report inotify events for files on sysfs.
*/
void battery_module::idle() { void battery_module::idle() {
// Manually poll values as a fallback for systems that
// doesn't report inotify events for files on sysfs
if (m_interval.count() > 0) { if (m_interval.count() > 0) {
auto now = chrono::system_clock::now(); auto now = chrono::system_clock::now();
if (chrono::duration_cast<decltype(m_interval)>(now - m_lastpoll) > m_interval) { if (chrono::duration_cast<decltype(m_interval)>(now - m_lastpoll) > m_interval) {
m_lastpoll = now; m_lastpoll = now;
m_log.info("%s: Polling values (inotify fallback)", name()); m_log.info("%s: Polling values (inotify fallback)", name());
file_util::get_contents(m_path_capacity); file_util::get_contents(m_valuepath[battery_value::CAPACITY_PERC]);
} }
} }
inotify_module::idle(); inotify_module::idle();
} }
/**
* Update values when tracked files have changed
*/
bool battery_module::on_event(inotify_event* event) { bool battery_module::on_event(inotify_event* event) {
if (event != nullptr) { if (event != nullptr) {
m_log.trace("%s: Inotify event reported for %s", name(), event->filename); m_log.trace("%s: Inotify event reported for %s", name(), event->filename);
@ -106,45 +121,29 @@ namespace modules {
percentage = current_percentage(); percentage = current_percentage();
} }
// Ignore unchanged state if (event != nullptr && state == m_state && percentage == m_percentage && m_unchanged--) {
if (event != nullptr && m_state == state && m_percentage == percentage) {
return false; return false;
} }
m_percentage = percentage; m_percentage = percentage;
m_state = state; m_state = state;
m_unchanged = SKIP_N_UNCHANGED;
string time_remaining; string time_remaining;
auto rate = strtoul(file_util::get_contents("/sys/class/power_supply/BAT0/current_now").c_str(), nullptr, 10) / 1000;
// int rate{atoi(file_util::get_contents("/sys/class/power_supply/BAT0/current_now").c_str()) / 1000};
// time_remaining = to_string(rate);
auto capacity = strtoul(file_util::get_contents("/sys/class/power_supply/BAT0/charge_now").c_str(), nullptr, 10) / 1000;
auto voltage = strtoul(file_util::get_contents("/sys/class/power_supply/BAT0/voltage_now").c_str(), nullptr, 10) / 1000;
capacity = capacity * 1000 / voltage;
rate = rate * 1000 / voltage;
printf("rate=%lu cap=%lu volt=%lu\n", rate, capacity, voltage);
int seconds = 3600 * capacity / rate;
int hours = seconds / 3600; if (m_state == battery_state::CHARGING && m_label_charging) {
seconds -= 3600 * hours; if (!m_timeformat.empty())
int minutes = seconds / 60; time_remaining = current_time();
seconds -= 60 * minutes;
char buffer[9]{0};
snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d", hours, minutes, seconds);
time_remaining = buffer;
if (m_label_charging) {
m_label_charging->reset_tokens(); m_label_charging->reset_tokens();
m_label_charging->replace_token("%percentage%", to_string(m_percentage) + "%"); m_label_charging->replace_token("%percentage%", to_string(m_percentage) + "%");
m_label_charging->replace_token("%time%", time_remaining); m_label_charging->replace_token("%time%", time_remaining);
} } else if (m_state == battery_state::DISCHARGING && m_label_discharging) {
if (m_label_discharging) { if (!m_timeformat.empty())
time_remaining = current_time();
m_label_discharging->reset_tokens(); m_label_discharging->reset_tokens();
m_label_discharging->replace_token("%percentage%", to_string(m_percentage) + "%"); m_label_discharging->replace_token("%percentage%", to_string(m_percentage) + "%");
m_label_discharging->replace_token("%time%", time_remaining); m_label_discharging->replace_token("%time%", time_remaining);
} } else if (m_state == battery_state::FULL && m_label_full) {
if (m_label_full) {
m_label_full->reset_tokens(); m_label_full->reset_tokens();
m_label_full->replace_token("%percentage%", to_string(m_percentage) + "%"); m_label_full->replace_token("%percentage%", to_string(m_percentage) + "%");
} }
@ -152,6 +151,9 @@ namespace modules {
return true; return true;
} }
/**
* Get the output format based on state
*/
string battery_module::get_format() const { string battery_module::get_format() const {
if (m_state == battery_state::FULL) if (m_state == battery_state::FULL)
return FORMAT_FULL; return FORMAT_FULL;
@ -161,6 +163,9 @@ namespace modules {
return FORMAT_DISCHARGING; return FORMAT_DISCHARGING;
} }
/**
* Generate the module output using defined drawtypes
*/
bool battery_module::build(builder* builder, string tag) const { bool battery_module::build(builder* builder, string tag) const {
if (tag == TAG_ANIMATION_CHARGING) if (tag == TAG_ANIMATION_CHARGING)
builder->node(m_animation_charging->get()); builder->node(m_animation_charging->get());
@ -179,15 +184,18 @@ namespace modules {
return true; return true;
} }
/**
* Get the current battery state
*/
battery_state battery_module::current_state() { battery_state battery_module::current_state() {
auto adapter_status = file_util::get_contents(m_path_adapter); auto adapter_status = file_util::get_contents(m_valuepath[battery_value::ADAPTER]);
if (adapter_status.empty()) { if (adapter_status.empty()) {
return battery_state::UNKNOWN; return battery_state::DISCHARGING;
} else if (adapter_status[0] == '0') { } else if (adapter_status[0] == '0') {
return battery_state::DISCHARGING; return battery_state::DISCHARGING;
} else if (adapter_status[0] != '1') { } else if (adapter_status[0] != '1') {
return battery_state::UNKNOWN; return battery_state::DISCHARGING;
} else if (m_percentage < m_fullat) { } else if (m_percentage < m_fullat) {
return battery_state::CHARGING; return battery_state::CHARGING;
} else { } else {
@ -199,7 +207,7 @@ namespace modules {
* Get the current capacity level * Get the current capacity level
*/ */
int battery_module::current_percentage() { int battery_module::current_percentage() {
auto capacity = file_util::get_contents(m_path_capacity); auto capacity = file_util::get_contents(m_valuepath[battery_value::CAPACITY_PERC]);
auto value = math_util::cap<int>(std::atof(capacity.c_str()), 0, 100); auto value = math_util::cap<int>(std::atof(capacity.c_str()), 0, 100);
if (value >= m_fullat) { if (value >= m_fullat) {
@ -209,6 +217,57 @@ namespace modules {
} }
} }
/**
* Get estimate of remaining time until fully dis-/charged
*/
string battery_module::current_time() {
if (m_state == battery_state::FULL) {
return "";
}
int rate{atoi(file_util::get_contents(m_valuepath[battery_value::RATE]).c_str()) / 1000};
int volt{atoi(file_util::get_contents(m_valuepath[battery_value::VOLTAGE]).c_str()) / 1000};
int now{atoi(file_util::get_contents(m_valuepath[battery_value::CAPACITY]).c_str()) / 1000};
int max{atoi(file_util::get_contents(m_valuepath[battery_value::CAPACITY_MAX]).c_str()) / 1000};
int cap{0};
if (m_state == battery_state::CHARGING) {
cap = max - now;
} else if (m_state == battery_state::DISCHARGING) {
cap = now;
}
struct tm t {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
if (rate && volt && cap) {
cap = cap * 1000 / volt;
rate = rate * 1000 / volt;
if (!rate)
rate = -1;
chrono::seconds sec{3600 * cap / rate};
m_log.trace("%s: sec=%d %d%% cap=%lu rate=%lu volt=%lu", name(), sec.count(), static_cast<int>(m_percentage), cap,
rate, volt);
if (sec.count() > 0) {
t.tm_hour = chrono::duration_cast<chrono::hours>(sec).count();
sec -= chrono::seconds{3600 * t.tm_hour};
t.tm_min = chrono::duration_cast<chrono::minutes>(sec).count();
sec -= chrono::seconds{60 * t.tm_min};
t.tm_sec = chrono::duration_cast<chrono::seconds>(sec).count();
}
}
char buffer[256]{0};
strftime(buffer, sizeof(buffer), m_timeformat.c_str(), &t);
return {buffer};
}
/** /**
* Subthread runner that emit update events * Subthread runner that emit update events
* to refresh <animation-charging> in case it is used. * to refresh <animation-charging> in case it is used.