Automate your house, your life and everything else around with platypush

Automation Guru
Jul 28 · 15 min read

In the last years, we have experienced a terrific spike of products and solutions targeting home automation and automation in general. After a couple of years of hype around IoT, assistants and home automation the market is slowly cooling down, and a few solutions are emerging out of the primordial oh-look-at-this-shiny-new-toy chaos.

There are however a couple of issues I’ve still got with most of the available solutions that led me to invest more time in building my project.

First, one class of solutions for home automation is provided by large for-profit companies. Such solutions, like the Google Home platform and Alexa, are intuitive to set up and use, but they’re quite rigid in the possibility of customization — meaning that you’ve only got the integrations that Google or Amazon provide you with, based on the partnerships they decide to build, you’ve only got to use the user interfaces they provide you with on the devices they decide to support, you can’t install the platform wherever you like or interact with it in any way outside of what’s provided by the company, and such decisions also subject to change over time. Additionally, a truly connected house will generate a lot of data about you (much more than what you generate by browsing the web), and I feel a bit uncomfortable sharing that data with people who don’t seem to bother to share it with anyone else without my consent. Plus, the integrations provided by such platforms often break (see this, this and this, and they’re only a few examples). It can be quite annoying if all of a sudden you can no longer control your expensive lights or cameras from your expensive assistant or smart screen, and the code for controlling them runs on somebody else’s cloud, and that somebody else just informs you that “they’re working on that”.

Another issue with home automation solutions so far is fragmentation. You’ll easily have to buy 3–4 different bridges, devices or adapters if you want to control your smart lights, smart buttons, switches or a living room media centre. Compatibility with existing remotes is also most of the times neglected. Not to mention the number of apps you’ll have to download — each provided by a different author, each eating up space on your phone, and don’t expect that many options to make them communicate with each other. Such fragmentation has been indeed one of the core issues I’ve tried to tackle when building platypush, as I aimed to have only one central entry point, one control panel, one dashboard, one protocol to control everything, while still providing all the needed flexibility in terms of supported communication backends and APIs. The core idea is that, if there is a Python library or API to do what you want to do (or at least something that can be wrapped in a simple Python logic), then there should also be a plugin to effortlessly integrate what you want to do into the ecosystem you already have. It’s similar to the solution that Google has later tried to provide with the device custom actions, even though the latter is limited to assistant interactions so far, and it’s currently subject to change because of the deprecation of the assistant library.

Another class of solutions for such problems come from open source products like the Home Assistant. While my solution and the Home Assistant share quite a few things — they both started being developed around the same time, both started out as a bunch of scripts that we developers used to control our own things, and we eventually ended up glueing together in a larger platform for general use, both are open source and developed in Python, and both aim to bring the logic for controlling your house inside of your house itself instead of somebody else’s cloud — there are a couple of reasons why I eventually decided to keep working on my own platform instead of converging my efforts into Home Assistant.

First, Home Assistant has a strong Raspberry Pi-first approach. The suggested way to get started is to flash the image to an SD card and boot up your RPi. While my solution is heavily tested within the Raspberry Pi ecosystem as well, it aims to run on any device that comes with a Python interpreter. You can easily install it and run it on any x86/x86_64 architecture too if you want to handle your automation logic on an old laptop, a desktop or any Intel-based micro-computer. You can easily run it on other single-board computers, such as the Asus Tinkerboard, any BananaPi or Odroid device. You can even run it on Android if you have an implementation of the Python interpreter installed. You can even run a stripped-down version on a microcontroller that runs micropython. You can run it in a Python virtual environment, in a Docker container, in a virtual machine or KVM — if you can name it, you can probably do it already. I have even managed to run it on a 10-year-old Nokia N900, both on the original Maemo and Arch Linux. And, most of all, it has a very small memory and CPU footprint. Running hotword detection, assistant, web panel, camera, lights, music control and a few sensors on a small Raspberry Zero is granted to take nothing more than 5% of CPU load and just a few MBs of RAM.

Second, since products like the Home Assistant focus on one platform specifically, they can afford to provide a smooth installation process and standardized tools (flash the image to the SD card, boot your RPi, open the web panel, configure everything through an intuitive wizard, done). My project has instead a slightly steeper learning curve, but it rewards the user with much more room for customization. You are expected to install it via pip or GitHub for now, install the dependencies based on the plugins you want, and manually create or edit a configuration file. But it provides much, much more flexibility. It can listen for messages on MQTT, HTTP (but you don’t have to run the webserver if you don’t want to), websocket, TCP socket, Redis, Kafka, Pushbullet — you name it, it has probably got it already. It allows you to create powerful procedures and event hooks written in an intuitive YAML-based language where you can also embed short snippets of Python logic. It has home automation in mind but it doesn’t stop there: you can use it to send messages or read notifications from Android devices, control robots, turn your old box into a fully capable media center (with support for torrents, YouTube, Kodi, Plex, Chromecast, vlc, subtitles and many other players and formats) or a music center (with support for local collections, Spotify, SoundCloud, radios etc.), stream audio from your house, play raw or sampled sounds from a MIDI interface, monitor access to the file system on a server, run custom actions when some NFC tag is detected by your reader, read and extract content from RSS feeds and send it to your Kindle, read and write data to a remote database, send metrics to Grafana — basically, do anything that comes with a plugin.

Another issue I’ve tried to tackle is the developer and power user experience. I wanted to make it easy to use the automation platform as a library or a general-purpose API, so you can easily invoke a custom logic to turn on the lights or control the music in any of your custom scripts through something as simple as a get_plugin('light.hue').on() call, or by sending a simple JSON request over whichever API, queue or socket communication you have set up. I also wanted to make it easy to create complex custom actions (something like “when I get home, turn on the lights if it’s already dark, turn on the fan if it’s hot, the thermostat if it’s cold, the dehumidifier if it’s too humid, and play the music you were listening on your phone”) through native pre-configured action — similar to what is offered by Node-Red but with more flexibility and ability to access the local context, and less agnostic when it comes to plugins, similar to what is offered by IFTTT and Microsoft Flow but running in your own network instead of somebody else’s cloud, similar to the flexibility offered by apps like Tasker and AutoApps, but not limited to your Android device. My goal was also to build a platform that aims to be completely agnostic about how the messages are exchanged and which specific logic is contained in the plugins. As long as you’ve got plugins and backends that implement a certain small set of elements, then you can plug them in.

Finally, extensibility was also a key factor I had in mind. I know how fundamental the contribution of other developers is if you want your platform to support as many things as possible out there. One of my goals has been to provide developers with the possibility of building a simple plugin in around 15 lines of Python code, and a web interface in around 40 lines of HTML+Javascript code.

Let’s briefly analyze how platypush is designed to better grasp how it can provide more features and flexibility than most of the platforms I’ve seen so far.

There are a couple of connected elements at the foundations of platypush that allow users to build whichever solution they like:

  • Plugins: they are arguably the most important component of the platform. A plugin is a Python class that handles a type of device or service (like lights, music, calendar etc.), and it exposes a set of methods that enable you to programmatically invoke actions over those devices and services (like turn on, play, get upcoming events etc.).
  • Backends: they are threads that run in the background and listen for something to happen (an HTTP request, a websocket or message queue message, a voice assistant interaction, a new played song or movie…). When it happens, they will generate events, and other parts of the platform can asynchronously react to those events.
  • Messages: a message in platypush is nothing but a simple JSON string that comes with a type and a set of arguments. You have three main types of messages on the platform:
  • Requests: they are messages used to require a certain plugin action to be executed. The format of the action name is quite simple (plugin_name.method_name), and you can, of course, pass extra arguments to the action. Actions are mapped one-to-one to methods in the associated plugin class through the @action annotation. It means that a request object is transparent to the organization of the plugin, and such a paradigm enables the user to build flexible JSON-RPC-like APIs. For example, the on action of the light.hue plugin accepts lights and groups as optional parameters. It means that you can easily build a request like this and deliver it to platypush through whichever backend you prefer (note that args are optional in this case):

If you have the HTTP backend running, for example, you can easily dispatch such a request to it through the available JSON-RPC execute endpoint:

And you can also easily send requests programmatically through your own Python scripts, basically using platypush as a library in other scripts or projects:

  • Responses: they are messages that contain the output and errors resulting from the execution of a request. If you send this request for example:

You’ll get back a response like this:

If you send a request over a synchronous backend (e.g. the HTTP or TCP backend) then you can expect the response to be delivered back on the same channel. If you send it over an asynchronous backend (e.g. a message queue or a websocket) then the response will be sent asynchronously, creating a dedicated queue or channel if required.

  • Events: they are messages that can be triggered by backends (and also some plugins) when a certain condition is verified. They can be delivered to connected web clients via websocket, or you can build your own custom logic on them through pre-configured event hooks. Event hooks are similar to applets on IFTTT or profiles in Tasker — they execute a certain action (or set of actions) when a certain event occurs. For example, if you enable the Google Assistant backend and some speech is detected then a SpeechRecognizedEvent will be fired. You can create an event hook like this in your configuration file to execute custom actions when a certain phrase is detected (note that regular expressions and extractions of parts from the phrase, at least to some extent, are also supported):

Some of you may have noticed that the syntax of the expressions wrapped by ${} resembles a lot Python. That’s not a coincidence: you can write custom Python expressions in your pre-configured event hooks and (as we’ll see soon) procedures. You can also access context data through the special context variable. As we saw in the example above, that allows you to easily access the output and errors of the latest executed command (but also the event that triggered the hook, through context.get('event')). If the previous command returned a key-value map, or if we extracted named-value pairs from one of the event arguments, then you can also omit the context and access those directly by name — in the example above you can access the artist either through context.get('artist') or simply artist, for example. We’ll see soon that you can even build more complex logic in your hooks and procedures, using if-else statements and for loops (either synchronous or in parallel).

  • Procedures: the last fundamental element of platypush are procedures. They are groups of actions that can embed more complex logic, like conditions and loops. They can also embed small snippets of Python logic to access the context variables or evaluate expressions, as we have seen in the event hook example. For example, this procedure can execute some custom code when you get home that queries a luminosity and a temperature sensor connected over USB interface (e.g. Arduino), and turns on your Hue lights if it’s below a certain threshold, says a welcome message over the text-to-speech plugin, switches on a fan connected over a TPLink smart plug if the temperature is above a certain threshold, and plays your favourite Spotify playlist through mopidy:

Now you can easily execute the procedure through a normal request:

{"type": "request", "action": "procedure.at_home"}

You can also create a Tasker profile with a WiFi-connected or an AutoLocation trigger that fires when you enter your home area, and that profile can send the JSON request to your platypush device over e.g. HTTP /execute endpoint or MQTT — and you’ve got your custom welcome when you arrive home!

Now that you’ve got a basic idea of what’s possible with platypush and which are its main components, it’s time to get the hands dirty, getting it installed and configure your own plugins and rules.

You can install platypush through pip:

pip install platypush# Install base+advised dependencies
curl | pip install -r /dev/stdin

or through GitHub:

git clone
cd platypush
python build
[sudo] python install
[sudo] pip install -r requirements.txt

You’ll also need to install/enable Redis, as it’s used by platypush as the default internal message bus:

# Installation on Debian and derived distros
apt install redis-server
# Installation on Arch and derived distros
pacman -S redis
# Enable and start the service
systemctl enable redis.service
systemctl start redis.service

Now it’s time to create your own configuration. It is advised to run platypush as an unprivileged user. By default, the configuration file at the user level will be stored under ~/.config/platypush/config.yaml. In this first example, we’ll get kick-started with a basic configuration that includes the webserver and the plugin to control Philips Hue lights.

Remember that the installation procedure above will also install the base and advised requirements for a basic working setup (e.g. Flask for the webserver and the websocket libraries for Python), but based on the plugins you want to enable you might need extra dependencies. You can check the dependencies required by your plugins either on the official documentation (check here in the specific case of light.hue), from the requirements.txt (optional dependencies are commented) or from the Python docstring itself:

So let’s grab the dependencies:

pip install phue

Note that your distribution package manager may already distribute some of these dependencies (under Debian/Ubuntu/Raspbian you could check for apt-get install python3-dependency-name, under Arch Linux pacman -S python-dependency-name, make sure that you always install Python 3 dependencies!). In some cases installing the distribution provided packages instead of the pip ones is a better option, as your default package manager will keep them up-to-date. In other cases, instead, you may want to install the pip or even the GitHub versions, especially if there are new developments required by the plugin and the distribution packages are out of date. The plugin docstring however usually specifies these cases.

Now that we’ve got our dependencies installed, it’s time to configure the plugin. Create an empty ~/.config/platypush/config.yaml configuration file with a configuration that looks like this:

Note that the configuration for a specific plugin expects the same parameters as the constructor of the plugin class. This makes plugin initialization consistent, and you can refer to the official documentation to either configure or programmatically instantiate your plugins.

Now let’s add a backend to the configuration to make our plugin communicate with the external world. We’ll add the HTTP backend in this example. The HTTP backend is actually a quite powerful feature. It spawns a Flask web server that can receive action requests over the /execute endpoint, and it also provides a powerful web panel that provides you with a web-based interface to control your plugins, as well as a dashboard that can be used to display custom widgets on a large screen. By default, it will spawn another process that runs the webserver, but you can also configure it to be launched by an external web server (uwsgi+nginx is probably the most popular and scalable option). Enabling the webserver in your config.yaml is quite straightforward:

Two notes:

  • In the config.yaml plugin configurations are usually named after the plugin itself with no prefix (e.g. light.hue or music.mpd), while backends are prefixed by the backend. string (e.g. backend.http).
  • If the plugin’s constructor has no mandatory arguments then you can enable a plugin or a backend by simply specifying enabled=True (disabled=False will also work).

enabled=True will configure a web server with the default parameters — web process listens on port 8008, web socket service listens on port 8009, no SSL configuration, it runs a Flask server in another process with no uswgi wrapper, no configuration for the dashboard. You can easily override these defaults — again, just look at the documentation of the backend constructor to get the possible parameters:

It’s also advised to configure the logging so that platypush redirects its messages to a file. By default, it’ll log to the standard output, and that can get easily messy:

Another strongly advised configuration is to set up an access token. Without a token platypush will accept action requests on its configured backend without performing authentication — and that’s usually not what you may want in your own connected house:

So now that you have put together a basicconfig.yaml with your first plugin and your first backend, it’s time to finally start platypush:

platypush  # If installed as root or platypush is in your PATH
python -m platypush # Otherwise

You can also create a systemd service for it and have it to automatically start. Copy something like this to ~/.config/systemd/user/platypush.service:


systemctl --user start platypush  # Will start the service
systemctl --user enable platypush # Will spawn it at startup

You can open another shell to monitor the log messages and see if there are any errors:

tail -f ~/.local/log/platypush/platypush.log

If everything went smooth, you may still notice a log line saying something like:

Bridge registration error: The link button has not been pressed in the last 30 seconds.

As you may have guessed, you’ll need to press the link button on your Hue bridge to complete the pairing. After that, you’re ready to go, and you should see traces with information about your bridge popping up in your log file. You can now test your HTTP backend by sending a get_lights command:

Or turning on your lights:

Note that in this case, if you specified Living Room (or any specific group name) as your default group in the plugin configuration, then the plugin will only operate on those lights if no parameters are passed to the methods. You can, of course, override it passing lights and/or groups as args parameter. Remember that the documentation provides you with all the required and optional parameters for each plugin/backend and its methods.

One last step (probably the most rewarding): point your browser to http://localhost:8008. A first-time user registration form will be shown — fill it with the credentials for your first user. You’ll now be prompted to the platypush web panel:

Now you’ve got all the basic notions to start playing with platypush on your own! Remember that all you need to know to initialize a specific plugin, interact with it and which dependencies it requires is in the official documentation. Stay tuned for the next articles that will show you how to do more with platypush — configuring your voice assistant, control your media, manage your music collection, read data from sensors or NFC tags, control motors, enable multi-room music control, implement a security system for your house with real-time video and audio stream, control your switches and smart devices, turn your old TV remote into a universal remote to control anything attached to platypush, and much more.

The Startup

Medium's largest active publication, followed by +526K people. Follow to join our community.

Automation Guru

Written by

Home automation, IoT, programming machine learning, economics, opinions and more

The Startup

Medium's largest active publication, followed by +526K people. Follow to join our community.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade