Files
wezterm-config/battery.lua
T
rootiest b7897d344d Test multiplatform support
Testing changes to better support other platforms
2024-10-01 10:52:37 -04:00

234 lines
7.7 KiB
Lua

--- Battery module for Wezterm.
---@module "battery"
---@class M The battery module
---@field invert boolean Whether to invert colors
---@function get_batteries() Get the list of batteries
---@function get_battery_icons() Get the battery icons
---@function get_battery_stats() Get the battery stats
local M = {}
local wezterm = require("wezterm")
--- Whether to invert the colors for light backgrounds.
M.invert = false
--- Converts a hex color to its opposite brightness.
---@param hex_color string The hex color in the format "#RRGGBB".
---@param invert? boolean Whether to invert the colors (optional).
---@return string hex_color The hex color with the opposite brightness.
local function invert_color_brightness(hex_color, invert)
if invert == nil then
invert = M.invert
end
if invert == false then
return hex_color
end
-- Validate the input hex_color format.
if not hex_color:match("^#%x%x%x%x%x%x$") then
error("Invalid hex color format. Use #RRGGBB.")
end
-- Convert hex to RGB.
local r = tonumber(hex_color:sub(2, 3), 16)
local g = tonumber(hex_color:sub(4, 5), 16)
local b = tonumber(hex_color:sub(6, 7), 16)
-- Validate RGB values.
if not (r and g and b and r >= 0 and r <= 255 and g >= 0 and g <= 255 and b >= 0 and b <= 255) then
error("Invalid RGB values extracted from hex color.")
end
-- Calculate brightness.
local brightness = (r * 0.299 + g * 0.587 + b * 0.114)
-- Invert color based on brightness.
if brightness < 128 then
-- Dark color: make it lighter.
r = math.min(r + (255 - r) * 0.33, 255)
g = math.min(g + (255 - g) * 0.33, 255)
b = math.min(b + (255 - b) * 0.33, 255)
else
-- Light color: make it darker.
r = math.max(r - r * 0.66, 0)
g = math.max(g - g * 0.66, 0)
b = math.max(b - b * 0.66, 0)
end
-- Convert RGB back to hex.
return string.format("#%02x%02x%02x", math.floor(r + 0.5), math.floor(g + 0.5), math.floor(b + 0.5))
end
--- RGBColor class representing an RGB color.
---@class RGBColor
---@field r number Red value (0-255).
---@field g number Green value (0-255).
---@field b number Blue value (0-255).
--- Blends two RGB colors together based on a blend factor.
---@param color1 RGBColor The first RGB color.
---@param color2 RGBColor The second RGB color.
---@param factor number The blend factor (between 0 and 1).
---@return RGBColor color The blended RGB color.
local function blend_color(color1, color2, factor)
return {
r = color1.r + (color2.r - color1.r) * factor,
g = color1.g + (color2.g - color1.g) * factor,
b = color1.b + (color2.b - color1.b) * factor,
}
end
--- Rounds a decimal value to the nearest integer.
---@param val number The value to round.
---@return integer The rounded value.
local function round(val)
return math.floor(val + 0.5)
end
--- Converts an RGB color to a hex string.
---@param color RGBColor The RGB color to convert.
---@return string hex_color The hex string representation of the color.
local function rgb_to_hex(color)
return string.format("#%02x%02x%02x", round(color.r), round(color.g), round(color.b))
end
--- Gets the color representing a battery charge level.
---@param charge number The charge level (0.0 to 1.0).
---@return string hex_color The corresponding hex color.
local function get_charge_color(charge)
local red = { r = 255, g = 0, b = 0 }
local yellow = { r = 255, g = 255, b = 0 }
local green = { r = 0, g = 255, b = 0 }
local color = {}
if charge <= 0.5 then
local factor = charge / 0.5
color = blend_color(red, yellow, factor)
else
local factor = (charge - 0.5) / 0.5
color = blend_color(yellow, green, factor)
end
return invert_color_brightness(rgb_to_hex(color))
end
--- BatteryComponent class representing battery information.
---@class BatteryComponent
---@field battery string The battery icon and percentage. (e.g. "󰂉 100%")
---@field remaining fun(): string The remaining time as a string. (e.g. "10:00")
---@field icon string The icon representing the battery status. (e.g. "󰢜 ")
---@field percent number The battery percentage. (0.0 to 1.0)
---@field time number The remaining time in minutes.
---@field condition string The battery condition ("Full", "Empty", "Charging", "Discharging", "Unknown").
--- Returns battery components for each battery.
--- If no batteries are detected, an empty table is returned.
---@return BatteryComponent[] batteries A list of battery components.
---@see BatteryComponent
function M.get_batteries()
local batteries = {}
local battery_info = wezterm.battery_info()
local icons_charging =
{ "󰢟 ", "󰢜 ", "󰂆 ", "󰂇 ", "󰂈 ", "󰢝 ", "󰂉 ", "󰢞 ", "󰂊 ", "󰂋 ", "󰂅 " }
local icons_discharging =
{ "󰂎 ", "󰁺 ", "󰁻 ", "󰁼 ", "󰁽 ", "󰁾 ", "󰁿 ", "󰂀 ", "󰂁 ", "󰂂 ", "󰁹 " }
for _, b in ipairs(battery_info) do
local condition = b.state
local charge = b.state_of_charge
local remaining = 0
local percent = charge * 100
local icon = -- Default battery state is Unknown
wezterm.format({ { Foreground = { Color = invert_color_brightness("#232634") } }, { Text = "󰂑 " } })
if condition == "Full" then -- Battery is 100% charged
icon =
wezterm.format({ { Foreground = { Color = invert_color_brightness("#93e398") } }, { Text = "󰁹 " } })
elseif condition == "Empty" then -- Battery is 0% charged
icon =
wezterm.format({ { Foreground = { Color = invert_color_brightness("#f37ca0") } }, { Text = "󱃍 " } })
elseif condition == "Charging" then -- Battery is charging
remaining = b.time_to_full
icon = wezterm.format({
{ Foreground = { Color = get_charge_color(charge) } },
{ Text = icons_charging[math.min(math.ceil(charge * 10), 11)] },
})
elseif condition == "Discharging" then -- Battery is discharging
remaining = b.time_to_empty
icon = wezterm.format({
{ Foreground = { Color = get_charge_color(charge) } },
{ Text = icons_discharging[math.min(math.ceil(charge * 10), 11)] },
})
elseif condition == "Unknown" and charge > 0.5 then -- Battery is (probably) at charge limit
icon =
wezterm.format({ { Foreground = { Color = invert_color_brightness("#93e398") } }, { Text = "󱈑 " } })
end
table.insert(batteries, {
battery = icon .. string.format("%.2f%%", percent),
remaining = function()
if remaining and remaining > 0 then
return string.format("%d:%02d", math.floor(remaining / 60), math.floor(remaining % 60))
else
return ""
end
end,
icon = icon,
percent = percent,
time = remaining,
condition = condition,
})
end
return batteries
end
--- Returns battery icons for all batteries.
--- If no batteries are detected, an empty string is returned.
---@return string battery_icons A formatted string containing the battery icons.
function M.get_battery_icons()
local battery_states = M.get_batteries()
if #battery_states == 0 then
return ""
end
local result = {}
for _, state in ipairs(battery_states) do
table.insert(result, state.icon)
end
return table.concat(result, " | ")
end
--- Returns battery statistics for all batteries.
--- If no batteries are detected, an empty string is returned.
---@return string battery_stats A formatted string containing the battery stats.
function M.get_battery_stats()
local battery_states = M.get_batteries()
if #battery_states == 0 then
return ""
end
local result = {}
for _, state in ipairs(battery_states) do
local color = get_charge_color(state.percent / 100)
local percent = wezterm.format({
{ Foreground = { Color = color } },
{ Text = string.format(" (%.2f%%)", state.percent) },
})
local remaining_time = wezterm.format({
{ Foreground = { Color = color } },
{ Text = state.remaining() },
})
table.insert(result, string.format("%s%s", state.icon, percent) .. remaining_time)
end
return table.concat(result, " | ")
end
return M