A Python IoT framework to easily integrate sensors of any kind into your projects. We also have sample clients for standard web sites and Adobe AEM Screens.
The number one goal of this effort was to make it dead simple to use sensors. So, we've isolated and simplified everything other than data collection. In SimpleSensor's basic use, just collect data and put it in the queue. Everything else is handled by the other threads, which we have developed for you. There may be a bit of configuration that needs to be done to turn on and connect to your communication channels, but that’s it. You could also extend SimpleSensor and add your own modules and communication channels; we'll gladly take pull requests that follow the contribution guidelines.
The application is broken down into the following domains
- Communication channels
- Logging
- Data Collection
For examples on how to use these domains, including things you can do with a collection point, an Adobe AEM client, AEM Screens, or plain old Javascipt, see the Examples section.
For printable camera enclosures, see the enclosures subdirectory, and feel free to contribute your own!
Today we have two communications channels built into the platform. We will add more over time.
- Local websocket
- MQTT
Azure IoT Hub is currently under development
CollectionPointEvent | ||
---|---|---|
Property | Type | Description |
cpid |
String | Unique ID of the collection point, ie. FD1 or 123456 |
cptype |
String | Label of the collection point type, ie. FaceDetector or WaterHeaterMonitor |
topic |
String | Label of the event, ie Detected or PowerOn |
eventTime |
ISO 8601 String | Time stamp of when the message was created |
extendedData |
Dictionary | Key/value pairs of any extra data the sensor needs to emit |
localOnly |
Boolean | Whether or not to broadcast the event on low-cost channels only (placeholder, keep False for now) |
- Greater platform support (stable Linux and macOS installation instructions)
- Wider range of sensors (taking suggestions, open an issue!)
- Greater camera support
- Handle
localOnly
flags - Target detection zone - specify where in camera feed detections should be triggered using grid system
- Communication from sensors to SimpleSensor (code is there, events are not handled yet)
Today we have two support for two types of cameras in our facedetect module:
- IDS XS
- Standard usb webcams
Currently tested platforms:
- Windows 10
- AEM 6.3 w/ AEM Screens
- Python 3.6 w/ Anaconda
- OpenCV 3.2
To shutdown, press ESC while the console has focus.
Get Anaconda, from command prompt or Anaconda Shell run:
conda create -n ss python=3.6 anaconda
activate ss
orsource activate ss
pip install -r requirements.txt
Collection Point Params | ||
---|---|---|
Property | Type | Description |
collection_point_id |
String | Becomes the cpid property of created CollectionPointEvents |
collection_point_type |
String | Becomes the cptype property of created CollectionPointEvents |
Websocket Params | ||
---|---|---|
Property | Type | Description |
websocket_port |
Integer | Port number to use for websocket |
websocket_host |
String | Host name to use for websocket, ie. 127.0.0.1 or 0.0.0.0 to allow all network machines access |
Azure Params | ||
---|---|---|
Property | Type | Description |
azure_message_timeout |
Integer | Time until a message times out |
azure_timeout |
Integer | Time until Azure connection times out |
azure_minimum_polling_time |
Integer | Frequency at which to poll for messages |
azure_device_created |
Boolean | Flag to set whether the client has already been initialized, ie. exists on the Azure Console |
MQTT Params | ||
---|---|---|
Property | Type | Description |
mqtt_host |
String | Host name of MQTT broker ie. io.adafruit.com |
mqtt_port |
Integer | Port number of MQTT broker, ie. 1883 |
mqtt_feed_name |
String | Feed name to publish to |
mqtt_username |
String | Username to MQTT broker |
mqtt_keep_alive |
Integer | Maximum period in seconds allowed between communications with the broker. If no other messages are being exchanged, this controls the rate at which the client will send ping messages to the broker. |
mqtt_publish_json |
Boolean | Flag to publish entire message contents as JSON string |
mqtt_publish_face_values |
Boolean | Flag to publish only the values of the extended data to respective feeds. |
If neither mqtt_publish_face_values or mqtt_publish_json are enabled |
Values of the extended data field are published to their own feeds. See MQTT Examples. |
NOTE: See also Paho MQTT
Modules | ||
---|---|---|
Property | Type | Description |
collection_modules |
Array | Collection point module folder names to use, ie. camCollectionPoint |
communication_modules |
Array | Communication method folder names to use, ie. websocketServer |
NOTE: These are folder names, not file/class/module names. When adding new modules keep the folder names unique.
Etc | ||
---|---|---|
Property | Type | Description |
test_mode |
Boolean | Flag to allow for debugging workflows |
slack_channel_webhook_url |
String | Unused for now - for future Slack integration |
Open /config/secrets.conf.sample
in your favorite text editor, include keys needed by communication methods you wish to use:
Secrets | ||
---|---|---|
Property | Type | Description |
mqtt_key |
String | API key or password for your MQTT broker account |
azure_connection_string |
String | Connection string retrieved from your Azure portal |
NOTE: Save this file as secrets.conf
In config/base.conf
, add/remove the folder names of the modules you wish to use. For example, to use both websocket and MQTT messages, set your communication_modules
to ["websocketServer", "mqttClient"]
- Duplicate and rename the
/collection_modules/baseCollectionPoint
directory - Add/replace
collection_modules
array inconfig/base.conf
with the name of your new collection point's folder - Write your initialization logic in the
__init__()
function - Write your collection loop logic in the
run()
function - Optionally import constants through a module configuration file, use
camCollectionPoint
as an example
NOTE: For details on the included camera/face detection module, see the module's README
These are some samples of things you can do within the run()
function of your collectionPoint.py
_cpType = 'elite_sensor'
_cpid = 'sensor_1337'
_topic = 'Detected'
_coords = (47.6062, 122.3321)
_localOnly = False
msg = (_cpid, _cpType, _topic, extendedData={'location':_coords}, _localOnly)
self.outQueue.put(msg)
self.mTracker = MultiTracker("KCF", self.moduleConfig, self.loggingQueue)
ok = self.mmTracker.add(bbox={'x':x,'y':y,'w':w,'h':h}, frame=frame)
A sample Javascript client connection via websocket can be found here
<div id="displayEventsElement"></div>
<script src="websocketClient.js" type="text/javascript"></script>
document.addEventListener("DOMContentLoaded", function(event) {
var _displayEventsElement = document.getElementById('displayEventsElement');
var cecWebsocketClient = new WebsocketClient("127.0.0.1", 13254); // Connect to localhost at port 13254
});
websocketClient.addEventListener("message", function(eventData){
// Do something with the event data
displayEventsElement.append(JSON.stringify(eventData));
});
Publishing messages to MQTT requires an MQTT broker (Adafruit IO, for example) to be configured and enabled in /config/base.conf
. There are two ways to publish MQTT messages currently.
The first method is to publish the entire JSON string dump of the message, if your broker supports it. This is enabled by setting mqtt_publish_json
to True
in /config/base.conf
. The MQTTClient dumps the JSON into utf8 encoding and publishes it like so:
_payload = json.dumps(_msg.__dict__).encode('utf8')
self._client.publish('{0}/feeds/{1}'.format(self._username, _feedName), payload=_payload)
The second method is to publish the values of each key in the extended data to separate feeds. To use this method, set both mqtt_send_json
and mqtt_publish_face_values
to False
in /config/base.conf
. The MQTTClient
will now publish each field of the extendedData
dictionary message to it's own MQTT feed.
For example, the message created by this code:
_cpType = 'elite_sensor'
_cpid = 'sensor_1337'
_topic = 'Detected'
_localOnly = False
_extendedData = {
"age": 23,
"gender": 0,
"glasses": 1,
"beard": 0.7
}
msg = (_cpid, _cpType, _topic, extendedData=_extendedData, _localOnly)
Would result in 4 publish events:
username/feeds/age
would receive an update value of23
username/feeds/gender
would receive an update value of0
username/feeds/glasses
would receive an update value of1
username/feeds/beard
would receive an update value of0.7
Note A modified version of this function is also in mqttClient.py
, which can be used in conjunction with the Azure Face API (or another prediction engine with the same output structure) and the sample camCollectionPoint
. It only publishes prediction data, and flattens a dictionary of values to an average.
self.logger.info("Starting a thing")
self.logger.debug("Made it here!1112")
self.logger.warning("This could be bad")
self.logger.warn("This also could be bad")
self.logger.critical("This *is* bad")
self.logger.error("Failed to do the thing: %s"%error)
Make a cool collection module that you want to share? Or fix up a bug? Maybe you made SimpleSensor work on a new platform? We're open for contributions, we just ask that they follow some guidelines to keep things clean and efficient.
If you're looking for a place to jump in with contributing, check out our TO DO
All submissions should come in the form of pull requests and need to be reviewed by project contributors. Read GitHub's pull request documentation for more information on sending pull requests.
Issues should either include a proposal for a feature or, in the case of bugs, include the expected behavior, the actual behavior, your environment details, and ideally steps to reproduce. They should also ideally address actual issues with the code, not issues with setting up the environment. Please follow the issue template for consistency.
Pull requests should include references to the title of the issue, and changes proposed in the pull request. Please follow the pull request template for consistency.
Try to make your code readable first and foremost. Then match the code that surrounds it. Otherwise follow these current styles:
- mixedCase function/variable/object names
- mixedCase file and folder names
- _mixedCase constant names
- lowercase_with_underscores config file settings, ie.
some_prop = 1337
- CamelCase config object variable, ie.
self.config['SomeProp']
- docstrings for all functions, ie.
""" Function summary """
This project adheres to the Adobe code of conduct. By participating, you are expected to uphold this code. Please report unacceptable behavior to FILLINEMAILHERE.