Review of "Bluetooth Battery Meter" version 45

Details Page Preview

A GNOME extension that displays indicator icons in the system tray, acting as a meter for Bluetooth device battery levels. It also provides detailed battery information via icons and text in the Bluetooth quick settings menu Note: Certain Bluetooth devices do not report battery level until Bluez's experimental features are enabled in system. Check Readme for details. The extension also supports multiple device-specific modes to provide detailed battery reporting and device controls, depending on the capabilities exposed by the device: UPower: Devices such as Logitech Lightspeed keyboards and mice that report battery levels through UPower. GATT Battery Service: Devices that expose one or more battery levels via the standard GATT Battery Service (BAS), such as ZMK-based split keyboards. Socket Mode: Provides multiple battery levels and advanced device controls—such as Active Noise Cancellation (ANC) and other supported features using a custom socket-based protocol. Supported devices: - AirPods - Beats earbuds / headphones - Sony earbuds / headphones - Nothing earbuds / headphones - CMF earbuds / headphones - Samsung Galaxy Buds

Extension Homepage
https://github.com/maniacx/Bluetooth-Battery-Meter

No comments.

Diff Against

Files

Note: Binary files aren't shown on the web site. To see all files, please download the extension zipfile.

Shexli (experimental) warning 4

Shexli found 4 issues that may need reviewer attention.

EGO026 warning

JavaScript files should be reachable from extension.js or prefs.js

Some JavaScript files are not reachable from `extension.js` or `prefs.js` imports.

Don't include unnecessary files

  • script/moreSettings.js

EGO008 warning

extensions using unlock-dialog must document it in disable() comments

Extensions using `unlock-dialog` should document the reason in `disable()` comments.

Session Modes

  • extension.js
    unlock-dialog declared but no nearby disable() comment found

EGO015 warning

signals connected by extension should be disconnected in disable()

Signals assigned in `enable()` are missing matching disconnect calls in `disable()` or its helper methods.

Disconnect all signals

  • preferences/batteryWidgetSettings.js:61
            colorButton.connect(signalEmitted, () => {
                const color = colorButton.get_rgba();
                const hexColor = rgbaToHex(color);
                entry.set_placeholder_text(hexColor);
                const colors = settings.get_strv(colorKey);
                colors[this._idx] = hexColor;
        
  • preferences/batteryWidgetSettings.js:70
            entry.connect('changed', () => {
                const text = entry.get_text().trim();
                if (isValidHexColor(text)) {
                    const colors = settings.get_strv(colorKey);
                    colors[this._idx] = text;
                    settings.set_strv(colorKey, colors);
                 
  • preferences/batteryWidgetSettings.js:80
            settings.connect('changed::level-indicator-custom-colors', () => {
                const colors = settings.get_strv(colorKey);
                const hex = colors[this._idx] || '#000000';
                entry.set_placeholder_text(hex);
                colorButton.set_rgba(hexToRgba(hex));
            })
  • preferences/device.js:100
                button.connect('clicked', () => {
                    popover.hide();
                    const pairedDevice = settings.get_strv('device-list');
                    const existingPathIndex =
                        pairedDevice.findIndex(item => JSON.parse(item).path === pathInfo.path);
                    
  • preferences/device.js:133
            quickSettingsSwitchRow.connect('notify::active', () => {
                const pairedDevice = settings.get_strv('device-list');
                const existingPathIndex =
                    pairedDevice.findIndex(item => JSON.parse(item).path === pathInfo.path);
                if (existingPathIndex !== -1)
  • preferences/device.js:176
            dropDown.connect('notify::selected', () => {
                const index = dropDown.get_selected();
                const selectedId = indicatorOptions[index].id;
                const pairedDevice = settings.get_strv('device-list');
                const existingPathIndex =
                    pairedDevice.fin
  • preferences/device.js:216
            this._customiseButton.connect('clicked', () => {
                const parentWindow = this._customiseButton.get_ancestor(Gtk.Window);
                const configureWindow =
                    new ConfigureWindow(settings, this._macAddress,
                        deviceItem, this._pathInfo, parentWindow);
  • preferences/device.js:232
            this._deleteButton.connect('clicked', () => {
                const pairedDevices = settings.get_strv('device-list');
                const existingPathIndex = pairedDevices.findIndex(entry => {
                    const parsedEntry = JSON.parse(entry);
                    return parsedEntry.path === pathIn
  • preferences/devices/airpods/devicePrefs.js:29
            this._customiseButton.connect('clicked', () => {
                const parentWindow = this._customiseButton.get_ancestor(Gtk.Window);
                const configureWindow = new ConfigureWindow(settings, this._macAddress,
                    pathInfo.path, parentWindow, _, true);
    
                configureW
  • preferences/devices/airpods/devicePrefs.js:45
            this._deleteButton.connect('clicked', () => {
                const pairedDevices = settings.get_strv('airpods-list');
                const existingPathIndex = pairedDevices.findIndex(entry => {
                    const parsedEntry = JSON.parse(entry);
                    return parsedEntry.path === pathI
  • preferences/devices/galaxyBuds/devicePrefs.js:29
            this._customiseButton.connect('clicked', () => {
                const parentWindow = this._customiseButton.get_ancestor(Gtk.Window);
                const configureWindow = new ConfigureWindow(settings, this._macAddress,
                    pathInfo.path, parentWindow, _, true);
    
                configureW
  • preferences/devices/galaxyBuds/devicePrefs.js:46
            this._deleteButton.connect('clicked', () => {
                const pairedDevices = settings.get_strv('galaxy-buds-list');
                const existingPathIndex = pairedDevices.findIndex(entry => {
                    const parsedEntry = JSON.parse(entry);
                    return parsedEntry.path === p
  • preferences/devices/nothingBuds/devicePrefs.js:29
            this._customiseButton.connect('clicked', () => {
                const parentWindow = this._customiseButton.get_ancestor(Gtk.Window);
                const configureWindow = new ConfigureWindow(settings, this._macAddress,
                    pathInfo.path, parentWindow, _, true);
    
                configureW
  • preferences/devices/nothingBuds/devicePrefs.js:46
            this._deleteButton.connect('clicked', () => {
                const pairedDevices = settings.get_strv('nothing-buds-list');
                const existingPathIndex = pairedDevices.findIndex(entry => {
                    const parsedEntry = JSON.parse(entry);
                    return parsedEntry.path === 
  • preferences/devices/sony/devicePrefs.js:29
            this._customiseButton.connect('clicked', () => {
                const parentWindow = this._customiseButton.get_ancestor(Gtk.Window);
                const configureWindow = new ConfigureWindow(settings, this._macAddress,
                    pathInfo.path, parentWindow, _, true);
    
                configureW
  • preferences/devices/sony/devicePrefs.js:45
            this._deleteButton.connect('clicked', () => {
                const pairedDevices = settings.get_strv('sony-list');
                const existingPathIndex = pairedDevices.findIndex(entry => {
                    const parsedEntry = JSON.parse(entry);
                    return parsedEntry.path === pathInfo
  • preferences/gattBas.js:134
                button.connect('clicked', () => {
                    popover.hide();
                    const pairedDevice = settings.get_strv('gattbas-list');
                    const existingPathIndex =
                    pairedDevice.findIndex(item => JSON.parse(item).path === pathInfo.path);
                    con
  • preferences/gattBas.js:177
            this._customiseButton.connect('clicked', () => {
                const parentWindow = this._customiseButton.get_ancestor(Gtk.Window);
                const configureWindow =
                    new ConfigureWindow(settings, this._macAddress, deviceItem,
                        this._pathInfo, parentWindow);
  • preferences/gattBas.js:193
            this._deleteButton.connect('clicked', () => {
                const pairedDevices = settings.get_strv('gattbas-list');
                const existingPathIndex = pairedDevices.findIndex(entry => {
                    const parsedEntry = JSON.parse(entry);
                    return parsedEntry.path === pathI
  • preferences/upowerDevices.js:111
            aliasRow.connect('apply', row => {
                const newAlias = row.text.trim();
                const onlineDevice = settings.get_strv('upower-device-list');
                const existingPathIndex =
            onlineDevice.findIndex(item => JSON.parse(item).path === pathInfo.path);
    
                if (e
  • preferences/upowerDevices.js:81
                button.connect('clicked', () => {
                    popover.hide();
                    const onlineDevice = settings.get_strv('upower-device-list');
                    const existingPathIndex =
                        onlineDevice.findIndex(item => JSON.parse(item).path === pathInfo.path);
             
  • preferences/upowerDevices.js:149
            dropDown.connect('notify::selected', () => {
                const index = dropDown.get_selected();
                const selectedId = indicatorOptions[index].id;
    
                const onlineDevice = settings.get_strv('upower-device-list');
                const existingPathIndex =
                    onlineDe
  • preferences/upowerDevices.js:190
            this._customiseButton.connect('clicked', () => {
                const parentWindow = this._customiseButton.get_ancestor(Gtk.Window);
                const configureWindow =
                    new ConfigureWindow(settings, deviceItem, this._pathInfo, parentWindow);
                configureWindow.present()
  • preferences/upowerDevices.js:205
            this._deleteButton.connect('clicked', () => {
                const upowerDevices = settings.get_strv('upower-device-list');
                const existingPathIndex = upowerDevices.findIndex(entry => {
                    const parsedEntry = JSON.parse(entry);
                    return parsedEntry.path ===

EGO033 warning

preferences classes should not retain window-scoped objects on instance fields without close-request cleanup

Preferences code stores window-scoped objects on the exported prefs class without `close-request` cleanup.

Destroy all objects

  • prefs.js:84
    this._sidebarListBox = new Gtk.ListBox()
  • prefs.js:94
    this._contentToastOverlay = new Adw.ToastOverlay()

All Versions