Skip to content

Commit 8952a86

Browse files
authored
Merge pull request #7 from lapfelix/connect-disconnect
Turn it into a Swift package manager package + use SimpleCLI
2 parents 0008cc9 + 0032a8f commit 8952a86

File tree

9 files changed

+154
-81
lines changed

9 files changed

+154
-81
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.DS_Store
2+
/.build
3+
/Packages
4+
/*.xcodeproj

BluetoothConnector.swift

Lines changed: 0 additions & 70 deletions
This file was deleted.

Package.resolved

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// swift-tools-version:4.2
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
import PackageDescription
5+
6+
let package = Package(
7+
name: "BluetoothConnector",
8+
dependencies: [
9+
// Dependencies declare other packages that this package depends on.
10+
.package(url: "[email protected]:lapfelix/SimpleCLI.git", from: "0.1.0"),
11+
],
12+
targets: [
13+
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
14+
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
15+
.target(
16+
name: "BluetoothConnector",
17+
dependencies: ["SimpleCLI"]),
18+
.testTarget(
19+
name: "BluetoothConnectorTests",
20+
dependencies: ["BluetoothConnector"]),
21+
]
22+
)

README.md

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,39 @@
22
Simple macOS CLI to connect/disconnect a Bluetooth device. I made it to easily connect my BeatsX earbuds (I thought the W1 chip would make the switch from my iPhone to my Mac seamless, but we're not there yet, apparently). There's probably a program that already does this but I didn't find it.
33

44
You can download a compiled version here: https://github.com/lapfelix/BluetoothConnector/releases
5-
(or from here when AWS is down: https://www.dropbox.com/s/kxt8c5n3cgxfenh/BluetoothConnector.zip?dl=1)
65

76
If you have issues running the compiled binary (`permission denied` error), try running this command in Terminal:
8-
97
`chmod +x /path/to/BluetoothConnector`
108

119
# Usage
12-
#### With the Swift CLI
10+
#### Compiling and installing
1311
```
14-
swift BluetoothConnector.swift 00-00-00-00-00-00
12+
swift build
13+
mv .build/release/BluetoothConnector /usr/local/bin/BluetoothConnector
1514
```
15+
16+
#### Running
1617
Replace `00-00-00-00-00-00` is your device's MAC address. You can get it by alt-clicking the Bluetooth menu icon or by running BluetoothConnector without any arguments
1718

18-
#### By compiling and then running
19+
To toggle the connection (connect/disconnect):
1920
```
20-
swiftc BluetoothConnector.swift
21-
./BluetoothConnector 00-00-00-00-00-00
21+
BluetoothConnector 00-00-00-00-00-00
2222
```
2323

24-
#### With Automator to bind a shortcut to it (this is how I'm using it)
25-
I included an Automator workflow service that calls BluetoothConnector from `/usr/local/bin` to make it easier to run BluetoothConnector with a keyboard workflow (this is how I'm using it). First you need to compile a binary and move it to `/usr/local/bin/` like this:
24+
To connect:
2625
```
27-
swiftc BluetoothConnector.swift
28-
mv BluetoothConnector /usr/local/bin/BluetoothConnector
26+
BluetoothConnector --connect 00-00-00-00-00-00
27+
BluetoothConnector -c 00-00-00-00-00-00
28+
```
29+
30+
To disconnect:
2931
```
32+
BluetoothConnector --disconnect 00-00-00-00-00-00
33+
BluetoothConnector -d 00-00-00-00-00-00
34+
```
35+
36+
#### With Automator to bind a shortcut to it (this is how I'm using it)
37+
I included an Automator workflow service that calls BluetoothConnector from `/usr/local/bin` to make it easier to run BluetoothConnector with a keyboard workflow (this is how I'm using it).
38+
3039
Then open the Automator workflow and you should get a prompt to install it (don't forget to change the MAC address).
3140
To bind a shortcut to the Automator service, launch System Preferences and Go to `Keyboard > Shortcuts > Services`, find your service and add a shortcut to it.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import IOBluetooth
2+
import SimpleCLI
3+
4+
func printHelp() {
5+
print(cliParser.helpString(CommandLine.arguments))
6+
print("\nGet the MAC address from the list below (if your device is missing, pair it with your computer first):");
7+
IOBluetoothDevice.pairedDevices().forEach({(device) in
8+
guard let device = device as? IOBluetoothDevice,
9+
let addressString = device.addressString,
10+
let deviceName = device.name
11+
else { return }
12+
print("\(addressString) - \(deviceName)")
13+
})
14+
}
15+
16+
let cliParser = SimpleCLI(configuration: [
17+
Argument(longName: "connect", shortName: "c", type: .keyOnly, defaultValue: "false"),
18+
Argument(longName: "disconnect", shortName: "d", type: .keyOnly, defaultValue: "false"),
19+
Argument(longName: "address", type: .valueOnly, obligatory: true, inputName: "00-00-00-00-00-00"),
20+
])
21+
let dictionary = cliParser.parseArgs(CommandLine.arguments)
22+
23+
guard let deviceAddress = dictionary["address"] else {
24+
printHelp()
25+
exit(-3)
26+
}
27+
28+
guard let bluetoothDevice = IOBluetoothDevice(addressString: deviceAddress) else {
29+
print("Device not found")
30+
exit(-2)
31+
}
32+
33+
if !bluetoothDevice.isPaired() {
34+
print("Not paired to device")
35+
exit(-4)
36+
}
37+
38+
var connectOnly = false
39+
if let connectString = dictionary["connect"] {
40+
connectOnly = Bool(connectString) ?? false
41+
}
42+
43+
var disconnectOnly = false
44+
if let disconnectString = dictionary["disconnect"] {
45+
disconnectOnly = Bool(disconnectString) ?? false
46+
}
47+
48+
var error : IOReturn = -1
49+
var action = "Toggle"
50+
if !connectOnly && bluetoothDevice.isConnected() {
51+
action = "Disconnection"
52+
error = bluetoothDevice.closeConnection()
53+
}
54+
else if (!disconnectOnly) {
55+
action = "Connection"
56+
error = bluetoothDevice.openConnection()
57+
}
58+
59+
if error > 0 {
60+
print("Error: \(action) failed")
61+
exit(-1)
62+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import XCTest
2+
@testable import BluetoothConnector
3+
4+
final class BluetoothConnectorTests: XCTestCase {
5+
func testExample() {
6+
// This is an example of a functional test case.
7+
// Use XCTAssert and related functions to verify your tests produce the correct
8+
// results.
9+
}
10+
11+
static var allTests = [
12+
("testExample", testExample),
13+
]
14+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import XCTest
2+
3+
#if !os(macOS)
4+
public func allTests() -> [XCTestCaseEntry] {
5+
return [
6+
testCase(BluetoothConnectorTests.allTests),
7+
]
8+
}
9+
#endif

Tests/LinuxMain.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import XCTest
2+
3+
import BluetoothConnectorTests
4+
5+
var tests = [XCTestCaseEntry]()
6+
tests += BluetoothConnectorTests.allTests()
7+
XCTMain(tests)

0 commit comments

Comments
 (0)