-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAPBeaconManager.m
331 lines (284 loc) · 12.7 KB
/
APBeaconManager.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
//
// APBeaconManager.m
// APBeacons
//
// Created by Ashutosh Priyadarshy on 10/28/13.
// Copyright (c) 2013 EEMe labs. All rights reserved.
//
// APBeaconManager manages a Central and a Peripheral as needed.
//
#import "APBeaconManager.h"
@interface APBeaconManager ()
@property CBPeripheralManager *peripheralManager;
@property CBCentralManager *centralManager;
@property APBeaconRegion *beaconRegion;
@property NSTimer *scanTimer;
@property NSMutableDictionary *txBeaconRegions;
@property APBeaconService *wildcardBeaconService;
@property CBPeripheral *readablePeripheral;
@property NSMutableDictionary *scannedPeripherals;
@property NSMutableDictionary *verifiedAndPopulatedPeripherals;
@end
@implementation APBeaconManager
- (id)init
{
return [self initWithDelegate:nil];
}
- (id)initWithDelegate:(id)delegate
{
self = [super init];
if (self) {
self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil options:nil];
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:nil];
self.beaconRegion = [[APBeaconRegion alloc] init];
self.delegate = delegate;
self.wildcardBeaconService = [[APBeaconService alloc] initWildcardService];
self.scannedPeripherals = [[NSMutableDictionary alloc] init];
self.verifiedAndPopulatedPeripherals = [[NSMutableDictionary alloc] init];
self.scanTimer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(beginScanningForBeacons) userInfo:nil repeats:YES];
}
return self;
}
/*
* Calls made by our delegate to make things happen.
*/
-(void)startBroadcastingBeaconRegion:(APBeaconRegion *)beaconRegion
{
// Post an error message if we're overwriting a previous identifier.
if ([self.beaconRegion.identifier isEqualToString:self.beaconRegion.identifier]){
NSLog(@"Beacon Region previously exists for identifier %@. Will overwrite.", beaconRegion.identifier);
}
// Assign this beacon region to be the manager's beacon region.
self.beaconRegion = beaconRegion;
}
-(void)stopBroadcastingBeaconRegion:(APBeaconRegion *)beaconRegion
{
// Eventually we might support broadcasting multiple APBeacons from one device.
if (beaconRegion == nil) {
// If we pass nil, we ask peripheralManger to stop broadcasting our saved beaconRegion.
[self.peripheralManager removeService:self.beaconRegion.service.defaultService];
} else {
[self.peripheralManager removeService:beaconRegion.service.defaultService];
}
}
/*
* Things we do internally with the Peripheral and BLE.
*/
-(void)setupServiceForBeaconRegion
{
if (self.peripheralManager.state == CBPeripheralManagerStatePoweredOn) {
// Add beacon region to monitored regions and remove from queue.
[self.peripheralManager addService:self.beaconRegion.service.defaultService];
} else {
NSLog(@"Unable to broadcast service. Peripheral Manager not powered on.");
}
}
-(void)advertiseServiceForBeaconRegion
{
[self.peripheralManager startAdvertising:@{CBAdvertisementDataServiceUUIDsKey:
[self.beaconRegion.service availableServiceUUIDs],
CBAdvertisementDataLocalNameKey:
@"APBeacons"
}
];
}
#pragma mark - CBPeripheralManagerDelegate Methods
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral
{
// Log the state change message to the console.
NSString *stateDescription = nil;
switch (peripheral.state) {
case CBPeripheralManagerStateResetting:
stateDescription = @"resetting"; break;
case CBPeripheralManagerStateUnsupported:
stateDescription = @"unsupported"; break;
case CBPeripheralManagerStateUnauthorized:
stateDescription = @"unauthorized"; break;
case CBPeripheralManagerStatePoweredOff:
stateDescription = @"off"; break;
case CBPeripheralManagerStatePoweredOn:
stateDescription = @"on"; break;
default:
stateDescription = @"unknown"; break;
}
NSLog(@"peripheralManager:%@ didUpdateState: %@ (%d)", peripheral, stateDescription, (int)peripheral.state);
switch (peripheral.state) {
case CBPeripheralManagerStatePoweredOn:
[self setupServiceForBeaconRegion];
[self advertiseServiceForBeaconRegion];
break;
default:
break;
}
}
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error
{
NSLog(@"peripheralManager:%@ didAddService:%@ error:%@",
peripheral, service, [error localizedDescription]);
}
- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error
{
if (error) {
NSLog(@"APBeaconManager - (peripheralManagerDidStartAdvertising:)%@ error:%@", peripheral, [error localizedDescription]);
// Inform delegate of advertisement failure.
if ([self.delegate respondsToSelector:@selector(beaconManager:failedToAdvertiseRegion:)]) {
[self.delegate beaconManager:self failedToAdvertiseRegion:self.beaconRegion];
}
} else {
// Inform delegate of advertisement success.
if ([self.delegate respondsToSelector:@selector(beaconManager:didStartAdvertisingRegion:)]) {
[self.delegate beaconManager:self didStartAdvertisingRegion:self.beaconRegion];
}
}
}
/*
*
*
* *** CENTRAL MANAGER CODE ***
*
*
*/
//
// FUNCTIONS CALLED BY USER/CALLER
//
#pragma mark - ManagerSetup
-(void)beginScanningForBeacons
{
NSArray *services = [self.wildcardBeaconService availableServiceUUIDs];
[self.centralManager scanForPeripheralsWithServices:services
options:nil];
// TODO MEMORY LEAK
// Add the refresh timer to the run loop and start refreshing our client list at 1 Hz.
// [[NSRunLoop currentRunLoop] addTimer:self.scanTimer forMode:NSDefaultRunLoopMode];
}
#pragma mark - CBCentralManagerDelegate Methods
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
NSLog(@"centralManagerDidUpdateState:%@", central);
NSString * state = nil;
switch ([self.centralManager state]) {
case CBCentralManagerStateUnsupported:
state = @"The platform/hardware doesn't support Bluetooth Low Energy.";
break;
case CBCentralManagerStateUnauthorized:
state = @"The app is not authorized to use Bluetooth Low Energy.";
break;
case CBCentralManagerStatePoweredOff:
state = @"Bluetooth is currently powered off.";
break;
case CBCentralManagerStatePoweredOn:
state = @"Powered On";
[self beginScanningForBeacons];
break;
case CBCentralManagerStateUnknown:
default:
state = @"Unknown";
}
NSLog(@"centralManagerDidUpdateState: %@ to %@", central, state);
}
- (void)centralManager:(CBCentralManager *)central
didDiscoverPeripheral:(CBPeripheral *)peripheral
advertisementData:(NSDictionary *)advertisementData
RSSI:(NSNumber *)RSSI {
NSLog(@"Discovered %@, %@, %d", peripheral.name, advertisementData, [RSSI intValue]);
self.readablePeripheral = peripheral;
[self.centralManager connectPeripheral:self.readablePeripheral options:nil];
}
- (void) centralManager:(CBCentralManager *)central
didConnectPeripheral:(CBPeripheral *)peripheral
{
NSLog(@"centralManager:didConnectPeripheral:%@", peripheral);
[peripheral setDelegate:self];
NSLog(@"discovering services...");
[peripheral discoverServices:[self.wildcardBeaconService availableServiceUUIDs]];
}
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
for (CBService *service in peripheral.services) {
NSLog(@"Discovered service %@", service);
if (service.isPrimary) {
[peripheral discoverCharacteristics:[self.wildcardBeaconService availableCharacteristicUUIDs] forService:service];
}
}
}
- (void)peripheral:(CBPeripheral *)peripheral
didDiscoverCharacteristicsForService:(CBService *)service
error:(NSError *)error
{
NSLog(@"peripheral:%@ didDiscoverCharacteristicsForService:%@ error:%@",
peripheral, service, [error localizedDescription]);
if (error) {
NSLog(@"Discovered characteristics for %@ with error: %@",
service.UUID, [error localizedDescription]);
return;
} else {
for (CBCharacteristic *characteristic in service.characteristics){
NSLog(@"Discovered characteristic %@", characteristic);
[peripheral readValueForCharacteristic:characteristic];
}
}
}
- (void)peripheral:(CBPeripheral *)peripheral
didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic
error:(NSError *)error {
if (!error) {
NSData *data = characteristic.value;
NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"Value for <CBCharacteristic = %@> is %@", characteristic, dataString);
APMutableBeacon *mutableBeacon;
// Pull out the appropriate APMutableBeacon associated with this peripheral.
if ([self.scannedPeripherals objectForKey:peripheral.identifier]) {
mutableBeacon = [self.scannedPeripherals objectForKey:peripheral.identifier];
} else if ([self.verifiedAndPopulatedPeripherals objectForKey:peripheral.identifier]) {
mutableBeacon = [self.verifiedAndPopulatedPeripherals objectForKey:peripheral.identifier];
} else {
// Create a new one if we don't have it in either set.
mutableBeacon = [[APMutableBeacon alloc] init];
[self.scannedPeripherals setObject:mutableBeacon forKey:peripheral.identifier];
}
//
// Fill in all beacon data values by traversing characteristics.
//
// If the hash value checks out. We can verify this sensor.
if ([characteristic.UUID isEqual:[self.wildcardBeaconService verificationHashCharacteristicUUID]]){
if ([self.wildcardBeaconService verifyHash:dataString]) {
NSLog(@"Found Valid APBeacon.");
mutableBeacon.validated = YES;
} else {
// If the scanned peripheral doesn't pass our checks, forget about it.
[self.scannedPeripherals removeObjectForKey:peripheral.identifier];
}
}
// Update the numeric fields of the mutableBeacon.
if ([characteristic.UUID isEqual:[self.wildcardBeaconService proximityUUIDCharacteristicUUID]]) {
[mutableBeacon setProximityUUID:[[NSUUID alloc] initWithUUIDString:dataString]];
}
if ([characteristic.UUID isEqual:[self.wildcardBeaconService majorCharacteristicUUID]]) {
[mutableBeacon setMajor:[NSNumber numberWithInteger:[dataString integerValue]]];
}
if ([characteristic.UUID isEqual:[self.wildcardBeaconService minorCharacteristicUUID]]) {
[mutableBeacon setMinor:[NSNumber numberWithInteger:[dataString integerValue]]];
}
if([mutableBeacon isPopulated]) {
// Inform our delegate we found one!
mutableBeacon.rssi = [peripheral.RSSI integerValue];
// Add the mutableBeacon to the verifiedAndPopulated collection, remove it from scannedPeripherals.
[self.verifiedAndPopulatedPeripherals setObject:mutableBeacon forKey:peripheral.identifier];
[self.scannedPeripherals removeObjectForKey:peripheral.identifier];
NSArray *rangedBeacons = [self.verifiedAndPopulatedPeripherals allValues];
NSArray *copiedRangedBeacons = [[NSArray alloc] initWithArray:rangedBeacons copyItems:YES];
if ([self.delegate respondsToSelector:@selector(beaconManager:didRangeBeacons:inRegion:)]) {
[self.delegate beaconManager:self didRangeBeacons:copiedRangedBeacons inRegion:nil];
}
[peripheral readRSSI]; // Request an RSSI update.
}
[self.scannedPeripherals setObject:mutableBeacon forKey:peripheral.identifier];
}
}
- (void)peripheralDidUpdateRSSI:(CBPeripheral *)peripheral error:(NSError *)error
{
if([self.scannedPeripherals objectForKey:peripheral.identifier]){
APMutableBeacon *mutableBeacon = [self.scannedPeripherals objectForKey:peripheral.identifier];
[mutableBeacon setRssi:[peripheral.RSSI integerValue]];
}
}
@end