From f880054af982feb85564b6e829b7beb5f7cff4c2 Mon Sep 17 00:00:00 2001 From: Serhiy Mytrovtsiy Date: Thu, 2 Sep 2021 20:13:25 +0200 Subject: [PATCH] feat: changed the behavior of the Bluetooth module. Removed Mini and Battery widget (it will be reserved to the Battery module only). Added Sensors widget. Added an option to select a few devices to show. (#589) --- Modules/Bluetooth/config.plist | 29 +++------- Modules/Bluetooth/main.swift | 68 +++++++++-------------- Modules/Bluetooth/settings.swift | 94 ++++++++++++-------------------- 3 files changed, 68 insertions(+), 123 deletions(-) diff --git a/Modules/Bluetooth/config.plist b/Modules/Bluetooth/config.plist index ab83739e..0397460e 100644 --- a/Modules/Bluetooth/config.plist +++ b/Modules/Bluetooth/config.plist @@ -17,32 +17,17 @@ Order 0 - mini - - Title - BLE - Default - - Preview - - Title - BLE - Value - 0.98 - - Unsupported colors - - pressure - - Order - 1 - - battery + sensors Default + Preview + + Values + 98% + Order - 2 + 1 diff --git a/Modules/Bluetooth/main.swift b/Modules/Bluetooth/main.swift index f952b571..84383a9b 100644 --- a/Modules/Bluetooth/main.swift +++ b/Modules/Bluetooth/main.swift @@ -34,41 +34,44 @@ public struct BLEDevice { var peripheral: CBPeripheral? var isPeripheralConnected: Bool = false + + var id: String { + get { + return self.uuid?.uuidString ?? self.address + } + } + + var state: Bool { + get { + return Store.shared.bool(key: "ble_\(self.id)", defaultValue: false) + } + } } public class Bluetooth: Module { - private var devicesReader: DevicesReader? = nil + private var devicesReader: DevicesReader = DevicesReader() private let popupView: Popup = Popup() - private let settingsView: Settings - - private var selectedBattery: String = "" + private let settingsView: Settings = Settings() public init() { - self.settingsView = Settings("Bluetooth") - super.init( popup: self.popupView, settings: self.settingsView ) guard self.available else { return } - self.devicesReader = DevicesReader() - self.selectedBattery = Store.shared.string(key: "\(self.config.name)_battery", defaultValue: self.selectedBattery) - - self.settingsView.selectedBatteryHandler = { [unowned self] value in - self.selectedBattery = value + self.settingsView.callback = { [unowned self] in + self.devicesReader.read() } - self.devicesReader?.callbackHandler = { [unowned self] value in + self.devicesReader.callbackHandler = { [unowned self] value in self.batteryCallback(value) } - self.devicesReader?.readyCallback = { [unowned self] in + self.devicesReader.readyCallback = { [unowned self] in self.readyHandler() } - if let reader = self.devicesReader { - self.addReader(reader) - } + self.addReader(self.devicesReader) } private func batteryCallback(_ raw: [BLEDevice]?) { @@ -79,38 +82,21 @@ public class Bluetooth: Module { let active = value.filter{ $0.isPaired && ($0.isConnected || !$0.batteryLevel.isEmpty) } DispatchQueue.main.async(execute: { self.popupView.batteryCallback(active) + self.settingsView.setList(active) }) - self.settingsView.setList(active) - var battery = active.first?.batteryLevel.first - if self.selectedBattery != "" { - let pair = self.selectedBattery.split(separator: "@") - - guard let device = value.first(where: { $0.name == pair.first! }) else { -// error("cannot find selected battery: \(self.selectedBattery)") - return - } - - if pair.count == 1 { - battery = device.batteryLevel.first - } else if pair.count == 2 { - battery = device.batteryLevel.first{ $0.key == pair.last! } + var list: [KeyValue_t] = [] + active.forEach { (d: BLEDevice) in + if d.state { + d.batteryLevel.forEach { (p: KeyValue_t) in + list.append(KeyValue_t(key: p.key, value: "\(p.value)%")) + } } } self.widgets.filter{ $0.isActive }.forEach { (w: Widget) in switch w.item { - case let widget as Mini: - guard let percentage = Double(battery?.value ?? "0") else { - return - } - widget.setValue(percentage/100) - case let widget as BatterykWidget: - var percentage: Double? = nil - if let value = battery?.value { - percentage = (Double(value) ?? 0) / 100 - } - widget.setValue(percentage: percentage) + case let widget as SensorsWidget: widget.setValues(list) default: break } } diff --git a/Modules/Bluetooth/settings.swift b/Modules/Bluetooth/settings.swift index a791d390..9abab7a5 100644 --- a/Modules/Bluetooth/settings.swift +++ b/Modules/Bluetooth/settings.swift @@ -14,21 +14,15 @@ import Kit internal class Settings: NSStackView, Settings_v { public var callback: (() -> Void) = {} - public var selectedBatteryHandler: (String) -> Void = {_ in } - private let title: String - private var selectedBattery: String - private var button: NSPopUpButton? + private var list: [String: Bool] = [:] - public init(_ title: String) { - self.title = title - self.selectedBattery = Store.shared.string(key: "\(self.title)_battery", defaultValue: "") - + public init() { super.init(frame: NSRect( x: 0, y: 0, width: Constants.Settings.width - (Constants.Settings.margin*2), - height: 0 + height: 20 )) self.orientation = .vertical @@ -46,10 +40,29 @@ internal class Settings: NSStackView, Settings_v { fatalError("init(coder:) has not been implemented") } - internal func load(widgets: [widget_t]) { - self.subviews.forEach{ $0.removeFromSuperview() } + internal func load(widgets: [widget_t]) {} + + internal func setList(_ list: [BLEDevice]) { + if self.list.count != list.count && !self.list.isEmpty { + self.subviews.forEach{ $0.removeFromSuperview() } + self.list = [:] + } - self.addArrangedSubview(self.deviceSelector()) + list.forEach { (d: BLEDevice) in + if self.list[d.id] == nil { + let row: NSView = toggleTitleRow( + frame: NSRect(x: 0, y: 0, width: self.frame.width - (Constants.Settings.margin*2), height: Constants.Settings.row), + title: d.name, + action: #selector(self.handleSelection), + state: d.state + ) + row.subviews.filter{ $0 is NSControl }.forEach { (control: NSView) in + control.identifier = NSUserInterfaceItemIdentifier(rawValue: "\(d.uuid?.uuidString ?? d.address)") + } + self.list[d.id] = true + self.addArrangedSubview(row) + } + } let h = self.arrangedSubviews.map({ $0.bounds.height + self.spacing }).reduce(0, +) - self.spacing + self.edgeInsets.top + self.edgeInsets.bottom if self.frame.size.height != h { @@ -57,56 +70,17 @@ internal class Settings: NSStackView, Settings_v { } } - private func deviceSelector() -> NSView { - let view: NSView = NSView(frame: NSRect(x: 0, y: 0, width: self.frame.width - Constants.Settings.margin*2, height: Constants.Settings.row)) + @objc private func handleSelection(_ sender: NSControl) { + guard let id = sender.identifier else { return } - let rowTitle: NSTextField = LabelField( - frame: NSRect(x: 0, y: (view.frame.height - 16)/2, width: view.frame.width - 52, height: 17), - localizedString("Battery to show") - ) - rowTitle.font = NSFont.systemFont(ofSize: 13, weight: .light) - rowTitle.textColor = .textColor - - self.button = NSPopUpButton(frame: NSRect(x: view.frame.width - 140, y: -1, width: 140, height: 30)) - self.button!.target = self - self.button?.action = #selector(self.handleSelection) - - view.addSubview(rowTitle) - view.addSubview(self.button!) - - return view - } - - internal func setList(_ list: [BLEDevice]) { - var batteries: [String] = [] - list.forEach { (d: BLEDevice) in - if d.batteryLevel.count == 1 { - batteries.append(d.name) - } else { - d.batteryLevel.forEach { (pair: KeyValue_t) in - batteries.append("\(d.name)@\(pair.key)") - } - } + var state: NSControl.StateValue? = nil + if #available(OSX 10.15, *) { + state = sender is NSSwitch ? (sender as! NSSwitch).state: nil + } else { + state = sender is NSButton ? (sender as! NSButton).state: nil } - DispatchQueue.main.async(execute: { - if self.button?.itemTitles.count != batteries.count { - self.button?.removeAllItems() - } - - if batteries != self.button?.itemTitles { - self.button?.addItems(withTitles: batteries.map{ $0.replacingOccurrences(of: "@", with: " - ")}) - if self.selectedBattery != "" { - self.button?.selectItem(withTitle: self.selectedBattery.replacingOccurrences(of: "@", with: " - ")) - } - } - }) - } - - @objc private func handleSelection(_ sender: NSPopUpButton) { - guard let item = sender.selectedItem else { return } - self.selectedBattery = item.title.replacingOccurrences(of: " - ", with: "@") - Store.shared.set(key: "\(self.title)_battery", value: self.selectedBattery) - self.selectedBatteryHandler(self.selectedBattery) + Store.shared.set(key: "ble_\(id.rawValue)", value: state! == NSControl.StateValue.on) + self.callback() } }