Add bluetooth widget

This commit is contained in:
Relz 2022-10-04 15:23:22 +03:00
parent ba7c276fec
commit 3685d1c7f7
8 changed files with 200 additions and 2 deletions

View file

@ -93,6 +93,19 @@ You can switch theme mode in menu.
PS: also in rc.lua and theme.lua you can make your own experiments, suggestions are appreciated. For example, you can create widget for bluetooth, add calendar popup to ClockCalendarWidget, extend theme modes. Let's make the world better together!
## Extend connected bluetooth device info with battery level
Bluetooth widget shows alias name for connected bluetooth device. There is a way to show battery level also. This feature is not enabled by default. You have to turn on it manually. To do it, edit `bluetooth.service` file by adding `--experimental` flag to bluetooth daemon executing:
```ini
ExecStart=/usr/lib/bluetooth/bluetoothd
```
change to
```ini
ExecStart=/usr/lib/bluetooth/bluetoothd --experimental
```
## Subscribe to Direct Rendering Manager change event
There is `update_screens` function in rc.lua that configures _xrandr_ output. It's useful to call this function when external monitors is connected/disconnected.

View file

@ -0,0 +1,95 @@
local wibox = require("wibox")
local gears = require("gears")
local beautiful = require("beautiful")
BluetoothWidget_prototype = function()
local this = {}
this.__public_static = {
-- Public Static Variables
-- Public Static Funcs
}
this.__private_static = {
-- Private Static Variables
config_path = gears.filesystem.get_configuration_dir()
-- Private Static Funcs
}
this.__public = {
-- Public Variables
name = "BluetoothWidget",
icon = wibox.widget.imagebox(),
value = wibox.container.background()
-- Public Funcs
}
this.__private = {
-- Private Variables
bluetooth_status = false,
bluetooth_device = nil,
-- Private Funcs
update = function()
get_bluetooth_device(function(bluetooth_device)
this.__private.bluetooth_device = bluetooth_device
local icon_file_name = "bluetooth"
if not this.__private.bluetooth_status then
icon_file_name = icon_file_name .. "_off"
elseif bluetooth_device.connected then
icon_file_name = icon_file_name .. "_connected"
end
this.__public.icon.image = gears.color.recolor_image(this.__private_static.config_path .. "/themes/relz/icons/widgets/bluetooth/" .. icon_file_name .. ".svg", beautiful.text_color)
if this.__private.show_text and bluetooth_device ~= nil then
this.__public.value.text = ""
if bluetooth_device.alias ~= nil then
this.__public.value.text = bluetooth_device.alias
end
if bluetooth_device.battery_percentage ~= nil then
if this.__public.value.text ~= "" then
this.__public.value.text = this.__public.value.text .. " "
end
this.__public.value.text = this.__public.value.text .. string.rep(" ", 3 - get_percent_number_digits_count(bluetooth_device.battery_percentage)) .. bluetooth_device.battery_percentage .. "%"
end
if this.__public.value.text ~= "" then
this.__public.value.text = " " .. this.__public.value.text .. " "
end
end
end)
end
}
this.__construct = function(show_text)
-- Constructor
this.__private.show_text = show_text
if this.__private.show_text then
this.__public.value = wibox.widget.textbox()
this.__public.value.font = beautiful.font_family_mono .. "Bold 9"
end
subscribe_bluetooth_status(function(bluetooth_status)
this.__private.bluetooth_status = bluetooth_status
this.__private.update()
end)
dbus.add_match("system", "interface='org.freedesktop.DBus.Properties'")
dbus.connect_signal("org.freedesktop.DBus.Properties", function(data, interface, chprop)
if interface == "org.bluez.Device1" then
if chprop.Connected ~= nil then
local mac_address = data.path:match("dev_(.+)"):gsub("_", ":")
if chprop.Connected or mac_address == this.__private.bluetooth_device.mac_address then
this.__private.update()
end
end
elseif interface == "org.bluez.MediaControl1"and data.member == "PropertiesChanged" then
this.__private.update()
end
end)
end
return this
end
BluetoothWidget = createClass(BluetoothWidget_prototype)

View file

@ -148,7 +148,7 @@ MicrophoneWidget_prototype = function()
local port = nil
if device.active_port ~= nil and device.ports[device.active_port] ~= nil then
port = device.ports[device.active_port]
elseif device.monitor_sink ~= nil then
elseif device.monitor_sink ~= nil and device.monitor_sink.active_port ~= nil then
port = device.monitor_sink.ports[device.monitor_sink.active_port]
end

3
rc.lua
View file

@ -6,6 +6,7 @@ require("modules/panel")
require("modules/widgets/cpu_widget")
require("modules/widgets/memory_widget")
require("modules/widgets/network_widget")
require("modules/widgets/bluetooth_widget")
require("modules/widgets/brightness_widget")
require("modules/widgets/battery_widget")
require("modules/widgets/calendar_widget")
@ -67,6 +68,7 @@ local calendar_widget = CalendarWidget(calendar_command)
local clock_widget = ClockWidget(calendar_command)
local menu_widget = MenuWidget(session_lock_command)
local network_widget = NetworkWidget(false, network_configuration_command)
local bluetooth_widget = BluetoothWidget(true)
local volume_widget = VolumeWidget(true)
local microphone_widget = MicrophoneWidget(true)
local keyboard_layout_widget = KeyboardLayoutWidget()
@ -137,6 +139,7 @@ screen_0_panel.widgets = {
cpu_widget,
memory_widget,
network_widget,
bluetooth_widget,
volume_widget,
microphone_widget,
brightness_widget,

View file

@ -0,0 +1,10 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_404_2)">
<path d="M17.71 7.71L12 2H11V9.59L6.41 5L5 6.41L10.59 12L5 17.59L6.41 19L11 14.41V22H12L17.71 16.29L13.41 12L17.71 7.71ZM13 5.83L14.88 7.71L13 9.59V5.83ZM14.88 16.29L13 18.17V14.41L14.88 16.29Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_404_2">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 451 B

View file

@ -0,0 +1,10 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_405_2)">
<path d="M7 12L5 10L3 12L5 14L7 12ZM17.71 7.71L12 2H11V9.59L6.41 5L5 6.41L10.59 12L5 17.59L6.41 19L11 14.41V22H12L17.71 16.29L13.41 12L17.71 7.71ZM13 5.83L14.88 7.71L13 9.59V5.83ZM14.88 16.29L13 18.17V14.41L14.88 16.29V16.29ZM19 10L17 12L19 14L21 12L19 10Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_405_2">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 514 B

View file

@ -0,0 +1,10 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_404_5)">
<path d="M13 5.83L14.88 7.71L13.28 9.31L14.69 10.72L17.71 7.7L12 2H11V7.03L13 9.03V5.83ZM5.41 4L4 5.41L10.59 12L5 17.59L6.41 19L11 14.41V22H12L16.29 17.71L18.59 20L20 18.59L5.41 4ZM13 18.17V14.41L14.88 16.29L13 18.17Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_404_5">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 475 B

View file

@ -672,7 +672,64 @@ audio_stop = function()
awful.spawn("dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.Stop", false)
end
-- Keyboard layout
-- Bluetooth
local get_bluetooth_adapter_index = function(callback)
awful.spawn.easy_async("rfkill list bluetooth", function(rfkill_list_bluetooth_output)
callback(string.match(rfkill_list_bluetooth_output, "^(%d+): "))
end)
end
local bluetooth_status_subscribers = {}
subscribe_bluetooth_status = function(callback)
table.insert(bluetooth_status_subscribers, callback)
if #bluetooth_status_subscribers == 1 then
get_bluetooth_adapter_index(function(index)
awful.spawn.with_line_callback("rfkill event", {
stdout = function(line)
if string.match(line, "idx " .. index .. " ") then
for _,bluetooth_status_subscriber in ipairs(bluetooth_status_subscribers) do
bluetooth_status_subscriber(string.match(line, "soft 0 hard 0") ~= nil)
end
end
end
})
end)
end
end
is_bluetooth_device_connected = function(callback)
awful.spawn.easy_async("bluetoothctl info", function(bluetoothctl_info_output)
callback(string.match(bluetoothctl_info_output, "Missing device address argument\n") == nil)
end)
end
get_bluetooth_device = function(callback)
is_bluetooth_device_connected(function(is_connected)
if not is_connected then
callback({
connected = false
})
end
awful.spawn.easy_async("bluetoothctl info", function(bluetoothctl_info_output)
local bluetooth_device = {
mac_address = bluetoothctl_info_output:match("^Device (.-) "),
name = bluetoothctl_info_output:match("Name: (.-)\n"),
alias = bluetoothctl_info_output:match("Alias: (.-)\n"),
paired = bluetoothctl_info_output:match("Paired: yes") ~= nil,
bonded = bluetoothctl_info_output:match("Bonded: yes") ~= nil,
trusted = bluetoothctl_info_output:match("Trusted: yes") ~= nil,
blocked = bluetoothctl_info_output:match("Blocked: yes") ~= nil,
connected = bluetoothctl_info_output:match("Connected: yes") ~= nil,
battery_percentage = tonumber(bluetoothctl_info_output:match("Battery Percentage: %w+ %((%d-)%)"))
}
callback(bluetooth_device)
end)
end)
end
-- Show help