Can I turn my new 4th gen AppleTV into an iBeacon? - ios

I read you can write apps for the new AppleTV. I read it runs a version of iOS. I read too that it has bluetooth capabilities.
Question, very simply, could I turn my AppleTV into an iBeacon, albeit a very expensive one? :)

The way you make an iOS device broadcast is, you create a CLBeaconRegion object with the UUID, major and minor you want to broadcast, call its peripheralDataWithMeasuredPower method, and pass the dictionary you obtain this way to CBPeripheralManager's startAdvertising method.
Now, tvOS lacks the CLBeaconRegion class, but has the CBPeripheralManager complete with the startAdvertising method. This means you should be able to generate the dictionary to pass to startAdvertising on an iOS device, take a loot at its content, and replicate it in your tvOS app.
In fact, people have already been doing it with OS X Mavericks in the past (I think Apple blocked this in Yosemite): http://www.blendedcocoa.com/blog/2013/11/02/mavericks-as-an-ibeacon/
Note: I haven't tried this method myself. It's entirely possible Apple blocked this trick on tvOS just liked they did on OS X.

Seeing as people "jailbreak" (or outright install Linux on) Apple TVs, you can install pretty much anything. Since iBeacon is a standard Apple opened (and for which there is open source code), I'd say the answer is "yes".

Ok, I got around to testing this at last, with the result ok, some promise. Here is the details, tvOS doesn't have CLRegion, will not compile, so the official route for iBeacon don't work.
Did however get it to see the iBeacon with this code; so there is hope... maybe not with iBeacons, but Bluetooth...
This is the BLE code I used...
import Foundation
import CoreBluetooth
class BLEManager {
var centralManager : CBCentralManager!
var bleHandler : BLEHandler // delegate
required init() {
self.bleHandler = BLEHandler()
self.centralManager = CBCentralManager(delegate: self.bleHandler, queue: dispatch_get_main_queue())
}
}
class BLEHandler : NSObject, CBCentralManagerDelegate {
override init() {
super.init()
}
func centralManagerDidUpdateState(central: CBCentralManager) {
switch (central.state)
{
case .Unsupported:
print("BLE is unsupported")
case .Unauthorized:
print("BLE is unauthorized")
case .Unknown:
print("BLE is unknown")
case .Resetting:
print("BLE is reseting")
case .PoweredOff:
print("BLE is powered off")
case .PoweredOn:
print("BLE is powered on")
central.scanForPeripheralsWithServices(nil, options: nil)
default:
print("BLE default")
}
print("centralManagerDidUpdateState")
}
func centralManager(central: CBCentralManager, didConnectPeripheral peripheral: CBPeripheral) {
print("didConnectPeripheral")
}
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
print("\(peripheral.name) : \(RSSI) dBm")
}
func centralManager(central: CBCentralManager, didFailToConnectPeripheral peripheral: CBPeripheral, error: NSError?) {
}
func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?) {
print("didDiscoverServices")
}
func peripheral(peripheral: CBPeripheral, didDiscoverCharacteristicsForService service: CBService, error: NSError?) {
print("didDiscoverCharacteristicsForService")
}
func peripheral(peripheral: CBPeripheral, didUpdateValueForCharacteristic characteristic: CBCharacteristic, error: NSError?) {
print("didUpdateValueForCharacteristic")
}
func peripheral(peripheral: CBPeripheral, didUpdateNotificationStateForCharacteristic characteristic: CBCharacteristic, error: NSError?) {
print("didUpdateNotificationStateForCharacteristic")
}
func centralManager(central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: NSError?) {
print("didDisconnectPeripheral")
}
}
With these lines in the viewontroller
var bleManager: BLEManager!
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
bleManager = BLEManager()
Need to say a special thank you to rhythmic-fistman who helped me with the code!

Related

Can bluetooth trigger a certain action in an app?

I am planning to start an ios application and was wondering if there is a way to trigger certain action such as finding location using google map on bluetooth connection (or disconnection)?
I know an apple map application has similar feature where it shows where your car is parked. Any references would be appreiciate.
Thanks.
You can try this in your viewController where you want to check:
var manager: CBCentralManager!
override func viewDidLoad(){
super.viewDidLoad()
manager = CBCentralManager()
manager.delegate = self
}
//CBCentralManager Delegate method
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOn:
//poweredOn
case .resetting:
//resetting
case .unauthorized:
//unauthorized
case .unknown:
//unknown
case .unsupported:
//UnSupported
case .poweredOff:
//Bluetooth is not Connected.Please Enable it
}
the status in delegate will tell you what's the status of your Bluetooth.
Hope it helps!
Well there delegate methods which get fired after different states of connection. The best way I suggest you to do is implement the following delegate methods.
func centralManager(_ central: CBCentralManager,
didConnect peripheral: CBPeripheral) {
}
func centralManager(_ central: CBCentralManager,
didDisconnectPeripheral peripheral: CBPeripheral,
error: Error?) {
}
And call a function to get your location in these cases. Additionally there are other delegate methods such as
func centralManagerDidUpdateState(_ central: CBCentralManager) {
}
func centralManager(_ central: CBCentralManager,
didFailToConnect peripheral: CBPeripheral,
error: Error?) {
}
But these cases does not cover the all the cases of connection and disconnection. But depending on your requirement you can implement either one. These are CBCentralManagerDelegate delegates.
CBCentralManager
CBCentralManagerDelegate

didDisconnectPeripheral not called iOS Swift 3

I'm trying to disconnect from a BLE device. I'm doing cancelPeripheralConnection() and its never calling didDisconnectPeripheral().
I'm setting notify value to one characteristic, so while disconnecting i'm setting it false.
{
BLEOperations.sharedInstance.connectedDevice?.setNotifyValue(false, for: BLEOperations.sharedInstance.peripheralCharacteristics[0])
BLEOperations.sharedInstance.manager.cancelPeripheralConnection(BLEOperations.sharedInstance.connectedDevice!)
}
I'm trying to disconnect connectedPeripheral when user tries to connect to new device. The current device is not disconnected but new device is connected along with old one.
{
BLEOperations.sharedInstance.manager.connect(self.peripheralArray[indexPath.row] as! CBPeripheral , options: nil)
}
What am i doing wrong here? When device connection is lost the method is being called.
It will work fine when the CentralManager has only one instance in the entire flow.
Instead of using centralmanagerdelegates in viewcontroller, you can create a CentralManager operations class (which is a singleton) and perform operations.
I did that and its working fine.
Well, probably we would need more information about your code. But the method you are looking for, is this one.
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
disconnected(central: central)
}
You should set your centralManager's delegate to self, and everytime a peripheral is disconnected it should call the delegate.
Doing some tests, the disconnection process is not as nice as it seems, you can see your peripheral is still disconnecting but the delegate has been called (this can be adjusted with the hardware itself or the firmware on your peripheral).
BTW, you can connect several devices at once.
Cancels an active or pending connection to peripheral
class ViewController: UIViewController, CBCentralManagerDelegate {
var manager: CBCentralManager!
var peripherals: [CBPeripheral]?
#IBAction func actionScanBLEDevice(_ sender: AnyObject) {
manager = CBCentralManager (delegate: self, queue: nil)
}
#IBAction func actionDisconnectBLE(_ sender: AnyObject) {
//Use As per need where you want to disconnect perticular peripheral
manager.cancelPeripheralConnection(peripheral[0]) // Disconnent numbers of BLE Devices
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
//Manage Some Condition then disconnect
print("Disconnected from peripheral")
peripherals?.append(peripheral)
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
print("Discovered peripheral \(peripheral.name,peripheral.identifier.uuidString)")
print("advertisementData\(advertisementData)")
// Use discovered peripheral here
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
print("Checking")
switch(central.state)
{
case.unsupported:
print("BLE is not supported")
case.unauthorized:
print("BLE is unauthorized")
case.unknown:
print("BLE is Unknown")
case.resetting:
print("BLE is Resetting")
case.poweredOff:
print("BLE service is powered off")
case.poweredOn:
print("BLE service is powered on")
print("Start Scanning")
manager.scanForPeripherals(withServices: nil, options: nil)
}
}
}

Bluetooth doesn't discover devices

First off, I know this question has been asked a lot on SO. And I went through all of them trying to resolve it but to no avail.
I have a simple app that scans for Bluetooth enabled devices. Here's my code.
import CoreBluetooth
import UIKit
class ViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate {
var manager: CBCentralManager!
var peripheral: CBPeripheral!
override func viewDidLoad() {
super.viewDidLoad()
manager = CBCentralManager(delegate: self, queue: nil)
}
// MARK: - CBCentralManagerDelegate
func centralManagerDidUpdateState(central: CBCentralManager) {
print(#function)
switch central.state {
case .Unsupported:
print("Unsupported")
case .Unauthorized:
print("Unauthorized")
case .PoweredOn:
print("Powered On")
central.scanForPeripheralsWithServices(nil, options: nil)
case .Resetting:
print("Resetting")
case .PoweredOff:
print("Powered Off")
case .Unknown:
print("Unknown")
}
}
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
print(#function)
print("Discovered \(peripheral.name) at \(RSSI)")
if peripheral.name!.containsString(name) {
manager.stopScan()
self.peripheral = peripheral
self.peripheral.delegate = self
manager.connectPeripheral(peripheral, options: nil)
}
}
func centralManager(central: CBCentralManager, didConnectPeripheral peripheral: CBPeripheral) {
print(#function)
}
func centralManager(central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: NSError?) {
central.scanForPeripheralsWithServices(nil, options: nil)
}
// MARK: - CBPeripheralDelegate
func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?) {
print(#function)
guard let services = peripheral.services else {
return
}
for service in services {
print(service.UUID)
if service.UUID == serviceUUID {
peripheral.discoverCharacteristics(nil, forService: service)
}
}
}
func peripheral(peripheral: CBPeripheral, didDiscoverCharacteristicsForService service: CBService, error: NSError?) {
print(#function)
guard let characteristics = service.characteristics else {
return
}
for characteristic in characteristics {
print(characteristic.UUID)
if characteristic.UUID == scratchUUID {
peripheral.setNotifyValue(true, forCharacteristic: characteristic)
}
}
}
func peripheral(peripheral: CBPeripheral, didUpdateValueForCharacteristic characteristic: CBCharacteristic, error: NSError?) {
print(#function)
var count: UInt32 = 0
if characteristic.UUID == scratchUUID {
characteristic.value!.getBytes(&count, length: sizeof(UInt32))
print(String(format: "%llu", count))
}
}
}
I ran it in my iPhone 5 (iOS 9.3.4). I had a laptop running Windows, an Android phone, an iPad Air 2 and my Mac all Bluetooth turned on. But none of those devices were discovered. centralManagerDidUpdateState method gets called and scanning starts but that's it. didDiscoverPeripheral delegate method never gets called.
I repeated the process, this time ran it in the iPad Air 2 but same result.
If I go to the Bluetooth menu on the device I do see other devices being discovered.
In some SO answers, I saw that running it in the main queue would work. Like so, manager = CBCentralManager(delegate: self, queue: dispatch_get_main_queue()). But that did not work for me either.
I'd really appreciate any help on this.
Your other devices are probably not advertising a BLE service (like Paulw11 mentioned in the comments). All of the devices you mentioned will not appear as discoverable unless they are in the Bluetooth Settings page.
There are apps for iOS, Android, and Windows that have them advertise over LE. The easiest to use is LightBlue for iOS. Run your app on the iPhone while the other iOS device is advertising and you should see the advertisements. It might be useful to get an LE advertisement scanner on Windows or Android to double check that LightBlue is advertising.
Note Windows does not yet support Peripheral Role for Bluetooth LE,
so even though you will be able to see the advertisement you will not
be able to connect to it.

not getting data / services from BLE device in ios

I need to connect with a BLE device and then handle data as per sent via different button in it.
For that I wrote following code.
import CoreBluetooth
class HomeViewController: UIViewController,CBPeripheralDelegate,CBCentralManagerDelegate
{
var centralManager : CBCentralManager!
var peri : CBPeripheral!
override func viewDidLoad()
{
super.viewDidLoad()
centralManager = CBCentralManager(delegate: self, queue: nil)
}
func centralManagerDidUpdateState(central: CBCentralManager) {
if central.state == .Unknown
{
print("Unknown")
}
else if central.state == .Unsupported
{
print("Unsupported")
}
else if central.state == .Unauthorized
{
print("Unauthorized")
}
else if central.state == .Resetting
{
print("Resetting")
}
else if central.state == .PoweredOn
{
print("Powered On")
startScan()
}
else if central.state == .PoweredOff
{
print("Powered Off")
}
}
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
print("Discovered: \(peripheral.name) at \(RSSI)")
print("AdvertisementData:\(advertisementData)")
if peri != peripheral
{
peri = peripheral
centralManager.connectPeripheral(peripheral, options: nil)
}
}
func centralManager(central: CBCentralManager, didFailToConnectPeripheral peripheral: CBPeripheral, error: NSError?) {
print("Failed to connect \(peripheral) cause of \(error)")
}
func centralManager(central: CBCentralManager, didConnectPeripheral peripheral: CBPeripheral) {
print("connected to \(peripheral)")
// centralManager.stopScan()
print("Available services:\(peripheral.services)")
}
func peripheral(peripheral: CBPeripheral, didDiscoverIncludedServicesForService service: CBService, error: NSError?) {
print("Services\(service) and error\(error)")
}
func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?) {
print("Services and error\(error)")
}
func startScan(){
print("Scanning...")
centralManager.scanForPeripheralsWithServices(nil, options: nil)
}
}
And here is my Log for this code.
Powered On
Scanning...
Discovered: Optional("**** BLE") at 127
AdvertisementData:["kCBAdvDataIsConnectable": 1, "kCBAdvDataServiceUUIDs": (
1802
)]
connected to <CBPeripheral: 0x12756d910, identifier = 6197****-EB0A-F1E8-BEF4-1AFAC629C5BC, name = **** BLE, state = connected>
Available services:nil
This is output is generated when one button is clicked from BLE device. But I am unable to receive or read data when another button is clicked.
Android developer of same app has integrated with both button.
So there is no any problem in device.
Can anyone help me to guide where I'm going wrong in this code??
Pandafox's answer is perfect just one thing is missing from it.
Which is setting delegate of peripheral.
Here is the complete code to discover peripheral, connect to it and discover its services and characteristics.
1.Connect peripheral
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
print("Discovered: \(peripheral.name) at \(RSSI)")
print("AdvertisementData:\(advertisementData)")
if peri != peripheral
{
peri = peripheral
peri.delegate = self
centralManager.connectPeripheral(peri, options: nil)
}
}
Connection failure or success
func centralManager(central: CBCentralManager, didFailToConnectPeripheral peripheral: CBPeripheral, error: NSError?) {
print("Failed to connect \(peripheral) cause of \(error)")
}
func centralManager(central: CBCentralManager, didConnectPeripheral peripheral: CBPeripheral) {
print("connected to \(peripheral)")
// centralManager.stopScan()
peripheral.discoverServices(nil)
}
3.DiscoverServices
func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?) {
print("Services:\(peripheral.services) and error\(error)")
if let services = peripheral.services {
for service in services {
peripheral.discoverCharacteristics(nil, forService: service)
}
}
}
Discover Characteristics and set notification
func peripheral(peripheral: CBPeripheral, didDiscoverCharacteristicsForService service: CBService, error: NSError?)
{
print("peripheral:\(peripheral) and service:\(service)")
for characteristic in service.characteristics!
{
peripheral.setNotifyValue(true, forCharacteristic: characteristic)
}
}
Handle notification for update value of characteristics
func peripheral(peripheral: CBPeripheral, didUpdateValueForCharacteristic characteristic: CBCharacteristic, error: NSError?)
{
print("characteristic changed:\(characteristic)")
}
You also have to discover the services and characteristics after connecting to the device.
For example, in your "didConnectPeripheral" method, you will have to do something like:
func centralManager(central: CBCentralManager, didConnectPeripheral peripheral: CBPeripheral) {
print("connected to \(peripheral)")
peripheral.delegate = self
peripheral.discoverServices(nil)
print("Discovering services!")
}
And then:
func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?) {
print("Discovered services: \(peripheral.services), Error\(error)")
if let services = peripheral.services {
for service in services {
peripheral.discoverCharacteristics(nil, forService: service)
}
}
}
And then you have to handle each characteristic:
func peripheral(peripheral: CBPeripheral, didDiscoverCharacteristicsForService service: CBService, error: NSError)
You must remember to store each characteristic manually, as they will be deallocated if you don't.
In order to receive streaming data (notifications) you will have to enable notify for each characteristic.
peripheral.setNotifyValue(true, forCharacteristic: characteristic)
You also have to implement:
func peripheral(peripheral: CBPeripheral, didUpdateValueForCharacteristic characteristic: CBCharacteristic, error: NSError)
Order to handle the incoming values.
As you can see, there's quite a bit of boiler plate code required to get started.
After connecting to the peripheral you have to call discoverServices on the peripheral with the UUID of the services you want to discover, you then have to discover the characteristics of the service. If you want updates when a button is clicked, you will have to turn notifications on for the characteristic corresponding to that button
I would highly recommend this link from apple for follow up reading if you still need help. It describes what you need to do step by step in a better fashion than I could ever describe here.
Apple BLE Guide

Beacon is not discovering iOS

I am trying to discover the ibeacon. i am running app on iPhone 4s. device bluetooth is turned on. beacon is also discovering in other iOS apps.
When i am searching for services following delegate "centralManagerDidUpdateState" gets called and State is "On" after this nothing happens. It is expected that "didDiscoverPeripheral" should get called but nothing happens. What wrong i am doing here?
Hi,
import UIKit
import FBSDKLoginKit
import SwiftQRCode
import CoreBluetooth
#objc
class DashboardViewController: UIViewController {
var myCentralManager:CBCentralManager!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
myCentralManager = CBCentralManager(delegate: self, queue: nil)
// let options = [CBCentralManagerScanOptionAllowDuplicatesKey:true]
myCentralManager.scanForPeripheralsWithServices(nil, options: nil)
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.navigationBar.hidden = true
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
// MARK: CBCentralManagerDelegate
extension DashboardViewController: CBCentralManagerDelegate {
func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
peripheral.delegate = self
// if peripheral.state == CBPeripheralState.Disconnected {
//
// central.connectPeripheral(peripheral, options: nil)
// }
}
func centralManager(central: CBCentralManager, didConnectPeripheral peripheral: CBPeripheral) {
peripheral.delegate = self
peripheral.discoverServices(nil)
}
func centralManager(central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: NSError?) {
}
func centralManager(central: CBCentralManager, didFailToConnectPeripheral peripheral: CBPeripheral, error: NSError?) {
}
}
// MARK: CBCentralManagerDelegate
extension DashboardViewController: CBPeripheralDelegate {
func centralManagerDidUpdateState(central: CBCentralManager) {
switch central.state{
case CBCentralManagerState.PoweredOn:
print("On.")
break
case CBCentralManagerState.PoweredOff:
print("Off.")
break
case CBCentralManagerState.Resetting:
print("Resetting.")
break
case CBCentralManagerState.Unauthorized:
print("Unauthorized.")
break
case CBCentralManagerState.Unknown:
print("Unknown.")
break
case CBCentralManagerState.Unsupported:
print("Unsupported.")
break
}
}
func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?) {
for service in peripheral.services!{
peripheral.discoverCharacteristics(nil, forService: service)
}
}
func peripheral(peripheral: CBPeripheral, didDiscoverCharacteristicsForService service: CBService, error: NSError?) {
for charactristics in service.characteristics!{
peripheral.setNotifyValue(true, forCharacteristic: charactristics)
}
}
func peripheral(peripheral: CBPeripheral, didUpdateNotificationStateForCharacteristic characteristic: CBCharacteristic, error: NSError?) {
if characteristic.isNotifying {
print("Notifying...")
}
}
func peripheral(peripheral: CBPeripheral, didUpdateValueForCharacteristic characteristic: CBCharacteristic, error: NSError?) {
print(characteristic.value)
}
}
It is not possible to detect iBeacons with CoreBluetooth APIs and the CBCentralManager class as shown in the code. While iBeacons are bluetooth LE devices, Apple has security blocks that prevent iOS devices from reading the raw advertisement data with the didConnectPeripheral callback.
If you want to detect iBeacons on iOS, you must use the CoreLocation APIs. You can read more about how to do this here.

Resources