I use Slack a lot to collaborate with colleagues and I'd like to be able to plan meetings and book physical spaces without changing to another app.
Physical spaces are a scarce resource. So, I want to know which rooms are free right now, really free not just free in the calendar. I also want to know if I need to take a sweater or a bottle of ice water with me. Lastly, I'd be nice to know if left a power hungry piece of kit switched in the lab or the lights on in the photographic studio.
My architecture design philosophy is: keep it simple and local. There are too many hijacked IoT devices being used in botnets and far too much data unnecessarily leaching into the 'cloud'.
I'd like each room to be autonomous. No centralized infrastructure. Just one Slack bot per room. Packaged in easy to install industrial format. With no external IP addresses and no external firewall ports to open.
ComponentsThe room I have in mind for the prototype has motion activated lighting and a manually operated, powered window blind.
I'd like to make the blind open and close automatically depending on how much light there is in the room. The blind is a simple mechanism, which can be hacked to close when powered is applied and open again when the power is removed. It runs on 24VDC, so I'll need a relay plus a light sensor.
There is already a PIR motion sensor that I can use so the other sensors I need to add are a temperature sensor and a electric current sensor.
Obviously I need to create a Slack bot and for convenience I want a touch screen display outside the door that can be used to start and end meetings.
This is an ideal project for the LinkIt Smart 7688 DUO. It's MPU is powerful enough to run the Slack bot and it's ATmega32u4 MCU is ideal for interfacing the sensors and relay to the MPU.
Since Seeed make a Grove breakout board for the DUO, I'm going to use that with Grove sensors and relay for the prototype. The Grove Electricity Sensor is limited to 10A at 250VAC, not enough to monitor a real wall socket (max 16A at 250VAC) but enough for the prototype.
Step 1: Prepare the DUOThe prerequisites for starting this project are that the DUO is:
- Connected to a Wi-Fi access point with internet access (needed for connecting to slack.com)
- The firmware is upgraded to v0.9.4 or later. The project was developed against v0.9.4.
- You are able to access the system console via SSH
The following are optional but I strongly recommend them:
- You are able to access the kernel console using a Serial (or UART) connection.
- Mount the OpenWRT root FS is on a SD card. Akash Ravichandran has a great project detailing how to do this. This project should squeeze into the 32MB on-board flash storage but I've not tried it. Having the root FS on a SD card reduce wear on the on-board flash and means you can backup and/or clone the FS.
This project relies heavily on scheduling events (room bookings) and sending the appropriate UTC timestamps to Slack.
I chose to use python for this project. Personal preference - I've been using Node a lot lately and I wanted a change. Unfortunately the standard python libs for handling dates and times are messy, especially in Python v2, which is what ships with the DUO.
This is made more difficult by the absence of timezone info in the normal locations /etc/localtime and /usr/share/zoneinfo. /etc/localtime is typically a symlink to a zone file under /usr/share/zoneinfo. The python pytz module relies on the file being present.
There is an OpenWRT zoneinfo package but it isn't included in the buildchain for the DUO.
Workaround
The simplest workaround is to sent the correct date, time and timezone info using LuCi. Since my room is in Amsterdam in the Netherlands, my timezone is Europe/Amsterdam.
Then copy a UTC zoneinfo file to /etc/localtime. The file from any UNIX-like OS should work. I copied mine from my Mac. I haven't tried in with Windows.
Step 3: The Sensors, Relay and MPU/MCU interfaceThe sensors are:
- Grove Electricity Sensor, an analog AC current sensor connected to pin A2.
- Grove Temperature Sensor, a simple thermistor based analog sensor connected to pin A1. The sensor isn't particularly accurate but ±2˚C is good enough to measure the difference between comfortable and uncomfortable.
- Grove Light Sensor, a simple LDR-based analog sensor connected to pin A0. Again, like the temperature sensor, it's not particularly accurate but with some experimentation it's good enough for signalling when the room is too light or too dark. The room lights are motion triggered and assumption is that they are always on if there is someone in the room.
- Grove Relay, a single pole, single throw (SPST) relay connected to pin D9.
- The PIR motion sensor is a digital sensor connected to D8. It has an active low output, meaning that when movement is detected the signal pin goes from high to low.
I chose to use the Firmata protocol (v2.4.1) to communicate between the MPU and the MCU. I'm familiar with it and it works. This project only uses simple sensors but Firmata also works well with more complex I2C bus based sensors.
On the MCU side I'm running FirmataPlus and on the MPU side I use PyMata. My experience is that PyMata is more reliable than pyMata.
Instructions for installing and configuring FirmataPlus can be found in section 6.6.5 (page 80) of the MediaTek LinkIt Smart 7688 Developers Guide v1.1.
# set MCU pin modes
mcu.set_pin_mode(MOTION_SENSOR, mcu.INPUT, mcu.DIGITAL)
mcu.set_digital_latch(MOTION_SENSOR, mcu.DIGITAL_LATCH_LOW,
mcu_utils.motion_sensor_callback)
mcu.set_pin_mode(ELECTRICITY_RELAY, mcu.OUTPUT, mcu.DIGITAL)
mcu.set_pin_mode(ELECTRICITY_SENSOR, mcu.INPUT, mcu.ANALOG)
mcu.set_pin_mode(TEMP_SENSOR, mcu.INPUT, mcu.ANALOG)
mcu.set_pin_mode(LIGHT_SENSOR, mcu.INPUT, mcu.ANALOG)
Since the sensors are polled, the motion sensor is defined as a digital latch. The sensor has a trigger latency of approx. 7s (to prevent false triggers when someone walks past the room) , without the latch the activation might otherwise be missed.
Step 4: Creating A Slack BotThere are two kinds of Slack bot users: custom bots and app bots. The Slack bot users page explains the difference.
You need to create a custom bot user. To do this you will need a Slack account with permissions that allow you to add a bot to your team.
You will be asked to provide a username for the bot, which must be 21 characters or less and contain only lowercase letters, numbers, periods, hyphens, and underscores.
And that's it! You can give it an icon and first and last name and a short description of what the bot does but these are optional.
The essential piece of information is the API token. To keep it safe, the app expects it to be in an environment variable called SLACK_BOT_TOKEN.
If you are planning to run the app as root then you can add the token to /etc/profile, there is no .bashrc in OpenWRT.
export SLACK_BOT_TOKEN='xoxb-16***bl'
Now you're ready to install the app.
Step 5: Installing and Configuring the AppThe majority of the application is written in python, using the Flask web development microframework; a sqlite3 database with Flask-SQLAlchemy as the object-relational mapping (ORM) tool; the PyMata firmata client; and Slack's python slackclient.
Logically, the application consist of: a webapp that serves a single page, the room display; and four scheduled jobs.
There are four scheduled jobs, managed using the APScheduler Flask extension:
- the read_slack_rtm() job runs every 2 seconds to check for Slack RTM (realtime messaging) events directed at the bot;
- the mcu_handler() job runs every 30 seconds to check the sensor readings and triggers the blinds (relay) to open or close depending on the light sensor reading;
- the send_reminders() job runs at 0, 15, 30 and 45 past the hour to send reminders for up coming meetings. It also checks the current room temperature and sends an additional warning if attendees are likely to find the room uncomfortably warm or cool; and
- the cleanup_bookings() job which runs every day at 03:00 UTC to remove old meetings, reminders and other expired entries from the database.
The scheduler maintains a pool of 2 workers and under normal conditions, e.g. monitoring two moderately chatty Slack channels, the 5 minute average load on the DUO is less than 0.2. This peaks at >0.8 during startup and around 0.5 when responding to two simultaneous meeting requests (one via slack and on via the room display).
Installation
To install Flask, PyMata and the python Slack client run these commands, one after another:
root@mylinkit:/# pip install flask
root@mylinkit:/# pip install flask-sqlalchemy
root@mylinkit:/# pip install flask-wtf
root@mylinkit:/# pip install flask-apscheduler
root@mylinkit:/# pip install slackclient
root@mylinkit:/# pip install PyMata
Typically, I would install this kind of webapp under /var/www but on OpenWRT /var is a temporary FS and any files will be lost following a reboot. Instead, I have the app installed under /srv.
After downloading and copying the code to the DUO, you need to run two scripts before you can start the app.
print_bot_id.py
The first script, print_bot_id.py, tests that the Slack client is working by printing a list of the users in your Slack team.
Prerequisite
- The SLACK_BOT_TOKEN environment variable must be set
The output looks something like this:
root@mylinkit:/srv/slack-bot#python print_bot_id.py
'griff' (U043WV7ER) is Jason Griffin
...
'room1a' (U2J8UADHA) is Room 1A Bot
'room1d' (U2J9BGW82) is Room 1D Bot
...
'slackbot' (USLACKBOT) is slackbot
Slack uses user IDs in the format U*** to represent users in messages. Before your bot can respond to commands it needs to know what it's ID is.
Find your bot's user name in the list, copy the user ID and store it in an environment variable called BOT_ID.
In the case of my bot 'room1d', the entry in /etc/profile is:
export BOT_ID='U2J9BGW82'
db_create.py
The second script, db_create.py, creates a sqlite database file called app.db. This database is used to store bookings.
Prerequisite
- FirmataPlus is installed on the MCU.
The output looks something like this:
root@mylinkit:/srv/slack-bot#python db_create.py
...
PyMata version 2.14 Copyright(C) 2013-17 Alan Yorinks All rights reserved.
...
The script takes it's config from the Flask app which includes instantiating a PyMata client. This takes around 10 seconds.
The script doesn't output any messages to indicate that the database has been created, instead it just creates a shiny new app.db file.
run-flask.py
Before running the app you also need to edit run-flask.py to set the IP address that the app will bind to.
app.run(host='192.168.0.17', debug=False)
Change the host='' to the device's IP address. Ideally, the device should be assigned a fixed IP address.
Note: setting debug=True will cause Flask to instantiate two instances of the scheduler. The only noticeable effect is that users will receive two reminders for each of their meetings.
Step 6: Testing the Approot@mylinkit:/srv/slack-bot#python run-flask.py
...
PyMata version 2.14 Copyright(C) 2013-17 Alan Yorinks All rights reserved.
...
... Added job "job1" to job store "default"
... Added job "job2" to job store "default"
... Added job "job3" to job store "default"
... Added job "job4" to job store "default"
... Scheduler started
...
... * Running on http://192.168.0.17:5000/ (Press CTRL+C to quit)
...
... Received slack RTM event(s): [{'type': 'hello'}]
The * Running on ... (Press CTRL+C to quit) message indicates that the flask app has started successfully.
The Received slack RTM event(s): [{'type': 'hello'}] message is a friendly handshake from Slack acknowledging that the bot is connected.
The start up takes around 20 seconds to complete.
Sending the Bot a Command
The bot responds to five commands: free, book, show, name and status.
The status command is a good place to start testing but you need to wait at least 30 seconds, so that the mcu_handler() job has chance to run, otherwise the response the bot sends will be wrong.
- Occupied/Unoccupied: the state of the motion sensor
- The blinds are open/closed: open when the relay is open; closed when the relay is closed
- Temperature: the room temperature
- The wall socket is in use/not in use: the latest and 5 minute average for the electricity sensor. In this case, my coffee machine - 890W at 240VAC
The free command has a number of options the simplest is: free now. The free now command is a synonym for free today and the bot will respond with a list of up to 4 options for the next available booking.
The day of the booking can be controlled by using terms like tomorrow or a day name, for example Monday or fri. The time range for the booking suggestions can be controlled using terms like free this afternoon or free wed morning.
A booking suggestion can be converted to an actual booking using the book command and an option number, for example book 3.
A booking can be given a title using the name command., either separately or combined with the book command.
The list of bookings for a room can be viewed using the show command.
The bot responds to show with a list of just your bookings The show all command lists the bookings for all users, your bookings are indicated by a number next to the booking. This number can be used with the name command.
Fifteen minutes before the meeting starts the bot will send you a reminder. The bot will warn you if the room is unusually warm or cold.
Room Display
The room display shows the room's name, and details of the current and next bookings, if there are any. If the current meeting was booked via Slack the user's profile image is displayed.
The background colour of the page changes to reflect the room's state: green when the room is available; amber when a meeting a due to start; and red when there is a meeting in progress.
The page can be accessed at http://mylinkit.local:5000 and auto refreshes once a minute. Port 5000 is Flask's default port and can be changed in run-flask.py, for example to port='8080'.
app.run(host='192.168.0.17', port='8080', debug=False)
The HTML and CSS for the page are in app/templates/index.html. The page uses the Bootstrap 3 framework and is optimised for a tablet with a screen resolution of 1024x768.
The optional background image is a JPEG image file: app/static/background.jpg.
When the room is available, the 'Meet Now' button can be used to make an immediate booking. The booking is set to start at the next 15 minute 'slot', e.g. 0, 15, 30 or 45 minutes past the hour.
This booking is called 'Impromptu Meeting' and can be seen via Slack using the show command.
Since it wasn't made via Slack, your 'walk in' meetings are not listed when you use the show command.
InstallationThe prototype was fitted into an industrial electrical enclosure for easy installation. The DUO power supply, electrical sensor, relay and motion sensor connections are provided using electrical terminal blocks.
The only complication was the need to attach the light sensor to the lid of the enclosure. This was necessary to avoid the sensor reading being influenced by the LEDs on the DUO and the relay. Unfortunately, this meant compromising the enclosure's dust and moisture proof seal by drilling 2 screw holes in the lid.
Note: Unfortunately, the DUO developed a technical fault a few hours after it was installed in the meeting room. So it hasn't been possible to get a video of the prototype in use in the meeting room.
Next StepsThe goal of this project has been to demonstrate that boards like the DUO can be used to implement powerful apps independent of ‘the cloud’.
Additional Booking Functionality
The app is missing a number of obvious features. Features such as booking by date, modifying bookings and removing bookings which weren’t necessary to achieve the project goal but are ‘must haves’ for a production-ready product.
Additional Sensors and Actuators
The choice of sensors and actuators largely depends on how well the bot can be integrated with a room’s existing infrastructure such as heating and lighting. Ideally you want to make use of these existing systems rather than duplicating functions such as temperature and humidity measurement.
One area I’m keen to explore is replacing the single element PIR motion sensor with a multi-element sensor, such as Panasonic's high-gain, high-performance Grid-Eye 8 x 8 pixel IR imaging array. This I2C sensor has a 0.25 degrees Celsius resolution which offers the possibility of occupant counting and simple behaviour tracking without the privacy concerns that are associated with using optical cameras.
Multiple Rooms
It would be interesting to support multiple rooms in a way that allows the bots to collaborate. Such collaborations might include proactively exchanging bookings in the case of problems, for example a broken conference phone.
One possibility would be to use a Slack channel, which would make it easier for rooms to alert maintenance staff to an issue. However, it also comes close to breaking my design philosophy of not unnecessarily sharing data outside of the local system.
Comments
Please log in or sign up to comment.