Skip to content

Commit

Permalink
Initial iBeacon support
Browse files Browse the repository at this point in the history
  • Loading branch information
robbiet480 committed Apr 21, 2017
1 parent cc6f00c commit 9e0e446
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 63 deletions.
8 changes: 8 additions & 0 deletions HomeAssistant/Classes/Components/Zone.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,21 @@ class Zone: Entity {
dynamic var enterNotification = true
dynamic var exitNotification = true

// Beacons
dynamic var UUID: String?
var Major: Int?
var Minor: Int?

override func mapping(map: Map) {
super.mapping(map: map)

Latitude <- map["attributes.latitude"]
Longitude <- map["attributes.longitude"]
Radius <- map["attributes.radius"]
TrackingEnabled <- map["attributes.track_ios"]
UUID <- map["attributes.beacon.uuid"]
Major <- map["attributes.beacon.major"]
Minor <- map["attributes.beacon.minor"]
}

func locationCoordinates() -> CLLocationCoordinate2D {
Expand Down
139 changes: 79 additions & 60 deletions HomeAssistant/HAAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ public class HomeAssistantAPI {

private var manager: Alamofire.SessionManager?

let beaconManager = BeaconManager()

var cachedEntities: [Entity]?

func Setup(baseURL: String, password: String, deviceID: String?) {
Expand Down Expand Up @@ -139,7 +141,7 @@ public class HomeAssistantAPI {
}
}

// swiftlint:disable:next function_body_length
// swiftlint:disable:next function_body_length cyclomatic_complexity
func submitLocation(updateType: LocationUpdateTypes,
coordinates: CLLocationCoordinate2D,
accuracy: CLLocationAccuracy,
Expand Down Expand Up @@ -186,6 +188,14 @@ public class HomeAssistantAPI {
var shouldNotify = false

switch updateType {
case .BeaconRegionEnter:
notificationBody = "\(zone!.Name) entered via iBeacon"
notificationIdentifer = "\(zone!.Name)_beacon_entered"
shouldNotify = prefs.bool(forKey: "beaconEnterNotifications")
case .BeaconRegionExit:
notificationBody = "\(zone!.Name) exited via iBeacon"
notificationIdentifer = "\(zone!.Name)_beacon_exited"
shouldNotify = prefs.bool(forKey: "beaconExitNotifications")
case .RegionEnter:
notificationBody = "\(zone!.Name) entered"
notificationIdentifer = "\(zone!.Name)_entered"
Expand Down Expand Up @@ -235,53 +245,38 @@ public class HomeAssistantAPI {
print("Skipping zone set to not track!")
continue
}
do {
try Location.monitor(regionAt: zone.locationCoordinates(), radius: zone.Radius, enter: { _ in
print("Entered in region!")
self.submitLocation(updateType: LocationUpdateTypes.RegionEnter,
coordinates: zone.locationCoordinates(),
accuracy: 1,
zone: zone)
}, exit: { _ in
print("Exited from the region")
self.submitLocation(updateType: LocationUpdateTypes.RegionExit,
coordinates: zone.locationCoordinates(),
accuracy: 1,
zone: zone)
}, error: { req, error in
CLSLogv("Error in region monitoring: %@", getVaList([error.localizedDescription]))
if zone.UUID != nil {
beaconManager.startScanning(zone: zone)
} else {
do {
try Location.monitor(regionAt: zone.locationCoordinates(), radius: zone.Radius,
enter: { _ in
print("Entered in region!")
self.submitLocation(updateType: LocationUpdateTypes.RegionEnter,
coordinates: zone.locationCoordinates(),
accuracy: 1,
zone: zone)
}, exit: { _ in
print("Exited from the region")
self.submitLocation(updateType: LocationUpdateTypes.RegionExit,
coordinates: zone.locationCoordinates(),
accuracy: 1,
zone: zone)
}, error: { req, error in
CLSLogv("Error in region monitoring: %@", getVaList([error.localizedDescription]))
Crashlytics.sharedInstance().recordError(error)
req.cancel()
})
} catch let error {
CLSLogv("Error when setting up zones for tracking: %@",
getVaList([error.localizedDescription]))
Crashlytics.sharedInstance().recordError(error)
req.cancel()
})
} catch let error {
CLSLogv("Error when setting up zones for tracking: %@", getVaList([error.localizedDescription]))
Crashlytics.sharedInstance().recordError(error)
}
}
}
}

}
// let location = Location()
//
// self.getBeacons().then { beacons -> Void in
// for beacon in beacons {
// print("Got beacon from HA", beacon.UUID, beacon.Major, beacon.Minor)
// try Beacons.monitorForBeacon(proximityUUID: beacon.UUID!,
// major: UInt16(beacon.Major!),
// minor: UInt16(beacon.Minor!),
// onFound: { beaconsFound in
// // beaconsFound is an array of found beacons ([CLBeacon]) but
// // in this case it contains only one beacon
// print("beaconsFound", beaconsFound)
// }) { error in
// // something bad happened
// print("Error happened on beacons", error)
// }
// }
// }.catch {error in
// print("Error when getting beacons!", error)
// Crashlytics.sharedInstance().recordError((error as Any) as! NSError)
// }

}

Expand Down Expand Up @@ -961,24 +956,6 @@ public class HomeAssistantAPI {
}
}

func getBeacons() -> Promise<[Beacon]> {
let queryUrl = baseAPIURL+"ios/beacons"
return Promise { fulfill, reject in
_ = self.manager!.request(queryUrl,
method: .get).validate().responseArray { (response: DataResponse<[Beacon]>) in
switch response.result {
case .success:
fulfill(response.result.value!)
case .failure(let error):
CLSLogv("Error when attemping to getBeacons(): %@",
getVaList([error.localizedDescription]))
Crashlytics.sharedInstance().recordError(error)
reject(error)
}
}
}
}

func getImage(imageUrl: String) -> Promise<UIImage> {
var url = imageUrl.replacingOccurrences(of: "/api/", with: "")
url = url.replacingOccurrences(of: "/local/", with: "")
Expand Down Expand Up @@ -1189,6 +1166,48 @@ class Bonjour {

}

class BeaconManager: NSObject, CLLocationManagerDelegate {

var locationManager: CLLocationManager!

var zones: [String: Zone] = [String: Zone]()

override init() {
super.init()

locationManager = CLLocationManager()
locationManager.delegate = self
}

func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
print("Entered region", region.identifier)
let zone = zones[region.identifier]!
HomeAssistantAPI.sharedInstance.submitLocation(updateType: .BeaconRegionExit,
coordinates: zone.locationCoordinates(),
accuracy: 1,
zone: zone)
}

func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
print("Exited region", region.identifier)
let zone = zones[region.identifier]!
HomeAssistantAPI.sharedInstance.submitLocation(updateType: .BeaconRegionExit,
coordinates: zone.locationCoordinates(),
accuracy: 1,
zone: zone)
}

func startScanning(zone: Zone) {
print("Begin scanning iBeacons for zone", zone.ID)
let beaconRegion = CLBeaconRegion(proximityUUID: UUID(uuidString: zone.UUID!)!,
major: CLBeaconMajorValue(zone.Major!),
minor: CLBeaconMinorValue(zone.Minor!), identifier: zone.ID)
zones[zone.ID] = zone
locationManager.startMonitoring(for: beaconRegion)
}

}

enum LocationUpdateTypes {
case RegionEnter
case RegionExit
Expand Down
29 changes: 27 additions & 2 deletions HomeAssistant/Resources/SwiftGen/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ enum L10n {
}

enum VersionRow {
/// 0.35.0
/// 0.42.0
static let placeholder = L10n.tr("settings.status_section.version_row.placeholder")
/// Version
static let title = L10n.tr("settings.status_section.version_row.title")
Expand Down Expand Up @@ -304,6 +304,16 @@ enum L10n {
/// Notifications
static let header = L10n.tr("settings_details.location.notifications.header")

enum BeaconEnter {
/// Enter Zone via iBeacon Notifications
static let title = L10n.tr("settings_details.location.notifications.beacon_enter.title")
}

enum BeaconExit {
/// Exit Zone via iBeacon Notifications
static let title = L10n.tr("settings_details.location.notifications.beacon_exit.title")
}

enum Enter {
/// Enter Zone Notifications
static let title = L10n.tr("settings_details.location.notifications.enter.title")
Expand All @@ -324,6 +334,21 @@ enum L10n {
/// To disable location tracking add track_ios: false to each zones settings or under customize.
static let footer = L10n.tr("settings_details.location.zones.footer")

enum BeaconMajor {
/// iBeacon Major
static let title = L10n.tr("settings_details.location.zones.beacon_major.title")
}

enum BeaconMinor {
/// iBeacon Minor
static let title = L10n.tr("settings_details.location.zones.beacon_minor.title")
}

enum BeaconUuid {
/// iBeacon UUID
static let title = L10n.tr("settings_details.location.zones.beacon_uuid.title")
}

enum EnterExitTracked {
/// Enter/exit tracked
static let title = L10n.tr("settings_details.location.zones.enter_exit_tracked.title")
Expand Down Expand Up @@ -361,7 +386,7 @@ enum L10n {
static let footer = L10n.tr("settings_details.notifications.sounds_section.footer")

enum Button {
/// Update push settings
/// Import Sounds
static let title = L10n.tr("settings_details.notifications.sounds_section.button.title")
}

Expand Down
5 changes: 5 additions & 0 deletions HomeAssistant/Resources/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,15 @@
"settings_details.location.notifications.header" = "Notifications";
"settings_details.location.notifications.enter.title" = "Enter Zone Notifications";
"settings_details.location.notifications.exit.title" = "Exit Zone Notifications";
"settings_details.location.notifications.beacon_enter.title" = "Enter Zone via iBeacon Notifications";
"settings_details.location.notifications.beacon_exit.title" = "Exit Zone via iBeacon Notifications";
"settings_details.location.notifications.location_change.title" = "Location Change Zone Notifications";
"settings_details.location.zones.enter_exit_tracked.title" = "Enter/exit tracked";
"settings_details.location.zones.location.title" = "Location";
"settings_details.location.zones.radius.title" = "Radius";
"settings_details.location.zones.beacon_uuid.title" = "iBeacon UUID";
"settings_details.location.zones.beacon_major.title" = "iBeacon Major";
"settings_details.location.zones.beacon_minor.title" = "iBeacon Minor";
"settings_details.location.zones.footer" = "To disable location tracking add track_ios: false to each zones settings or under customize.";

"settings_details.notifications.title" = "Notification Settings";
Expand Down
33 changes: 32 additions & 1 deletion HomeAssistant/Views/SettingsDetailViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,22 @@ class SettingsDetailViewController: FormViewController {
prefs.set(val, forKey: "exitNotifications")
}
})
<<< SwitchRow {
$0.title = L10n.SettingsDetails.Location.Notifications.BeaconEnter.title
$0.value = prefs.bool(forKey: "beaconEnterNotifications")
}.onChange({ (row) in
if let val = row.value {
prefs.set(val, forKey: "beaconEnterNotifications")
}
})
<<< SwitchRow {
$0.title = L10n.SettingsDetails.Location.Notifications.BeaconExit.title
$0.value = prefs.bool(forKey: "beaconExitNotifications")
}.onChange({ (row) in
if let val = row.value {
prefs.set(val, forKey: "beaconExitNotifications")
}
})
<<< SwitchRow {
$0.title = L10n.SettingsDetails.Location.Notifications.LocationChange.title
$0.value = prefs.bool(forKey: "significantLocationChangeNotifications")
Expand Down Expand Up @@ -89,7 +105,22 @@ class SettingsDetailViewController: FormViewController {
<<< LabelRow {
$0.title = L10n.SettingsDetails.Location.Zones.Radius.title
$0.value = "\(Int(zone.Radius)) m"
}
}
<<< LabelRow {
$0.title = L10n.SettingsDetails.Location.Zones.BeaconUuid.title
$0.value = zone.UUID
$0.hidden = Condition(booleanLiteral: (zone.UUID == nil))
}
<<< LabelRow {
$0.title = L10n.SettingsDetails.Location.Zones.BeaconMajor.title
$0.value = String(describing: zone.Major)
$0.hidden = Condition(booleanLiteral: (zone.Major == nil))
}
<<< LabelRow {
$0.title = L10n.SettingsDetails.Location.Zones.BeaconMinor.title
$0.value = String(describing: zone.Minor)
$0.hidden = Condition(booleanLiteral: (zone.Minor == nil))
}
}
if zoneEntities.count > 0 {
self.form
Expand Down

0 comments on commit 9e0e446

Please sign in to comment.