mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
"Add Bluetooth UI enhancements including signal/battery icons, details grid/list toggle, unnamed devices filter, persistent device list settings, and bluetooth agent for full feature pairing"
This commit is contained in:
@@ -7,6 +7,7 @@ import Quickshell.Wayland
|
||||
import qs.Commons
|
||||
import qs.Services.Networking
|
||||
import qs.Widgets
|
||||
import qs.Services.UI
|
||||
|
||||
NBox {
|
||||
id: root
|
||||
@@ -14,8 +15,15 @@ NBox {
|
||||
property string label: ""
|
||||
property string tooltipText: ""
|
||||
property var model: {}
|
||||
// Header control mode: "layout" (default) shows grid/list toggle; "filter" shows unnamed-devices filter toggle
|
||||
property string headerMode: "layout"
|
||||
// Per-list expanded details (by device key)
|
||||
property string expandedDeviceKey: ""
|
||||
// Local layout toggle for details: true = grid (2 cols), false = rows (1 col)
|
||||
// Persisted under Settings.data.ui.bluetoothDetailsViewMode
|
||||
property bool detailsGrid: (Settings.data && Settings.data.ui && Settings.data.ui.bluetoothDetailsViewMode !== undefined)
|
||||
? (Settings.data.ui.bluetoothDetailsViewMode === "grid")
|
||||
: true
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: column.implicitHeight + Style.marginM * 2
|
||||
@@ -27,14 +35,37 @@ NBox {
|
||||
|
||||
spacing: Style.marginM
|
||||
|
||||
NText {
|
||||
text: root.label
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mSecondary
|
||||
font.weight: Style.fontWeightBold
|
||||
visible: root.model.length > 0
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
visible: root.model.length > 0
|
||||
Layout.leftMargin: Style.marginM
|
||||
spacing: Style.marginS
|
||||
|
||||
NText {
|
||||
text: root.label
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mSecondary
|
||||
font.weight: Style.fontWeightBold
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
// (moved) details view toggle is now inside the expanded info box
|
||||
|
||||
// Filter toggle (for Available devices): hide unnamed devices
|
||||
NIconButton {
|
||||
visible: root.headerMode === "filter"
|
||||
// Option A: filter/filter-off
|
||||
// Off (show all): filter; On (hide unnamed): filter-off
|
||||
icon: (Settings.data && Settings.data.ui && Settings.data.ui.bluetoothHideUnnamedDevices) ? "filter-off" : "filter"
|
||||
tooltipText: (Settings.data && Settings.data.ui && Settings.data.ui.bluetoothHideUnnamedDevices)
|
||||
? I18n.tr("tooltips.hide-unnamed-devices")
|
||||
: I18n.tr("tooltips.show-all-devices")
|
||||
onClicked: {
|
||||
if (Settings.data && Settings.data.ui) {
|
||||
Settings.data.ui.bluetoothHideUnnamedDevices = !(Settings.data.ui.bluetoothHideUnnamedDevices);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
@@ -52,8 +83,7 @@ NBox {
|
||||
readonly property bool isBusy: BluetoothService.isDeviceBusy(modelData)
|
||||
readonly property bool isExpanded: root.expandedDeviceKey === BluetoothService.deviceKey(modelData)
|
||||
|
||||
function getContentColor(defaultColor) {
|
||||
if (defaultColor === undefined) defaultColor = Color.mOnSurface;
|
||||
function getContentColor(defaultColor = Color.mOnSurface) {
|
||||
if (modelData.pairing || modelData.state === BluetoothDeviceState.Connecting)
|
||||
return Color.mPrimary;
|
||||
if (modelData.blocked)
|
||||
@@ -149,72 +179,14 @@ NBox {
|
||||
}
|
||||
}
|
||||
|
||||
// Spacer to push connect button to the right
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
// Spacer to push actions to the right
|
||||
Item { Layout.fillWidth: true }
|
||||
|
||||
// Call to action
|
||||
NButton {
|
||||
id: button
|
||||
visible: (modelData.state !== BluetoothDeviceState.Connecting)
|
||||
enabled: (canConnect || canDisconnect || canPair) && !isBusy
|
||||
outlined: !button.hovered
|
||||
fontSize: Style.fontSizeXS
|
||||
fontWeight: Style.fontWeightMedium
|
||||
backgroundColor: {
|
||||
if (device.canDisconnect && !isBusy) {
|
||||
return Color.mError;
|
||||
}
|
||||
return Color.mPrimary;
|
||||
}
|
||||
tooltipText: root.tooltipText
|
||||
text: {
|
||||
if (modelData.pairing) {
|
||||
return I18n.tr("bluetooth.panel.pairing");
|
||||
}
|
||||
if (modelData.blocked) {
|
||||
return I18n.tr("bluetooth.panel.blocked");
|
||||
}
|
||||
if (modelData.connected) {
|
||||
return I18n.tr("bluetooth.panel.disconnect");
|
||||
}
|
||||
if (device.canPair) {
|
||||
return I18n.tr("bluetooth.panel.pair");
|
||||
}
|
||||
return I18n.tr("bluetooth.panel.connect");
|
||||
}
|
||||
icon: (isBusy ? "busy" : null)
|
||||
onClicked: {
|
||||
if (modelData.connected) {
|
||||
BluetoothService.disconnectDevice(modelData);
|
||||
} else {
|
||||
if (device.canPair) {
|
||||
BluetoothService.pairDevice(modelData);
|
||||
} else {
|
||||
BluetoothService.connectDeviceWithTrust(modelData);
|
||||
}
|
||||
}
|
||||
}
|
||||
onRightClicked: {
|
||||
BluetoothService.forgetDevice(modelData);
|
||||
}
|
||||
}
|
||||
|
||||
// Extra actions
|
||||
// Actions (Info on the left to match Wi‑Fi, then Unpair, then main CTA)
|
||||
RowLayout {
|
||||
spacing: Style.marginXS
|
||||
|
||||
// Unpair for saved devices when not connected
|
||||
NIconButton {
|
||||
visible: (modelData.paired || modelData.trusted) && !modelData.connected && !isBusy && !modelData.blocked
|
||||
icon: "trash"
|
||||
tooltipText: I18n.tr("bluetooth.panel.unpair")
|
||||
baseSize: Style.baseWidgetSize * 0.8
|
||||
onClicked: BluetoothService.unpairDevice(modelData)
|
||||
}
|
||||
|
||||
// Info for connected device
|
||||
// Info for connected device (placed before the CTA for consistency with Wi‑Fi)
|
||||
NIconButton {
|
||||
visible: modelData.connected
|
||||
icon: "info-circle"
|
||||
@@ -225,6 +197,62 @@ NBox {
|
||||
root.expandedDeviceKey = (root.expandedDeviceKey === key) ? "" : key;
|
||||
}
|
||||
}
|
||||
|
||||
// Unpair for saved devices when not connected
|
||||
NIconButton {
|
||||
visible: (modelData.paired || modelData.trusted) && !modelData.connected && !isBusy && !modelData.blocked
|
||||
icon: "trash"
|
||||
tooltipText: I18n.tr("bluetooth.panel.unpair")
|
||||
baseSize: Style.baseWidgetSize * 0.8
|
||||
onClicked: BluetoothService.unpairDevice(modelData)
|
||||
}
|
||||
|
||||
// Main Call to action
|
||||
NButton {
|
||||
id: button
|
||||
visible: (modelData.state !== BluetoothDeviceState.Connecting)
|
||||
enabled: (canConnect || canDisconnect || canPair) && !isBusy
|
||||
outlined: !button.hovered
|
||||
fontSize: Style.fontSizeXS
|
||||
fontWeight: Style.fontWeightMedium
|
||||
backgroundColor: {
|
||||
if (device.canDisconnect && !isBusy) {
|
||||
return Color.mError;
|
||||
}
|
||||
return Color.mPrimary;
|
||||
}
|
||||
tooltipText: root.tooltipText
|
||||
text: {
|
||||
if (modelData.pairing) {
|
||||
return I18n.tr("bluetooth.panel.pairing");
|
||||
}
|
||||
if (modelData.blocked) {
|
||||
return I18n.tr("bluetooth.panel.blocked");
|
||||
}
|
||||
if (modelData.connected) {
|
||||
return I18n.tr("bluetooth.panel.disconnect");
|
||||
}
|
||||
if (device.canPair) {
|
||||
return I18n.tr("bluetooth.panel.pair");
|
||||
}
|
||||
return I18n.tr("bluetooth.panel.connect");
|
||||
}
|
||||
icon: (isBusy ? "busy" : null)
|
||||
onClicked: {
|
||||
if (modelData.connected) {
|
||||
BluetoothService.disconnectDevice(modelData);
|
||||
} else {
|
||||
if (device.canPair) {
|
||||
BluetoothService.pairDevice(modelData);
|
||||
} else {
|
||||
BluetoothService.connectDeviceWithTrust(modelData);
|
||||
}
|
||||
}
|
||||
}
|
||||
onRightClicked: {
|
||||
BluetoothService.forgetDevice(modelData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,45 +260,186 @@ NBox {
|
||||
Rectangle {
|
||||
visible: device.isExpanded
|
||||
Layout.fillWidth: true
|
||||
height: infoColumn.implicitHeight + Style.marginS * 2
|
||||
implicitHeight: infoColumn.implicitHeight + Style.marginS * 2
|
||||
radius: Style.radiusS
|
||||
color: Color.mSurfaceVariant
|
||||
border.width: Style.borderS
|
||||
border.color: Color.mOutline
|
||||
clip: true
|
||||
onVisibleChanged: {
|
||||
if (visible && infoColumn && infoColumn.forceLayout) {
|
||||
Qt.callLater(function () { infoColumn.forceLayout(); });
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
// Grid/List toggle moved here to the top-right corner of the info box
|
||||
NIconButton {
|
||||
id: detailsToggle
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.margins: Style.marginS
|
||||
// Use Tabler layout icons; "grid" alone doesn't exist in our font
|
||||
icon: root.detailsGrid ? "layout-list" : "layout-grid"
|
||||
tooltipText: root.detailsGrid ? I18n.tr("tooltips.list-view") : I18n.tr("tooltips.grid-view")
|
||||
onClicked: {
|
||||
root.detailsGrid = !root.detailsGrid;
|
||||
if (Settings.data && Settings.data.ui) {
|
||||
Settings.data.ui.bluetoothDetailsViewMode = root.detailsGrid ? "grid" : "list";
|
||||
}
|
||||
}
|
||||
z: 1
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
id: infoColumn
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginS
|
||||
spacing: Style.marginXS
|
||||
|
||||
RowLayout {
|
||||
spacing: Style.marginS
|
||||
NIcon { icon: BluetoothService.getSignalIcon(modelData); pointSize: Style.fontSizeM; color: Color.mOnSurface }
|
||||
NText { text: BluetoothService.getSignalStrength(modelData); pointSize: Style.fontSizeXS; color: Color.mOnSurface }
|
||||
NText { visible: modelData.signalStrength > 0; text: (modelData.signalStrength || 0) + "%"; pointSize: Style.fontSizeXS; color: Color.mOnSurfaceVariant }
|
||||
// Layout toggle based on local state
|
||||
columns: root.detailsGrid ? 2 : 1
|
||||
columnSpacing: Style.marginM
|
||||
rowSpacing: Style.marginXS
|
||||
// Ensure proper relayout when switching grid/list while open
|
||||
onColumnsChanged: {
|
||||
if (infoColumn.forceLayout) {
|
||||
Qt.callLater(function() { infoColumn.forceLayout(); });
|
||||
}
|
||||
}
|
||||
|
||||
// Icons only; labels shown as tooltips on hover
|
||||
|
||||
// Row 1: Signal | Battery
|
||||
RowLayout {
|
||||
spacing: Style.marginS
|
||||
NIcon { icon: "hash"; pointSize: Style.fontSizeM; color: Color.mOnSurface }
|
||||
NText { text: I18n.tr("bluetooth.panel.device-address") + ": "; pointSize: Style.fontSizeXS; color: Color.mOnSurfaceVariant }
|
||||
NText { text: modelData.address || "-"; pointSize: Style.fontSizeXS; color: Color.mOnSurface }
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginXS
|
||||
NIcon {
|
||||
icon: BluetoothService.getSignalIcon(modelData)
|
||||
pointSize: Style.fontSizeXS
|
||||
color: Color.mOnSurface
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: TooltipService.show(parent, I18n.tr("bluetooth.panel.signal"))
|
||||
onExited: TooltipService.hide()
|
||||
}
|
||||
}
|
||||
NText {
|
||||
// Extract value from helper (remove leading label if present)
|
||||
text: (function(){ var s = BluetoothService.getSignalStrength(modelData); var idx = s.indexOf(":"); return idx !== -1 ? s.substring(idx+1).trim() : s; })()
|
||||
pointSize: Style.fontSizeXS
|
||||
color: Color.mOnSurface
|
||||
Layout.fillWidth: true
|
||||
// Wrap only when needed to avoid extra spacing
|
||||
wrapMode: implicitWidth > width ? Text.WrapAtWordBoundaryOrAnywhere : Text.NoWrap
|
||||
elide: Text.ElideNone
|
||||
maximumLineCount: 4
|
||||
clip: true
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginXS
|
||||
NIcon {
|
||||
icon: "battery"
|
||||
pointSize: Style.fontSizeXS
|
||||
color: Color.mOnSurface
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: TooltipService.show(parent, I18n.tr("bluetooth.panel.battery"))
|
||||
onExited: TooltipService.hide()
|
||||
}
|
||||
}
|
||||
NText {
|
||||
text: modelData.batteryAvailable ? (function(){ var b = BluetoothService.getBattery(modelData); var i = b.indexOf(":"); return i !== -1 ? b.substring(i+1).trim() : b; })() : "-"
|
||||
pointSize: Style.fontSizeXS
|
||||
color: Color.mOnSurface
|
||||
Layout.fillWidth: true
|
||||
wrapMode: implicitWidth > width ? Text.WrapAtWordBoundaryOrAnywhere : Text.NoWrap
|
||||
elide: Text.ElideNone
|
||||
maximumLineCount: 4
|
||||
clip: true
|
||||
}
|
||||
}
|
||||
|
||||
// Row 2: Paired | Trusted
|
||||
RowLayout {
|
||||
spacing: Style.marginS
|
||||
NIcon { icon: "shield-check"; pointSize: Style.fontSizeM; color: Color.mOnSurface }
|
||||
NText { text: I18n.tr("bluetooth.panel.paired") + ": " + (modelData.paired ? I18n.tr("common.yes") : I18n.tr("common.no")); pointSize: Style.fontSizeXS; color: Color.mOnSurface }
|
||||
NText { text: "•"; pointSize: Style.fontSizeXS; color: Color.mOnSurfaceVariant }
|
||||
NText { text: I18n.tr("bluetooth.panel.trusted") + ": " + (modelData.trusted ? I18n.tr("common.yes") : I18n.tr("common.no")); pointSize: Style.fontSizeXS; color: Color.mOnSurface }
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginXS
|
||||
NIcon {
|
||||
icon: "link"
|
||||
pointSize: Style.fontSizeXS
|
||||
color: Color.mOnSurface
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: TooltipService.show(parent, I18n.tr("bluetooth.panel.paired"))
|
||||
onExited: TooltipService.hide()
|
||||
}
|
||||
}
|
||||
NText {
|
||||
text: modelData.paired ? I18n.tr("common.yes") : I18n.tr("common.no")
|
||||
pointSize: Style.fontSizeXS
|
||||
color: Color.mOnSurface
|
||||
Layout.fillWidth: true
|
||||
wrapMode: implicitWidth > width ? Text.WrapAtWordBoundaryOrAnywhere : Text.NoWrap
|
||||
elide: Text.ElideNone
|
||||
maximumLineCount: 2
|
||||
clip: true
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginXS
|
||||
NIcon {
|
||||
icon: "shield-check"
|
||||
pointSize: Style.fontSizeXS
|
||||
color: Color.mOnSurface
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: TooltipService.show(parent, I18n.tr("bluetooth.panel.trusted"))
|
||||
onExited: TooltipService.hide()
|
||||
}
|
||||
}
|
||||
NText {
|
||||
text: modelData.trusted ? I18n.tr("common.yes") : I18n.tr("common.no")
|
||||
pointSize: Style.fontSizeXS
|
||||
color: Color.mOnSurface
|
||||
Layout.fillWidth: true
|
||||
wrapMode: implicitWidth > width ? Text.WrapAtWordBoundaryOrAnywhere : Text.NoWrap
|
||||
elide: Text.ElideNone
|
||||
maximumLineCount: 2
|
||||
clip: true
|
||||
}
|
||||
}
|
||||
|
||||
// Row 3: Address (single row; spans two columns when grid)
|
||||
RowLayout {
|
||||
visible: modelData.batteryAvailable
|
||||
spacing: Style.marginS
|
||||
NIcon { icon: "battery"; pointSize: Style.fontSizeM; color: Color.mOnSurface }
|
||||
NText { text: BluetoothService.getBattery(modelData); pointSize: Style.fontSizeXS; color: Color.mOnSurface }
|
||||
Layout.fillWidth: true
|
||||
Layout.columnSpan: infoColumn.columns === 2 ? 2 : 1
|
||||
spacing: Style.marginXS
|
||||
NIcon {
|
||||
icon: "hash"
|
||||
pointSize: Style.fontSizeXS
|
||||
color: Color.mOnSurface
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: TooltipService.show(parent, I18n.tr("bluetooth.panel.device-address"))
|
||||
onExited: TooltipService.hide()
|
||||
}
|
||||
}
|
||||
NText {
|
||||
text: modelData.address || "-"
|
||||
pointSize: Style.fontSizeXS
|
||||
color: Color.mOnSurface
|
||||
Layout.fillWidth: true
|
||||
// MAC addresses usually fit; wrap only if necessary
|
||||
wrapMode: implicitWidth > width ? Text.WrapAtWordBoundaryOrAnywhere : Text.NoWrap
|
||||
elide: Text.ElideNone
|
||||
maximumLineCount: 2
|
||||
clip: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ SmartPanel {
|
||||
NToggle {
|
||||
id: bluetoothSwitch
|
||||
checked: BluetoothService.enabled
|
||||
onToggled: function(checked) { BluetoothService.setBluetoothEnabled(checked); }
|
||||
onToggled: checked => BluetoothService.setBluetoothEnabled(checked)
|
||||
baseSize: Style.baseWidgetSize * 0.65
|
||||
}
|
||||
|
||||
@@ -158,10 +158,11 @@ SmartPanel {
|
||||
// Connected devices
|
||||
BluetoothDevicesList {
|
||||
label: I18n.tr("bluetooth.panel.connected-devices")
|
||||
headerMode: "layout"
|
||||
property var items: {
|
||||
if (!BluetoothService.adapter || !Bluetooth.devices)
|
||||
return [];
|
||||
var filtered = Bluetooth.devices.values.filter(function(dev) { return dev && !dev.blocked && dev.connected; });
|
||||
var filtered = Bluetooth.devices.values.filter(dev => dev && !dev.blocked && dev.connected);
|
||||
filtered = BluetoothService.dedupeDevices(filtered);
|
||||
return BluetoothService.sortDevices(filtered);
|
||||
}
|
||||
@@ -174,10 +175,11 @@ SmartPanel {
|
||||
BluetoothDevicesList {
|
||||
label: I18n.tr("bluetooth.panel.paired-devices")
|
||||
tooltipText: I18n.tr("tooltips.connect-disconnect-devices")
|
||||
headerMode: "layout"
|
||||
property var items: {
|
||||
if (!BluetoothService.adapter || !Bluetooth.devices)
|
||||
return [];
|
||||
var filtered = Bluetooth.devices.values.filter(function(dev) { return dev && !dev.blocked && !dev.connected && (dev.paired || dev.trusted); });
|
||||
var filtered = Bluetooth.devices.values.filter(dev => dev && !dev.blocked && !dev.connected && (dev.paired || dev.trusted));
|
||||
filtered = BluetoothService.dedupeDevices(filtered);
|
||||
return BluetoothService.sortDevices(filtered);
|
||||
}
|
||||
@@ -189,10 +191,57 @@ SmartPanel {
|
||||
// Available devices (for pairing)
|
||||
BluetoothDevicesList {
|
||||
label: I18n.tr("bluetooth.panel.available-devices")
|
||||
headerMode: "filter"
|
||||
property var items: {
|
||||
if (!BluetoothService.adapter || !Bluetooth.devices)
|
||||
return [];
|
||||
var filtered = Bluetooth.devices.values.filter(function(dev) { return dev && !dev.blocked && !dev.paired && !dev.trusted; });
|
||||
var filtered = Bluetooth.devices.values.filter(dev => dev && !dev.blocked && !dev.paired && !dev.trusted);
|
||||
// Optionally hide devices without a meaningful name when the filter is enabled
|
||||
if (Settings.data && Settings.data.ui && Settings.data.ui.bluetoothHideUnnamedDevices) {
|
||||
filtered = filtered.filter(function (dev) {
|
||||
// Extract display name
|
||||
var dn = "";
|
||||
if (dev && dev.name) dn = dev.name; else if (dev && dev.deviceName) dn = dev.deviceName; else dn = "";
|
||||
if (dn === undefined || dn === null) dn = "";
|
||||
var s = String(dn).trim();
|
||||
|
||||
// 1) Hide empty or whitespace-only
|
||||
if (s.length === 0) return false;
|
||||
|
||||
// 2) Hide common placeholders
|
||||
var lower = s.toLowerCase();
|
||||
if (lower === "unknown" || lower === "unnamed" || lower === "n/a" || lower === "na") return false;
|
||||
|
||||
// 3) Hide if the name equals the device address (ignoring separators)
|
||||
var addr = "";
|
||||
if (dev && dev.address) addr = String(dev.address); else if (dev && dev.bdaddr) addr = String(dev.bdaddr); else if (dev && dev.mac) addr = String(dev.mac);
|
||||
if (addr && addr.length > 0) {
|
||||
var normName = s.toLowerCase().replace(/[^0-9a-z]/g, "");
|
||||
var normAddr = addr.toLowerCase().replace(/[^0-9a-z]/g, "");
|
||||
if (normName.length > 0 && normName === normAddr) return false;
|
||||
}
|
||||
|
||||
// 4) Hide address-like strings
|
||||
// - Colon-separated hex: 00:11:22:33:44:55
|
||||
var macColonHex = /^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$/;
|
||||
if (macColonHex.test(s)) return false;
|
||||
// - Hyphen-separated hex: 00-11-22-33-44-55
|
||||
var macHyphenHex = /^([0-9A-Fa-f]{2}-){5}[0-9A-Fa-f]{2}$/;
|
||||
if (macHyphenHex.test(s)) return false;
|
||||
// - Hyphen-separated alnum pairs (to catch non-hex variants like AB-CD-EF-GH-01-23)
|
||||
var macHyphenAny = /^([0-9A-Za-z]{2}-){5}[0-9A-Za-z]{2}$/;
|
||||
if (macHyphenAny.test(s)) return false;
|
||||
// - Cisco dotted hex: 0011.2233.4455
|
||||
var macDotted = /^[0-9A-Fa-f]{4}\.[0-9A-Fa-f]{4}\.[0-9A-Fa-f]{4}$/;
|
||||
if (macDotted.test(s)) return false;
|
||||
// - Bare hex: 001122334455
|
||||
var macBare = /^[0-9A-Fa-f]{12}$/;
|
||||
if (macBare.test(s)) return false;
|
||||
|
||||
// Keep device otherwise (has a meaningful user-facing name)
|
||||
return true;
|
||||
});
|
||||
}
|
||||
filtered = BluetoothService.dedupeDevices(filtered);
|
||||
return BluetoothService.sortDevices(filtered);
|
||||
}
|
||||
@@ -210,9 +259,9 @@ SmartPanel {
|
||||
return false;
|
||||
}
|
||||
|
||||
var availableCount = Bluetooth.devices.values.filter(function(dev) {
|
||||
return dev && !dev.paired && !dev.pairing && !dev.blocked && (dev.signalStrength === undefined || dev.signalStrength > 0);
|
||||
}).length;
|
||||
var availableCount = Bluetooth.devices.values.filter(dev => {
|
||||
return dev && !dev.paired && !dev.pairing && !dev.blocked && (dev.signalStrength === undefined || dev.signalStrength > 0);
|
||||
}).length;
|
||||
return (availableCount === 0);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user