Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # coding=utf-8
- """
- iBeacon Scanner
- ===============
- This scanner works exclusively on iOS real devices, simulator don't support
- iBeacon ranging API at all.
- The usage is quite simple:
- 1. Create a scanner with `scanner = IBeaconScanner()`
- 2. Register your iBeacon using the ibeacon uuid like:
- scanner.register_beacon("E2C56DB5-DFFB-48D2-B060-D0F5A71096E0")
- 3. Monitor the event you want
- scanner.bind(on_beacon_update=do_something)
- 4. Start the scanner
- scanner.start_monitoring()
- Output example captured from a test run:
- (('on_beacon_entered', <__main__.IBeaconScanner object at 0x1704bd238>), {'uuid': 'E2C56DB5-DFFB-48D2-B060-D0F5A71096E0'})
- (('on_beacon_update', <__main__.IBeaconScanner object at 0x1704bd238>), {'rssi': -57, 'proximity': 'near', 'major': 4250, 'minor': 9865, 'uuid': 'E2C56DB5-DFFB-48D2-B060-D0F5A71096E0'})
- (('on_beacon_update', <__main__.IBeaconScanner object at 0x1704bd238>), {'rssi': -54, 'proximity': 'near', 'major': 4250, 'minor': 9865, 'uuid': 'E2C56DB5-DFFB-48D2-B060-D0F5A71096E0'})
- (('on_beacon_update', <__main__.IBeaconScanner object at 0x1704bd238>), {'rssi': -52, 'proximity': 'immediate', 'major': 4250, 'minor': 9865, 'uuid': 'E2C56DB5-DFFB-48D2-B060-D0F5A71096E0'})
- (('on_beacon_update', <__main__.IBeaconScanner object at 0x1704bd238>), {'rssi': -52, 'proximity': 'immediate', 'major': 4250, 'minor': 9865, 'uuid': 'E2C56DB5-DFFB-48D2-B060-D0F5A71096E0'})
- (('on_beacon_update', <__main__.IBeaconScanner object at 0x1704bd238>), {'rssi': -53, 'proximity': 'immediate', 'major': 4250, 'minor': 9865, 'uuid': 'E2C56DB5-DFFB-48D2-B060-D0F5A71096E0'})
- (('on_beacon_update', <__main__.IBeaconScanner object at 0x1704bd238>), {'rssi': -65, 'proximity': 'immediate', 'major': 4250, 'minor': 9865, 'uuid': 'E2C56DB5-DFFB-48D2-B060-D0F5A71096E0'})
- (('on_beacon_update', <__main__.IBeaconScanner object at 0x1704bd238>), {'rssi': -74, 'proximity': 'near', 'major': 4250, 'minor': 9865, 'uuid': 'E2C56DB5-DFFB-48D2-B060-D0F5A71096E0'})
- (('on_beacon_update', <__main__.IBeaconScanner object at 0x1704bd238>), {'rssi': -76, 'proximity': 'near', 'major': 4250, 'minor': 9865, 'uuid': 'E2C56DB5-DFFB-48D2-B060-D0F5A71096E0'})
- (('on_beacon_update', <__main__.IBeaconScanner object at 0x1704bd238>), {'rssi': -77, 'proximity': 'near', 'major': 4250, 'minor': 9865, 'uuid': 'E2C56DB5-DFFB-48D2-B060-D0F5A71096E0'})
- (('on_beacon_update', <__main__.IBeaconScanner object at 0x1704bd238>), {'rssi': -77, 'proximity': 'near', 'major': 4250, 'minor': 9865, 'uuid': 'E2C56DB5-DFFB-48D2-B060-D0F5A71096E0'})
- (('on_beacon_leaved', <__main__.IBeaconScanner object at 0x1704bd238>), {'uuid': 'E2C56DB5-DFFB-48D2-B060-D0F5A71096E0'})
- """
- from kivy.event import EventDispatcher
- from pyobjus import autoclass, protocol
- CLLocationManager = autoclass("CLLocationManager")
- CLBeaconRegion = autoclass("CLBeaconRegion")
- NSUUID = autoclass("NSUUID")
- class IBeaconScanner(EventDispatcher):
- """
- iBeacon Scanner class that works exclusively on iOS real device.
- """
- PROXIMITY = ["unknown", "immediate", "near", "far"]
- __events__ = ("on_beacon_entered", "on_beacon_update", "on_beacon_leaved",
- "on_error")
- def __init__(self):
- super(IBeaconScanner, self).__init__()
- self._regions = {}
- self._regions_nsuuid = {}
- self._regions_seen = []
- self._region_activated = False
- def start_monitoring(self):
- """Start the scanner monitoring"""
- print "start monitoring"
- self._clm = CLLocationManager.alloc().init()
- self._clm.delegate = self
- self._clm.requestAlwaysAuthorization()
- def stop_monitoring(self):
- """Stop the scanner monitoring"""
- self._deactivate_ibeacons()
- self._regions_seen = []
- self._clm.delegate = None
- del self._clm
- def register_beacon(self, uuid, name=None):
- """Register a beacon to be tracked, using the ibeacon `uuid`"""
- assert(len(uuid) == 36)
- uuid = uuid.upper()
- nsuuid = NSUUID.alloc().initWithUUIDString_(uuid)
- self._regions[uuid] = CLBeaconRegion.alloc(
- ).initWithProximityUUID_identifier_(nsuuid, name or uuid)
- self._regions_nsuuid[uuid] = nsuuid
- def unregister_beacon(self, uuid):
- """Unregister a beacon to be tracked."""
- if uuid not in self._regions:
- return
- self._regions_nsuuid.pop(uuid)
- region = self._regions.pop(uuid)
- if self._region_activated:
- self._clm.stopRangingBeaconsInRegion_(region)
- if uuid in self._regions_seen:
- self._regions_seen.remove(uuid)
- self.dispatch("on_beacon_leaved", uuid=uuid)
- def on_beacon_entered(self, uuid):
- """Event fired when a beacon just entered in the sight of the device"""
- print 'entered:', uuid
- pass
- def on_beacon_leaved(self, uuid):
- """Event fired when a beacon is not in the sight of the device"""
- pass
- def on_beacon_update(self, uuid, major, minor, proximity, rssi):
- """Event fired when we got information about a beacon"""
- print uuid, major, minor, proximity, rssi
- pass
- def on_error(self, uuid, msg):
- """Event fired when a beacon have an issue / monitoring issues"""
- pass
- # (implementation internal)
- @protocol("CLLocationManagerDelegate")
- def locationManager_didChangeAuthorizationStatus_(self, manager, status):
- if status == 3: # kCLAuthorizationStatusAuthorized
- self._activate_ibeacons()
- elif status == 2: # kCLAuthorizationStatusDenied
- pass
- elif status == 1: # kCLAuthorizationStatusRestricted
- pass
- else: # kCLAuthorizationStatusNotDetermined
- pass
- @protocol("CLLocationManagerDelegate")
- def locationManager_didRangeBeacons_inRegion_(self, manager, beacons,
- region):
- uuid = region.proximityUUID.UUIDString().UTF8String()
- if uuid not in self._regions:
- return
- beacon = None
- count = beacons.count()
- if count:
- beacon = beacons.objectAtIndex_(0)
- if beacon.rssi == 0:
- beacon = None
- if beacon:
- if uuid not in self._regions_seen:
- self._regions_seen.append(uuid)
- self.dispatch("on_beacon_entered", uuid=uuid)
- self.dispatch("on_beacon_update",
- uuid=uuid,
- major=beacon.major.integerValue(),
- minor=beacon.minor.integerValue(),
- proximity=self.PROXIMITY[beacon.proximity],
- rssi=beacon.rssi)
- else:
- if uuid in self._regions_seen:
- self._regions_seen.remove(uuid)
- self.dispatch("on_beacon_leaved", uuid=uuid)
- @protocol("CLLocationManagerDelegate")
- def locationManager_rangingBeaconsDidFailForRegion_withError_(
- self, manager, region, error
- ):
- uuid = region.proximityUUID.UUIDString().UTF8String()
- msg = error.localizedDescription.UTF8String()
- self.dispatch("on_error", uuid=uuid, msg=msg)
- def _activate_ibeacons(self):
- for region in self._regions.values():
- self._clm.startRangingBeaconsInRegion_(region)
- self._region_activated = True
- def _deactivate_ibeacons(self):
- for region in self._regions.values():
- self._clm.stopRangingBeaconsInRegion_(region)
- self._region_activated = False
- if __name__ == "__main__":
- from kivy.app import App
- from kivy.uix.button import Button
- def dprint(*args, **kwargs):
- print(args, kwargs)
- class IbeaconScanner(App):
- def build(self):
- print 'here'
- self._scanner = IBeaconScanner()
- # self._scanner.register_beacon(
- # "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0")
- self._scanner.register_beacon(
- "C4C8A2C0-E38B-11E4-B571-0025BFBA50C3")
- from functools import partial
- self._scanner.bind(
- on_beacon_entered=partial(dprint, "on_beacon_entered"),
- on_beacon_update=partial(dprint, "on_beacon_update"),
- on_beacon_leaved=partial(dprint, "on_beacon_leaved"),
- on_error=partial(dprint, "on_error"))
- return Button(text="Start Scanner", on_release=self.start_scanner)
- def start_scanner(self, *args):
- print 'start scanner'
- self._scanner.start_monitoring()
- def on_pause(self):
- return True
- IbeaconScanner().run()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement