No project description provided
Project description
Crownstone BLE
This only works on linux due to the usage of BlueZ.
Since the Crownstone communicates through BLE, we can use BLE to tell it to do things!
This library works with Python 3.5 and higher. It does not work with Python 3.4...
Requirements
We need libbluetooth-dev for developing with BLE.
sudo apt-get install build-essential libbluetooth-dev libglib2.0-dev python3-setuptools python3.7-dev
Installing Crownstone BLE
If you want to use python virtual environments, take a look at the README_VENV
You can use the setup.py to install CrownstoneBLE. If you are on other platforms than Linux, you can use setup.py to install just Crownstone.
sudo python3 setup.py install
Allow non-sudo use of the ble scanner
sudo setcap 'cap_net_raw,cap_net_admin+eip' /usr/local/lib/python3.7/site-packages/bluepy/bluepy-helper
Running Example
All scripts containing BLE should be run as sudo because scanning using BlueZ requires root rights.
sudo python3 ./examples/examplesBle/example_continuous_scanning.py
Documentation
To use Crownstone BLE, you first import it from crownstone_ble.
from crownstone_ble.BLE import CrownstoneBle
ble = CrownstoneBle(hciIndex=0)
CrownstoneBle is composed of a number of top level methods and modules for specific commands. We will first describe these top level methods.
__init__(hciIndex=0)
When initializing the CrownstoneBle class, you can provide an hciIndex. On linux, 0 means /dev/hci0, 1 /dev/hci1 etc. Usually 0 is the right one. You can check which one you need with:
hciconfig
The constructor is not explicitly called with init, but like this:
ble = CrownstoneBle(hciIndex=0)
isCrownstoneInSetupMode(address: string, scanDuration=3)
This will scan (blocking) for scanDuration. After which it will return True or False, depending if the Crownstone with this address (like "f7:19:a4:ef:ea:f6") is in setup mode or not.
shutDown()
Shut down the BLE module. This is should be done on closing your script
setSettings(adminKey: string, memberKey: string, basicKey: string, serviceDataKey: string, localizationKey: string, meshApplicationKey: string, meshNetworkKey: string, referenceId="PythonLib")
The Crownstone uses encryption by default. There are 3 keys used. You can find more information on that in the protocol. These keys are 16 characters long like "adminKeyForCrown" or 32 characters as a hex string like "9332b7abf19b86ff48156d88c687def6". The referenceId is optional. If you know what you're doing, you can disable the encryption but it should never be required.
loadSettingsFromFile(path: string)
As an alternative to using setSettings, you can load it from a json file. The path is relative to the script being executed. An example of this json file is:
{ "admin": "adminKeyForCrown", "member": "memberKeyForHome", "basic": "basicKeyForOther", "serviceDataKey": "MyServiceDataKey", "localizationKey": "aLocalizationKey", "meshApplicationKey": "MyGoodMeshAppKey", "meshNetworkKey": "MyGoodMeshNetKey", }
connect(address: string)
This will connect to the Crownstone with the provided MAC address. You get get this address by scanning or getting the nearest Crownstone. More on this below.
setupCrownstone(address: string, sphereId: int, crownstoneId: int, meshDeviceKey: string, ibeaconUUID: string, ibeaconMajor: uint16, ibeaconMinor: uint16)
New Crownstones are in setup mode. In this mode they are open to receiving encryption keys. This method facilitates this process. No manual connection is required.
- address is the MAC address.
- sphereId is a uint8 id for this Crownstone's sphere (Required for FW 3.0.0+)
- crownstoneId is a uint8 id for this Crownstone
- meshDeviceKey is a 16 character string (Required for FW 3.0.0+)
- ibeaconUUID is a string like "d8b094e7-569c-4bc6-8637-e11ce4221c18"
- ibeaconMajor is a number between 0 and 65535
- ibeaconMinor is a number between 0 and 65535
disconnect()
This will disconnect from the connected Crownstone.
getCrownstonesByScanning(scanDuration=3)
This will scan for scanDuration in seconds and return an array of the Crownstone it has found. This is an array of dictionaries that look like this:
{ "address": string, # mac address like "f7:19:a4:ef:ea:f6" "setupMode": boolean, # is this Crownstone in setup mode? "validated": boolean, # does this Crownstone share our encryption key set? "rssi": Float # average of the rssi of this Crownstone. If None, there have been no valid measurements. }
This array can be directly put in the 'addressesToExclude' field of the 'getNearest..' methods.
startScanning(scanDuration=3)
This will start scanning for Crownstones in a background thread. The scanDuration denotes how long we will scan for. Once scanning is active, Topic.advertisement events will be triggered with the advertisements of the Crownstones that share our encryption keys or are in setup mode.
stopScanning()
This will stop an active scan.
getNearestCrownstone(rssiAtLeast=-100, scanDuration=3, returnFirstAcceptable=False, addressesToExclude=[])
This will search for the nearest Crownstone. It will return ANY Crownstone, not just the ones sharing our encryption keys.
- rssiAtLeast, you can use this to indicate a maximum distance
- scanDuration, the amount of time we scan (in seconds)
- returnFirstAcceptable, if this is True, we return on the first Crownstone in the rssiAtLeast range. If it is False, we will scan for the timeout duration and return the closest one.
- addressesToExclude, this is an array of either address strings (like "f7:19:a4:ef:ea:f6") or an array of dictionaries that each contain an address field (like what you get from "getCrownstonesByScanning").
getNearestValidatedCrownstone(rssiAtLeast=-100, scanDuration=3, returnFirstAcceptable=False, addressesToExclude=[])
Same as getNearestCrownstone but will only search for Crownstones with the same encryption keys.
getNearestSetupCrownstone(rssiAtLeast=-100, scanDuration=3, returnFirstAcceptable=False, addressesToExclude=[])
Same as getNearestCrownstone but will only search for Crownstones in setup mode.
Control Module
The modules contain groups of methods. You can access them like this:
from crownstone_ble import CrownstoneBle
# initialize the Bluetooth Core
ble = CrownstoneBle()
# set the switch stat eusing the control module
ble.connect(address) # address is a mac address
ble.control.setSwitchState(0)
ble.disconnect()
Methods:
setSwitchState(switchState: float)
You can switch the Crownstone. 0 for off, 1 for on, between 0 and 1 to dim. If you want to dim, make sure dimming is enabled. You can enable this using the allowDimming method.
switchRelay(switchState)
DEVELOPMENT ONLY: you can switch the relay. 0 for off, 1 for on. Use the setSwitchState instead.
switchPWM(switchState)
DEVELOPMENT ONLY: you can switch the IGBTs. 0 for off, 1 for on, in between for dimming. Use the setSwitchState instead.
commandFactoryReset()
Assuming you have the encryption keys, you can use this method to put the Crownstone back into setup mode.
allowDimming(allow: bool)
Enable or disable dimming on this Crownstone. Required if you want to dim with setSwitchState.
disconnect()
Tell the Crownstone to disconnect from you. This can help if your Bluetooth stack does not reliably disconnect.
lockSwitch(lock: bool)
Lock the switch. If locked, it's switchState cannot be changed.
reset()
Restart the Crownstone.
State Module
This is used to get state variables from the Crownstone. [https://github.com/crownstone/bluenet/blob/master/docs/PROTOCOL.md#state-packet-1]
The modules contain groups of methods. You can access them like this:
from crownstone_ble import CrownstoneBle
# initialize the Bluetooth Core
ble = CrownstoneBle()
# set the switch state using the control module
ble.connect(address)
switchstate = ble.state.getSwitchState()
ble.disconnect()
getSwitchState()
Get the switch state in the raw form [https://github.com/crownstone/bluenet/blob/master/docs/PROTOCOL.md#switch_state_packet]
getSwitchStateFloat()
Get the switchState in the range [0 .. 1]
getTime()
Get the time on the Crownstone as a timestamp since epoch in seconds. This has been corrected for location.
EventBus
You can obtain the eventBus directly from the lib:
from CrownstoneLib import CrownstoneEventBus, Topics
subscribe(TopicName: enum, functionPointer)
Returns a subscription ID that can be used to unsubscribe again with the unsubscribe method
unsubscribe(subscriptionId: number)
This will stop the invocation of the function you provided in the subscribe method, unsubscribing you from the event.
These can be used like this:
# simple example function to print the data you receive
def showNewData(data):
print("received new data: ", data)
# Set up event listeners
CrownstoneEventBus.subscribe(Topics.newDataAvailable, showNewData)
# unsubscribe again
CrownstoneEventBus.unsubscribe(subscriptionId)
Events
These events are available for the BLE part of this lib:
Topics.newDataAvailable
This is a topic with a summary of the data of an advertisement. If you care about the power usage, you can get it from here. For the full advertisement, you can use the Topics.advertisement as shown below. Data format is a dictionary with the fields shown below:
{ id: int # crownstone id (0-255) setupMode: bool # is in setup mode switchState: int temperature: int # chip temp in Celcius powerFactor: int # factor between real and apparent powerUsageReal: int # power usage in watts (W) powerUsageApparent: int # power usage in VA accumulatedEnergy: int timestamp: int # time on Crownstone seconds since epoch with locale correction dimmingAvailable: bool # dimming is available for use (it is not in the first 60 seconds after boot) dimmingAllowed: bool # this Crownstone can dim switchLocked: bool # this Crownstone is switch-locked hasError: bool # this crownstone has an error, if the crownstone has an error, the errors: {} dict is only valid if errorMode: true. This boolean is always valid. errorMode: bool # summary type errorMode : the errors JSON is valid. This alternates with normal advertisements errors: { overCurrent: bool overCurrentDimmer: bool temperatureChip: bool temperatureDimmer: bool dimmerOnFailure: bool dimmerOffFailure: bool bitMask: int } timeIsSet: bool # this crownstone knows what time it is }
Topics.advertisement
Data format is a dictionary with the fields shown below:
{ name: string rssi: int address: string # mac address serviceUUID: string serviceData: { opCode: int dataType: int stateOfExternalCrownstone: int # adv contains state of external crownstone hasError: bool # this crownstone has an error setupMode: bool # is in setup mode id: int # crownstone id (0-255) switchState: int flagsBitmak: int temperature: int # chip temp powerFactor: int # factor between real and appearent powerUsageReal: int # usage in watts (W) powerUsageApparent: int # usage in VA accumulatedEnergy: int timestamp: int # time on Crownstone seconds since epoch with locale correction dimmingAvailable: bool # dimming is available for use (it is not in the first 60 seconds after boot) dimmingAllowed: bool # this Crownstone can dim switchLocked: bool # this Crownstone is switch-locked errorMode: bool # advertisement type errorMode : the errors JSON is valid. This alternates with normal advertisements errors: { overCurrent: bool overCurrentDimmer: bool temperatureChip: bool temperatureDimmer: bool dimmerOnFailure: bool dimmerOffFailure: bool bitMask: int } uniqueElement: int # something that identifies this advertisement uniquely. Can be used to skip duplicate payloads timeIsSet: bool # this crownstone knows what time it is }
Troubleshooting
installation
old request module (ImportError: cannot import name 'DependencyWarning') --> check [https://github.com/nickoala/telepot/issues/80]
cant find bluetooth.h
sudo apt install libbluetooth-dev
/bluez-5.47/attrib/gatt.c:190: undefined reference to bswap_128'\n/tmp/ccT6fQZS.o: In function
get_uuid128':\n/home/pi/gits/bluepy/bluepy/./bluez-5.47/attrib/gatt.c:204: undefined reference to `bswap_128'\ncollect2: error: ld returned 1 exit status\nMakefile:28: recipe for target 'bluepy-helper' failed\nmake: *** [bluepy-helper] Error 1\nmake: Leaving directory '/home/pi/gits/bluepy/bluepy'\n"
You will need to manually install bluez
sudo apt purge bluez
wget http://www.kernel.org/pub/linux/bluetooth/bluez-5.47.tar.xz
tar -xf bluez-5.47.tar.xz
cd bluez-5.47
sudo apt-get install libical-dev
./configure --prefix=/usr --mandir=/usr/share/man --sysconfdir=/etc --localstatedir=/var --enable-library --disable-udev
make
sudo make install
ZipImportError: bad local file header --> update python-setuptools
helper not found --> go into the bluepy folder and sudo make -B
Bluetooth on Linux
If bluetooth seems stuck, try sudo rfkill block bluetooth sudo rfkill unblock bluetooth
running
Something about threading when setup is complete --> you don't have Python 3.5
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file crownstone-ble-0.5.3.tar.gz
.
File metadata
- Download URL: crownstone-ble-0.5.3.tar.gz
- Upload date:
- Size: 31.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.1.1 pkginfo/1.5.0.1 requests/2.23.0 setuptools/46.4.0 requests-toolbelt/0.9.1 tqdm/4.46.0 CPython/3.7.6
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | f4974e2769c54749e6b406875f7b1c44403677756b45f2403a4a934ba15624ee |
|
MD5 | 01383227be2cfc15fa6160aecac2e0a6 |
|
BLAKE2b-256 | 9478dc0105265624bb9706fad0bc313eeb5d60493c91f245dd75ceeb778d0158 |
File details
Details for the file crownstone_ble-0.5.3-py3-none-any.whl
.
File metadata
- Download URL: crownstone_ble-0.5.3-py3-none-any.whl
- Upload date:
- Size: 37.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.1.1 pkginfo/1.5.0.1 requests/2.23.0 setuptools/46.4.0 requests-toolbelt/0.9.1 tqdm/4.46.0 CPython/3.7.6
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | b02d47b123a43a41e9ee15ce0051759de3bc94ebc2f9d836a2b26c4492c2dc45 |
|
MD5 | 0173278686dca86737721a769dbfd7e6 |
|
BLAKE2b-256 | 27e358c6291f7ae45eb20b1d554184fa4e6a62bd48330897c3535f23c1321e68 |