Add bluetooth panel (wip), add clock / clock+date setting

This commit is contained in:
Ly-sec 2025-08-16 15:35:10 +02:00
parent 56ad08816e
commit 2fd20c69f9
9 changed files with 911 additions and 9 deletions

View file

@ -96,6 +96,7 @@ Singleton {
property bool useFahrenheit: false
property bool reverseDayMonth: false
property bool use12HourClock: false
property bool showDateWithClock: false
}
// screen recorder

View file

@ -9,8 +9,20 @@ Singleton {
id: root
property var date: new Date()
property string time: Settings.data.location.use12HourClock ? Qt.formatDateTime(date, "h:mm AP") : Qt.formatDateTime(
date, "HH:mm")
property string time: {
let timeFormat = Settings.data.location.use12HourClock ? "h:mm AP" : "HH:mm"
let timeString = Qt.formatDateTime(date, timeFormat)
if (Settings.data.location.showDateWithClock) {
let dayName = date.toLocaleDateString(Qt.locale(), "ddd")
dayName = dayName.charAt(0).toUpperCase() + dayName.slice(1)
let day = date.getDate()
let month = date.toLocaleDateString(Qt.locale(), "MMM")
return timeString + " - " + dayName + ", " + day + " " + month
}
return timeString
}
readonly property string dateString: {
let now = date
let dayName = now.toLocaleDateString(Qt.locale(), "ddd")

View file

@ -109,9 +109,9 @@ Variants {
anchors.verticalCenter: parent.verticalCenter
}
// Bluetooth {
// anchors.verticalCenter: parent.verticalCenter
// }
Bluetooth {
anchors.verticalCenter: parent.verticalCenter
}
Battery {
anchors.verticalCenter: parent.verticalCenter
}

58
Modules/Bar/Bluetooth.qml Normal file
View file

@ -0,0 +1,58 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Quickshell
import Quickshell.Wayland
import qs.Commons
import qs.Services
import qs.Widgets
NIconButton {
id: root
readonly property bool bluetoothEnabled: Settings.data.network.bluetoothEnabled
sizeMultiplier: 0.8
showBorder: false
visible: bluetoothEnabled
Component.onCompleted: {
console.log("[Bluetooth] Component loaded, bluetoothEnabled:", bluetoothEnabled)
console.log("[Bluetooth] BluetoothService available:", typeof BluetoothService !== 'undefined')
if (typeof BluetoothService !== 'undefined') {
console.log("[Bluetooth] Connected devices:", BluetoothService.connectedDevices.length)
}
}
icon: {
// Show different icons based on connection status
if (BluetoothService.connectedDevices.length > 0) {
return "bluetooth_connected"
} else if (BluetoothService.isDiscovering) {
return "bluetooth_searching"
} else {
return "bluetooth"
}
}
tooltipText: "Bluetooth Devices"
onClicked: {
if (!bluetoothMenuLoader.active) {
bluetoothMenuLoader.isLoaded = true
}
if (bluetoothMenuLoader.item) {
if (bluetoothMenuLoader.item.visible) {
// Panel is visible, hide it with animation
if (bluetoothMenuLoader.item.hide) {
bluetoothMenuLoader.item.hide()
} else {
bluetoothMenuLoader.item.visible = false
}
} else {
// Panel is hidden, show it
bluetoothMenuLoader.item.visible = true
}
}
}
BluetoothMenu {
id: bluetoothMenuLoader
}
}

View file

@ -0,0 +1,397 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Quickshell
import Quickshell.Wayland
import qs.Commons
import qs.Services
import qs.Widgets
// Loader for Bluetooth menu
NLoader {
id: root
content: Component {
NPanel {
id: bluetoothPanel
function hide() {
bluetoothMenuRect.scaleValue = 0.8
bluetoothMenuRect.opacityValue = 0.0
hideTimer.start()
}
// Connect to NPanel's dismissed signal to handle external close events
Connections {
target: bluetoothPanel
ignoreUnknownSignals: true
function onDismissed() {
// Start hide animation
bluetoothMenuRect.scaleValue = 0.8
bluetoothMenuRect.opacityValue = 0.0
// Hide after animation completes
hideTimer.start()
}
}
// Also handle visibility changes from external sources
onVisibleChanged: {
if (visible && Settings.data.network.bluetoothEnabled) {
// Always refresh devices when menu opens to get fresh device objects
BluetoothService.refreshDevices()
} else if (bluetoothMenuRect.opacityValue > 0) {
// Start hide animation
bluetoothMenuRect.scaleValue = 0.8
bluetoothMenuRect.opacityValue = 0.0
// Hide after animation completes
hideTimer.start()
}
}
// Timer to hide panel after animation
Timer {
id: hideTimer
interval: Style.animationSlow
repeat: false
onTriggered: {
bluetoothPanel.visible = false
bluetoothPanel.dismissed()
}
}
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
Rectangle {
id: bluetoothMenuRect
color: Colors.mSurface
radius: Style.radiusLarge * scaling
border.color: Colors.mOutlineVariant
border.width: Math.max(1, Style.borderThin * scaling)
width: 340 * scaling
height: 500 * scaling
anchors.top: parent.top
anchors.right: parent.right
anchors.topMargin: Style.marginTiny * scaling
anchors.rightMargin: Style.marginTiny * scaling
// Animation properties
property real scaleValue: 0.8
property real opacityValue: 0.0
scale: scaleValue
opacity: opacityValue
// Animate in when component is completed
Component.onCompleted: {
scaleValue = 1.0
opacityValue = 1.0
}
// Animation behaviors
Behavior on scale {
NumberAnimation {
duration: Style.animationSlow
easing.type: Easing.OutExpo
}
}
Behavior on opacity {
NumberAnimation {
duration: Style.animationNormal
easing.type: Easing.OutQuad
}
}
ColumnLayout {
anchors.fill: parent
anchors.margins: Style.marginLarge * scaling
spacing: Style.marginMedium * scaling
RowLayout {
Layout.fillWidth: true
spacing: Style.marginMedium * scaling
NText {
text: "bluetooth"
font.family: "Material Symbols Outlined"
font.pointSize: Style.fontSizeXL * scaling
color: Colors.mPrimary
}
NText {
text: "Bluetooth"
font.pointSize: Style.fontSizeLarge * scaling
font.bold: true
color: Colors.mOnSurface
Layout.fillWidth: true
}
NIconButton {
icon: "refresh"
tooltipText: "Refresh Devices"
sizeMultiplier: 0.8
enabled: Settings.data.network.bluetoothEnabled && !BluetoothService.isDiscovering
onClicked: {
BluetoothService.refreshDevices()
}
}
NIconButton {
icon: "close"
tooltipText: "Close"
sizeMultiplier: 0.8
onClicked: {
bluetoothPanel.hide()
}
}
}
NDivider {}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
// Loading indicator
ColumnLayout {
anchors.centerIn: parent
visible: Settings.data.network.bluetoothEnabled && BluetoothService.isDiscovering
spacing: Style.marginMedium * scaling
NBusyIndicator {
running: BluetoothService.isDiscovering
color: Colors.mPrimary
size: Style.baseWidgetSize * scaling
Layout.alignment: Qt.AlignHCenter
}
NText {
text: "Scanning for devices..."
font.pointSize: Style.fontSizeNormal * scaling
color: Colors.mOnSurfaceVariant
Layout.alignment: Qt.AlignHCenter
}
}
// Bluetooth disabled message
ColumnLayout {
anchors.centerIn: parent
visible: !Settings.data.network.bluetoothEnabled
spacing: Style.marginMedium * scaling
NText {
text: "bluetooth_disabled"
font.family: "Material Symbols Outlined"
font.pointSize: Style.fontSizeXXL * scaling
color: Colors.mOnSurfaceVariant
Layout.alignment: Qt.AlignHCenter
}
NText {
text: "Bluetooth is disabled"
font.pointSize: Style.fontSizeLarge * scaling
color: Colors.mOnSurfaceVariant
Layout.alignment: Qt.AlignHCenter
}
NText {
text: "Enable Bluetooth to see available devices"
font.pointSize: Style.fontSizeNormal * scaling
color: Colors.mOnSurfaceVariant
Layout.alignment: Qt.AlignHCenter
}
}
// Device list
ListView {
id: deviceList
anchors.fill: parent
visible: Settings.data.network.bluetoothEnabled && !BluetoothService.isDiscovering
model: []
spacing: Style.marginMedium * scaling
clip: true
// Combine all devices into a single list for the ListView
property var allDevices: {
const devices = []
// Add connected devices first
for (const device of BluetoothService.connectedDevices) {
devices.push({
device: device,
type: 'connected',
section: 'Connected Devices'
})
}
// Add paired devices
for (const device of BluetoothService.pairedDevices) {
devices.push({
device: device,
type: 'paired',
section: 'Paired Devices'
})
}
// Add available devices
for (const device of BluetoothService.availableDevices) {
devices.push({
device: device,
type: 'available',
section: 'Available Devices'
})
}
return devices
}
// Update model when devices change
onAllDevicesChanged: {
deviceList.model = allDevices
}
// Also watch for changes in the service arrays
Connections {
target: BluetoothService
function onConnectedDevicesChanged() {
deviceList.model = deviceList.allDevices
}
function onPairedDevicesChanged() {
deviceList.model = deviceList.allDevices
}
function onAvailableDevicesChanged() {
deviceList.model = deviceList.allDevices
}
}
delegate: Item {
width: parent ? parent.width : 0
height: Style.baseWidgetSize * 1.5 * scaling
Rectangle {
anchors.fill: parent
radius: Style.radiusMedium * scaling
color: modelData.device.connected ? Colors.mPrimary : (deviceMouseArea.containsMouse ? Colors.mTertiary : "transparent")
RowLayout {
anchors.fill: parent
anchors.margins: Style.marginSmall * scaling
spacing: Style.marginSmall * scaling
NText {
text: BluetoothService.getDeviceIcon(modelData.device)
font.family: "Material Symbols Outlined"
font.pointSize: Style.fontSizeXL * scaling
color: modelData.device.connected ? Colors.mSurface : (deviceMouseArea.containsMouse ? Colors.mSurface : Colors.mOnSurface)
}
ColumnLayout {
Layout.fillWidth: true
spacing: Style.marginTiny * scaling
NText {
text: modelData.device.name || modelData.device.deviceName || "Unknown Device"
font.pointSize: Style.fontSizeNormal * scaling
elide: Text.ElideRight
Layout.fillWidth: true
color: modelData.device.connected ? Colors.mSurface : (deviceMouseArea.containsMouse ? Colors.mSurface : Colors.mOnSurface)
}
NText {
text: {
if (modelData.device.connected) {
return "Connected"
} else if (modelData.device.paired) {
return "Paired"
} else {
return "Available"
}
}
font.pointSize: Style.fontSizeSmall * scaling
color: modelData.device.connected ? Colors.mSurface : (deviceMouseArea.containsMouse ? Colors.mSurface : Colors.mOnSurfaceVariant)
}
NText {
text: BluetoothService.getBatteryText(modelData.device)
font.pointSize: Style.fontSizeSmall * scaling
color: modelData.device.connected ? Colors.mSurface : (deviceMouseArea.containsMouse ? Colors.mSurface : Colors.mOnSurfaceVariant)
visible: modelData.device.batteryAvailable
}
}
Item {
Layout.preferredWidth: Style.baseWidgetSize * 0.7 * scaling
Layout.preferredHeight: Style.baseWidgetSize * 0.7 * scaling
visible: modelData.device.pairing || modelData.device.state === 2 // Connecting state
NBusyIndicator {
visible: modelData.device.pairing || modelData.device.state === 2
running: modelData.device.pairing || modelData.device.state === 2
color: Colors.mPrimary
anchors.centerIn: parent
size: Style.baseWidgetSize * 0.7 * scaling
}
}
NText {
visible: modelData.device.connected
text: "connected"
font.pointSize: Style.fontSizeSmall * scaling
color: modelData.device.connected ? Colors.mSurface : (deviceMouseArea.containsMouse ? Colors.mSurface : Colors.mOnSurface)
}
}
MouseArea {
id: deviceMouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: {
if (modelData.device.connected) {
BluetoothService.disconnectDevice(modelData.device)
} else if (modelData.device.paired) {
BluetoothService.connectDevice(modelData.device)
} else {
BluetoothService.pairDevice(modelData.device)
}
}
}
}
}
}
// Empty state when no devices found
ColumnLayout {
anchors.centerIn: parent
visible: Settings.data.network.bluetoothEnabled &&
!BluetoothService.isDiscovering &&
deviceList.count === 0
spacing: Style.marginMedium * scaling
NText {
text: "bluetooth_disabled"
font.family: "Material Symbols Outlined"
font.pointSize: Style.fontSizeXXL * scaling
color: Colors.mOnSurfaceVariant
Layout.alignment: Qt.AlignHCenter
}
NText {
text: "No Bluetooth devices"
font.pointSize: Style.fontSizeLarge * scaling
color: Colors.mOnSurfaceVariant
Layout.alignment: Qt.AlignHCenter
}
NText {
text: "Click the refresh button to discover devices"
font.pointSize: Style.fontSizeNormal * scaling
color: Colors.mOnSurfaceVariant
Layout.alignment: Qt.AlignHCenter
}
}
}
}
}
}
}
}

View file

@ -58,6 +58,7 @@ ColumnLayout {
checked: Settings.data.network.bluetoothEnabled
onToggled: checked => {
Settings.data.network.bluetoothEnabled = checked
BluetoothService.setBluetoothEnabled(checked)
}
}
}

View file

@ -95,6 +95,15 @@ ColumnLayout {
Settings.data.location.reverseDayMonth = checked
}
}
NToggle {
label: "Show Date with Clock"
description: "Display date alongside time (e.g., 18:12 - Sat, 23 Aug)"
checked: Settings.data.location.showDateWithClock
onToggled: checked => {
Settings.data.location.showDateWithClock = checked
}
}
}
NDivider {

View file

@ -0,0 +1,424 @@
pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Bluetooth
import qs.Commons
Singleton {
id: root
// Bluetooth state properties
property bool isEnabled: Settings.data.network.bluetoothEnabled
property bool isDiscovering: false
property var connectedDevices: []
property var pairedDevices: []
property var availableDevices: []
property string lastConnectedDevice: ""
property string connectStatus: ""
property string connectStatusDevice: ""
property string connectError: ""
// Timer for refreshing device lists
property Timer refreshTimer: Timer {
interval: 5000 // Refresh every 5 seconds when discovery is active
repeat: true
running: root.isEnabled && Bluetooth.defaultAdapter && Bluetooth.defaultAdapter.discovering
onTriggered: root.refreshDevices()
}
Component.onCompleted: {
console.log("[Bluetooth] Service started")
if (isEnabled && Bluetooth.defaultAdapter) {
// Ensure adapter is enabled
if (!Bluetooth.defaultAdapter.enabled) {
Bluetooth.defaultAdapter.enabled = true
}
// Start discovery to find devices
if (!Bluetooth.defaultAdapter.discovering) {
Bluetooth.defaultAdapter.discovering = true
}
// Refresh devices after a short delay to allow discovery to start
Qt.callLater(function() {
refreshDevices()
})
}
}
// Function to enable/disable Bluetooth
function setBluetoothEnabled(enabled) {
if (enabled) {
// Store the currently connected devices before enabling
for (const device of connectedDevices) {
if (device.connected) {
lastConnectedDevice = device.name || device.deviceName
break
}
}
// Enable Bluetooth
if (Bluetooth.defaultAdapter) {
Bluetooth.defaultAdapter.enabled = true
// Start discovery to find devices
if (!Bluetooth.defaultAdapter.discovering) {
Bluetooth.defaultAdapter.discovering = true
}
// Refresh devices after enabling
Qt.callLater(refreshDevices)
} else {
console.warn("[Bluetooth] No Bluetooth adapter found!")
}
} else {
// Disconnect from current devices before disabling
for (const device of connectedDevices) {
if (device.connected) {
device.disconnect()
}
}
// Disable Bluetooth
if (Bluetooth.defaultAdapter) {
console.log("[Bluetooth] Disabling Bluetooth adapter")
Bluetooth.defaultAdapter.enabled = false
}
}
Settings.data.network.bluetoothEnabled = enabled
isEnabled = enabled
}
// Function to refresh device lists
function refreshDevices() {
if (!isEnabled || !Bluetooth.defaultAdapter) {
connectedDevices = []
pairedDevices = []
availableDevices = []
return
}
// Remove duplicate check since we already did it above
const connected = []
const paired = []
const available = []
let devices = null
// Try adapter devices first
if (Bluetooth.defaultAdapter.enabled && Bluetooth.defaultAdapter.devices) {
devices = Bluetooth.defaultAdapter.devices
}
// Fallback to global devices list
if (!devices && Bluetooth.devices) {
devices = Bluetooth.devices
}
if (!devices) {
connectedDevices = []
pairedDevices = []
availableDevices = []
return
}
// Use Qt model methods to iterate through the ObjectModel
let deviceFound = false
try {
// Get the row count using the Qt model method
const rowCount = devices.rowCount()
if (rowCount > 0) {
// Iterate through each row using the Qt model data() method
for (let i = 0; i < rowCount; i++) {
try {
// Create a model index for this row
const modelIndex = devices.index(i, 0)
if (!modelIndex.valid) continue
// Get the device object using the Qt.UserRole (typically 256)
const device = devices.data(modelIndex, 256) // Qt.UserRole
if (!device) {
// Try alternative role values
const deviceAlt = devices.data(modelIndex, 0) // Qt.DisplayRole
if (deviceAlt) {
device = deviceAlt
} else {
continue
}
}
deviceFound = true
if (device.connected) {
connected.push(device)
} else if (device.paired) {
paired.push(device)
} else {
available.push(device)
}
} catch (e) {
// Silent error handling
}
}
}
// Alternative method: try the values property if available
if (!deviceFound && devices.values) {
try {
const values = devices.values
if (values && typeof values === 'object') {
// Try to iterate through values if it's iterable
if (values.length !== undefined) {
for (let i = 0; i < values.length; i++) {
const device = values[i]
if (device) {
deviceFound = true
if (device.connected) {
connected.push(device)
} else if (device.paired) {
paired.push(device)
} else {
available.push(device)
}
}
}
}
}
} catch (e) {
// Silent error handling
}
}
} catch (e) {
console.warn("[Bluetooth] Error accessing device model:", e)
}
if (!deviceFound) {
console.log("[Bluetooth] No devices found")
}
connectedDevices = connected
pairedDevices = paired
availableDevices = available
}
// Function to start discovery
function startDiscovery() {
if (!isEnabled || !Bluetooth.defaultAdapter) return
isDiscovering = true
Bluetooth.defaultAdapter.discovering = true
}
// Function to stop discovery
function stopDiscovery() {
if (!Bluetooth.defaultAdapter) return
isDiscovering = false
Bluetooth.defaultAdapter.discovering = false
}
// Function to connect to a device
function connectDevice(device) {
if (!device) return
// Check if device is still valid (not stale from previous session)
if (!device.connect || typeof device.connect !== 'function') {
console.warn("[Bluetooth] Device object is stale, refreshing devices")
refreshDevices()
return
}
connectStatus = "connecting"
connectStatusDevice = device.name || device.deviceName
connectError = ""
try {
device.connect()
} catch (error) {
console.error("[Bluetooth] Error connecting to device:", error)
connectStatus = "error"
connectError = error.toString()
Qt.callLater(refreshDevices)
}
}
// Function to disconnect from a device
function disconnectDevice(device) {
if (!device) return
// Check if device is still valid (not stale from previous session)
if (!device.disconnect || typeof device.disconnect !== 'function') {
console.warn("[Bluetooth] Device object is stale, refreshing devices")
refreshDevices()
return
}
try {
device.disconnect()
// Clear connection status
connectStatus = ""
connectStatusDevice = ""
connectError = ""
} catch (error) {
console.warn("[Bluetooth] Error disconnecting device:", error)
Qt.callLater(refreshDevices)
}
}
// Function to pair with a device
function pairDevice(device) {
if (!device) return
// Check if device is still valid (not stale from previous session)
if (!device.pair || typeof device.pair !== 'function') {
console.warn("[Bluetooth] Device object is stale, refreshing devices")
refreshDevices()
return
}
try {
device.pair()
} catch (error) {
console.warn("[Bluetooth] Error pairing device:", error)
Qt.callLater(refreshDevices)
}
}
// Function to forget a device
function forgetDevice(device) {
if (!device) return
// Check if device is still valid (not stale from previous session)
if (!device.forget || typeof device.forget !== 'function') {
console.warn("[Bluetooth] Device object is stale, refreshing devices")
refreshDevices()
return
}
// Store device info before forgetting (in case device object becomes invalid)
const deviceName = device.name || device.deviceName || "Unknown Device"
try {
device.forget()
// Clear any connection status that might be related to this device
if (connectStatusDevice === deviceName) {
connectStatus = ""
connectStatusDevice = ""
connectError = ""
}
// Refresh devices after a delay to ensure the forget operation is complete
Qt.callLater(refreshDevices, 1000)
} catch (error) {
console.warn("[Bluetooth] Error forgetting device:", error)
Qt.callLater(refreshDevices, 500)
}
}
// Function to get device icon
function getDeviceIcon(device) {
if (!device) return "bluetooth"
// Use device icon if available, otherwise fall back to device type
if (device.icon) {
return device.icon
}
// Fallback icons based on common device types
const name = (device.name || device.deviceName || "").toLowerCase()
if (name.includes("headphone") || name.includes("earbud") || name.includes("airpods")) {
return "headphones"
} else if (name.includes("speaker")) {
return "speaker"
} else if (name.includes("keyboard")) {
return "keyboard"
} else if (name.includes("mouse")) {
return "mouse"
} else if (name.includes("phone") || name.includes("mobile")) {
return "smartphone"
} else if (name.includes("laptop") || name.includes("computer")) {
return "laptop"
}
return "bluetooth"
}
// Function to get device status text
function getDeviceStatus(device) {
if (!device) return ""
if (device.connected) {
return "Connected"
} else if (device.pairing) {
return "Pairing..."
} else if (device.paired) {
return "Paired"
} else {
return "Available"
}
}
// Function to get battery level text
function getBatteryText(device) {
if (!device || !device.batteryAvailable) return ""
const percentage = Math.round(device.battery * 100)
return `${percentage}%`
}
// Watch for Bluetooth adapter changes
Connections {
target: Bluetooth.defaultAdapter
ignoreUnknownSignals: true
function onEnabledChanged() {
root.isEnabled = Bluetooth.defaultAdapter.enabled
Settings.data.network.bluetoothEnabled = root.isEnabled
if (root.isEnabled) {
Qt.callLater(refreshDevices)
} else {
connectedDevices = []
pairedDevices = []
availableDevices = []
}
}
function onDiscoveringChanged() {
root.isDiscovering = Bluetooth.defaultAdapter.discovering
if (Bluetooth.defaultAdapter.discovering) {
Qt.callLater(refreshDevices)
}
}
function onStateChanged() {
if (Bluetooth.defaultAdapter.state >= 4) {
Qt.callLater(refreshDevices)
}
}
function onDevicesChanged() {
Qt.callLater(refreshDevices)
}
}
// Watch for global device changes
Connections {
target: Bluetooth
ignoreUnknownSignals: true
function onDevicesChanged() {
Qt.callLater(refreshDevices)
}
}
}

View file

@ -156,13 +156,13 @@ Singleton {
readonly property Process initProc: Process {
stdout: StdioCollector {
onStreamFinished: {
console.log("[BrightnessService] Raw brightness data for", monitor.modelData.name + ":", text.trim())
console.log("[Brightness] Raw brightness data for", monitor.modelData.name + ":", text.trim())
if (monitor.isAppleDisplay) {
var val = parseInt(text.trim())
if (!isNaN(val)) {
monitor.brightness = val / 101
console.log("[BrightnessService] Apple display brightness:", monitor.brightness)
console.log("[Brightness] Apple display brightness:", monitor.brightness)
}
} else if (monitor.isDdc) {
var parts = text.trim().split(" ")
@ -171,7 +171,7 @@ Singleton {
var max = parseInt(parts[1])
if (!isNaN(current) && !isNaN(max) && max > 0) {
monitor.brightness = current / max
console.log("[BrightnessService] DDC brightness:", current + "/" + max + " =", monitor.brightness)
console.log("[Brightness] DDC brightness:", current + "/" + max + " =", monitor.brightness)
}
}
} else {
@ -182,7 +182,7 @@ Singleton {
var max = parseInt(parts[1])
if (!isNaN(current) && !isNaN(max) && max > 0) {
monitor.brightness = current / max
console.log("[BrightnessService] Internal brightness:", current + "/" + max + " =", monitor.brightness)
console.log("[Brightness] Internal brightness:", current + "/" + max + " =", monitor.brightness)
}
}
}