Skip to content

Commit

Permalink
✨ broadcast status via ibeacon and remove blinkstick
Browse files Browse the repository at this point in the history
  • Loading branch information
leonardodino committed May 17, 2024
1 parent e8cf9b5 commit 24e93b0
Show file tree
Hide file tree
Showing 8 changed files with 346 additions and 71 deletions.
16 changes: 11 additions & 5 deletions BeezyLight.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@
96B8901227F4E78D00C39B4A /* AboutWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96B8901127F4E78D00C39B4A /* AboutWindow.swift */; };
96C2A1E227C5632D00768B18 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96C2A1E127C5632D00768B18 /* AppDelegate.swift */; };
96C2A1E627C5632E00768B18 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 96C2A1E527C5632E00768B18 /* Assets.xcassets */; };
96C2A1F527C567C300768B18 /* BlinkStick.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96C2A1F327C567C300768B18 /* BlinkStick.swift */; };
96D07DC629D7974B009C2C23 /* AudioInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96D07DC529D7974B009C2C23 /* AudioInput.swift */; };
96D5B44827C57CE300C4FFCA /* Debouncer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96D5B44727C57CE300C4FFCA /* Debouncer.swift */; };
96DC603527F0F7D000A5FE00 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 96DC603427F0F7D000A5FE00 /* Main.storyboard */; };
96F790FF2BE681FF00ED3242 /* BLEBeacon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96F790FE2BE681FF00ED3242 /* BLEBeacon.swift */; };
96F791012BE6C52D00ED3242 /* BLEBeaconManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96F791002BE6C52D00ED3242 /* BLEBeaconManager.swift */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
Expand All @@ -42,10 +43,11 @@
96C2A1E127C5632D00768B18 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
96C2A1E527C5632E00768B18 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
96C2A1EA27C5632E00768B18 /* BeezyLight.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = BeezyLight.entitlements; sourceTree = "<group>"; };
96C2A1F327C567C300768B18 /* BlinkStick.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlinkStick.swift; sourceTree = "<group>"; };
96D07DC529D7974B009C2C23 /* AudioInput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioInput.swift; sourceTree = "<group>"; };
96D5B44727C57CE300C4FFCA /* Debouncer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Debouncer.swift; sourceTree = "<group>"; };
96DC603427F0F7D000A5FE00 /* Main.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; };
96F790FE2BE681FF00ED3242 /* BLEBeacon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BLEBeacon.swift; sourceTree = "<group>"; };
96F791002BE6C52D00ED3242 /* BLEBeaconManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BLEBeaconManager.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -80,7 +82,8 @@
962377FD27F4ECD8002D718F /* StatusItem.swift */,
96B8901127F4E78D00C39B4A /* AboutWindow.swift */,
966385462A2BF0420092FC62 /* LaunchAtLogin.swift */,
96C2A1F327C567C300768B18 /* BlinkStick.swift */,
96F790FE2BE681FF00ED3242 /* BLEBeacon.swift */,
96F791002BE6C52D00ED3242 /* BLEBeaconManager.swift */,
96D5B44727C57CE300C4FFCA /* Debouncer.swift */,
96D07DC529D7974B009C2C23 /* AudioInput.swift */,
);
Expand Down Expand Up @@ -165,7 +168,7 @@
attributes = {
BuildIndependentTargetsInParallel = 1;
LastSwiftUpdateCheck = 1320;
LastUpgradeCheck = 1430;
LastUpgradeCheck = 1520;
TargetAttributes = {
96C2A1DD27C5632D00768B18 = {
CreatedOnToolsVersion = 13.2.1;
Expand Down Expand Up @@ -210,13 +213,14 @@
buildActionMask = 2147483647;
files = (
965A05CF29C5224000559377 /* AudioSystemObject.swift in Sources */,
96C2A1F527C567C300768B18 /* BlinkStick.swift in Sources */,
96C2A1E227C5632D00768B18 /* AppDelegate.swift in Sources */,
966385472A2BF0420092FC62 /* LaunchAtLogin.swift in Sources */,
965A05CB29C5212400559377 /* AudioDevice.swift in Sources */,
96F790FF2BE681FF00ED3242 /* BLEBeacon.swift in Sources */,
965A05CD29C5214000559377 /* AudioObject.swift in Sources */,
962377FE27F4ECD8002D718F /* StatusItem.swift in Sources */,
96D5B44827C57CE300C4FFCA /* Debouncer.swift in Sources */,
96F791012BE6C52D00ED3242 /* BLEBeaconManager.swift in Sources */,
965A05D129C522C000559377 /* AudioObjectProperty.swift in Sources */,
965A05D329C524F100559377 /* AudioBufferListWrapper.swift in Sources */,
96D07DC629D7974B009C2C23 /* AudioInput.swift in Sources */,
Expand Down Expand Up @@ -265,6 +269,7 @@
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
Expand Down Expand Up @@ -327,6 +332,7 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
Expand Down
2 changes: 1 addition & 1 deletion Config/BeezyLight.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<true/>
<key>com.apple.security.device.audio-input</key>
<true/>
<key>com.apple.security.device.usb</key>
<key>com.apple.security.device.bluetooth</key>
<true/>
</dict>
</plist>
2 changes: 2 additions & 0 deletions Config/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@
<dict>
<key>LSUIElement</key>
<true/>
<key>NSBluetoothAlwaysUsageDescription</key>
<string>This application advertises microphone usage as an BLE Beacon (iBeacon)</string>
</dict>
</plist>
12 changes: 6 additions & 6 deletions Sources/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ enum State: Equatable {

@main
class AppDelegate: NSObject, NSApplicationDelegate {
private var blinkStick: BlinkStick?
private var beacon = BLEBeaconManager.shared
private var statusItem: StatusItem?
private var audioInput: AudioInput?
private var state: State = .error
Expand All @@ -18,14 +18,14 @@ class AppDelegate: NSObject, NSApplicationDelegate {
audioInput = AudioInput{ self.setState($0 ? .busy : .idle) }
render()

blinkStick = BlinkStick()
beacon.load()
DispatchQueue.main.async {
self.audioInput?.startListener()
}
}

func applicationWillTerminate(_: Notification) {
blinkStick?.setColor(r: 0, g: 0, b: 0)
beacon.stop()
}
}

Expand All @@ -34,13 +34,13 @@ extension AppDelegate {
switch state {
case .idle:
statusItem?.setIcon(.idle)
blinkStick?.setColor(r: 0, g: 0, b: 0)
beacon.stop()
case .busy:
statusItem?.setIcon(.busy)
blinkStick?.setColor(r: 255, g: 0, b: 0)
beacon.start()
case .error:
statusItem?.setIcon(.error)
blinkStick?.setColor(r: 255, g: 0, b: 255)
beacon.stop()
}
}

Expand Down
104 changes: 104 additions & 0 deletions Sources/BLEBeacon.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import Foundation
import AppKit
import CoreBluetooth

fileprivate extension UUID {
var data: Data { withUnsafeBytes(of: self.uuid, { Data($0) }) }
}

final class BLEBeacon: NSObject, Identifiable {
let id: UUID
private let callback: (CBManagerState) -> Void
private var emitter: CBPeripheralManager?
private var isAdvertising = false
private var wasAdvertisingBeforeSleep = false

func start() {
guard let emitter, isAdvertising != true else { return }
emitter.startAdvertising(advertisementData)
}

func stop() {
guard let emitter, emitter.isAdvertising else { return }
emitter.stopAdvertising()
isAdvertising = false
}

init(_ uuid: UUID, callback: @escaping ((CBManagerState) -> Void)) {
self.id = uuid
self.callback = callback
super.init()
emitter = CBPeripheralManager(delegate: self, queue: nil)
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(willSleep), name: NSWorkspace.willSleepNotification, object: nil)
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(didWake), name: NSWorkspace.didWakeNotification, object: nil)
}
deinit {
stop()
}
}

extension BLEBeacon {
@objc
func willSleep(_: Notification) {
if let emitter, emitter.isAdvertising {
wasAdvertisingBeforeSleep = true
stop()
} else {
wasAdvertisingBeforeSleep = false
}
}

@objc
func didWake(_: Notification) {
guard wasAdvertisingBeforeSleep else { return }
start()
}
}

extension BLEBeacon {
var advertisementData: [String: Data] {
let major: UInt16 = 0
let minor: UInt16 = 0
let measuredPower: Int8 = -59

var advBytes = Data(capacity: 21)
advBytes.append(id.data)
advBytes.append(contentsOf: [
UInt8((major >> 8)),
UInt8(major & 255),

UInt8((minor >> 8) & 255),
UInt8(minor & 255),

UInt8(bitPattern: measuredPower),
])

return ["kCBAdvDataAppleBeaconKey": advBytes]
}
}


extension BLEBeacon: CBPeripheralManagerDelegate {
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
callback(state)
}


func peripheralManagerDidStartAdvertising(_: CBPeripheralManager, error _: Error?) {
if let emitter, emitter.isAdvertising { isAdvertising = true }
}

var state: CBManagerState { emitter?.state ?? .unknown }

var status: String {
switch state {
case .poweredOff: return "powered off"
case .poweredOn: return "powered on"
case .unauthorized: return "not authorized"
case .unknown: return "unknown"
case .resetting: return "resetting"
case .unsupported: return "unsupported"
@unknown default: return "unknown"
}
}
}
Loading

0 comments on commit 24e93b0

Please sign in to comment.