diff --git a/Modules/Bluetooth/config.plist b/Modules/Bluetooth/config.plist index e682d921..2639fe97 100644 --- a/Modules/Bluetooth/config.plist +++ b/Modules/Bluetooth/config.plist @@ -39,7 +39,7 @@ popup notifications - + diff --git a/Modules/Bluetooth/main.swift b/Modules/Bluetooth/main.swift index b74fc476..240ed082 100644 --- a/Modules/Bluetooth/main.swift +++ b/Modules/Bluetooth/main.swift @@ -28,15 +28,14 @@ public struct BLEDevice: Codable { var isPeripheralInitialized: Bool = false var id: String { - get { - return self.uuid?.uuidString ?? self.address - } + get { self.uuid?.uuidString ?? self.address } } var state: Bool { - get { - return Store.shared.bool(key: "ble_\(self.id)", defaultValue: false) - } + get { Store.shared.bool(key: "ble_\(self.id)", defaultValue: false) } + } + var notificationThreshold: String { + Store.shared.string(key: "ble_\(self.id)_notification", defaultValue: "") } private enum CodingKeys: String, CodingKey { @@ -80,11 +79,15 @@ public class Bluetooth: Module { private var devicesReader: DevicesReader? private let popupView: Popup = Popup() private let settingsView: Settings = Settings() + private let notificationsView: Notifications public init() { + self.notificationsView = Notifications(.bluetooth) + super.init( popup: self.popupView, - settings: self.settingsView + settings: self.settingsView, + notifications: self.notificationsView ) guard self.available else { return } @@ -106,6 +109,7 @@ public class Bluetooth: Module { DispatchQueue.main.async(execute: { self.popupView.batteryCallback(active) self.settingsView.setList(active) + self.notificationsView.callback(active) }) var list: [Stack_t] = [] diff --git a/Modules/Bluetooth/notifications.swift b/Modules/Bluetooth/notifications.swift new file mode 100644 index 00000000..dce1aab9 --- /dev/null +++ b/Modules/Bluetooth/notifications.swift @@ -0,0 +1,84 @@ +// +// notifications.swift +// Bluetooth +// +// Created by Serhiy Mytrovtsiy on 24/06/2024 +// Using Swift 5.0 +// Running on macOS 14.5 +// +// Copyright © 2024 Serhiy Mytrovtsiy. All rights reserved. +// + +import Cocoa +import Kit + +class Notifications: NotificationsWrapper { + private var list: [String: Bool] = [:] + + private let emptyView: EmptyView = EmptyView(msg: localizedString("No Bluetooth devices are available")) + private var section: PreferencesSection = PreferencesSection() + + public init(_ module: ModuleType) { + super.init(module) + + self.addArrangedSubview(self.emptyView) + self.addArrangedSubview(self.section) + self.section.isHidden = true + + self.addArrangedSubview(NSView()) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + internal func callback(_ list: [BLEDevice]) { + if self.list.count != list.count && !self.list.isEmpty { + self.section.removeFromSuperview() + self.section = PreferencesSection() + self.addArrangedSubview(self.section) + self.list = [:] + } + + if list.isEmpty && self.emptyView.isHidden { + self.emptyView.isHidden = false + self.section.isHidden = true + return + } else if !list.isEmpty && !self.emptyView.isHidden { + self.emptyView.isHidden = true + self.section.isHidden = false + } + + list.forEach { (d: BLEDevice) in + if self.list[d.id] == nil { + let btn = selectView( + action: #selector(self.changeSensorNotificaion), + items: notificationLevels, + selected: d.notificationThreshold + ) + btn.identifier = NSUserInterfaceItemIdentifier(rawValue: "\(d.uuid?.uuidString ?? d.address)") + section.add(PreferencesRow(d.name, component: btn)) + self.list[d.id] = true + } + } + + let devices = list.filter({ !$0.notificationThreshold.isEmpty }) + let title = localizedString("Bluetooth threshold") + + for d in devices { + if let threshold = Double(d.notificationThreshold) { + for l in d.batteryLevel { + let subtitle = localizedString("\(localizedString(d.name)): \(l.value)%") + if let value = Double(l.value) { + self.checkDouble(id: d.id, value: value/100, threshold: threshold, title: title, subtitle: subtitle, less: true) + } + } + } + } + } + + @objc private func changeSensorNotificaion(_ sender: NSMenuItem) { + guard let id = sender.identifier, let key = sender.representedObject as? String else { return } + Store.shared.set(key: "ble_\(id.rawValue)_notification", value: key) + } +} diff --git a/Stats.xcodeproj/project.pbxproj b/Stats.xcodeproj/project.pbxproj index 2764bb60..0010518f 100644 --- a/Stats.xcodeproj/project.pbxproj +++ b/Stats.xcodeproj/project.pbxproj @@ -45,6 +45,7 @@ 5C4E8BE92B71031A00F148B6 /* Kit.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C4E8BE82B7102A700F148B6 /* Kit.h */; settings = {ATTRIBUTES = (Private, ); }; }; 5C5647F82A3F6B100098FFE9 /* Telemetry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C5647F72A3F6B100098FFE9 /* Telemetry.swift */; }; 5C621D822B4770D6004ED7AF /* process.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C621D812B4770D6004ED7AF /* process.swift */; }; + 5C7C1DF42C29A3A00060387D /* notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7C1DF32C29A3A00060387D /* notifications.swift */; }; 5C8E001029269C7F0027C75A /* protocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFE493829265055000F2856 /* protocol.swift */; }; 5CA518382B543FE600EBCCC4 /* portal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA518372B543FE600EBCCC4 /* portal.swift */; }; 5CD342F42B2F2FB700225631 /* notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CD342F32B2F2FB700225631 /* notifications.swift */; }; @@ -446,6 +447,7 @@ 5C4E8BE82B7102A700F148B6 /* Kit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Kit.h; sourceTree = ""; }; 5C5647F72A3F6B100098FFE9 /* Telemetry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Telemetry.swift; sourceTree = ""; }; 5C621D812B4770D6004ED7AF /* process.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = process.swift; sourceTree = ""; }; + 5C7C1DF32C29A3A00060387D /* notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = notifications.swift; sourceTree = ""; }; 5C9F90A02A76B30500D41748 /* et */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = et; path = et.lproj/Localizable.strings; sourceTree = ""; }; 5CA518372B543FE600EBCCC4 /* portal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = portal.swift; sourceTree = ""; }; 5CD342F32B2F2FB700225631 /* notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = notifications.swift; sourceTree = ""; }; @@ -802,6 +804,7 @@ 9A11AB35266FD9F4000C1C05 /* readers.swift */, 9A94B81E26822DE0001F4F2B /* popup.swift */, 9A8B923C2696445C00FD6D83 /* settings.swift */, + 5C7C1DF32C29A3A00060387D /* notifications.swift */, 9A11AAD2266FD77F000C1C05 /* Info.plist */, 9A11AB25266FD828000C1C05 /* config.plist */, ); @@ -1532,7 +1535,7 @@ New, ); LastSwiftUpdateCheck = 1410; - LastUpgradeCheck = 1530; + LastUpgradeCheck = 1540; ORGANIZATIONNAME = "Serhiy Mytrovtsiy"; TargetAttributes = { 5C22299C29CCB3C400F00E69 = { @@ -1820,6 +1823,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 5C7C1DF42C29A3A00060387D /* notifications.swift in Sources */, 9A11AB36266FD9F4000C1C05 /* readers.swift in Sources */, 9A94B81F26822DE0001F4F2B /* popup.swift in Sources */, 9A8B923D2696445C00FD6D83 /* settings.swift in Sources */, diff --git a/Stats.xcodeproj/xcshareddata/xcschemes/SMC.xcscheme b/Stats.xcodeproj/xcshareddata/xcschemes/SMC.xcscheme index 7effd0f0..57bdfb75 100644 --- a/Stats.xcodeproj/xcshareddata/xcschemes/SMC.xcscheme +++ b/Stats.xcodeproj/xcshareddata/xcschemes/SMC.xcscheme @@ -1,6 +1,6 @@