-
Notifications
You must be signed in to change notification settings - Fork 33
Open
Description
Thanks for all the work in reverse-engineering this! Here's a version that that works with osx/macos, basically using cocoa apis to replay the bytes you reverse-engineered. The code is pretty ugly and there's no error handling (I have no idea what the ack sequences you used are for, but it seems to work fine without it). Link to IOKit and Foundation when compiling.
#import <Cocoa/Cocoa.h>
const char* USAGE_DECL = "Usage:\n\t-h --help Help (print this message)\n\t-b --battery Get battery life \n\t-s --serial Print serial number\n\t-f --firmware Get firmware version\n\t-z --getsleep Get auto-off timeout\n\t-v --voice Set voice on/off\n\t-q --getnc Get current noise cancelling level\n\t-n --setnc [high/low/off] Set current noise cancelling level";
int main(int argc, char *argv[])
{
if (argc == 1 || !strcmp(argv[1], "-h"))
{
printf("%s\n", USAGE_DECL);
return 0;
}
return NSApplicationMain(argc, (const char **)argv);
}
#import <Cocoa/Cocoa.h>
#import <IOBluetooth/objc/IOBluetoothRFCOMMChannel.h>
#import <IOBluetooth/objc/IOBluetoothDevice.h>
@interface AppDelegate : NSObject <NSApplicationDelegate>
{
IOBluetoothRFCOMMChannel *mRFCOMMChannel;
}
@property(assign) IBOutlet NSWindow *window;
@end
#import <IOBluetooth/objc/IOBluetoothSDPUUID.h>
#import <IOBluetoothUI/objc/IOBluetoothDeviceSelectorController.h>
#include <getopt.h>
extern int *_NSGetArgc(void);
extern char ***_NSGetArgv(void);
extern const char* USAGE_DECL;
#define ANY 0x00
#define NC_HIGH 0x01
#define NC_LOW 0x03
#define NC_OFF 0x00
#define VP_MASK 0x20
enum PromptLanguage {
PL_EN = 0x21,
PL_FR = 0x22,
PL_IT = 0x23,
PL_DE = 0x24,
PL_ES = 0x26,
PL_PT = 0x27,
PL_ZH = 0x28,
PL_KO = 0x29,
PL_NL = 0x2e,
PL_JA = 0x2f,
PL_SV = 0x32
};
int numBytesToss = 0;
int displayCode = 0; // 0 = don't display, 1 = display string, 2 = display uint8_t
bool isInit = false;
@implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[self discover];
}
- (void)init_connection
{
const unsigned char bytes[] = {0x00, 0x01, 0x01, 0x00};
NSData *dt = [NSData dataWithBytes:bytes length:sizeof(bytes)];
numBytesToss = 4;
displayCode = 1;
isInit = true;
[self sendMessage:dt];
}
- (void)get_battery_level
{
const unsigned char bytes[] = {0x02, 0x02, 0x01, 0x00};
NSData *dt = [NSData dataWithBytes:bytes length:sizeof(bytes)];
numBytesToss = 4;
displayCode = 2;
isInit = false;
[self sendMessage:dt];
}
- (void)get_serial_number
{
const unsigned char bytes[] = {0x00, 0x07, 0x01, 0x00};
NSData *dt = [NSData dataWithBytes:bytes length:sizeof(bytes)];
numBytesToss = 4;
displayCode = 1;
isInit = false;
[self sendMessage:dt];
}
- (void)get_auto_off
{
const unsigned char bytes[] = {0x01, 0x04, 0x03, 0x01, ANY};
NSData *dt = [NSData dataWithBytes:bytes length:sizeof(bytes)];
numBytesToss = 4;
displayCode = 2;
isInit = false;
[self sendMessage:dt];
}
- (void)get_noise_cancelling
{
const unsigned char bytes[] = {0x01, 0x06, 0x03, 0x02, ANY, 0x0b};
NSData *dt = [NSData dataWithBytes:bytes length:sizeof(bytes)];
numBytesToss = 3;
displayCode = 4;
isInit = false;
[self sendMessage:dt];
}
- (void)set_noise_cancelling:(char)newLevel
{
const unsigned char bytes[] = {0x01, 0x06, 0x02, 0x01, newLevel};
NSData *dt = [NSData dataWithBytes:bytes length:sizeof(bytes)];
numBytesToss = 4;
displayCode = 2;
isInit = false;
[self sendMessage:dt];
}
- (void)set_voice:(BOOL) newVal
{
const uint8_t lang = PL_JA;
uint8_t language = newVal ? lang | VP_MASK : lang & ~VP_MASK;
const unsigned char bytes[] = { 0x01, 0x03, 0x02, 0x01, language };
NSData *dt = [NSData dataWithBytes:bytes length:sizeof(bytes)];
numBytesToss = 4;
displayCode = 2;
isInit = false;
[self sendMessage:dt];
}
- (void)get_firmware_version
{
const unsigned char bytes[] = {0x00, 0x05, 0x01, 0x00};
NSData *dt = [NSData dataWithBytes:bytes length:sizeof(bytes)];
numBytesToss = 4;
displayCode = 1;
isInit = false;
[self sendMessage:dt];
}
- (void)sendMessage:(NSData *)dataToSend
{
//[self log:@"Sending Message\n"];
[mRFCOMMChannel writeSync:(void *)dataToSend.bytes length:dataToSend.length];
}
- (void)log:(NSString *)text
{
printf("%s", [text UTF8String]);
}
- (void)dispatchAction
{
int argc = *_NSGetArgc();
char **argv = *_NSGetArgv();
if (argc == 1)
{
printf("%s\n", USAGE_DECL);
[NSApp terminate:nil];
}
int c;
const char *short_opt = "hfbszqnv:";
struct option long_opt[] = {{"help", no_argument, NULL, 'h'},
{"battery", no_argument, NULL, 'b'},
{"voice", required_argument, NULL, 'v'},
{"serial", no_argument, NULL, 's'},
{"firmware", no_argument, NULL, 'f'},
{"getsleep", no_argument, NULL, 'z'},
{"getnc", no_argument, NULL, 'q'},
{"setnc", required_argument, NULL, 'n'},
{"voice", required_argument, NULL, 'v'},
{NULL, 0, NULL, 0}};
while ((c = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1)
{
switch (c)
{
case -1: /* no more arguments */
case 0: /* long options toggles */
break;
case 'b':
[self get_battery_level];
break;
case 's':
[self get_serial_number];
break;
case 'f':
[self get_firmware_version];
break;
case 'z':
[self get_auto_off];
break;
case 'q':
[self get_noise_cancelling];
break;
case 'h':
printf("%s\n", USAGE_DECL);
[NSApp terminate:nil];
break;
case 'n':
{
if (!strcmp(optarg, "high"))
{
[self set_noise_cancelling:NC_HIGH];
}
else if (!strcmp(optarg, "low"))
{
[self set_noise_cancelling:NC_LOW];
}
else
{
[self set_noise_cancelling:NC_OFF];
}
}
break;
case 'v':
{
if (!strcmp(optarg, "on"))
{
[self set_voice: true];
}
else if (!strcmp(optarg, "off"))
{
[self set_voice: false];
}
}
break;
case ':':
case '?':
fprintf(stderr, "Try `--help\' for more information.\n");
[NSApp terminate:nil];
break;
default:
fprintf(stderr, "invalid option -- %c\n", c);
fprintf(stderr, "Try `--help\' for more information.\n");
[NSApp terminate:nil];
};
};
}
- (void)discover
{
IOBluetoothDeviceSelectorController *deviceSelector;
IOBluetoothSDPUUID *sppServiceUUID;
IOBluetoothRFCOMMChannel *chan;
[self log:@"Attempting to connect. If frozen, ensure Bose QC35 is paired and active\n"];
// The device selector will provide UI to the end user to find a remote device
deviceSelector = [IOBluetoothDeviceSelectorController deviceSelector];
if (deviceSelector == nil)
{
[self log:@"Error - unable to allocate IOBluetoothDeviceSelectorController.\n"];
return;
}
sppServiceUUID = [IOBluetoothSDPUUID uuid16:kBluetoothSDPUUID16ServiceClassSerialPort];
IOBluetoothDevice *device = [IOBluetoothDevice deviceWithAddressString:@"00:00:00:00:00:00"];
IOBluetoothSDPServiceRecord *sppServiceRecord = [device getServiceRecordForUUID:sppServiceUUID];
if (sppServiceRecord == nil)
{
[self log:@"Error - no spp service in selected device. ***This should never happen since the selector forces the user to select only devices with spp.***\n"];
return;
}
// To connect we need a device to connect and an RFCOMM channel ID to open on the device:
UInt8 rfcommChannelID;
if ([sppServiceRecord getRFCOMMChannelID:&rfcommChannelID] != kIOReturnSuccess)
{
[self log:@"Error - no spp service in selected device. ***This should never happen an spp service must have an rfcomm channel id.***\n"];
return;
}
// Open asyncronously the rfcomm channel when all the open sequence is completed my implementation of "rfcommChannelOpenComplete:" will be called.
if (([device openRFCOMMChannelAsync:&chan withChannelID:rfcommChannelID delegate:self] != kIOReturnSuccess) && (chan != nil))
{
// Something went bad (looking at the error codes I can also say what, but for the moment let's not dwell on
// those details). If the device connection is left open close it and return an error:
[self log:@"Error - open sequence failed.***\n"];
return;
}
mRFCOMMChannel = chan;
}
- (void)rfcommChannelOpenComplete:(IOBluetoothRFCOMMChannel *)rfcommChannel status:(IOReturn)error
{
if (error != kIOReturnSuccess)
{
[self log:@"Error - failed to open the RFCOMM channel with error %08lx.\n"];
return;
}
else
{
[self log:@"Connected\n"];
[self init_connection];
}
}
- (void)rfcommChannelData:(IOBluetoothRFCOMMChannel *)rfcommChannel data:(void *)dataPointer length:(size_t)dataLength
{
if (isInit)
{
[self dispatchAction];
return;
}
NSData *message = [[NSData alloc] initWithBytes:((char *)dataPointer + numBytesToss)length:dataLength];
NSLog(@"%@", message);
if (displayCode == 1)
{
NSString *message = [[NSString alloc] initWithBytes:((char *)dataPointer + numBytesToss)length:dataLength encoding:NSUTF8StringEncoding];
[self log:message];
}
else if (displayCode == 4)
{
char val = *(char *)([message bytes]);
if (val == NC_HIGH)
{
printf("%s", "High");
}
else if (val == NC_LOW)
{
printf("%s", "Low");
}
else
{
printf("%s", "Off");
}
}
else
{
char val = *(char *)([message bytes]);
printf("%d", val);
}
[NSApp terminate:nil];
}
@end
LEI and bill-7
Metadata
Metadata
Assignees
Labels
No labels