使用 CLLocationManager 获取 iBeacon 邻近更新
Get iBeacon proximity updates using CLLocationManager
我正在尝试编写一个应用程序,定期接收有关与当前找到的 iBeacon 的接近程度的更新。我有两个我想跟踪的 iBeacon,它们是我自己的,我将用于跟踪的 UUID 硬编码到我的代码中。 iBeacon 本身具有相同的主 ID 但具有不同的次 ID。我的问题是关于如何定期更新关于使用预设 UUID 找到的 iBeacons 的接近度。我知道这需要使用 CLLocationManager 来完成,因为它实现了 iBeacon 测距。
我目前对 CLLocationManager 的实验:
import UIKit;
import CoreLocation;
import HomeKit;
class FirstViewController: UIViewController, CLLocationManagerDelegate, HMHomeManagerDelegate, UIPickerViewDelegate, UIPickerViewDataSource{
let homeManager = HMHomeManager();
let locationManager = CLLocationManager();
var beaconsInProximity: [CLBeacon] = [];
let beaconUUID: String = "11984894-7042-9801-839A-ADECCDFEDFF0";
let beaconMajor = 0x1;
let beaconMinor: [Int] = [0x1, 0x7];
let homeName = "Home";
var lamps = [HMAccessory]();
var lampNames = [String]();
var pickerNames = [String: HMAccessory]();
var selectedLamp: HMAccessory!;
var lightHome: HMHome!;
var firstLight: HMAccessory!;
var secondLight: HMAccessory!;
var thirdTestLight: HMAccessory!;
let beaconRegion: CLBeaconRegion = CLBeaconRegion(proximityUUID: NSUUID(uuidString:"11984894-7042-9801-839A-ADECCDFEDFF0")as! UUID, identifier: "Beaconons");
@IBOutlet weak var lampSwitch: UISwitch!
@IBOutlet weak var configureLampButton: UIButton!
@IBOutlet weak var lampPicker: UIPickerView!
@IBOutlet weak var identifyLampButton: UIButton!
@IBOutlet weak var lampSelectedLabel: UILabel!
@IBOutlet weak var lampStatusLabel: UILabel!
@IBOutlet weak var beaconStatusLabel: UILabel!
override func viewDidLoad(){
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
if(selectedLamp != nil){
updateLampLabel(selectedLamp);
updateLampStatusLabel(selectedLamp);
}else{
updateLampLabelNoLamp();
updateLampStatusLabelNoStatus();
}
homeManager.delegate = self;
lampPicker.delegate = self;
lampPicker.dataSource = self;
locationManager.delegate = self;
locationManager.requestAlwaysAuthorization();
locationManager.startMonitoring(for: beaconRegion);
locationManager.startRangingBeacons(in: beaconRegion);
locationManager.requestState(for: beaconRegion);
}
override func didReceiveMemoryWarning(){
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func homeManagerDidUpdateHomes(_ manager: HMHomeManager) {
for home in homeManager.homes{
for accessory in home.accessories{
if(accessory.name.contains("lamp")){
lamps.append(accessory);
pickerNames[String(describing: accessory.services[1].characteristics[0].value)] = accessory;
print("Added accessory " + String(describing: accessory.services[1].characteristics[0].value) + " to lamp list");
}
}
}
if(lamps.count != 0){
for index in 0...(lamps.count - 1){
lampNames.append(String(describing: lamps[index].services[1].characteristics[0].value));
}
}
}
func locationManager(_: CLLocationManager, didRangeBeacons: [CLBeacon], in: CLBeaconRegion){
for beacon in didRangeBeacons{
if(beacon.proximity == CLProximity.immediate){
beaconsInProximity.append(beacon);
}else{
if(beaconsInProximity.contains(beacon)){
for index in 0...beaconsInProximity.count{
if(beaconsInProximity[index] == beacon){
beaconsInProximity.remove(at: index);
}
}
}
}
}
for beacon in beaconsInProximity{
if(beacon.major.intValue == beaconMajor){
if(beacon.minor.intValue == 0x3){
if(selectedLamp != nil){
switchLamp(selectedLamp, true);
beaconStatusLabel.text = "region detected";
}
}else{
if(selectedLamp != nil){
switchLamp(selectedLamp, false);
beaconStatusLabel.text = "lamping all the lamps that have ever lamped";
}
}
}
}
}
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
let bRegion = region as! CLBeaconRegion;
if(bRegion.proximityUUID == beaconRegion.proximityUUID){
print("Correct region");
beaconStatusLabel.text = "Entered beacon region";
}
}
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
let bRegion = region as! CLBeaconRegion;
if(bRegion.proximityUUID == beaconRegion.proximityUUID){
print("Exited correct region");
beaconStatusLabel.text = "Beacon region left";
}
}
@IBAction func configureLampButtonPressed(_ sender: AnyObject){
if(pickerNames[lampNames[lampPicker.selectedRow(inComponent: 0)]] != nil){
selectedLamp = pickerNames[lampNames[lampPicker.selectedRow(inComponent: 0)]];
}
if(selectedLamp != nil){
updateLampLabel(selectedLamp);
updateLampStatusLabel(selectedLamp);
}else{
updateLampLabelNoLamp();
updateLampStatusLabelNoStatus();
}
}
@IBAction func identifyLampButtonPressed(_ sender: AnyObject){
if(selectedLamp != nil){
identifyLamp(selectedLamp);
}
}
@IBAction func lampSwitchFlipped(_ sender: AnyObject){
if(selectedLamp != nil){
switchLamp(selectedLamp, lampSwitch.isOn);
updateLampStatusLabel(selectedLamp);
}else{
lampSwitch.setOn(!lampSwitch.isOn, animated: true);
updateLampStatusLabelNoStatus();
}
}
func updateLampLabelNoLamp(){
lampSelectedLabel.text = "No lamp selected";
}
func updateLampLabel(_ lamp: HMAccessory){
lampSelectedLabel.text = "Selected lamp: " + lamp.name;
}
func numberOfComponents(in pickerView: UIPickerView) -> Int{
return 1;
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int{
return lampNames.count;
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String?{
return lampNames[row];
}
func booleanToInt(_ value: Bool) -> Int{
if(value){
return 1;
}else{
return 0;
}
}
func sendLocalNotificationWithMessage(_ message: String!){
}
func switchLamp(_ lamp: HMAccessory, _ value: Bool){
lamp.services[1].characteristics[1].writeValue(booleanToInt(value), completionHandler: {
error in
if let error = error{
print("Something went wrong! \(error)");
}
})
}
func identifyLamp(_ lamp: HMAccessory){
lamp.services[0].characteristics[3].writeValue(1, completionHandler: {
error in
if let error = error{
print("Something went wrong! \(error)");
}
})
}
func updateLampStatusLabel(_ lamp: HMAccessory){
lampStatusLabel.text = "Lamp status: " + String(describing: selectedLamp.services[1].characteristics[1].value);
}
func updateLampStatusLabelNoStatus(){
lampStatusLabel.text = "Select a lamp";
}
}
应用本身的目标是根据用户是否在附近来切换灯泡。
beacon.proximity 字段将在每次回调 func locationManager(_: CLLocationManager, didRangeBeacons: [CLBeacon], in: CLBeaconRegion)
时更新。这些回调将每秒出现一次,并带有一组具有此字段的 CLBeacon 对象。所以每 1 秒你就会得到一个更新。
根据代码的设置方式,如果信标紧邻,beaconsInProximity
似乎会正确更新,稍后将在回调方法中调用 switchLamp
。
但是请注意,CLBeacon
的 proximity
字段可能不会像您希望的那样快速更新。该字段派生自 accuracy
字段,该字段是以米为单位的距离估计值。如果距离估计小于 0.5 米,则 accuracy
将设置为立即。如果距离估计 > 0.5 米,它将被设置为另一个值。
accuracy
中的距离估计基于 20 秒 运行 RSSI 测量的平均值。因此,如果您从 5 米外快速移动到信标发射器旁边,accuracy
字段将在 20 秒内从 ~5.0 缓慢变为 ~0.0。 proximity
字段显示 immediate
需要将近 20 秒。
此外,重要的是要了解必须使用信标内设置的测量功率值正确校准信标,以尽可能准确地估计距离。如果您在 accuracy
中获得非常不准确的距离估计,即使在静止不动 20 秒后,您可能需要进行此校准。
我正在尝试编写一个应用程序,定期接收有关与当前找到的 iBeacon 的接近程度的更新。我有两个我想跟踪的 iBeacon,它们是我自己的,我将用于跟踪的 UUID 硬编码到我的代码中。 iBeacon 本身具有相同的主 ID 但具有不同的次 ID。我的问题是关于如何定期更新关于使用预设 UUID 找到的 iBeacons 的接近度。我知道这需要使用 CLLocationManager 来完成,因为它实现了 iBeacon 测距。
我目前对 CLLocationManager 的实验:
import UIKit;
import CoreLocation;
import HomeKit;
class FirstViewController: UIViewController, CLLocationManagerDelegate, HMHomeManagerDelegate, UIPickerViewDelegate, UIPickerViewDataSource{
let homeManager = HMHomeManager();
let locationManager = CLLocationManager();
var beaconsInProximity: [CLBeacon] = [];
let beaconUUID: String = "11984894-7042-9801-839A-ADECCDFEDFF0";
let beaconMajor = 0x1;
let beaconMinor: [Int] = [0x1, 0x7];
let homeName = "Home";
var lamps = [HMAccessory]();
var lampNames = [String]();
var pickerNames = [String: HMAccessory]();
var selectedLamp: HMAccessory!;
var lightHome: HMHome!;
var firstLight: HMAccessory!;
var secondLight: HMAccessory!;
var thirdTestLight: HMAccessory!;
let beaconRegion: CLBeaconRegion = CLBeaconRegion(proximityUUID: NSUUID(uuidString:"11984894-7042-9801-839A-ADECCDFEDFF0")as! UUID, identifier: "Beaconons");
@IBOutlet weak var lampSwitch: UISwitch!
@IBOutlet weak var configureLampButton: UIButton!
@IBOutlet weak var lampPicker: UIPickerView!
@IBOutlet weak var identifyLampButton: UIButton!
@IBOutlet weak var lampSelectedLabel: UILabel!
@IBOutlet weak var lampStatusLabel: UILabel!
@IBOutlet weak var beaconStatusLabel: UILabel!
override func viewDidLoad(){
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
if(selectedLamp != nil){
updateLampLabel(selectedLamp);
updateLampStatusLabel(selectedLamp);
}else{
updateLampLabelNoLamp();
updateLampStatusLabelNoStatus();
}
homeManager.delegate = self;
lampPicker.delegate = self;
lampPicker.dataSource = self;
locationManager.delegate = self;
locationManager.requestAlwaysAuthorization();
locationManager.startMonitoring(for: beaconRegion);
locationManager.startRangingBeacons(in: beaconRegion);
locationManager.requestState(for: beaconRegion);
}
override func didReceiveMemoryWarning(){
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func homeManagerDidUpdateHomes(_ manager: HMHomeManager) {
for home in homeManager.homes{
for accessory in home.accessories{
if(accessory.name.contains("lamp")){
lamps.append(accessory);
pickerNames[String(describing: accessory.services[1].characteristics[0].value)] = accessory;
print("Added accessory " + String(describing: accessory.services[1].characteristics[0].value) + " to lamp list");
}
}
}
if(lamps.count != 0){
for index in 0...(lamps.count - 1){
lampNames.append(String(describing: lamps[index].services[1].characteristics[0].value));
}
}
}
func locationManager(_: CLLocationManager, didRangeBeacons: [CLBeacon], in: CLBeaconRegion){
for beacon in didRangeBeacons{
if(beacon.proximity == CLProximity.immediate){
beaconsInProximity.append(beacon);
}else{
if(beaconsInProximity.contains(beacon)){
for index in 0...beaconsInProximity.count{
if(beaconsInProximity[index] == beacon){
beaconsInProximity.remove(at: index);
}
}
}
}
}
for beacon in beaconsInProximity{
if(beacon.major.intValue == beaconMajor){
if(beacon.minor.intValue == 0x3){
if(selectedLamp != nil){
switchLamp(selectedLamp, true);
beaconStatusLabel.text = "region detected";
}
}else{
if(selectedLamp != nil){
switchLamp(selectedLamp, false);
beaconStatusLabel.text = "lamping all the lamps that have ever lamped";
}
}
}
}
}
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
let bRegion = region as! CLBeaconRegion;
if(bRegion.proximityUUID == beaconRegion.proximityUUID){
print("Correct region");
beaconStatusLabel.text = "Entered beacon region";
}
}
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
let bRegion = region as! CLBeaconRegion;
if(bRegion.proximityUUID == beaconRegion.proximityUUID){
print("Exited correct region");
beaconStatusLabel.text = "Beacon region left";
}
}
@IBAction func configureLampButtonPressed(_ sender: AnyObject){
if(pickerNames[lampNames[lampPicker.selectedRow(inComponent: 0)]] != nil){
selectedLamp = pickerNames[lampNames[lampPicker.selectedRow(inComponent: 0)]];
}
if(selectedLamp != nil){
updateLampLabel(selectedLamp);
updateLampStatusLabel(selectedLamp);
}else{
updateLampLabelNoLamp();
updateLampStatusLabelNoStatus();
}
}
@IBAction func identifyLampButtonPressed(_ sender: AnyObject){
if(selectedLamp != nil){
identifyLamp(selectedLamp);
}
}
@IBAction func lampSwitchFlipped(_ sender: AnyObject){
if(selectedLamp != nil){
switchLamp(selectedLamp, lampSwitch.isOn);
updateLampStatusLabel(selectedLamp);
}else{
lampSwitch.setOn(!lampSwitch.isOn, animated: true);
updateLampStatusLabelNoStatus();
}
}
func updateLampLabelNoLamp(){
lampSelectedLabel.text = "No lamp selected";
}
func updateLampLabel(_ lamp: HMAccessory){
lampSelectedLabel.text = "Selected lamp: " + lamp.name;
}
func numberOfComponents(in pickerView: UIPickerView) -> Int{
return 1;
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int{
return lampNames.count;
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String?{
return lampNames[row];
}
func booleanToInt(_ value: Bool) -> Int{
if(value){
return 1;
}else{
return 0;
}
}
func sendLocalNotificationWithMessage(_ message: String!){
}
func switchLamp(_ lamp: HMAccessory, _ value: Bool){
lamp.services[1].characteristics[1].writeValue(booleanToInt(value), completionHandler: {
error in
if let error = error{
print("Something went wrong! \(error)");
}
})
}
func identifyLamp(_ lamp: HMAccessory){
lamp.services[0].characteristics[3].writeValue(1, completionHandler: {
error in
if let error = error{
print("Something went wrong! \(error)");
}
})
}
func updateLampStatusLabel(_ lamp: HMAccessory){
lampStatusLabel.text = "Lamp status: " + String(describing: selectedLamp.services[1].characteristics[1].value);
}
func updateLampStatusLabelNoStatus(){
lampStatusLabel.text = "Select a lamp";
}
}
应用本身的目标是根据用户是否在附近来切换灯泡。
beacon.proximity 字段将在每次回调 func locationManager(_: CLLocationManager, didRangeBeacons: [CLBeacon], in: CLBeaconRegion)
时更新。这些回调将每秒出现一次,并带有一组具有此字段的 CLBeacon 对象。所以每 1 秒你就会得到一个更新。
根据代码的设置方式,如果信标紧邻,beaconsInProximity
似乎会正确更新,稍后将在回调方法中调用 switchLamp
。
但是请注意,CLBeacon
的 proximity
字段可能不会像您希望的那样快速更新。该字段派生自 accuracy
字段,该字段是以米为单位的距离估计值。如果距离估计小于 0.5 米,则 accuracy
将设置为立即。如果距离估计 > 0.5 米,它将被设置为另一个值。
accuracy
中的距离估计基于 20 秒 运行 RSSI 测量的平均值。因此,如果您从 5 米外快速移动到信标发射器旁边,accuracy
字段将在 20 秒内从 ~5.0 缓慢变为 ~0.0。 proximity
字段显示 immediate
需要将近 20 秒。
此外,重要的是要了解必须使用信标内设置的测量功率值正确校准信标,以尽可能准确地估计距离。如果您在 accuracy
中获得非常不准确的距离估计,即使在静止不动 20 秒后,您可能需要进行此校准。