From 38f9ba81cdddf27c751b00653532baee25a313b8 Mon Sep 17 00:00:00 2001 From: Michael Carlberg Date: Sat, 19 Nov 2016 15:42:31 +0100 Subject: [PATCH] feat(battery): Estimated time remaining Adds a new token %time% that will display an estimate of the remaining time until fully charged/discharged --- cmake/build/options.cmake | 32 +++--- cmake/build/summary.cmake | 47 ++++++--- include/config.hpp.cmake | 46 +++++---- include/modules/battery.hpp | 38 +++++-- src/modules/battery.cpp | 199 +++++++++++++++++++++++------------- 5 files changed, 236 insertions(+), 126 deletions(-) diff --git a/cmake/build/options.cmake b/cmake/build/options.cmake index d2f59b4b..3d2a3bd3 100644 --- a/cmake/build/options.cmake +++ b/cmake/build/options.cmake @@ -59,27 +59,35 @@ option(ENABLE_DAMAGE_EXT "Enable Damage X extension" OFF) set(SETTING_ALSA_SOUNDCARD "default" 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" CACHE STRING "Path to bspwm socket") set(SETTING_BSPWM_STATUS_PREFIX "W" 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" CACHE STRING "Path to file containing cpu info") set(SETTING_PATH_MEMORY_INFO "/proc/meminfo" 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%" 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") # }}} diff --git a/cmake/build/summary.cmake b/cmake/build/summary.cmake index ecc11464..3d455f72 100644 --- a/cmake/build/summary.cmake +++ b/cmake/build/summary.cmake @@ -32,18 +32,35 @@ else() message(STATUS " Linking C++ library: system default") endif() -message(STATUS "---------------------------") -message(STATUS " Build testsuite ${BUILD_TESTS}") -message(STATUS " Enable debug logger ${DEBUG_LOGGER}") -message(STATUS " Enable extra tracing ${VERBOSE_TRACELOG}") -message(STATUS " Enable ccache support ${ENABLE_CCACHE}") -message(STATUS "---------------------------") -message(STATUS " Enable alsa support ${ENABLE_ALSA}") -message(STATUS " Enable i3 support ${ENABLE_I3}") -message(STATUS " Enable mpd support ${ENABLE_MPD}") -message(STATUS " Enable network support ${ENABLE_NETWORK}") -message(STATUS "---------------------------") -message(STATUS " Enable X RandR ${ENABLE_RANDR_EXT}") -message(STATUS " Enable X Render ${ENABLE_RENDER_EXT}") -message(STATUS " Enable X Damage ${ENABLE_DAMAGE_EXT}") -message(STATUS "---------------------------") +message(STATUS "--------------------------------------------") +message(STATUS " Build testsuite ${BUILD_TESTS}") +message(STATUS " Enable debug logger ${DEBUG_LOGGER}") +message(STATUS " Enable extra tracing ${VERBOSE_TRACELOG}") +message(STATUS " Enable ccache support ${ENABLE_CCACHE}") +message(STATUS "--------------------------------------------") +message(STATUS " Enable alsa support ${ENABLE_ALSA}") +message(STATUS " Enable i3 support ${ENABLE_I3}") +message(STATUS " Enable mpd support ${ENABLE_MPD}") +message(STATUS " Enable network support ${ENABLE_NETWORK}") +message(STATUS "--------------------------------------------") +message(STATUS " Enable X RandR ${ENABLE_RANDR_EXT}") +message(STATUS " Enable X Render ${ENABLE_RENDER_EXT}") +message(STATUS " Enable X Damage ${ENABLE_DAMAGE_EXT}") +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 "--------------------------------------------") diff --git a/include/config.hpp.cmake b/include/config.hpp.cmake index 57080092..005754c1 100644 --- a/include/config.hpp.cmake +++ b/include/config.hpp.cmake @@ -28,18 +28,23 @@ #endif #define BUILDER_SPACE_TOKEN "%__" + #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_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_MEMORY_INFO "@SETTING_PATH_MEMORY_INFO@" -#define PATH_TEMPERATURE_INFO "@SETTING_PATH_TEMPERATURE_INFO@" #define PATH_MESSAGING_FIFO "@SETTING_PATH_MESSAGING_FIFO@" +#define PATH_TEMPERATURE_INFO "@SETTING_PATH_TEMPERATURE_INFO@" auto print_build_info = []() { // clang-format off @@ -51,18 +56,23 @@ auto print_build_info = []() { << (ENABLE_MPD ? "+" : "-") << "mpd " << (ENABLE_NETWORK ? "+" : "-") << "network " << "\n\n" - << "ALSA_SOUNDCARD " << ALSA_SOUNDCARD << "\n" - << "BSPWM_SOCKET_PATH " << BSPWM_SOCKET_PATH << "\n" - << "BSPWM_STATUS_PREFIX " << BSPWM_STATUS_PREFIX << "\n" - << "BUILDER_SPACE_TOKEN " << BUILDER_SPACE_TOKEN << "\n" - << "CONNECTION_TEST_IP " << CONNECTION_TEST_IP << "\n" - << "PATH_ADAPTER_STATUS " << PATH_ADAPTER_STATUS << "\n" - << "PATH_BACKLIGHT_MAX " << PATH_BACKLIGHT_MAX << "\n" - << "PATH_BACKLIGHT_VAL " << PATH_BACKLIGHT_VAL << "\n" - << "PATH_BATTERY_CAPACITY " << PATH_BATTERY_CAPACITY << "\n" - << "PATH_CPU_INFO " << PATH_CPU_INFO << "\n" - << "PATH_MEMORY_INFO " << PATH_MEMORY_INFO << "\n" - << "PATH_TEMPERATURE_INFO " << PATH_TEMPERATURE_INFO << "\n"; + << "ALSA_SOUNDCARD " << ALSA_SOUNDCARD << "\n" + << "BSPWM_SOCKET_PATH " << BSPWM_SOCKET_PATH << "\n" + << "BSPWM_STATUS_PREFIX " << BSPWM_STATUS_PREFIX << "\n" + << "BUILDER_SPACE_TOKEN " << BUILDER_SPACE_TOKEN << "\n" + << "CONNECTION_TEST_IP " << CONNECTION_TEST_IP << "\n" + << "PATH_ADAPTER_STATUS " << PATH_ADAPTER_STATUS << "\n" + << "PATH_BACKLIGHT_MAX " << PATH_BACKLIGHT_MAX << "\n" + << "PATH_BACKLIGHT_VAL " << PATH_BACKLIGHT_VAL << "\n" + << "PATH_BATTERY_CAPACITY " << PATH_BATTERY_CAPACITY << "\n" + << "PATH_BATTERY_CAPACITY " << PATH_BATTERY_CAPACITY << "\n" + << "PATH_BATTERY_CAPACITY_MAX " << PATH_BATTERY_CAPACITY_MAX << "\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 }; diff --git a/include/modules/battery.hpp b/include/modules/battery.hpp index ccc522d4..9cf36436 100644 --- a/include/modules/battery.hpp +++ b/include/modules/battery.hpp @@ -1,5 +1,6 @@ #pragma once +#include "common.hpp" #include "config.hpp" #include "drawtypes/animation.hpp" #include "drawtypes/label.hpp" @@ -13,7 +14,23 @@ POLYBAR_NS 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 { public: using inotify_module::inotify_module; @@ -29,6 +46,7 @@ namespace modules { protected: int current_percentage(); battery_state current_state(); + string current_time(); void subthread(); private: @@ -43,6 +61,8 @@ namespace modules { static constexpr auto TAG_LABEL_DISCHARGING = ""; static constexpr auto TAG_LABEL_FULL = ""; + static const int SKIP_N_UNCHANGED{3}; + animation_t m_animation_charging; ramp_t m_ramp_capacity; progressbar_t m_bar_capacity; @@ -50,18 +70,14 @@ namespace modules { label_t m_label_discharging; label_t m_label_full; - string m_battery; - string m_adapter; - string m_path_capacity; - string m_path_adapter; - - battery_state m_state = battery_state::UNKNOWN; - std::atomic_int m_percentage{0}; - + battery_state m_state{battery_state::DISCHARGING}; + map m_valuepath; + std::atomic m_percentage{0}; + int m_fullat{100}; interval_t m_interval; chrono::system_clock::time_point m_lastpoll; - - int m_fullat = 100; + string m_timeformat; + int m_unchanged{SKIP_N_UNCHANGED}; }; } diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index ab782c93..6fe4e1f1 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -4,41 +4,40 @@ POLYBAR_NS namespace modules { + /** + * Bootstrap module by setting up required components + */ void battery_module::setup() { - // Load configuration values {{{ + // Load configuration values + auto battery = m_conf.get(name(), "battery", "BAT0"); + auto adapter = m_conf.get(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(name(), "battery", "BAT0"); - m_adapter = m_conf.get(name(), "adapter", "ADP1"); m_fullat = m_conf.get(name(), "full-at", 100); m_interval = interval_t{m_conf.get(name(), "poll-interval", 5.0f)}; m_lastpoll = chrono::system_clock::now(); - m_path_capacity = string_util::replace(PATH_BATTERY_CAPACITY, "%battery%", m_battery); - 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 {{{ - + // Load state and capacity level m_percentage = current_percentage(); m_state = current_state(); - // }}} - // Add formats and elements {{{ - + // Add formats and elements m_formatter->add(FORMAT_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( - 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)) 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); if (m_formatter->has(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%"); - } - if (m_formatter->has(TAG_LABEL_DISCHARGING, FORMAT_DISCHARGING)) { - m_label_discharging = - load_optional_label(m_conf, name(), TAG_LABEL_DISCHARGING, "%percentage%"); - } - if (m_formatter->has(TAG_LABEL_FULL, FORMAT_FULL)) { + if (m_formatter->has(TAG_LABEL_DISCHARGING, FORMAT_DISCHARGING)) + m_label_discharging = 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%"); + + // 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(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() { inotify_module::start(); m_threads.emplace_back(thread(&battery_module::subthread, this)); } + /** + * Release wake lock when stopping the module + */ void battery_module::teardown() { 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() { - // Manually poll values as a fallback for systems that - // doesn't report inotify events for files on sysfs if (m_interval.count() > 0) { auto now = chrono::system_clock::now(); if (chrono::duration_cast(now - m_lastpoll) > m_interval) { m_lastpoll = now; 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(); } + /** + * Update values when tracked files have changed + */ bool battery_module::on_event(inotify_event* event) { if (event != nullptr) { m_log.trace("%s: Inotify event reported for %s", name(), event->filename); @@ -106,45 +121,29 @@ namespace modules { percentage = current_percentage(); } - // Ignore unchanged state - if (event != nullptr && m_state == state && m_percentage == percentage) { + if (event != nullptr && state == m_state && percentage == m_percentage && m_unchanged--) { return false; } m_percentage = percentage; m_state = state; + m_unchanged = SKIP_N_UNCHANGED; 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; - seconds -= 3600 * hours; - int minutes = seconds / 60; - seconds -= 60 * minutes; - char buffer[9]{0}; - snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d", hours, minutes, seconds); - time_remaining = buffer; - - - if (m_label_charging) { + if (m_state == battery_state::CHARGING && m_label_charging) { + if (!m_timeformat.empty()) + time_remaining = current_time(); m_label_charging->reset_tokens(); m_label_charging->replace_token("%percentage%", to_string(m_percentage) + "%"); m_label_charging->replace_token("%time%", time_remaining); - } - if (m_label_discharging) { + } else if (m_state == battery_state::DISCHARGING && m_label_discharging) { + if (!m_timeformat.empty()) + time_remaining = current_time(); m_label_discharging->reset_tokens(); m_label_discharging->replace_token("%percentage%", to_string(m_percentage) + "%"); m_label_discharging->replace_token("%time%", time_remaining); - } - if (m_label_full) { + } else if (m_state == battery_state::FULL && m_label_full) { m_label_full->reset_tokens(); m_label_full->replace_token("%percentage%", to_string(m_percentage) + "%"); } @@ -152,6 +151,9 @@ namespace modules { return true; } + /** + * Get the output format based on state + */ string battery_module::get_format() const { if (m_state == battery_state::FULL) return FORMAT_FULL; @@ -161,6 +163,9 @@ namespace modules { return FORMAT_DISCHARGING; } + /** + * Generate the module output using defined drawtypes + */ bool battery_module::build(builder* builder, string tag) const { if (tag == TAG_ANIMATION_CHARGING) builder->node(m_animation_charging->get()); @@ -179,15 +184,18 @@ namespace modules { return true; } + /** + * Get the current battery 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()) { - return battery_state::UNKNOWN; + return battery_state::DISCHARGING; } else if (adapter_status[0] == '0') { return battery_state::DISCHARGING; } else if (adapter_status[0] != '1') { - return battery_state::UNKNOWN; + return battery_state::DISCHARGING; } else if (m_percentage < m_fullat) { return battery_state::CHARGING; } else { @@ -199,7 +207,7 @@ namespace modules { * Get the current capacity level */ 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(std::atof(capacity.c_str()), 0, 100); 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(m_percentage), cap, + rate, volt); + + if (sec.count() > 0) { + t.tm_hour = chrono::duration_cast(sec).count(); + sec -= chrono::seconds{3600 * t.tm_hour}; + t.tm_min = chrono::duration_cast(sec).count(); + sec -= chrono::seconds{60 * t.tm_min}; + t.tm_sec = chrono::duration_cast(sec).count(); + } + } + + char buffer[256]{0}; + strftime(buffer, sizeof(buffer), m_timeformat.c_str(), &t); + + return {buffer}; + } + /** * Subthread runner that emit update events * to refresh in case it is used.