New wiki

Fabio Manganiello 2024-05-25 23:19:28 +02:00
parent 9383bb5623
commit 0637685825
18 changed files with 2013 additions and 3312 deletions

@ -0,0 +1,883 @@
# A full configuration example
* [Identifying the required integrations](#identifying-the-required-integrations)
* [Preparing the configuration](#preparing-the-configuration)
* [File locations](#file-locations)
* [Configuration file](#configuration-file)
* [Working directory](#working-directory)
* [`backend.http`](#backend.http)
* [`db`](#db)
* [`mqtt`](#mqtt)
* [`assistant.google`](#assistant.google)
* [`light.hue`](#light.hue)
* [`music.mpd`](#music.mpd)
* [`media.jellyfin`](#media.jellyfin)
* [`media.vlc`](#media.vlc)
* [`serial`](#serial)
* [`zigbee.mqtt`](#zigbee.mqtt)
* [`camera.ffmpeg`](#tts)
* [`tts`](#tts)
* [Installing the configured service](#installing-the-configured-service)
* [UI installation](#ui-installation)
* [API installation](#api-installation)
* [platydock](#platydock)
* [platyvenv](#platyvenv)
* [Manual installation](#manual-installation)
Now that we have familiarized a bit with the building blocks of the platform,
let's get the hands dirty with a real-world configuration to showcase how
everything plays together.
We'll build a configuration that includes the following features:
- It exposes the Web server to control devices and services via UI or API.
- It includes a voice assistant - we'll use the `assistant.google` extension in
this example because it's probably the quickest to get started, but Platypush
provides other voice assistant integrations too if you want alternatives.
Note: the `assistant.google` integration won't integrate with another Google
Assistant device, but it'll run the assistant _on your own_ device, provided
that you have a microphone and a speaker.
- It integrates with a Philips Hue bridge through the `light.hue` extension -
but you can pick any other smart device integration of your liking, e.g.
Zigbee, Z-Wave, Bluetooth, SmartThings etc.
- It integrates with a music server like
[mpd](https://mpd.readthedocs.io/en/latest/user.html) or
[Mopidy](https://mopidy.com), a media service like
[Jellyfin](https://jellyfin.org) and a local media player like
[vlc](https://www.videolan.org/vlc), but you can pick any other media
integrations you like
([Plex](https://docs.platypush.tech/platypush/plugins/media.plex.html),
[Kodi](https://docs.platypush.tech/platypush/plugins/media.kodi.html),
[Chromecast](https://docs.platypush.tech/platypush/plugins/media.chromecast.html)...).
- It integrates with an Arduino-compatible device over USB and reads some
measurements from a temperature and a humidity sensor connected to it.
The Arduino will also have an IR receiver in this example, so you can use
a traditional infrared remote to control Platypush (for example, play/pause
the music, mute the voice assistant, turn off/on the lights etc.).
- It integrates with an MQTT broker, it subscribes to a topic where other
devices may send their sensor measurements, and stores them to a database.
- It listens to a custom MQTT topic where another device (for example your
phone, through Tasker or through a Platypush service running on Android)
sends updates when you enter/exit your home. When a message is received, it
will perform a custom routine - for example, turn on the lights, say a welcome
message and play the music when you arrive home, and turn off the lights and
stop the music when you leave home.
- It integrates with [zigbee2mqtt](https://www.zigbee2mqtt.io/) to control the
Zigbee devices connected to a bridge - usually in the form of a Zigbee USB
adapter.
- It integrates with a connected camera (like a USB camera, or a PiCamera if
you're using a RaspberryPi-like device) and provides a way to access the
stream over HTTP and programmatically control the capture process.
- It implements simple voice commands to control the lights and the music
playback.
- It performs a periodic backup of the application's configuration and working
directories, and uploads them to a remote server via scp.
## Identifying the required integrations
The first step is to identify the required integrations from [the documentation
page](https://docs.platypush.tech/). In the example above, we'll need:
- [`backend.http`](https://docs.platypush.tech/platypush/backend/http.html): the
default Web server, which exposes the RPC API, the UI, several Websocket
routes, media and camera services, and more.
- [`db`](https://docs.platypush.tech/platypush/plugins/db.html): used to perform
custom operations with databases (enabled by default).
- [`mqtt`](https://docs.platypush.tech/platypush/plugins/mqtt.html): used to
publish and subscribe to topics on an MQTT broker.
- [`assistant.google`](https://docs.platypush.tech/platypush/plugins/assistant.google.html):
the Google Assistant integration.
- [`light.hue`](https://docs.platypush.tech/platypush/plugins/light.hue.html):
to integrate with a Philips Hue bridge.
- [`music.mpd`](https://docs.platypush.tech/platypush/plugins/music.mpd.html):
the integration with MPD/Mopidy.
- [`media.jellyfin`](https://docs.platypush.tech/platypush/plugins/media.jellyfin.html):
the integration with Jellyfin.
- [`media.vlc`](https://docs.platypush.tech/platypush/plugins/media.vlc.html):
the integration with VLC.
- [`serial`](https://docs.platypush.tech/platypush/plugins/serial.html): a
plugin that allows you to easily interact with Arduino-compatible devices
connected to a USB/serial interface. You can also choose the
[`arduino`](https://docs.platypush.tech/platypush/plugins/arduino.html) plugin
if you don't want to write the code for the microprocessor yourself - the
Arduino integration works with micro-controllers flashed with the
[Firmata](https://www.arduino.cc/reference/en/libraries/firmata/) firmware, or
with a compatible firmware, but it offers slightly less flexibility than a
firmware customized for your specific hardware.
- [`zigbee.mqtt`](https://docs.platypush.tech/platypush/plugins/zigbee.mqtt.html):
used to interact with Zigbee devices through a zigbee2mqtt bridge, which
bridges raw Zigbee events to an MQTT broker.
- [`camera.ffmpeg`](https://docs.platypush.tech/platypush/plugins/camera.ffmpeg.html):
used to interact with a generic camera using directly
[`ffmpeg`](https://github.com/FFmpeg/FFmpeg). Note that
[`camera.gstreamer`](https://docs.platypush.tech/platypush/plugins/camera.ffmpeg.html)
is also available if you want a GStreamer interface instead.
- [`tts`](https://docs.platypush.tech/platypush/plugins/tts.html), or a
compatible text-to-speech plugin (other available options:
[`tts.google`](https://docs.platypush.tech/platypush/plugins/tts.google.html)
and
[`tts.mimic3`](https://docs.platypush.tech/platypush/plugins/tts.mimic3.html)):
to deliver our custom "welcome home" message through the speakers.
- [`ssh`](https://docs.platypush.tech/platypush/plugins/ssh.html): used to
expose SSH functionalities - including remote command execution and file copy.
Note: this is only a demo of what it is possible to do through this application,
not an exhaustive list of features, nor a mandatory configuration - feel free to
skip the sections that don't apply to your use-case.
## Preparing the configuration
### File locations
#### Configuration file
The configuration file will be in one the following locations:
1. `$XDG_CONFIG_HOME/platypush/config.yaml` if `XDG_CONFIG_HOME` is set.
2. `~/.config/platypush/config.yaml` otherwise, if you're running the
application as a non-privileged user.
3. `/etc/platypush/config.yaml` if you're running the application as a
privileged user, or you installed it through the system package manager, or
through a Docker image generated via `platydock`.
4. A path specified through the `-c/--config` startup option.
Your configuration file can also include fragments from other configuration
files through the `include` directive, which is useful if you have particularly
large configuration files that you want to split onto multiple files, or
configuration folders shared among several installations of the service (like in
a shared dotfiles repo):
```yaml
include:
# Relative paths are relative to the `config.yaml` location, or relative to
# the current configuration file if the `include` directive is used in a child
# file
- assistant.yaml
- audio.yaml
- db.yaml
- lights.yaml
- sensors.yaml
- zigbee.yaml
# ...
```
Note that, after the application is first started, a new `scripts` directory
will also be created under the configuration file's folder. This directory can
be used to store custom hooks and procedures in the form of small Python
snippets - we'll see them later.
#### Working directory
The working directory of the service will be one of the following:
1. `$XDG_DATA_HOME/platypush`, if `XDG_DATA_HOME` is set.
2. `~/.local/share/platypush` otherwise, if you're running the application as a
non-privileged user.
3. `/var/lib/platypush`, if you're running the application as a privileged user
or within a Docker container generated via `platydock`.
This is where the main database file will be stored, and where the integrations
will store their files.
### `backend.http`
This is usually straightforward for basic use-cases:
```yaml
backend.http:
# port: 8008
```
Note that the HTTP backend is enabled by default if no configuration file is
specified.
### `db`
The `db` plugin is enabled by default, and it's used for general purpose
interactions with databases supported by SQLAlchemy.
If no database is specified in the API calls, nor is specified in the
configuration, then the default application SQLite database will be used -
stored under `<WORKDIR>/main.db`. In this example, we want to use Platypush as a
central node to store on an external database data points published over an MQTT
topic from other devices, so we may want to configure the `db` integration to
use a different default database for external database calls:
```yaml
db:
# SQLAlchemy connection string. In this case, we connect to a Postgres
# database through the pg8000 driver
engine: postgresql+pg8000://user:pass@host/dbname
```
Note that SQLite comes with built-in support in SQLAlchemy, but other databases
need external drivers. If, like in the case above, you opt to interact with a
Postgres database through the `pg8000` driver, then you'll have to install the
driver on your host machine / virtual environment / Docker image as well - for
example, through `pip install pg8000`.
### `mqtt`
The configuration of the MQTT plugin will look something like this:
```yaml
mqtt:
host: mqtt-host
port: 1883
username: user
password: pass
# Topics that the plugin should subscribe to when started
topics:
- sensors
```
### `assistant.google`
A note before starting: the `assistant.google` integration is based on the
[`google-assistant-library`](https://pypi.org/project/google-assistant-library/),
which has been officially **deprecated** by Google.
Unfortunately, Google hasn't provided any alternatives to run their assistant on
other devices, and even their assistant actions SDK has gradually lost features.
However, the current version of the library still works fine on x86_64, armv7l
and aarch64 architectures, but it requires a bit of tweaking of the dependencies
on recent versions of Python, as it depends on some libraries that are by now
deprecated. And it's not assured that it'll keep working forever either - Google
may just introduced a breaking change on the API layer, and if a new
architecture comes out the library most likely won't be built for it. However,
given how many Assistant-compatible devices are out there (some of them even
several years old), I feel that it's unlikely that Google will introduce
breaking changes any time soon - the library has already been deprecated for
several years, but it still works fine on all of my devices.
The first step is to create a new Google development project and follow [these
instructions](https://developers.google.com/assistant/sdk/guides/library/python/embed/install-sample#generate_credentials)
on how to generate a credentials file.
Then download the credentials file - if the location isn't specified in the
configuration, the plugin will look for it either under
`~/.config/google-oauthlib-tool/credentials.json` or
`<WORKDIR>/credentials/google/assistant.json`.
Finally, add the configuration to Platypush:
```yaml
assistant.google:
# The device model ID. In theory it should be registered through the
# developers console, but `Platypush` (default value) should work just fine.
device_model_id: Platypush
# The sound to play when a conversation starts.
conversation_start_sound: /path/to/sound.mp3
# Override the default credentials file locations
credentials_file: /path/to/credentials.json
```
### `light.hue`
The `light.hue` configuration would like this:
```yaml
light.hue:
bridge: bridge-ip-or-hostname
```
Note that, when the application is first started after enabling the `light.hue`
plugin, a pairing process with the bridge is required. This is usually done by
pressing the _Pair_ button on the Hue bridge within 30-60 seconds after starting
the client. After pairing the first time, no further manual pairing should be
required.
### `music.mpd`
[Mopidy](https://mopidy.com) is the best supported music player for Platypush,
but the `music.mpd` integration should work also with a vanilla MPD
installation.
We love Mopidy because it comes with dozens of integrations (local files,
Spotify, Tidal, SoundCloud, TuneIn, Bandcamp, YouTube...) and it supports dozens
of clients as well - from command-line MPD clients like
[`mpc`](https://github.com/MusicPlayerDaemon/mpc), to ncurses clients like
[`ncmpcpp`](https://github.com/ncmpcpp/ncmpcpp), to full-featured Web interfaces
like [Iris](https://mopidy.com/ext/iris/), to mobile apps like
[M.A.L.P.](https://www.appbrain.com/app/m-a-l-p-mpd-client/org.gateshipone.malp),
and thanks to Platypush you can also build automation routines around your music
experience.
The main difference between MPD and Mopidy is that MPD is a music server thought
to mainly expose local audio files, while Mopidy adds a whole system of
extensions to that paradigm.
Note that, as of Mopidy 3.0, the MPD extensions has been decoupled from the core
installation, and you'll have to install the
[`mopidy-mpd`](https://mopidy.com/ext/mpd/) package separately.
Once you have a running MPD/Mopidy installation, you can easily configure the
Platypush plugin:
```yaml
music.mpd:
host: localhost # MPD/Mopidy server
port: 6600 # Listen port (default: 6600)
```
### `media.jellyfin`
Jellyfin is a general-purpose media server that you can use to host your music,
movies, videos etc., and consume/stream them on any client.
If you have a Jellyfin server to serve your media, then you can the Platypush
integration to build automation routines around it, or programmatically play
content on other device, or control playback:
```yaml
media.jellyfin:
# Jellyfin instance base URL
server: https://media.example.com
# You can get an API key from
# https://media.example.com/web/index.html#!/apikeys.html
api_key: secret
```
Note that integrations with
[Kodi](https://docs.platypush.tech/platypush/plugins/media.kodi.html) and
[Plex](https://docs.platypush.tech/platypush/plugins/media.plex.html) are also
available. However, since none of the Platypush developers currently uses Plex,
the Plex integration hasn't been tested in a while.
There is also a
[Chromecast](https://docs.platypush.tech/platypush/plugins/media.chromecast.html),
integration that allows you to programmatically (or via UI) cast any content to
a Chromecast-compatible device, but we won't cover it in this example (it should
be quite straightforward to configure and enable though).
### `media.vlc`
If you have VLC installed on your system, then you can enable the Platypush
integration as well. The VLC integration (but the same interface is also exposed
through MPlayer, MPV, omxplayer, barebone gstreamer etc.) allows you to turn
your device into a general-purpose media player that can be controlled remotely.
Here's an example configuration of the `media.vlc` integration, but the same
interface also applies all the other provided media integrations (just replace
e.g. `media.vlc` with `media.mpv`, for example):
```yaml
media.vlc:
# Volume level, between 0 and 100
volume: 50
# Where to store downloaded files
download_dir: ~/Downloads
# Play videos in fullscreen by default
fullscreen: True
# If youtube-dl or any compatible application is installed, play requested
# videos in this format by default. Default: `best`.
# youtube_format: 'mp4[height<=?480]'
# Extra arguments to pass to the executable. --play-and-exit may be a good
# idea with VLC, so the player terminates upon stop instead of lingering in
# the background.
args:
- --play-and-exit
# List of directories to search for media files. The media files in these
# folders can be searched through the `media.<player>.search` command, or
# through the Web interface.
media_dirs:
- /mnt/hd/media/movies
- /mnt/hd/media/series
- /mnt/hd/media/videos
- ~/Downloads
```
If you specify a list of `media_dirs`, then those directories will be scanned
for media files and stored in Platypush' database. That allows you to use
Platypush as its own media server with a built-in client - you can basically use
the Web interface to search your local collection, or results from
Jellyfin/Plex/YouTube/Kodi, and play it on your Platypush device or on any other
compatible player.
### `serial`
This is where we can start to get our hands a bit dirty with some code.
The most versatile way that Platypush provides to interact with external
microcontrollers or programmable peripherals is through the
[`serial`](https://docs.platypush.tech/platypush/plugins/serial.html)
integration.
`serial` reads data on the wire from a configured device and it expects to
receive data in JSON format like this:
```json
{
"temperature": 25.0,
"humidity": 15.0,
"ir": "0xffff"
}
```
In the example above, we may have an Arduino-compatible or ESP-compatible device
connected over USB, with a temperature/humidity sensor (like one from the
[DHT11/DHT22](https://learn.adafruit.com/dht/overview) line) and any infrared
receiver connected to it.
Let's say that you have an Arduino with a DHT temperature+humidity sensor
connected to the Arduino on the PIN number 2:
![Schema of a DHT11->Arduino
connection](https://www.electronicwings.com/public/images/user_images/images/Arduino/DHT11/DHT11_Interfacing_Diagram.png)
And you have a generic infrared receiver connected to the PIN number 7:
![Schema of an IR receiver -> Arduino
connection](http://www.circuitbasics.com/wp-content/uploads/2017/05/Arduino-IR-Remote-Receiver-Breakout-Board-Wiring-Diagram.png)
You can then write some microcontroller code like this to periodically check the
sensor for new values and send them down the wire in JSON format:
```arduino
#include <Arduino.h>
// Requires the ArduinoJson library: https://arduinojson.org/
#include <ArduinoJson.h>
// Requires the IRremote library for infrared decoding: https://www.arduino.cc/reference/en/libraries/irremote/
#include <IRremote.h>
#include <Time.h>
#include <Wire.h>
// Requires the dht-sensor-library: https://www.arduino.cc/reference/en/libraries/dht-sensor-library/
#include <dht.h>
// Custom values to tell when we haven't got any value
#define NO_IR_VALUE 0
#define NO_SENSOR_VALUE DHTLIB_INVALID_VALUE
// The PIN where the DHT sensor is connected
#define DHT11_PIN (2)
// The PIN where the IR sensor is connected
#define IR_PIN (7)
dht DHT;
IRrecv irRecv(IR_PIN);
decode_results ir_results;
float last_temp_value = NO_IR_VALUE;
float last_humidity_value = NO_SENSOR_VALUE;
unsigned long last_ir_value = NO_SENSOR_VALUE;
void setup() {
// Change this line if you specified a different baud rate when flashing
// your firmware
Serial.begin(9600);
// Wait for the Serial interface to be available
while (!Serial) delay(10);
// Prepares to receive data from the infrared receiver
irRecv.enableIRIn();
}
void loop() {
StaticJsonDocument<500> output;
bool has_changes = false;
// Read temperature/humidity data
DHT.read11(DHT11_PIN);
float temp = DHT.temperature;
float humidity = DHT.humidity;
if (temp != DHTLIB_INVALID_VALUE && temp != last_temp_value) {
doc["temperature"] = temp;
has_changes = true;
}
if (humidity != DHTLIB_INVALID_VALUE && humidity != last_humidity_value) {
doc["humidity"] = temp;
has_changes = true;
}
// Read IR data
if (irRecv.decode(&ir_results)) {
unsigned long value = ir_results.value;
if (value != NO_IR_VALUE && value != last_ir_value) {
doc["ir"] = String(value, HEX);
has_changes = true;
}
last_ir_value = value;
irRecv.resume();
} else {
if (last_ir_value != NO_IR_VALUE) {
doc["ir"] = String(NO_IR_VALUE, HEX);
has_changes = true;
}
last_ir_value = NO_IR_VALUE;
}
if (has_changes) {
// If we have some new data, then push it down the wire
serializeJson(doc, Serial);
// If we have received an infrared event, then wait half a second before
// resuming - that's because the infrared signal may be sent multiple times,
// or spurious signals may be received in the transition between the remote
// key up and key down
if (last_ir_value != NO_IR_VALUE) {
delay(500);
}
}
delay(10);
}
```
Compile the project, flash it to your device from the Arduino IDE or
command-line, connect it to your machine, and run a serial monitor to check that
it works. For example:
```bash
picocom /dev/ttyUSB0 -b9600
```
Point a remote at it for example and press a button. You should receive a JSON
message on the serial interface that the Platypush service can listen to, and
where you can hook your custom logic.
Note that in this example we've taken into consideration a traditional Arduino
device with a custom firmware written in C. This isn't the only available option
that Platypush provides to interact with microcontrollers. You also have:
- The
[`arduino`](https://docs.platypush.tech/platypush/plugins/arduino.html)
specific integration, which interacts with devices flashed with the Firmata
firmware or compatible with its protocol. While it takes away most of the
hassle of having to write custom code for your microcontroller, it provides a
bit less flexibility - for example, it's hard to get it to run out of the box
with devices that require specific libraries to work (like `IRremote` in our
case).
- The
[`esp`](https://docs.platypush.tech/platypush/plugins/esp.html) plugin, which
specifically interacts with ESP-compatible devices over Wi-Fi.
- The
[`sensor.mcp3008`](https://docs.platypush.tech/platypush/plugins/sensor.mcp3008.html),
if you want to go more minimal and you only need an analog->digital converter
for your sensors connected to e.g. your RaspberryPi.
Also, note that, while the `serial` plugin requires you to write your own code
for the microcontroller, it's also basically language-agnostic and
device-agnostic when it comes to the microcontroller on the other side. It can
be an Arduino with code written in C, an ESP microcontroller with a Node.js
interface, or a RaspberryPi Pico with MicroPython code. It doesn't really
matter: as long as it pushes JSON key-value dictionaries down the wire,
Platypush should be able to parse all of its messages and generate the
appropriate events that you can subscribe to.
Once your device is ready to produce JSON messages on the wire, you can
configure the Platypush integration:
```yaml
serial:
# The path to the USB interface with e.g. an Arduino or ESP microcontroller
# connected.
# A way to get a deterministic path name on Linux, instead of
# `/dev/ttyUSB<n>`, can be the following:
#
# - Get the vendor and product ID of your device via e.g. `lsusb`. For
# example, for an Arduino-compatible microcontroller:
#
# Bus 001 Device 008: ID 1a86:7523 QinHeng Electronics CH340 serial converter
#
# - In the case above, `1a86` is the vendor ID and `7523` is the product
# ID. Create a new udev rule for it, so every time the device is
# connected it will also be symlinked to e.g. `/dev/arduino`:
#
# echo 'SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", SYMLINK+="arduino"' | \
# sudo tee /etc/udev/rules.d/98-usb-serial.rules
device: /dev/ttyUSB0
# How often the interface should be polled for updates, in seconds
poll_interval: 1
# The tolerance argument can be used to tune when you want to be notified
# of data changes through `SensorDataChangeEvent` events. In the case
# below, if the microcontroller sends two consecutive temperature reads,
# one for 22.0 and one for 22.2, then only one `SensorDataChangeEvent` will
# be triggered (for the first read), since the absolute value of the
# difference between the two events is less than the configured tolerance.
# However, if the device sends two temperature reads, one for 22.0 and one
# for 22.7, then two `SensorDataChangeEvent` events will be triggered.
# The tolerance for all the metrics is set to a value close to zero by
# default - i.e. any read, unless it's exactly the same as the previous
# one, will trigger a new event.
tolerance:
temperature: 0.5
humidity: 0.75
luminosity: 5
# If a threshold is defined for a sensor, and the value of that sensor goes
# below/above that temperature between two reads, then a
# `SensorDataBelowThresholdEvent` or a `SensorDataAboveThresholdEvent` will
# be triggered respectively.
thresholds:
temperature: 25.0
```
Note that the same interface/configuration of the `serial` plugin applies to all
plugins that implement support for sensors.
### `zigbee.mqtt`
[`zigbee2mqtt`](https://www.zigbee2mqtt.io/) is a popular DIY framework for
running your own Zigbee bridge. You can connect any Hue-compatible light or
sensor to it, as well as most of the e.g. modern Ikea bulbs and any electronic
appliance that communicates over Zigbee.
You can flash a zigbee2mqtt-compatible firmware on a variety of USB dongles
following the instructions on the project's website or on the [plugin
documentation
page](https://docs.platypush.tech/platypush/plugins/zigbee.mqtt.html). Then you
can pull a zigbee2mqtt image and configure it to connect to an MQTT broker.
The next step is to enable the `zigbee.mqtt` integration in your configuration:
```yaml
zigbee.mqtt:
# Host of the MQTT broker
host: my-mqtt-broker
# Listen port of the MQTT broker
port: 1883
# Base topic, as specified in `<zigbee2mqtt_dir>/data/configuration.yaml`
topic_prefix: zigbee2mqtt
```
You should now be ready to pair and control Zigbee devices on your bridge
through Platypush.
### `camera.ffmpeg`
[`camera.ffmpeg`](https://docs.platypush.tech/platypush/plugins/camera.ffmpeg.html)
is the most extensively tested way of integrating with hardware cameras. It
supports basically any camera that speaks the v4l2 protocol and a variety of
other ffmpeg-supported protocols.
[`camera.gstreamer`](https://docs.platypush.tech/platypush/plugins/camera.gstreamer.html)
is also available if you want to use a
[GStreamer](https://gstreamer.freedesktop.org/) backend,
[`camera.cv`](https://docs.platypush.tech/platypush/plugins/camera.cv.html) if
you want to leverage the [OpenCV](https://opencv.org/) framework (usually
because you want to run some computer vision transformation on your images or
feed), and a deprecated
[`camera.pi`](https://docs.platypush.tech/platypush/plugins/camera.pi.html)
integration for the (now deprecated) PiCamera backend (note that PiCameras
are now v4l2-compatible, so they should work fine also using the FFmpeg or
GStreamer integrations).
Regardless of which integration you pick, all camera plugins share the same
configuration options/interface:
```yaml
camera.ffmpeg:
# Default video device to use
device: /dev/video0
# Default resolution
resolution:
- 640
- 480
# The directory that will be used to store captured frames/images
frames_dir: ~/Camera/Photos
# Default image scaling factors (default: 1, no scaling)
scale_x: 1.5
scale_y: 1.5
# Default rotation of the image, in degrees (default: 0, no rotation)
rotate: 90
# Grayscale mode (default: False):
grayscale: false
# Default frames per second (default: 16)
fps: 16
# Whether to flip the image along the horizontal axis (default: False)
horizontal_flip: false
# Whether to flip the image along the horizontal axis (default: False)
vertical_flip: false
```
### `tts`
Platypush currently supports three text-to-speech integrations:
- [tts](https://docs.platypush.tech/platypush/plugins/tts.html): it's the
default TTS plugin. It leverages Google Translate's unofficial frontend API to
render text to speech. This means that it requires no authentication nor extra
configuration, but the quality of the rendered speech and the flexibility of
the API may be a bit inferior to that that you would get through the
[tts.google](https://docs.platypush.tech/platypush/plugins/tts.google.html),
which leverages Google's official text-to-speech API (the `tts` plugin should
work just fine for simple interactions though). A full open-source alternative
is
[tts.mimic3](https://docs.platypush.tech/platypush/plugins/tts.mimic3.html),
which leverages the open-source [Mimic 3 TTS
engine](https://mycroft.ai/mimic-3/), part of the (now defunct) Mycroft open
voice suite.
All these plugins share the same base configuration, and some may have
integration-specific extra options (like voice or gender):
```yaml
tts:
language: en-US
volume: 100
```
## Installing the configured service
Once we're happy with our configuration, we can proceed with installing the
service with all of the extra dependencies required by the configured
extensions.
You can consult the [Installation](Installation) and [Installing
extensions](#Installing-extensions) wiki pages for more details about the
supported installation methods. This section assumes that you have already
installed the core service with its base dependencies.
Let's recap some of the supported ways, now that we have a full configuration.
### UI installation
This is a viable, user-friendly route if you have installed the base service
(via `pip`, package manager or vanilla Docker image) and you want to install
your dependencies in the same environment through the Web interface. Note that
this method requires the `backend.http` integration to be enabled (and by
default it is).
1. Start Platypush via `platypush [--start-redis] [...]`. NOTE: do not start
Platypush from your new configuration file yet. If some of the configured
extensions have missing dependencies, the service may fail to start too.
2. Head to the Web panel, by default at `http://ip:8008/`, and register/login.
3. Click on _Extensions_ at the bottom of the main menu. The page may take a
while to load the first time or after an update, as it has to scan the source
code for extensions metadata, but it's much faster once the extensions have
been cached.
4. Select the extensions that you want to install, and install their
dependencies through the _Install_ tab.
![Installation panel
screenshot](https://static.platypush.tech/screenshots/install-ui-screenshot.png)
When you are done installing all the extensions, you can proceed with restarting
the application.
### API installation
Alternatively, you can install the extra dependencies in your environment
through the Web API.
Get an authentication token (from the UI, _Settings_ ➡️ _Tokens_) and then use
the
[`application.install`](https://docs.platypush.tech/platypush/plugins/application.html#platypush.plugins.application.ApplicationPlugin.install)
API to install the extra dependencies:
```bash
# Repeat for all the desired integrations
curl -XPOST -H "Content-Type: application/json" \
-H "Authorization: Bearer <TOKEN>" \
-d '
{
"type": "request",
"action": "application.install",
"args": {
"extension": "mqtt"
}
}' \
'http://my-host:8008/execute'
```
You can also use the API through the Python REPL itself:
```python
>>> from platypush import run
>>> run('application.install', 'mqtt')
```
### platydock
It's quite easy to build a Platypush Docker image given a configuration file
through the `platydock` command:
```bash
# Available base images: -i {alpine,debian,fedora,ubuntu}
platydock -c /path/to/config.yaml -d device-name -t platypush
```
This command will build a Platypush base image, infer the extra dependencies
from the extensions that you specified in your configuration file, and install
everything that is required to start your service.
If instead of building the container you want to just generate a `Dockerfile`
for your installation, which you can customize to your liking, you can use the
`--print` option of `platydock`.
You can then start a container via:
```bash
docker run -it --rm
--name platypush \
-v /path/to/your/confdir:/etc/platypush \
-v /path/to/your/workdir:/var/lib/platypush \
-p 8008:8008 \
platypush
```
Note that you may need to expose extra devices to the container via `--device`
option if you configured extensions that require that access. For instance, if
you enabled a camera plugin your container may need to be started with `--device
/dev/video0`, or if you enabled a voice assistant, text-to-speech or
sound-dependent plugin then you may have to expose the sound devices to the
container via `--device /dev/snd`.
### platyvenv
Another option is to create a virtual environment for Platypush, with all the
required dependencies automatically installed.
If you already installed the base service, then you can leverage the `platyvenv`
utility for it, which supports the same base options as `platydock`:
```bash
platyvenv -c /path/to/config.yaml -d device-name -o venv-output-dir
```
This command will install a virtual environment under `venv-output-dir` with all
the dependencies required by your configured extensions.
You can then start it with:
```bash
source VENV_PATH/bin/activate
platypush -c /path/to/config.yaml [--start-redis] [...opts]
```
### Manual installation
Finally, you have the manual way.
Check out the documentation page of each of your extensions on [the main
documentation](https://docs.platypush.tech), scroll to the dependencies section,
and run the commands one by one.

214
APIs.md Normal file

@ -0,0 +1,214 @@
# APIs
## HTTP API
Actions and procedures can also be called using the JSON-RPC API exposed by
Platypush.
Your configuration requires the [`backend.http`
section](https://docs.platypush.tech/platypush/backend/http.html) enabled if
you want to use the HTTP API - default listen port: `8008`.
After ensuring that the HTTP backend is enabled, head to
`http://localhost:8008` and register a new user.
![Platypush local user registration
page](https://platypush-static.s3.nl-ams.scw.cloud/screenshots/registration-page-screenshot.png)
From the Web UI, head to _Settings__Tokens_, insert your password again and
click _Generate JWT token_.
![User token generation UI](https://platypush-static.s3.nl-ams.scw.cloud/screenshots/tokens-ui-screenshot.png)
Alternatively, you can retrieve a token via HTTP request:
```shell
curl -XPOST -H 'Content-Type: application/json' -d '
{
"username": "$YOUR_USER",
"password": "$YOUR_PASSWORD"
}' http://localhost:8008/auth
```
You can then send requests to Platypush using a simple RPC API:
```bash
curl -XPOST \
-d '{"type":"request", "action":"procedure.at_home"}' \
-H "Authorization: Bearer $YOUR_TOKEN" \
-H "Content-Type: application/json" \
http://localhost:8008/execute
{
"id": "724754df98968247a284557ce32f74bb",
"type": "response",
"target": "http",
"origin": "myhost",
"_timestamp": 1716575901.046127,
"response": {
"output": {
"success": true
},
"errors": []
}
}
```
If your procedure returned something, then that will be returned on the API
response too, so downstream consumers can use it.
The `POST /execute` endpoint accepts a payload in the format:
```javascript
{
"type": "request", // Constant
"action": "<plugin-name>.<action-name>", // Or procedure.<name>
"args": {
"arg1": "arg2",
// ...
}
}
```
In our `procedure.at_home` example, you can for instance create an automation
snippet paired with your phone that runs the routine whenever you arrive home
(or your phone does):
1. Install an app like [Tasker](https://tasker.joaoapps.com/) to create
automation tasks on your Android device.
2. Install a plugin like [AutoLocation](https://joaoapps.com/autolocation/) to
create automation tasks based on your phone's location.
3. Create a profile that triggers whenever you enter your home location (and/or
exit it).
![Tasker screenshot showing an At Home/Outside Home pair of
profiles](https://platypush-static.s3.nl-ams.scw.cloud/screenshots/tasker-screenshot-1.png)
4. Leverage the [HTTP
Request](https://tasker.joaoapps.com/userguide/en/help/ah_http_request.html)
Tasker action to send a request to your Platypush API to trigger the routine.
### The _Execute_ tab
The Web interface also provides an _Execute_ tab under the menu sidebar. You
can use this tab to dynamically discover the actions exposed by various plugins
(and also your own procedures):
![Screenshot of the Execute tab showing the autocomplete discovery of the
actions](https://platypush-static.s3.nl-ams.scw.cloud/screenshots/execute-panel-screenshot-1.jpg)
![Screenshot of the Execute tab showing the automatically generated
documentation for a given action and its
parameters](https://platypush-static.s3.nl-ams.scw.cloud/screenshots/execute-panel-screenshot-2.jpg)
![Screenshot of the Execute tab showing the output of an action being
run](https://platypush-static.s3.nl-ams.scw.cloud/screenshots/execute-panel-screenshot-3.jpg)
## Websocket API
### Events
You can subscribe to events generated by the application over the `/ws/events`
Websocket endpoint, and send events to this endpoint too.
This is useful if you want to synchronize Platypush events with another client,
or send custom events outside of those native to the application and build
custom automation hooks on them.
Sending events:
```bash
wscat -H "Authorization: Bearer $YOUR_TOKEN" \
-c "ws://localhost:8008/ws/events" \
-w 1 \
-x '
{
"type": "event",
"args": {
"type": "platypush.message.event.custom.CustomEvent",
"subtype": "foo",
"args": {
"bar": "baz"
}
}
}'
```
Receiving events:
```bash
wscat -H "Authorization: Bearer $YOUR_TOKEN" -c "ws://localhost:8008/ws/events"
```
### Actions
You can also send requests to the `/ws/requests` Websocket endpoint, and get
responses asynchronously on the same channel:
```bash
wscat -H "Authorization: Bearer $YOUR_TOKEN" \
-c "ws://localhost:8008/ws/requests" \
-w 1 \
-x '{"type": "requests", "action": "procedure.foo.bar"}'
```
## Web hooks
You can use Platypush to expose your custom routines as dynamic Web hooks that
can be called by any client.
All you need is to register a listener for a
[`WebhookEvent`](https://docs.platypush.tech/platypush/events/http.hook.html#platypush.message.event.http.hook.WebhookEvent)
```python
from platypush import run, when
from platypush.events.http.hook import WebhookEvent
hook_token = "abcdefabcdef"
# Expose the hook under the /hook/at_home endpoint
@when(WebhookEvent, hook="at_home")
def at_home_webhook(event: WebhookEvent):
# Unlike the calls to /execute, custom web hooks are unauthenticated.
# If you want authentication, you'll need to implement your custom logic by
# parsing the event headers
if event.headers.get("X-Token") != hook_token:
# Tuple with <response, http-code, [response-headers]>
event.send_response(("Unauthorized", 401))
return
run('procedure.at_home')
# Return anything back to the client
return {'status': 'ok'}
```
Then you can invoke your custom logic over HTTP:
```bash
curl -H 'X-Token: abcdefabcdef' 'http://localhost:8008/hook/at_home'
```
## Platypush as a library
You can also use Platypush as a library in your own scripts.
For example, you may want to run a bunch of scripts that control your lights
outside of Platypush, but use a Platypush configuration file as your script
configuration, and use a plugin as the API for your controller:
```python
from platypush import run
from platypush.config import Config
# Optional. Defaults to the default config.yaml if missing
Config.init('/path/to/my/config.yaml')
# ...
run('light.hue.toggle', groups=['Living Room', 'Hall'])
# ...
```

File diff suppressed because it is too large Load diff

190
Configuration.md Normal file

@ -0,0 +1,190 @@
# Configuration
## Configuration file
You can use the [default
`config.yaml`](https://git.platypush.tech/platypush/platypush/src/branch/master/platypush/config/config.yaml)
as a template/reference.
The location of the `config.yaml` to be used by the application is determined
in the following way:
1. It can be passed through the command-line `-c`/`--config` argument.
2. If not specified via `-c`, it will be read from the `PLATYPUSH_CONFIG`
environment variable.
3. If not specified, use `./config.yaml` if available.
4. If not available, and you are running Platypush within a Docker container,
or as a privileged user (and usually you shouldn't), or as a systemd service
created by a supported package manager, then `/etc/platypush/config.yaml`
will be used if available.
5. Otherwise, if you are running Platypush as a non-privileged user or in a
virtual environment, `$XDG_CONFIG_HOME/platypush/config.yaml` will be used
(defaults to `~/.config/platypush/config.yaml`).
### Scripts directory
By default, any custom Python scripts will be searched under
`<CONFDIR>/scripts`, where `<CONFDIR>` is the path to your `config.yaml`.
You can override it in your `config.yaml`:
```yaml
scripts_dir: /path/to/custom/scripts
```
Since everything under the scripts directory will be imported as a submodule,
you can create your own libraries of scripts that can import other scripts:
```python
# Content of scripts/music.py
from platypush import run
def music_play(plugin='music.mopidy', resource=None):
run(f'{plugin}.play', resource)
# Content of scripts/lights.py
from platypush import run
def lights_toggle(plugin='light.hue', groups=('Living Room',)):
run(f'{plugin}.toggle', groups=groups)
# Content of scripts/home.py
from platypush import procedure
from scripts.music import music_play
from scripts.lights import lights_toggle
@procedure
def at_home():
music_play()
lights_toggle()
```
### Splitting configuration on multiple files
The `config.yaml` file can become very complex, especially if you embed many
hooks and procedures in it in YAML format.
To make the configuration more maintainable, and also to isolate modules that
you can reuse across multiple instances, you can leverage the `include`
directive:
```yaml
# All paths are relative to config.yaml, or to the location of the current file
include:
- assistant.yaml
- db.yaml
- media.yaml
- mqtt.yaml
- sensors.yaml
# ...
```
## Working directory
This is where the application will store its data and integration plugins will
store their data. The order of precedence is:
* `-w`/`--workdir` command line argument.
* The `PLATYPUSH_WORKDIR` environment variable.
* The `workdir` field in the configuration file.
* `$XDG_DATA_HOME/platypush` (default: `~/.local/share/platypush`) if launched
with a non-privileged user, `/var/lib/platypush` if launched as root or with
a system user.
## Database
The application stores entities, variables, users, integrations state and more
on a database. The engine configuration supports the [SQLAlchemy engine
syntax](https://docs.sqlalchemy.org/en/20/core/engines.html).
**Note**: The application uses a local SQLite database by default, which is
natively supported by SQLAlchemy. The application has also been tested against
MySQL/MariaDB and Postgres, and should work fine with any modern relational
database supported by SQLAlchemy. However, any backend other than SQLite may
require an additional Python dependency for the SQLAlchemy driver (for example
[`pg8000`](https://pypi.org/project/pg8000/) for PostgreSQL).
Order of precedence for the engine:
* `--main-db`/`--db` command line argument.
* The `PLATYPUSH_DB` environment variable.
* The `main.db` field in the configuration file.
* `sqlite:///<WORKDIR>/main.db`
## Device ID
The device ID is a unique identifier for a Platypush instance on a network and
is used to reliably dispatch messages when multiple instances use a shared
backend.
The order of precedence is:
* `--device-id` command line argument.
* The `PLATYPUSH_DEVICE_ID` environment variable.
* The `device_id` field in the configuration file.
* The hostname of the machine.
## systemd service
If you installed Platypush from a system package manager then you'll also have
a `systemd` service installed for it.
You can start/enable Platypush like any other `systemd` service:
```
# systemctl start platypush
# systemctl enable platypush
```
Or, if you want to run the Platypush service as a generic user:
```bash
systemctl --user start platypush
systemctl --user enable platypush
```
Otherwise, you can create your own `systemd` service copying the [provided
`.service`
file](https://git.platypush.tech/platypush/platypush/src/branch/master/examples/systemd/platypush.service)
to e.g. `~/.config/systemd/user` or `/etc/systemd/system`.
## Redis
Platypush uses Redis as a in-memory queue to deliver messages and as a pub/sub
bus for inter-process communication.
If you installed Platypush through a package manager, then the Redis service
will automatically be installed and started if you launch the Platypush service
as a privileged user.
If you run Platypush in a container then by default it'll start its own Redis
instance through the `--start-redis` command-line option.
You can customize the Redis configuration through the:
1. `--redis-host`, `--redis-port` and `--redis-queue` command-line options.
2. `PLATYPUSH_REDIS_HOST`, `PLATYPUSH_REDIS_PORT` and `PLATYPUSH_REDIS_QUEUE`
environment variables.
3. Through your `config.yaml`:
```yaml
# See https://redis-py.readthedocs.io/en/latest/connections.html#redis.Redis
# for the full list of supported parameters
redis:
host: redis-host
port: 6379
username: redis-user
password: redis-pass
```
## nginx
If you want to access your Platypush web panel outside your home network, it may
be a good idea to use an nginx/Apache reverse proxy with a valid SSL certificate
(e.g. managed by certbot). A [sample an nginx
configuration](https://git.platypush.tech/platypush/platypush/src/branch/master/examples/nginx/nginx.sample.conf)
is provided in the repository.

40
Entities.md Normal file

@ -0,0 +1,40 @@
# Entities
Entities are another building block of Platypush. Many integrations will store
their state or connected devices in the form of entities - e.g. the sensors
detected by the Z-Wave/Zigbee/Bluetooth integration, or the lights connected to
a Hue bridge, or your cloud nodes, or your custom Arduino/ESP machinery, and so
on.
Entities provide a consistent interface to interact with your integrations
regardless of their type and the plugin that handles them. For instance, all
temperature sensors will expose the same interface, regardless if they are
Bluetooth or Zigbee sensors, and all the media plugins will expose the same
interface, regardless if they manage Chromecasts, Kodi, Plex, Jellyfin or a
local VLC player.
Once you enable the HTTP backend and a few integrations that export entities
and register a user, you can query the detected entities via:
```shell
curl -XPOST -H 'Content-Type: application/json' \
-H "Authorization: Bearer $YOUR_TOKEN" \
-d '{"type":"request", "action":"entities.get"}' \
http://localhost:8008/execute
```
All the entities expose the same interface and can be manipulated through the
same API. Also, when an entity is updated it always emits an
[`EntityUpdateEvent`](https://docs.platypush.tech/platypush/events/entities.html#platypush.message.event.entities.EntityUpdateEvent),
so you can easily create hooks that react to these events and act on multiple
types of entities.
If you enabled the HTTP backend, then you can also access all the entities from
the home panel of the Web UI.
![Screenshot of the entities UI](https://platypush-static.s3.nl-ams.scw.cloud/screenshots/Entities-screenshot-1.png)
![Screenshot of the entities UI](https://platypush-static.s3.nl-ams.scw.cloud/screenshots/Entities-screenshot-2.png)
![Screenshot of the application main
panel, showing the Bluetooth, Serial, SmartThings and System integrations](https://platypush-static.s3.nl-ams.scw.cloud/screenshots/main-panel-screenshot-1.png)

207
Home.md

@ -1,137 +1,100 @@
Platypush
=========
# Home
* [Architecture](#architecture)
* [Installation](#installation)
* [Quickstart](quickstart)
* [Plugins](plugins)
* [Backends](backends)
* [Run Platypush in a virtual environment](run-platypush-in-a-virtual-environment)
* [Run Platypush in a container](run-platypush-in-a-container)
* [Writing your plugins](writing-your-own-plugins)
* [Writing your backends](writing-your-own-backends)
- [Quickstart](Quickstart)
- [Installation](Installation)
- [Plugins installation](Plugins-installation)
- [APIs](APIs)
- [Variables](Variables)
- [Entities](Entities)
- [Configuration](Configuration)
- [A full configuration example](A-full-configuration-example)
- [The Web interface](The-Web-interface)
# Architecture
Platypush is a general-purpose and extensible platform for automation across
multiple services and devices with [hundreds of supported
integrations](https://docs.platypush.tech/plugins.html).
The base components are:
It enables users to create their own self-hosted pieces of automation based on
events (*if this happens then do that*)
and it provides a comprehensive and customizable user interface that collects
everything you need to visualize and control under one roof.
* __Messages__: _requests_, _responses_ or _events_. The main difference between a request and an event is that the former specifies an explicit action to be executed through a _plugin_ (and the sender will usually wait for a _response_), while an event only notifies that something happened on a connected data source and can either be ignored or trigger some custom actions specified in the configuration.
It borrows concepts from [IFTTT](https://ifttt.com),
[Tasker](https://tasker.joaoapps.com/) and [Home
Assistant](https://www.home-assistant.io/) to provide an environment where the
user can easily connect things together. It focuses on an automation-as-code
and API-first approach, offering power users great flexibility in customizing
their routines.
* The __Bus__: An internal queue where all the other components exchange messages.
It's built with compatibility and flexibility in mind, and it can easily run on
any device that can run a Python interpreter - from a Raspberry Pi, to an old
smartphone, to a beefy server.
* __Backends__: Components that poll other data sources (a local queue, a remote websocket, a Kafka instance, or even a vocal assistant, a programmable button or a sensor) and post either requests or events on the bus when something happens on the data source. Some of them can have a full-duplex integration with the bus, i.e. post requests and events on the bus as they come and deliver responses from the bus back to the sender (examples: PushBullet, Apache Kafka, HTTP services, sockets), while some are pure data sources that will only post events on the bus (examples: sensors, buttons, vocal assistants). Check [our documentation](https://docs.platypush.tech/en/latest/backends.html) for a complete reference of the available backends.
## Core concepts
* __Plugins__: Configurable components which expose _actions_ that can be triggered by requests or events. Examples: smart lights, music controls, YouTube or torrentcast features, text-to-speech, generic shell commands, etc.). They would usually deliver output and errors as responses on the bus. Check [our documentation](https://docs.platypush.tech/en/latest/plugins.html) for a complete reference of the available plugins.
The foundations of Platypush rest on a few simple building blocks that offer
great versatility to build arbitrarily complex automation routines:
* __Procedures__: Pre-configured lists of actions that can be triggered by requests or events.
- 🧩 **Plugins**. Plugins are the bread-and-butter of the platform. Each plugin
exposes an API to interact with an integration - there are plugins for media
players and devices, calendars, sensors, voice assistants, smart devices,
cloud services, and so on.
* Event __Hooks__: Pre-configured actions that will be executed when certain events are processed. They include:
* A _condition_, which can be fuzzly compared against an event. The matching logic will also return a _match score_ if the condition is met. The score is a function of the number of atomic matches (string tokens, scalars, key-values etc.) in the condition that are satisfied by a certain event. If multiple hooks are satisfied by a certain event, the algorithm will only run the ones with the highest score.
* One or more _actions_ to be executed in case of event match (e.g. "turn on the lights", "send a Messenger message to my s.o.", "turn on the heating", "play the radio" etc.)
- ⏻ **Actions**. These are the methods of a plugin transparently exposed to the
user over a simple JSON RPC API, and they are always expressed in the
format `<plugin_name>.<action_name>`. For instance,
[`light.hue.on`](https://docs.platypush.tech/platypush/plugins/light.hue.html#platypush.plugins.light.hue.LightHuePlugin.on)
can be used to turn on Philips Hue-compatible lights,
[`media.vlc.play`](https://docs.platypush.tech/platypush/plugins/media.vlc.html#platypush.plugins.media.vlc.MediaVlcPlugin.play)
to play some media on a VLC player, etc.
Check [our documentation](https://docs.platypush.tech/en/latest/events.html) for a complete reference of the available events.
- ⚙️ **Backends**. These are special integrations whose main purpose is to
deliver messages to the main application. The principal one is the
[`http` backend](https://docs.platypush.tech/platypush/backend/http.html),
which exposes the HTTP and WebSocket APIs, serves the main UI and is used
by several integrations to provide additional services. A [`nodered`
backend](https://docs.platypush.tech/platypush/backend/nodered.html) is
also available to expose a Platypush action component to a Node-RED
instance, as well as an internal [`redis`
backend](https://docs.platypush.tech/platypush/backend/redis.html) and an
(insecure) [`tcp`
backend](https://docs.platypush.tech/platypush/backend/tcp.html) to receive
raw messages.
# Installation
- 📧 **Events**. Plugins emit _events_ whenever some particular conditions happen
for example, a [new media track is
played](https://docs.platypush.tech/platypush/events/media.html#platypush.message.event.media.MediaPlayEvent),
a [voice assistant conversation has
started](https://docs.platypush.tech/platypush/events/assistant.html#platypush.message.event.assistant.ConversationStartEvent),
and so on.
First install Redis, as it's used as a bus for delivering messages to platypush. On Debian/Ubuntu for example:
- 🪝 **Hooks**. Users can define custom callbacks on events in the form of
*hooks*. Hooks can contain lists of actions to execute when a certain event
matches the hook *condition*, or any kind of custom logic - for example,
*send a notification on my phone when the presence sensor in my garage goes
on*, or *use a TTS plugin to process the digest of the latest RSS feeds if
I tell the voice assistant "play the news"*. Event hooks can be expressed
either in YAML format or as Python runtime scripts.
```shell
apt-get install redis-server
# Start and enable the service
systemctl start redis.service
systemctl enable redis.service
```
- 📜 **Procedures**. Procedures are custom snippets of logic that can be invoked
using the Platypush API. For example, you can define an `at_home` procedure
that will be executed when you arrive home, which turns on the lights, plays
the music, sets the thermostat temperature etc., and then call it using the
Platypush API from any device. Like event hooks, procedures can be defined
both in YAML format (good if you just want to execute lists of actions
without much added logic), or as Python scripts.
Then install platypush:
- 🕗 **Cronjobs**. Cronjobs are special procedures that can be executed either
at regular intervals (the [UNIX cron
syntax](https://linuxhandbook.com/crontab/) is supported), or at a specific
time (one-shot). Just like procedures, they can be defined either in YAML or
as Python scripts.
```shell
pip install platypush
```
### Manual Installation
```shell
git clone https://github.com/BlackLight/platypush
cd platypush
python setup.py build install
```
### Extra dependencies
There are four possible ways to install extra dependencies for the plugins and backend that you want to run on your
instance.
#### `platyvenv` and `platydock`
Upon installation, Platypush provides two utilities (`platyvenv` and `platydock`) that allow you to easily create
respectively a virtual environment or a Docker image for your Platypush application from a `config.yaml`.
Write your `config.yaml` file, including all the plugins and backends that you want to enable. You can also include
external YAML files through the `include` directive (as long as they are in the same folder as your `config.yaml`),
and also custom Python scripts in the `scripts` directory.
Make sure to define a `device_id` field in your `config.yaml`. It defines the name that `platyvenv`/`platydock` use to
identify and manage the service.
##### Building the environment/image
```shell
platyvenv build -c /path/to/your/config.yaml
# or
platydock build -c /path/to/your/config.yaml
```
##### Starting the environment/image
```shell
platyvenv start <device_id>
# or
platydock start <device_id>
```
##### Stopping the environment/image
```shell
platyvenv stop <device_id>
# or
platydock stop <device_id>
```
##### Removing the environment/image
```shell
platyvenv rm <device_id>
# or
platydock rm <device_id>
```
#### `pip` extras
You can easily install the optional dependencies for the extra plugins you want to use by using the [PEP-508 extras syntax](https://www.python.org/dev/peps/pep-0508/#extras). For example, if you want to run the [HTTP server](https://docs.platypush.tech/en/latest/platypush/backend/http.html) then you can install it through:
```shell
pip install "platypush[http]"
```
You can check the supported extras in the [`setup.py` file](https://github.com/BlackLight/platypush/blob/master/setup.py#L145). Installation of comma-separated lists of extras is also supported.
#### Plugins/backends documentation
The [official documentation](https://docs.platypush.tech) provides manual installation steps for all the supported
extensions.
### Configuration
Copy the [example `config.yaml`](https://github.com/BlackLight/platypush/blob/master/examples/conf/config.yaml) to
`/etc/platypush/config.yaml` (global installation, discouraged) or `~/.config/platypush/config/config.yaml`
(user installation, recommended).
Explore it to get a grasp of the basic components of Platypush and their configuration and change it according to your needs.
### Start up
After configuring the server, start it by simply running `platypush`, `python -m platypush`. You can also a systemd service by copying the [example `platypush.service`](https://github.com/BlackLight/platypush/blob/master/examples/systemd/platypush.service) to either `/usr/lib/systemd/user/platypush.service` (global installation) or `~/.config/systemd/user/platypush.service` (user installation).
**NOTE**: Platypush is only compatible with Python 3 and higher.
After installing the application, you're ready to [start configuring it](quickstart) according to your needs.
- 💡 **Entities**. Some plugins expose generic _entities_ - such a lights,
sensors, media players, switches, voice assistants etc. These entities can be
controlled through [the same generic
APIs](https://docs.platypush.tech/platypush/plugins/entities.html), emit [the
same types of
events](https://docs.platypush.tech/platypush/events/entities.html), can
be controlled from the same Web view or dashboard, and their state is
persisted across runs.

129
Installation.md Normal file

@ -0,0 +1,129 @@
# Installation
## System package manager installation
### Arch Linux
You can either install the
[`platypush`](https://aur.archlinux.org/packages/platypush) package (for the
latest stable version) or the
[`platypush-git`](https://aur.archlinux.org/packages/platypush-git) package
(for the latest git version) through your favourite AUR package manager. For
example, using `yay`:
```bash
$ yay platypush
# Or
$ yay platypush-git
```
The Arch Linux packages on AUR are automatically updated upon new git commits
or tags.
### Debian/Ubuntu
Currently the following releases are supported:
1. The current Debian `stable`
2. Debian `oldstable`
Ubuntu supported [to be added
soon](https://git.platypush.tech/platypush/platypush/issues/368).
- Add the Platypush APT key to your trusted keyring:
```
# wget -q -O \
/etc/apt/trusted.gpg.d/platypush.asc \
https://apt.platypush.tech/pubkey.txt
```
- Add the Platypush repository to your APT sources:
```
# wget -q -O \
/etc/apt/sources.list.d/platypush.list \
https://apt.platypush.tech/lists/platypush-<deb_version>-<branch>.list
```
Where:
- `deb_version` can be either *stable* (for the current Debian stable version) or
*oldstable* (for the previous Debian stable version)
- `branch` can be either *main* (for the latest releases) or *dev* (for a package
that is always in sync with the git version)
For example, to install the latest stable tags on Debian stable:
```
# wget -q -O \
/etc/apt/sources.list.d/platypush.list \
https://apt.platypush.tech/lists/platypush-stable-main.list
```
- Update your repos and install Platypush:
```
# apt update
# apt install platypush
```
### Fedora
RPM builds targeting the latest Fedora release are automatically built on every
push pipeline.
To install Platypush via RPM on Fedora:
- Add the Platypush RPM repository configuration to the package manager:
```
# yum config-manager --add-repo https://rpm.platypush.tech/platypush.repo
```
- Install Platypush, either the latest stable release or the rolling release
updated on every commit to the main branch:
```
# yum install platypush
# Or
# yum install platypush-git
```
## `pip`
```bash
$ pip install platypush
```
Or, for the latest git version:
```bash
# Official repo
$ pip install git+https://git.platypush.tech/platypush/platypush
# Github mirror
$ pip install git+https://github.com/blacklight/platypush
```
## Docker
```bash
$ git clone https://git.platypush.tech/platypush/platypush.git
$ cd platypush
# Copy .env.example to .env and edit docker-compose.yml if required.
# In particular, you may want /etc/platypush and /var/lib/platypush
# to point to directories on your hosts
$ docker compose up
```
Note that the default `Dockerfile` uses Alpine, but in `docker-compose.yml` you
can also specify an alternative `Dockerfile` - Debian, Ubuntu and Fedora are
supported.
## Manual installation
```shell
$ git clone https://git.platypush.tech/platypush/platypush.git
$ cd platypush
$ pip install .
```

96
Plugins-installation.md Normal file

@ -0,0 +1,96 @@
# Plugins installation
All the plugins included in the main repo will be available once you have
installed the core platform.
However, some plugins may require extra (optional) dependencies. You have
several ways of installing those dependencies:
## `pip`
You can install extra dependencies via pip extras:
```shell
pip install 'platypush[plugin1,plugin2,...]'
```
For example:
```shell
pip install 'platypush[light.hue,music.mpd,rss]'
```
Will install Platypush with the dependencies for the `light.hue`, `music.mpd`
and `rss` plugins.
## Web interface
Plugins can be installed from the Web interface too. Navigate to the
_Extensions_ entry in the sidebar, select the extension that you want to install,
select the _Install_ tab and click _Install_.
![Screenshot of the extensions installation Web
view](https://platypush-static.s3.nl-ams.scw.cloud/screenshots/install-ui-screenshot.jpg)
This section also includes the _Configuration_ tab, with a ready-to-paste
configuration snippet template for that plugin, as well as a documentation page
that includes all the actions supported by a given plugin and the events it
triggers.
## Docker
If you already have the base installation of Platypush on your machine, and you
have a configuration file with a custom set of integrations, then you may opt
to generate a custom Docker image from your configuration file, with all the
extra dependencies configured, using the `platydock` command.
The following command:
```shell
platydock -c /path/to/your/config.yaml -d platypush-test
```
Will create a Platypush Docker image for a device with ID `platypush-test`,
with all the requirements for the additional integrations listed in
`config.yaml`.
You can pass the `--print` option if you just want to print the content of the
output `Dockerfile` instead of generating the image.
By default the image will use Alpine Linux as a base. You can use the
`-i`/`--image` to specify another supported base image - `ubuntu`, `debian` or
`fedora`.
## Virtual environment
If you already have the base installation of Platypush on your machine, and you
have a configuration file with a custom set of integrations, then you may opt
to generate a custom virtual environment from your configuration file, with all
the extra dependencies configured, using the `platyvenv` command.
The following command:
```bash
platyvenv -c /path/to/your/config.yaml -o /path/to/your/venv
```
Will create a new virtual environment under `/path/to/your/venv` using the
specified `config.yaml` to determine which optional dependencies should be installed.
You can then run Platypush after activating your new environment:
```bash
source /path/to/your/venv/bin/activate
platypush -c /path/to/your/config.yaml
```
## Manual installation
The [plugin/backend documentation](https://docs.platypush.tech) reports all the
dependencies required by each plugin, as well as the commands to install them
on multiple platforms.
If you want to customize your installation, or if you need to install
dependencies for a plugin that requires some manual steps, you can check out
any plugin-specific installation steps from its documentation.

@ -1,942 +0,0 @@
* [MPD/Mopidy](#mpd-mopidy-support)
* [Web interface](#web-interface)
* [Start playback via HTTP request](#start-playback-via-http-request)
* [Search and play songs and albums using the integrated voice assistant](#search-and-play-songs-and-albums-using-the-integrated-voice-assistant)
* [Redis plugin](#redis-plugin)
* [Video and media](#video-and-media-support)
* [Philips Hue lights](#philips-hue-lights-support)
* [Switch plugins](#switch-plugins)
* [Belkin WeMo Switch](#belkin-wemo-switch)
* [TP-Link smart plugs](#tp-link-smart-plugs)
* [Switchbot](#switchbot)
* [Text-to-Speech](#text-to-speech-support)
* [Shell plugin](#shell-plugin)
* [HTTP requests plugin](#http-requests-plugin)
* [Database plugin](#database-plugin)
* [Variables](#variables)
* [Google Assistant plugin](#google-assistant-plugin)
* [Calendar plugin](#calendar-plugin)
* [Sensor plugins](#sensor-plugins)
* [MCP3008](#mcp3008)
* [Distance](#distance)
* [Serial](#serial)
* [Build a robot with the ZeroBorg plugin](#zeroborg)
* [MIDI plugin](#midi-plugin)
* [MQTT plugin](#mqtt-plugin)
* [Pushbullet plugin](#pushbullet-plugin)
* [Kafka plugin](#kafka-plugin)
* [IFTTT plugin](#ifttt-plugin)
* [Adafruit IO plugin](#adafruit-io-plugin)
* [Autoremote plugin](#autoremote-plugin)
* [Kodi plugin](#kodi-plugin)
* [Torrent plugin](#torrent-plugin)
* [Weather plugin](#weather-plugin)
A couple of plugins are available out of the box with Platypush under `plugins/`. Some of them may require extra Python or system dependencies. This page will show you some of the available plugins and some possible use cases, although the only real limit to how to connect things together is just your own imagination. This is not intended to be a complete reference of all the available plugins with their supported methods and dependencies, please refer to our [ReadTheDocs](https://docs.platypush.tech/en/latest/plugins.html) page for a more complete reference.
# MPD/Mopidy support
[Plugin reference](https://docs.platypush.tech/en/latest/platypush/plugins/music.mpd.html).
[MPD](https://musicpd.org/) is an application that allows to manage and play your music collection through a scalable client/server model. You can have your server running on a machine with a hard drive stuffed with mp3s, and access your collection from anywhere using a big family of compatible command line, graphical, web or mobile clients. [Mopidy](https://www.mopidy.com/) is an evolution of MPD that allows you to access music content from multiple sources through a wide set of plugins - e.g. Spotify, SoundCloud, Deezer, Pandora, TuneIn, Google Music.
Platypush can be a client for your MPD/Mopidy music server, allowing you to search for music, control your queue and the playback upon requests or events.
Configuration:
```yaml
music.mpd:
host: localhost
port: 6600
```
It's advised to turn on the [MPD/Mopidy backend](backends#mpd-mopidy-backend) as well if you want to receive live events on your playback status.
# Web interface
If you have enabled the HTTP backend, you can already point your browser to `http://hostname:8008` and you would see a new tab named `music.mpd` in your control panel. You can browse your music from here, modify the current playlist, search for music and control the playback state. If you're using mopidy and you have some plugins configured (Spotify, SoundCloud, TuneIn...), you'll be able to control music from your cloud accounts from here as well. The interface will look [like this](https://i.imgur.com/HbpDL5K.png).
Let's take a look at a couple more use cases.
## Start playback via HTTP request
Simple use case to get started, start playing the current track through a cURL command:
```shell
curl -XPOST -H 'Content-Type: application/json' \
-d '{"type":"request", "target":"hostname", "action":"music.mpd.play"}' \
http://hostname:8008/execute
```
## Search and play songs and albums using the integrated voice assistant
If you have configured already the [voice assistant backend](Backends#assistant-backend), you can configure an event hook to play a song or an album whenever you say _play [item] by [artist]_.
First configure an event hook for it that will invoke a new synchrounous procedure:
```yaml
event.hook.SearchSongVoiceCommand:
if:
type: platypush.message.event.assistant.SpeechRecognizedEvent
phrase: "play ${title} by ${artist}"
then:
-
action: procedure.search_and_play_song
```
Then define the `search_and_play_song` procedure:
```yaml
procedure.sync.search_and_play_song:
-
# Clear the content of the current playlist
action: music.mpd.clear
-
# Disable shuffle mode
action: music.mpd.random
args:
value: 0
-
# Perform an mpd search. Note how we re-used the "title" and "artist"
# context variables parsed from the voice assistant event
action: music.mpd.search
args:
filter:
- artist
- ${artist}
- any
- ${title}
-
# Play the first of the search results above. Note how we re-used the
# "output" context variable, that contains the output of the previous
# action (an array in the case of `music.mpd.search`), to get the file id
# of the first result and pass it to `music.mpd.play`
action: music.mpd.play
args:
resource: ${output[0]['file']}
```
# Redis plugin
[Plugin reference](https://docs.platypush.tech/en/latest/platypush/plugins/redis.html)
[Redis](https://redis.io/) is an in-memory data structure store. It can be used as an in-memory store, as a cache or as a message broker. Obviously, latter case is quite interesting when it comes to Platypush :)
Redis is also used heavily by some of our plugins and backends to communicate and synchronize properly, especially if the components live in different processes (for example, the web server) and need to exchange messages with the main process. Redis is a perfect candidate for the purpose. It has a low latency, and the server is lightweight enough to run even on a Raspberry Pi Zero without issues. It's therefore **strongly recommended** to enable both the Redis plugin and [backend](Backends#redis-backend) (and install a local Redis server) to take full advantage of Platypush features.
The Redis plugin has a quite simple API to deliver messages:
```shell
curl -XPOST -H 'Content-Type: application/json' \
-d '{"type":"request", "target":"hostname", "action":"redis.send_message", "args": {"queue":"my_queue_name", "msg":"My message"}}' \
http://hostname:8008/execute
```
# Video and media support
[Plugin reference](https://docs.platypush.tech/en/latest/platypush/plugins/video.omxplayer.html)
Platypush comes with support for video media, including YouTube, local media and torrents. It's quite handy to turn a RaspberryPi into a full-blown media server or a Chromecast on steroids, voice controls included.
```yaml
video.omxplayer:
media_dirs:
# Local media content will be search in these directories
- /mnt/exthd/media/movies
- /mnt/exthd/media/series
# Torrents and other remote content will be downloaded to this directory
download_dir: /mnt/exthd/downloads
# Torrent bind ports override (default ports: 6881, 6891)
torrent_ports:
- 7881
- 7891
args:
# You can specify any extra option passed to the OMXPlayer (https://elinux.org/Omxplayer) executable here
- -o
- alsa # or hdmi
- --subtitles
- /home/user/subs
- -orientation
- 90
# ...
```
Some cURL examples:
## Play local media
```shell
curl -XPOST -H 'Content-Type: application/json' \
-d '{"type":"request", "target":"hostname", "action":"video.omxplayer.play", "args": {"resource":"file:///path/video.mp4"}}' \
http://hostname:8008/execute
```
## Play media by URL
```shell
curl -XPOST -H 'Content-Type: application/json' \
-d '{"type":"request", "target":"hostname", "action":"video.omxplayer.play", "args": {"resource":"http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_60fps_stereo_abl.mp4"}}' \
http://hostname:8008/execute
```
## Play a YouTube video
**Note**: it requires [youtube-dl](https://rg3.github.io/youtube-dl/) installed on your system.
```shell
curl -XPOST -H 'Content-Type: application/json' \
-d '{"type":"request", "target":"hostname", "action":"video.omxplayer.play", "args": {"resource":"https://www.youtube.com/watch?v=XCAQ0MZTJmg"}}' \
http://hostname:8008/execute
```
## Play a torrent content by Magnet link
**Note**: it requires [python-libtorrent](https://pypi.org/project/python-libtorrent/).
This will first download the torrent content to `download_dir` and then play it (streaming while downloading isn't currently supported, the few tests I've run seem to stress the Raspberry Pi too much).
```shell
curl -XPOST -H 'Content-Type: application/json' \
-d '{"type":"request", "target":"hostname", "action":"video.omxplayer.play", "args": {"resource":"magnet:?magnet_uri"}}' \
http://hostname:8008/execute
```
## Search and play videos from any source
The OMXPlayer plugin comes with a powerful search feature that allows you to search and play videos from any available source (currently supported: local files, YouTube and torrent sources).
An example:
```shell
curl -XPOST -H 'Content-Type: application/json' \
-d '{"type":"request", "target":"hostname", "action":"video.omxplayer.search", "args":{"query":"A Space Odyssey"}}' \
http://hostname:8008/execute
```
Output:
```json
{
"type":"response",
"response": {
"output": [
{
"url": "file:///mnt/hd/media/movies/A Space Odyssey/2001 - A Space Odyssey.avi",
"title": "2001 - A Space Odyssey.avi"
},
{
"url": "magnet:?xt=urn:...",
"title": "2001: A Space Odyssey"
},
{
"url": "https://www.youtube.com/watch?v=oR_e9y-bka0",
"title": "2001: A SPACE ODYSSEY - Trailer"
}
]
}
}
```
You can also pass `"queue_results":true` if you want to add the search results to the current play queue, or `"autoplay":true` if you want to automatically play the first search result.
If you enabled the plugin you'll also have a nice [video search and control tab](https://i.imgur.com/820732a.png) added to your web panel.
# Philips Hue lights support
[Plugin reference](https://docs.platypush.tech/en/latest/platypush/plugins/light.hue.html)
Control your [Philips Hue](https://www2.meethue.com/en-us) lights with custom requests and events triggered by your devices.
```yaml
light.hue:
bridge: bridge_name_or_ip
# If no lights or groups to actions are specified in
# the action or in the default configuration, all the
# available lights will be targeted by the commands
lights:
- Hall
- Living Room Left
- Living Room Right
- Garage
groups:
- Bedroom
- Kitchen
```
If you enabled the plugin, you'll also have a [lights control tab](https://i.imgur.com/HwsNsms.png) on your web panel.
Some cURL examples:
## Turn on the kitchen lights
```shell
curl -XPOST -H 'Content-Type: application/json' \
-d '{"type":"request", "target":"hostname", "action":"light.hue.on", "args": {"groups":"Kitchen"}}' \
http://hostname:8008/execute
```
## Set the sunset scene on the living room lights
```shell
curl -XPOST -H 'Content-Type: application/json' \
-d '{"type":"request", "target":"hostname", "action":"light.hue.scene", "args": {"name":"Sunset", "groups":"Living Room"}}' \
http://hostname:8008/execute
```
## Blink your living room lights for five seconds
```shell
curl -XPOST -H 'Content-Type: application/json' \
-d '{"type":"request", "target":"hostname", "action":"light.hue.animate", "args": {"animation":"blink", "groups":"Living Room", "transition_seconds":0.5, "duration": 5}}' \
http://hostname:8008/execute
```
# Switch plugins
You can use Platypush to control a variety of smart plugs and switches in your smart home setup.
By enabling any of these plugins (even creating an empty config entry just with `disabled: False` if no confi is required), you'll also get the associated switch tab in the web panel where you can control your devices through a web interface.
## Belkin WeMo Switch
[Plugin reference](https://docs.platypush.tech/en/latest/platypush/plugins/switch.wemo.html)
The [WeMo Switch](http://www.belkin.com/us/p/P-F7C027/) is smart Wi-Fi controlled switch that can automate the control of any electric appliance - fridges, lights, coffee machines...
Example:
```shell
curl -XPOST -H 'Content-Type: application/json' \
-d '{"type":"request", "target":"hostname", "action":"switch.wemo.on", "args": {"device":"192.168.1.2"}}' \
http://hostname:8008/execute
```
## TP-Link smart plugs
[Plugin reference](https://docs.platypush.tech/en/latest/platypush/plugins/switch.tplink.html)
This plugin allows you to interact with TP-Link smart plugs like the [HS100](https://www.tp-link.com/us/products/details/cat-5516_HS100.html).
Example:
```shell
curl -XPOST -H 'Content-Type: application/json' \
-d '{"type":"request", "target":"hostname", "action":"switch.tplink.on", "args": {"device":"192.168.1.3"}}' \
http://hostname:8008/execute
```
## Switchbot
[Plugin reference](https://docs.platypush.tech/en/latest/platypush/plugins/switch.switchbot.html)
[Switchbot](https://www.switch-bot.com/) is a quite interesting smart home device as it aims to make non-smart devices "smart". It consists in a box with a small bluetooth-controlled mechanical arm that can apply enough pressure to press any button - you can use it to control through Platypush the buttons on your coffee machine, your dishwasher or your old tv for instance.
Note that, unlike the other plugins, Switchbot requires root privileges as it needs to send a raw bluetooth low-energy message using GATT to communicate with the Switchbot. It means that if you're running Platypush as an unprivileged user (and you should!!), you'll need for now to make a `sudo` rule for it. I plan to make it better/safer though.
Example:
```shell
curl -XPOST -H 'Content-Type: application/json' \
-d '{"type":"request", "target":"hostname", "action":"switch.switchbot.press", "args": {"device":"00:11:22:33:44:55"}}' \
http://hostname:8008/execute
```
# Text-to-Speech support
If `mplayer` is installed, you can trigger a machine to say custom phrases in any language through the `tts` plugin. Quite cool if you want to let your RaspberryPi to automatically read you out loud the news or when you get a notification on your phone.
```yaml
tts:
lang: en-gb # Default language
```
Example:
```shell
curl -XPOST -H 'Content-Type: application/json' \
-d '{"type":"request", "target":"hostname", "action":"tts.say", "args": {"phrase":"Connecting people"}}' \
http://hostname:8008/execute
```
# HTTP requests plugin
[Plugin reference](https://docs.platypush.tech/en/latest/platypush/plugins/http.request.html)
You can programmatically send HTTP requests in your event hooks and procedures through the `http.request` plugin.
Example:
```yaml
event.hook.TellMeTheWeather:
if:
type: platypush.message.event.assistant.SpeechRecognizedEvent
phrase: tell me the weather
then:
-
action: http.request.get
args:
url: https://your-weather-provider.com/api/weather
params:
- latlng: 0.0,0.0
headers:
- X-Auth-Token: YOUR_TOKEN
```
# Database plugin
[Plugin reference](https://docs.platypush.tech/en/latest/platypush/plugins/db.html)
You can use Platypush to programmatically insert, update, delete or select database records in your requests, procedures or event hooks. [SQLAlchemy](https://www.sqlalchemy.org/) is used as a backend, therefore Platypush support any engine schema natively supported by SQLAlchemy (SQLite, PostgreSQL, MySQL, Oracle...).
Configuration:
```yaml
db:
engine: sqlite:////home/user/temperature.db # (Optional) default engine; can still be overridden in your db calls
```
```yaml
procedure.sync.log_temperature:
-
action: http.request.get
args:
url: http://your-local-server/api/sensors/temperature # Example JSON output: {"temperature":21.0}
-
action: db.insert
# engine: mysql://.... # Optionally, you can override the default engine to point to another db
table: apartment_stats_temp
records:
-
temperature: ${temperature}
```
[update](https://docs.platypush.tech/en/latest/platypush/plugins/db.html#platypush.plugins.db.DbPlugin.update) and [delete](https://docs.platypush.tech/en/latest/platypush/plugins/db.html#platypush.plugins.db.DbPlugin.delete) methods are also available.
If you want to programmatically select from a table via HTTP call (returns a list of dictionaries):
```shell
curl -XPOST -H 'Content-type: application/json' \
-d 'msg={"type":"request","target":"hostname","action":"db.select", "args": {"query":"select * from temperature", "engine":"sqlite:////home/user/temperature.db"}}' \
http://hostname:8008
```
```json
{
"type": "response",
"response": {
"output": [{"id": 1, "temperature": "21.0","created_at":"<timestamp>"}],
"errors": []
}
}
```
# Variables
[Plugin reference](https://docs.platypush.tech/en/latest/platypush/plugins/variable.google.html)
`variable` is a special plugin that allows to set, get and unset variables that you can reuse for instance within a procedure execution, to store states that will be used by some other pieces of logic, or share data with other programs.
**NOTE**: this plugin relies on the [database plugin](#database-plugin) to be configured. It will indeed store the variables on a local database to persist them across restart and easily share them with other processes.
Example that sets a variable named `AT_HOME`:
```shell
curl -XPOST -H 'Content-type: application/json' \
-d 'msg={"type":"request","target":"hostname","action":"variable.set", "args": {"AT_HOME":1}' \
http://hostname:8008
```
You can now build some custom logic in a synchronous procedure based on that value:
```yaml
procedure.sync.turn_lights_on_sunset:
-
action: variable.get
args:
name: AT_HOME
- if ${AT_HOME}:
- action: light.hue.on
```
And then unset it if you don't need it anymore (will delete the row):
```shell
curl -XPOST -H 'Content-type: application/json' \
-d 'msg={"type":"request","target":"hostname","action":"variable.unset", "args": {"name":"AT_HOME"}' \
http://hostname:8008
```
# Google Assistant plugin
[Plugin reference](https://docs.platypush.tech/en/latest/platypush/plugins/assistant.google.html)
If you have the Google Assistant backend configured, then you can use the assistant plugin to programmatically start or stop an interaction with the assistant. For instance, you can start a conversation without saying the "Ok, Google" hotword by pressing a Flic button, if you have the Flic backend configured:
```yaml
event.hook.FlicButtonStartConversation:
if:
type: platypush.message.event.button.flic.FlicButtonEvent
btn_addr: <button MAC address>
sequence:
- ShortPressEvent
then:
action: assistant.google.start_conversation
```
# Calendar plugin
[Plugin reference](https://docs.platypush.tech/en/latest/platypush/plugins/calendar.html)
You can use the calendar plugin to get your upcoming events or (*TODO*) change or create events on multiple data sources - Google Calendar, Facebook events calendar, remote ICal resource etc.
Example configuration:
```yaml
calendar:
calendars:
-
type: platypush.plugins.google.calendar.GoogleCalendarPlugin
-
type: platypush.plugins.calendar.ical.IcalCalendarPlugin
url: https://www.facebook.com/ical/u.php?uid=USER_ID&key=FB_KEY
```
Note the use of the Google Calendar plugin type. The Google Calendar plugin belongs to the family of Google plugins, which currently includes Calendar, GMail and Maps (to be expanded). Check the instruction on the [Google plugin reference](https://docs.platypush.tech/en/latest/platypush/plugins/google.html) on how to generate OAuth2 tokens for your Google account that these plugins can use.
# Sensor plugins
You can query sensors connect through multiple interfaces through Platypush.
## MCP3008
[Plugin reference](https://docs.platypush.tech/en/latest/platypush/plugins/gpio.sensor.mcp3008.html)
The [MCP3008](https://learn.adafruit.com/raspberry-pi-analog-to-digital-converters/mcp3008) is a very useful ADC converter that you can use to read data from multiple analog sources and transmit them to a digital device that doesn't support direct GPIO analog input (like the Raspberry Pi). You can use it instead of an Arduino or serial device if you want lower latency, or a more compact or cheaper solution.
An example configuration:
```yaml
gpio.sensor.mcp3008:
# In this configuration the MCP3008 is connected in software SPI mode,
# a better option if you don't have many GPIO pins available although
# it introduces a bit of latency compared to the hardware SPI mode.
# Here we specify the GPIO pin numbers where you connect your MCP3008
# interface. Specify spi_port and spi_device instead if you want to run
# in hardware SPI mode.
CLK: 15
MISO: 22
MOSI: 17
CS: 25
# Reference tension, usually 3.3V or 5V on a Raspberry
Vdd: 3.3
# Here you can link names and analog conversion functions to the
# analog sources connected to your MCP3008 pins.
channels:
# Channels are identified by MCP3008 pin number
0:
name: temperature
# The conversion function is a snippet of Python code that you can specify to
# convert the output voltage of your analog sensor (normalized between 0 and 255)
# into a human readable value (e.g. meters, Celsius degrees, km/h, % etc.).
# In this example we used a simple LM35DZ (http://www.ti.com/lit/ds/symlink/lm35.pdf)
# temperature sensors and specified a function to convert the analog value into
# a Celsius degrees value.
# Note that the analog value is denoted as `x` in the function definition.
conv_function: 'round(x*100.0, 2)' # T = Vout / (10 [mV/C])
1:
name: light
# Here we used a simple ALS-PT9
# (http://www.everlight.com/file/ProductFile/201407061531031645.pdf) light sensor,
# that comes with an internal 10 kΩ resistor. The detected luminosity (in lumen)
# is proportional to the current (in μA) on the resistor, the conversion function
# simply uses Ohm's law (I = V/R) to get the lumens from the analog voltage value.
conv_function: 'round(x*1000.0, 6)'
```
cURL example:
```shell
curl -XPOST -H 'Content-Type: application/json' \
-d '{"type":"request","target":"hostname","action":"gpio.sensor.mcp3008.get_measurement"}' \
http://hostname:8008/execute
```
Output:
```json
{
"type": "response",
"response": {
"output": {
"temperature": 22.42,
"light": 50.5
},
"errors": []
}
}
```
If you want to poll your sensors periodically and get a Platypush event whenever their values change then you may want to enable the [MCP3008 backend](backends#mcp3008) as well.
## Distance
[Plugin reference](https://docs.platypush.tech/en/latest/platypush/plugins/gpio.sensor.distance.html)
I've tested this plugin quite successfully with the [HC-SR04](https://cdn.sparkfun.com/datasheets/Sensors/Proximity/HCSR04.pdf) ultrasound distance sensor, an inexpensive and accurate options for anyone playing with robotics.
The sensor works like an ultrasound radar - it sends a pulse controlled by the `trigger_pin`, and detects the returning signal through the `echo_pin`. Known the average speed of sound in air, it's relatively easy to get the distance of the nearest obstacle. All you have to do is connect the trigger and echo pins to the GPIO interface of your device. Note: take into account that the HC-SR04 works at 5V. If you're interfacing it with a device that works with a lower voltage (the GPIO logic of a Raspberry usually runs at 3.3V) you may want to use a voltage converter or set up your [cheap voltage divider](https://learn.sparkfun.com/tutorials/voltage-dividers) with two resistors to prevent damage to your GPIO interface.
Example configuration:
```yaml
gpio.sensor.distance:
trigger_pin: 13
echo_pin: 14
```
cURL example:
```shell
curl -XPOST -H 'Content-Type: application/json' \
-d '{"type":"request","target":"hostname","action":"gpio.sensor.distance.get_measurement"}' \
http://hostname:8008/execute
```
Output (in millimiters):
```json
{
"type": "response",
"response": {
"output": 313.17,
"errors": []
}
}
```
# Serial
[Plugin reference](https://docs.platypush.tech/en/latest/platypush/plugins/serial.html)
Use the serial plugin if you want to read analog data through serial interface (typical use cases: Arduino or compatible devices, serial ports etc.).
It's advised if the serial device returns data in JSON format (if you use Arduino you can use the [ArduinoJson](https://github.com/bblanchon/ArduinoJson) library to write JSON to the serial interface).
Example configuration:
```yaml
serial:
device: /dev/ACM0
baud_rate: 9600
```
If you're using udev to dynamically assign your device names, you might consider configuring [udev rules](https://wiki.archlinux.org/index.php/udev#About_udev_rules) to prevent the "dance" of the device names when they are disconnected and reconnected (and renamed to ACM0, ACM1, ACM2, or USB0, USB1 etc.).
cURL example:
```shell
curl -XPOST -H 'Content-Type: application/json' \
-d '{"type":"request", "target":"hostname", "action":"serial.get_measurement"}' \
http://hostname:8008/execute
```
Output:
```json
{
"type": "response",
"response": {
"output": {
"smoke": 0,
"temperature": 23.90402,
"humidity": 39.08141
},
"errors": []
}
}
```
Once you enable the `serial` or `mcp3008` plugin and backend you'll be able to monitor the state of your sensors or analog sources through a new tab on the [web panel](https://i.imgur.com/yQYGuhW.png).
# ZeroBorg
[Plugin reference](https://docs.platypush.tech/en/latest/platypush/plugins/gpio.zeroborg.html)
The [ZeroBorg](https://www.piborg.org/motor-control-1135/zeroborg) is a nice piece of circuitry that pairs quite well with Raspberry Pi and compatible devices. It comes with four outputs that you can use to control motors and servos by applying a current. It also comes with an infrared sensor that you can use to detect inputs from an IR remote, Platypush provides a backend to read events from this sensor as well.
The ZeroBorg plugin also allows you to interact with sensors data to control your motors.
Here's an example configuration I use for my 4WD robot controlled by a Raspberry Pi Zero paired with a ZeroBorg device. [MagPi](https://raspberrypi.org/magpi-issues/MagPi51.pdf) provides detailed instructions on how to build such a robot. Note that the calibration values came after numerous tests to get the robot to move straight. Your values may vary depending on your set up, the load of the connected components, the weight and how it is distributed.
```yaml
gpio.zeroborg:
directions:
up:
motor_1_power: 0.4821428571428572
motor_2_power: 0.4821428571428572
motor_3_power: -0.6707142857142858
motor_4_power: -0.6707142857142858
down:
motor_1_power: -0.4821428571428572
motor_2_power: -0.4821428571428572
motor_3_power: 0.825
motor_4_power: 0.825
left:
motor_1_power: -0.1392857142857143
motor_2_power: -0.1392857142857143
motor_3_power: -1.0553571428571429
motor_4_power: -1.0553571428571429
right:
motor_1_power: 1.0017857142857143
motor_2_power: 1.0017857142857143
motor_3_power: 0.6214285714285713
motor_4_power: 0.6214285714285713
auto:
sensors:
-
plugin: "gpio.sensor.distance",
threshold: 400.0,
timeout: 2.0,
above_threshold_direction: "up",
below_threshold_direction: "left"
```
Note that the special direction `auto` can contain a configuration that allows your device to move autonomously based on the inputs it gets from some sensors. In this case, I set the sensors configuration (a list) to periodically poll a GPIO-based ultrasound distance sensor plugin. `timeout` says after how long a poll attempt should fail. The plugin package is specified through `plugin` (`gpio.sensor.distance`) in this case, note that the plugin must be configured as well in order to work). The `threshold` value says around which value your logic should trigger. In this case, threshold=400 (40 cm). When the distance value is above that threshold (`above_threshold_direction`), then go "up" (no obstacles ahead). Otherwise (`below_threshold_direction`), turn "left" (avoid the obstacle). Feel free to build a more robust autopilot by adding more sensors :)
Once you enable the plugin, you can also drive the robot through the [control tab](https://i.imgur.com/2n3amcs.png) that will be available in the control panel. It also supports directional keys and spacebar long-press to toggle the automatic pilot (click inside of the tab first to focus it).
cURL example to drive your robot up and stop after 5 seconds:
```shell
curl -XPOST -H 'Content-Type: application/json' \
-d '{"type":"request", "target":"hostname", "action":"gpio.zeroborg.drive", "args": {"direction":"up"}}' \
http://hostname:8008/execute
sleep 5
curl -XPOST -H 'Content-Type: application/json' \
-d '{"type":"request", "target":"hostname", "action":"gpio.zeroborg.stop"}' \
http://hostname:8008/execute
```
# MIDI plugin
[Plugin reference](https://docs.platypush.tech/en/latest/platypush/plugins/midi.html)
Some of the musicians who ended up on this page probably know the [MIDI protocol](http://www.indiana.edu/~emusic/361/midi.htm). Platypush allows you to register a custom MIDI device that can send messages to the OS MIDI sequencer. It's like tricking your machine to think that Platypush is a MIDI keyboard or drum machine. You can for instance create some instrument tracks in LMMS or FruityLoops, link them to your Platypush MIDI source, and play notes through any kind of events that Platypush can process - you can use a Leap Motion to play notes by just waving your hand, use a proximity sensor, play notes with switches and buttons, and so on, the only limit being your creativity.
An example that sends the C4 (middle C, MIDI note 0x60 or 96 in decimal) at medium velocity (64) for 1 second:
```shell
curl -XPOST -H 'Content-Type: application/json' \
-d '{"type":"request", "target":"hostname", "action":"midi.play_note", "args": {"note":96, "velocity":64, "duration":1}}' \
http://hostname:8008/execute
```
You can also send raw MIDI messages through the [`send_message`](https://docs.platypush.tech/en/latest/platypush/plugins/midi.html#platypush.plugins.midi.MidiPlugin.send_message) action.
You can also listen for MIDI events and run actions based on them by using the MIDI backend.
# MQTT plugin
[Plugin reference](https://docs.platypush.tech/en/latest/platypush/plugins/mqtt.html)
[MQTT](http://mqtt.org/) is a message-queue based protocol to exchange messages across devices. MQTT implementations like Paho MQTT, Mosquitto and Adafruit IO are quite popular for communication across IoT devices. You can interact with an MQTT service through this plugin to build any arbitrary complex message-based logic across your Platypush devices. The asynchronous nature of MQTT makes it a better candidate over the HTTP plugin to deliver messages to other devices if you don't mind much about the response. An HTTP call to the Flask server to turn on the lights, for example, requires the sender to wrap the request into an HTTP message, the receiver to react immediately on that while the sender is synchrounously waiting for an answer, execute the light control action (which might take a while), and then wrap the response into an HTTP message back to the sender. Compare it instead with a message-queue based implementation, where you send the message to a queue where the receiver is listening and as soon as the receiver is available it will pick it up - no need for the sender to hang for the response. Also, the message queue doesn't require the receiver to be directly accessible (through IP/hostname and port) to the sender, as it happens in an HTTP interaction. The sender can send a message to the queue, hosted by an MQTT server, and as long as the receiver (or any receiver) is listening on the same queue, the message will be picked up.
Sending a message over an MQTT backend requires an MQTT `host` server and `port` (default: 1883), the `message` to be sent (anything that implements `__str__`), and the `topic` to send the message to - see it like a channel where one or multiple devices might be listening.
Example:
```shell
curl -XPOST -H 'Content-Type: application/json' \
-d '{"type":"request", "target":"hostname", "action":"mqtt.send_message", "args": {"topic":"my_topic", "server":"my_mqtt_server", "port":1883, "message":"It worked"}}' \
http://hostname:8008/execute
```
You can (or should) of course also use the [MQTT backend](Backends#mqtt-backend). By doing so you can send messages across that Platypush devices containing action requests, events or responses. You can create routing rules for messages, or propagate the events you receive on a device over another one - for example I've configured a rule for all of my devices with connected sensors to route their `SensorDataChangeEvent` events over MQTT to a hub device, where I can monitor from a single web panel the values of all my sensors around the house. Another good use case for MQTT is to send messages from your Android devices to your Platypush devices. The same developer of Taker has recently developed [Join](https://joaoapps.com/join/), that allows you among the other things to programmatically send messages over MQTT. You can then create tasks that send messages to Platypush when you get a call, receive an sms, connect to some WiFi network or enter some area. You can also create Join action to send custom commands to your Platypush devices from a tap on your phone.
# Pushbullet plugin
[Plugin reference](https://docs.platypush.tech/en/latest/platypush/plugins/pushbullet.html)
[Pushbullet](https://www.pushbullet.com/) is a quite cool piece of software to keep your devices in sync, exchange messages, synchronize clipboards and sms across devices, access remote files, and so on. You can install it as an Android or iPhone app, as a browser extension, or use it from your browser directly. It can be quite powerful if paired with Platypush - and it has the first backend I have developed as well. You can use the plugin in pair with the backend to do things like:
* Send URLs, text and Platypush messages to your Android devices
* Trigger Platypush actions through your Android device, wherever you are
* Synchronize the clipboards
* Synchronize your phone notifications to your Raspberry Pi
* Send and receive pictures
* ...and so on
Note that you need to enable the [Pushbullet backend](backends#pushbullet-backend) in order to use this plugin.
Example command to send a file to all of your connected Pushbullet devices:
```shell
curl -XPOST -H 'Content-Type: application/json' \
-d '{"type":"request", "target":"hostname", "action":"pushbullet.send_file", "args": {"filename":"/home/user/myphoto.jpg"}}' \
http://hostname:8008/execute
```
# Kafka plugin
[Plugin reference](https://docs.platypush.tech/en/latest/platypush/plugins/kafka.html)
Use this plugin if you want to synchronize messages to your devices over a Kafka message instance. Some use cases:
* Send asynchronous messages to other Kafka nodes
* Send raw messages to other Platypush devices and build event hooks on them
* Send directly JSON-formatted requests, responses or events to other Platypush devices
The configuration is quite straightforward:
```yaml
kafka:
server: host:9092 # Default hostname and port where the Kafka broker runs. If None (default), then
# you will either have to specify on `send_command`, or it will use the Kafka
# backend configured server, if the Kafka backend is running
```
If you want to use Kafka to deliver messages to other Platypush devices keep in mind that their [Kafka backends](backends#kafka-backend) will listen by default on a queue named `platypush.<device_id>`.
Example to send a text Kafka message:
```shell
curl -XPOST -H 'Content-Type: application/json' \
-d '{"type":"request", "target":"hostname", "action":"kafka.send_message", "args": {"msg":"My first Kafka message over Platypush"}}' \
http://localhost:8008/execute
```
# IFTTT plugin
[Plugin reference](https://docs.platypush.tech/en/latest/platypush/plugins/ifttt.html)
[IFTTT](https://ifttt.com) is a very versatile cloud service to automate tasks and trigger actions on particular events. IFTTT comes with some channels that may not be available on Platypush (e.g. trigger events on your Android or iOS device, send Telegram messages, create Trello tasks etc.), or you've been an IFTTT user already and you might want to integrate Platypush with your existing IFTTT configuration.
You can use Platypush to trigger IFTTT events using the [Maker API](https://ifttt.com/maker_webhooks).
Steps:
1. Click on the link above and sign in with your IFTTT account
2. Click on _Documentation_ and copy the key associated to your account
3. Configure the Platypush plugin:
```yaml
ifttt:
ifttt_key: YOUR_KEY
```
4. In this example suppose that you want to automatically send a Telegram message to yourself with some content. Create an IFTTT applet having _Webhooks_ as a trigger channel and specify `telegram_send` as `event_name`
5. Specify Telegram as action channel and select the "Send message" action. Specify your own number and select the ingredient `value1` as body
6. Start Platypush and try to send a message to yourself:
```shell
curl -XPOST -H 'Content-Type: application/json' \
-d '{"type":"request", "target":"hostname", "action":"ifttt.trigger_event", "args": {"event_name":"telegram_send", "values":["IT WORKED"]}}' \
http://hostname:8008/execute
```
# Adafruit IO plugin
[Plugin reference](https://docs.platypush.tech/en/latest/platypush/plugins/adafruit.io.html)
[Adafruit IO](https://io.adafruit.com) is a message-based cloud storage for numerical values. You can use it to store reads from your sensors, see the values on the Adafruit dashboard wherever you are, trigger IFTTT rules if a value goes below or above a threshold and so on.
First create an account on Adafruit and get your key from [Adafruit IO](https://io.adafruit.com). Then configure the Platypush plugin:
```yaml
adafruit.io:
username: your_user
key: your_key
```
Then you can create a feed on the web interface and configure for instance an event hook that forwards a new read from your temperature sensor to your remote feed:
```yaml
event.hook.OnSensorData:
if:
type: platypush.message.event.sensor.SensorDataChangeEvent
then:
action: procedure.OnSensorData
procedure.sync.OnSensorData:
- if ${'temperature' in data}:
-
action: adafruit.io.send
args:
feed: temperature
value: ${data.get('temperature')}
```
# Autoremote plugin
[Plugin reference](https://docs.platypush.tech/en/latest/platypush/plugins/autoremote.html)
[Tasker](https://tasker.joaoapps.com/) is a really powerful app for Android that allows you to automate everything on your system, create tasks and triggers to control app, settings, notifications and so on. Some of its features are quite similar to those offered by Platypush, but it runs on Android while Platypush will likely run on your favourite Linux-based computer or mini-computer :)
It'd be amazing to make those two worlds communicate, for instance by sending notifications to your phone about the media that plays on your Raspberry or triggering an alert on your phone if you're not at home and your Raspberry camera detected some motion.
It's relatively easy to send messages from Tasker to Platypush. Pick your favourite Tasker plugin (Pushbullet, MQTT or Node-Red over [Join](https://play.google.com/store/apps/details?id=com.joaomgcd.join), any web request plugin...) and you can start creating Tasker routines that send messages to Platypush over the configured channel.
In order to send messages from Platypush to Tasker instead you can leverage [AutoRemote](https://joaoapps.com/autoremote/), a Tasker plugin that allows you to send custom messages and notifications to an Android device through a multitude of ways (Chrome extension, web API, Tasker integration etc.).
All you need is to install the app on your Android device and follow the steps to register your device. You'll get a unique URL for your device. If you open it you'll notice a `key=` section in the URL. That's the key you'll need in order to configure the AutoRemote plugin on Platypush:
```yaml
autoremote:
devices:
OnePlus6: AUTOREMOTE_KEY_1
PixelC: AUTOREMOTE_KEY_2
Nexus5: AUTOREMOTE_KEY_3
```
Note that you can install and configure AutoRemote on multiple Android devices and configure the Platypush plugin with their keys and a unique name in order to send messages to them. You can now use the [send_message](https://docs.platypush.tech/en/latest/platypush/plugins/autoremote.html#platypush.plugins.autoremote.AutoremotePlugin.send_message) and [send_notification](https://docs.platypush.tech/en/latest/platypush/plugins/autoremote.html#platypush.plugins.autoremote.AutoremotePlugin.send_notification) to respectively send messages and notifications to your device and build Tasker routines on them.
# Kodi plugin
[Plugin reference](https://docs.platypush.tech/en/latest/platypush/plugins/media.kodi.html)
[Kodi](https://kodi.tv) is a popular media player and media center. It's compatible with plenty of devices and OSes (including Android, Linux, MacOS, Windows and several embedded players) and there are tons of add-ons available, for anything that goes from media library managemnt to subtitles to radio and tv channels to YouTube.
Kodi also comes with a powerful API, and Platypush can leverage it by allowing you to control your player. You can for instance build a DIY remote control system for your media center by using any IR remote and any IR sensor to navigate the Kodi interface or control the playback status. Or create rules to automatically add to your library and start playing a torrent file when it's done downloading. The [plugin reference](https://docs.platypush.tech/en/latest/platypush/plugins/media.kodi.html) is quite rich and the available methods should mirror almost at 100% what is provided by the Kodi API.
# Torrent plugin
[Plugin reference](https://docs.platypush.tech/en/latest/platypush/plugins/torrent.html)
You can use Platypush to search, download and manage torrents. Example configuration:
```yaml
torrent:
download_dir: ~//Downloads # Torrents download directory
torrent_ports: # Pair of ports used by the torrent tracker (default: 6881 and 6891)
- 6881
- 6891
```
Refer to the [plugin reference](https://docs.platypush.tech/en/latest/platypush/plugins/torrent.html) for documentation on the available methods and their interface.
Note also that the `download` method will generate few [types of events](https://docs.platypush.tech/en/latest/platypush/events/torrent.html) and you can build triggers on those, for instance an event hook that automatically starts your favourite player when a movie has done downloading or a notification for the progress of your downloads synced to your mobile device.
* [TorrentDownloadStartEvent](https://docs.platypush.tech/en/latest/platypush/events/torrent.html#platypush.message.event.torrent.TorrentDownloadStartEvent) when a torrent starts downloading.
* [TorrentSeedingStartEvent](https://docs.platypush.tech/en/latest/platypush/events/torrent.html#platypush.message.event.torrent.TorrentSeedingStartEvent) when a torrent starts seeding.
* [TorrentStateChangeEvent](https://docs.platypush.tech/en/latest/platypush/events/torrent.html#platypush.message.event.torrent.TorrentStateChangeEvent) when a torrent status changes.
* [TorrentDownloadProgressEvent](https://docs.platypush.tech/en/latest/platypush/events/torrent.html#platypush.message.event.torrent.TorrentDownloadProgressEvent) when the download progress state of a torrent changes.
* [TorrentDownloadStopEvent](https://docs.platypush.tech/en/latest/platypush/events/torrent.html#platypush.message.event.torrent.TorrentDownloadStopEvent) when the download of a torrent stops.
* [TorrentDownloadCompletedEvent](https://docs.platypush.tech/en/latest/platypush/events/torrent.html#platypush.message.event.torrent.TorrentDownloadCompletedEvent) when a torrent download is complete.
# Weather plugin
[Plugin reference](https://docs.platypush.tech/en/latest/platypush/plugins/weather.forecast.html)

@ -1,69 +0,0 @@
As an alternative to the procedure and hooks defined in `config.yaml`, you can also declare them as native Python entities. This is especially useful if your custom logic is more complex than a sequence of a few actions.
By default, you can place your custom scripts under `~/.config/platypush/scripts`. You can change the folder by setting the `scripts_dir` option in your `config.yaml`:
```yaml
scripts_dir: /path/to/my/scripts
```
N.B. It is quite important that the scripts folder, as well as its subfolder that are supposed to contain loadable script, contains an `__init__.py` file, even if it's empty. That serves to Python to identify a folder as a loadable module. The `__init__.py` in `scripts_dir` will be created automatically, but if you're planning to add more subfolders with your scripts then you'll have to manually create the file in them.
Defining a procedure in a custom script is just a matter of using the decorator `@procedure` on any function:
```python
# ~/.config/platypush/scripts/test.py
from platypush.procedure import procedure
from platypush.utils import run
@procedure
def test_procedure():
# Run some action
return run('music.mpd.pause')
```
You can then call the procedure either through any of the available APIs - e.g. over cURL:
```bash
curl -XPOST -H 'Content-Type: application/json' -d '
{
"type": "request",
"action":"procedure.test.test_procedure"
}' http://localhost:8008/execute
```
You can access a wide range of function directly from the platypush module in your code. Among those:
- `platypush.context.get_plugin` to load a plugin or get its loaded instance
- `platypush.context.get_backend` to get the loaded instance of a backend
- `platypush.context.get_bus` to access the internal bus directly (useful to dispatch any types of messages directly to the application)
- `platypush.utils.run` to run an action by full name (`plugin.method`) and arguments
Similarly, you can also define custom hooks:
```python
# ~/.config/platypush/scripts/test.py
from platypush.event.hook import hook
from platypush.message.event.assistant import SpeechRecognizedEvent
@hook(SpeechRecognizedEvent)
def test_procedure(event, **content):
print('Recognized speech: {}'.format(event.args['phrase']))
```
As well as cronjobs:
```python
# ~/.config/platypush/scripts/test.py
from platypush.cron import cron
# Run it every minute
@cron('* * * * *')
def test_cron(event, **content):
import os
import datetime
with open(os.path.expanduser('~/test.txt'), 'a') as f:
f.write('The time is: {}\n'.format(datetime.datetime.now().isoformat()))
```

@ -1,447 +1,323 @@
* [Quick start](#quick-start)
* [Procedures](#procedures)
* [For loops](#for-loops)
* [If conditions](#if-conditions)
* [Backends](#backends)
* [HTTP service configuration](#http-service-configuration)
* [PushBullet](#pushbullet-configuration)
* [Apache Kafka](#apache-kafka-configuration)
* [Local backend](#local-backend-configuration)
* [Google Assistant](#google-assistant-configuration)
* [Flic buttons](#flic-buttons-configuration)
* [Plugins](#plugins)
* [MPD/Mopidy](#mpd-mopidy-support)
* [Video and media](#video-and-media-support)
* [Philips Hue lights](#philips-hue-lights-support)
* [Belkin WeMo Switch](#belkin-wemo-switch)
* [Text-to-Speech](#text-to-speech-support)
* [Shell plugin](#shell-plugin)
* [HTTP requests plugin](#http-requests-plugin)
* [Database plugin](#database-plugin)
* [Event Hooks](#event-hooks)
* [Mobile notification mirroring](#mobile-notification-mirroring)
* [Include directive](#include-directive)
# Quick start
In this first example, let's suppose that you want to control your smart lights (we'll use the [Philips Hue](https://www2.meethue.com/en-us) plugin in this example, but you can easily use any other plugin) whenever you say "turn on the lights" to your embedded smart assistant (Platypush comes with [support for Google Assistant](https://docs.platypush.tech/en/latest/platypush/backend/assistant.google.html) through the [Google SDK](https://developers.google.com/assistant/sdk/), all you need is a microphone plugged into your device).
The bulk of the configuration of Platypush lives under the `config.yaml` file.
An extensive [`config.yaml`
example](https://git.platypush.tech/platypush/platypush/src/branch/master/platypush/config/config.yaml)
is provided in the repo. All the sections are optional - the only one enabled by
default is the HTTP server, `backend.http`, but that is optional too.
1. Start by creating an empty configuration file. It will be `/etc/platypush/config.yaml` if you want to share the configuration with all the user on the machine, or `~/.config/platypush/config.yaml` if you want a user-specific configuration.
Let's take an example where we want to control the following entities:
2. Download the `phue` Python package (``pip install phue``), required by the [Hue plugin](https://docs.platypush.tech/en/latest/platypush/plugins/light.hue.html)
- A Philips Hue bridge and its connected smart lights.
3. Add the Hue configuration to your file:
```yaml
light.hue:
bridge: 192.168.1.100 # IP address or host name of your bridge
groups:
- Living Room # Default groups to control if not specified in the request
```
4. Also, enable the web server backend, we'll use it to test our plugin through `curl`:
- An on-device voice assistant (we'll consider the Google Assistant in this
example as it's the easiest to configure, although Google deprecated the
Assistant libraries long ago).
- A compatible music player - we'll consider MPD/Mopidy in this example as they
are the ones best supported in Platypush, and Mopidy also offers plugins with
basically any audio backend out there.
We'll need the following plugins enabled in the `config.yaml`:
- [`light.hue`](https://docs.platypush.tech/platypush/plugins/light.hue.html)
- [`assistant.google`](https://docs.platypush.tech/platypush/plugins/assistant.google.html)
- [`music.mopidy`](https://docs.platypush.tech/platypush/plugins/music.mopidy.html)
or
[`music.mpd`](https://docs.platypush.tech/platypush/plugins/music.mpd.html)
(they expose the same API)
The documentation pages of these plugins already provide some comprehensive
configuration snippets that you can use.
The most basic configuration would be something like this:
```yaml
# Enable it if you want the enable the HTTP API and the Web interface
backend.http:
port: 8008
light.hue:
# IP/hostname of the Hue bridge
bridge: 192.168.1.10
# Default groups that should be targeted by actions if none is specified
# (default: all lights/groups)
groups:
- Living Room
# Check the plugin documentation on how to get the credentials
assistant.google:
music.mopidy: # Or music.mpd
# IP/hostname of the MPD/Mopidy server
host: 192.168.1.2
```
Note that `flask` is a required dependency to run the web server (``pip install flask``). `websockets` (``pip install websockets``) and ``redis`` (``pip install redis``) are optional dependency but I'd recommend to install them as well. Redis in particular is used by many components to communicate with each other, especially when they don't run in the same process, while websockets allows you to get live events on the web client.
Now that we have our integrations configured, let's build some automation routines.
5. Now start Platypush:
## Turn on the lights when I say so
In this case we will have to create a hook that listens to a
[`SpeechRecognizedEvent`](https://docs.platypush.tech/platypush/events/assistant.html#platypush.message.event.assistant.SpeechRecognizedEvent)
triggered by the assistant - for example, when we say "_OK, Google_" followed
by "_turn on the lights_".
We can declare the hook in YAML format directly in the `config.yaml`, or in one
of the files included in it through the `include:` directive:
```yaml
$ platypush
event.hook.turn_lights_on_voice_command:
if:
type: platypush.message.event.assistant.SpeechRecognizedEvent
# Note that a minimal regex-like syntax is supported here.
# This condition matches both a phrase that contains
# "turn on the lights" and one that contains "turn on lights"
phrase: "turn on (the)? lights"
then:
- action: light.hue.on
args:
groups:
- Living Room
```
If you want to use the web API you will first have to create a user through the web interface - just head to `http://your-host:8008/` and create the user.
You will then have to generate a token for your new user that can be used for API calls. You can either do it from the web panel (Settings -> Generate token) or command line:
Or we can declare the hook in a Python script - you just have to create a `.py`
file (e.g. `lights.py`) under a `scripts` directory located under the same
folder as your `config.yaml`:
```shell
curl -XPOST -H 'Content-Type: application/json' -d '
{
"username": "your-user",
"password": "your-pass"
}
' http://localhost:8008/auth
```python
from platypush import run, when
from platypush.events.assistant import SpeechRecognizedEvent
@when(SpeechRecognizedEvent, phrase="turn on (the)? lights")
def lights_on_voice_command(): # Also accepts an optional `event` argument
run('light.hue.on', groups=['Living Room'])
```
You can then store this token in an environment variable (e.g. `$PP_TOKEN`) and include it in your API calls over the `Authorization: Bearer` header.
Or, using the `get_plugin` API:
Note: if you have authentication issues with the Hue bridge, press the physical connect button on the bridge and restart the application.
```python
from platypush import get_plugin, when
from platypush.events.assistant import SpeechRecognizedEvent
6. Try to send commands to your lights:
```shell
curl -XPOST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $PP_TOKEN" \
-d '{"type":"request", "action":"light.hue.on"}' \
http://localhost:8008/execute
@when(SpeechRecognizedEvent, phrase="turn on (the)? lights")
def lights_on_voice_command():
get_plugin('light.hue').on(groups=['Living Room'])
```
Your lights should have turned on - congratulations!
## Play the music when I say so
Note that each request has:
- The `type=request` field set
- The action name. It will be the plugin package name followed by the name of the method in its main class, in this case `light.hue.on`
- An optional key-valued list of method arguments named `args`
A Platypush response will always have a structure like this:
```json
{
"id": "response_id",
"type": "response",
"target": "target",
"origin": "origin",
"response": {
"output": "The response output",
"errors": ["The response errors"]
}
}
```
7. Another way to set an authentication token is by creating a global token in your `config.yaml`:
The approach is similar for a "_play the music_" voice command. YAML:
```yaml
token: your_authentication_token
event.hook.play_music_voice_command:
if:
type: platypush.message.event.assistant.SpeechRecognizedEvent
phrase: "play (the)? music"
then:
- action: music.mopidy.play
```
If configured, the calls to the service will require this bearer token to be provided either:
Python:
- As a query string parameter (`?token=your_authentication_token`)
- As an HTTP header (`X-Token: your_authentication_token`)
- At the root of your JSON request (attribute name: `token`)
- On the `Authorization: Bearer` HTTP header.
```python
from platypush import run, when
from platypush.events.assistant import SpeechRecognizedEvent
The web interface will also require basic HTTP authentication through this token.
8. You can also point your browser to _http://localhost:8008/_, and you should now see the web interface where you can control your lights - change colors, state, and animate them. You'll only see one tab for now as you've only configured one plugin, but once you configure more plugins your web interface may look as something [like this](https://i.imgur.com/KLGovbA.png).
Note that if you configured a token you'll be promped with a basic HTTP authentication. The password will be your token, any username works for now.
9. Check out [the plugin docs](https://docs.platypush.tech/en/latest/platypush/plugins/light.hue.html) to know more about the configuration variables or the supported methods for this plugin. The configuration values for any plugin are simply the parameters passed to its constructor. Same for the methods - just pass them on `args`:
```shell
curl -XPOST -H 'Content-Type: application/json' \
-H "Authorization: Bearer $PP_TOKEN" \
-d '{"type":"request", "target":"your_device_id", "action":"light.hue.on", "args": {"groups":["Bedroom"]}}' \
http://localhost:8008/execute
@when(SpeechRecognizedEvent, phrase="play (the)? music")
def lights_on_voice_command():
run('music.mopidy.play')
```
10. Connect the action to your smart assistant now. Follow the steps on the [Google website](https://developers.google.com/assistant/sdk/guides/library/python/) to get the assistant SDK installed and the credential file ready. Then configure your assistant backend:
## Turn on the lights when the sun goes down
This example requires the [`sun`
plugin](https://docs.platypush.tech/platypush/plugins/sun.html) configured:
```yaml
backend.assistant.google:
device_model_id: your_model_id # From your Google project configuration
credentials_file: /path/to/your/credentials.json
sun:
latitude: LAT
longitude: LONG
```
11. Connect a microphone, restart the application and try to say "Ok Google" and try some basic interaction with the assistant. If everything went well, you should now be able to use the Google Assistant on your device.
12. Configure an event hook to run the "lights on" action whenever you say "turn on the lights":
You can then simply subscribe to
[`SunsetEvent`](https://docs.platypush.tech/platypush/events/sun.html#platypush.message.event.sun.SunsetEvent).
YAML:
```yaml
event.hook.LightsOnAssistantCommand:
if:
type: platypush.message.event.assistant.SpeechRecognizedEvent
phrase: "turn on the lights"
then:
action: light.hue.on
event.hook.sunset_lights_on:
if:
type: platypush.message.event.sun.SunsetEvent
then:
- action: light.hue.on
```
Event hooks:
Python:
- Start with the `event.hook.` string followed by a unique name
- Have an `if` and `then` clause. In the `if` clause you specify the `type` of the event that will trigger the hook (event inheritance works) and, optionally, the arguments in the event that must match to fire the action. In this case, `platypush.message.event.assistant.SpeechRecognizedEvent` has a `phrase` argument that contains the phrase recognized by the assistant. If the confition matches, then the list of requests in the `then` clause will be triggered (you don't need to specify `type`, as they will always be requests, nor `target`, as it will be the local host).
```python
from platypush import run, when
from platypush.events.sun import SunsetEvent
You can also pass (partial) regular expressions as phrase. Something like "turn on (the)? lights?" would fire the hook even if the recognized phrase is "turn on lights" or "turn on the light". You can also pass named arguments, that will be used to match parts of the phrase and can be re-used in the action:
@when(SunsetEvent)
def sunset_lights_on():
run('light.hue.on')
```
## Event matching and token extraction through hook templates
You can also operate token extraction from event arguments if the values are
strings.
For example, you can use advanced pattern matching and token extraction to
create voice assistant hooks that will match a template with parametrized field
which will be passed as arguments to your event hook:
```python
from platypush import run, when
from platypush.events.assistant import SpeechRecognizedEvent
@when(SpeechRecognizedEvent, phrase='play ${title} by ${artist}')
def on_music_play_command(event, title, artist):
results = run(
'music.mpd.search',
filter={
'artist': artist,
'title': title,
}
)
if results:
run('music.mpd.play', results[0]['file'])
```
## Complex hook conditions
Your event hooks can include more complex filters too. Structured filters
against partial event arguments are also possible, and relational operators are
supported as well. For example:
```python
from platypush import when
from platypush.events.sensor import SensorDataChangeEvent
@when(SensorDataChangeEvent, data=1):
def hook_1(event):
"""
Triggered when event.data == 1
"""
@when(SensorDataChangeEvent, data={'state': 1}):
def hook_2(event):
"""
Triggered when event.data['state'] == 1
"""
@when(SensorDataChangeEvent, data={
'temperature': {'$gt': 25},
'humidity': {'$le': 15}
}):
def hook_3(event):
"""
Triggered when event.data['temperature'] > 25 and
event.data['humidity'] <= 15.
"""
```
The supported relational fields are the same supported by ElasticSearch - `$gt`
for greater than, `$lt` for lesser than, `$ge` for greater or equal, `$ne` for
not equal, etc.
## Turn off the lights at 1 AM
We can use a `cron` for this case. YAML:
```yaml
event.hook.ChangeSceneAssistantCommand:
if:
type: platypush.message.event.assistant.SpeechRecognizedEvent
phrase: "set the scene on ${scene}"
then:
-
action: light.hue.scene
args:
name: ${scene}
cron.lights_off_night:
# Run this every day at 1 AM
cron_expression: '0 1 * * *'
actions:
- action: light.hue.off
```
In the example above we store whatever is detected after "set the scene on" in a context variable named `scene`, and reuse it in the command. Note that we used `args` to specify the arguments for the invoked method.
Python:
You can also run multiple actions in the same hook:
```python
from platypush import cron, run
@cron('0 1 * * *')
def lights_off_night():
run('light.hue.off')
```
## Greet me with lights and music when I come home
Let's create an `at_home` procedure for this purpose. We can also use a
text-to-speech plugin like the [`tts`
plugin](https://docs.platypush.tech/platypush/plugins/tts.html) (it requires no
configuration as it relies on the Google Translate frontend API, but other,
more sophisticated plugins are also available) to have a warm voice to welcome
us home. YAML:
```yaml
event.hook.LightsOnAssistantCommand:
if:
type: platypush.message.event.assistant.SpeechRecognizedEvent
phrase: "turn on the lights"
then:
-
action: light.hue.on
-
action: shell.exec
args:
cmd: echo "Lights turned on" >> /your/log/file
# Make sure that the sound plugin is also enabled, for audio processing
sound:
procedure.at_home:
- action: tts.say
args:
text: "Welcome home!"
# Get luminosity data from a sensor - e.g. LTR559
- action: gpio.sensor.ltr559.get_data
# If it's lower than a certain threshold, turn on the lights.
# Note that we can directly access attributes returned by the
# previous request(s) as local context variables within the
# procedure/hook/cron. In this case, `light` is an attribute returned
# on the response of the previous command.
# Otherwise, you can also use the special `output` variable to get only
# the response of the latest action, e.g. `output['light']`
# Also note the use of the special `if ${}` construct. It accepts
# a snippet of Python code and it can access variables within the
# current context.
- if ${light is not None and light < 110}:
- action: light.hue.on
- action: music.mopidy.play
args:
resource: "uri:to:my:favourite:playlist"
```
Note how the example above uses another plugin - `shell.exec` to run a shell command. Since this plugin doesn't require any configuration, you don't have to explicitly configure it in order to use it.
Python:
Also, please note that the actions executed in an event hook will be executed in parallel. If you want to execute action synchronously, consider wrapping them into a synchronous _procedure_.
```python
from platypush import procedure, run
Congratulation, you've set up your first rule! Check out the [plugins documentation](https://docs.platypush.tech/en/latest/plugins.html), the [backends documentation](https://docs.platypush.tech/en/latest/backends.html) and the [events documentation](https://docs.platypush.tech/en/latest/events.html) for the list of supported plugins, backends and events.
@procedure("at_home")
def at_home_proc():
run('tts.say', text='Welcome home!')
# Procedures
luminosity = run('gpio.sensor.ltr559.get_data').get('light', 0)
if luminosity < 110:
run('light.hue.on')
As I mentioned above, event hooks allow you to run multiple actions in parallel. Sometimes however you want to run the commands in a series, especially if you want to reuse parts of the output from the previous commands. Let's suppose for instance that we want our assistant to tell us the weather in our city using the embedded weather forecast plugin, if we're not that happy with the accuracy of the Google weather forecast. That would require to run an action to get the forecast, and another action to say out the weather conditions.
run('music.mopidy.play', resource='uri:to:my:favourite:playlist')
```
Start by configuring the weather forecast plugin:
You can then call the procedure from a hook or another script:
```python
from platypush import run
run('procedure.at_home')
```
Or, from YAML:
```yaml
weather.forecast:
lat: your_latitude
long: your_longitude
darksky_token: token # Get it from https://darksky.net/dev/register
units: si
procedure.some_other_procedure:
- action: procedure.at_home
```
Restart Platypush and test out the plugin with a curl call:
Or using the API (see next section).
```shell
curl -XPOST -H 'Content-Type: application/json' \
-H "Authorization: Bearer $PP_TOKEN" \
-d '{"type":"request", "target":"your_device_id", "action":"weather.forecast.get_current_weather"}' \
http://localhost:8008/execute
```
The response will look like this:
```json
{
"id": 123,
"type": "response",
"target": "device_id",
"origin": "device_id",
"response": {
"output": {
"...":"...",
"summary": "Clear",
"...":"..."
},
"errors": []
}
}
```
Now configure a synchronous procedure that gets the forecast and uses the text-to-speech plugin to tell you about the current conditions:
```yaml
procedure.sync.SayWeatherForecast:
-
action: shell.exec
args:
cmd: echo "Phrase recognized: ${event['args']['phrase']}" >> /your/log/file
-
action: weather.forecast.get_current_weather
-
action: tts.say
args:
phrase: ${summary}
```
We declared the procedure as synchronous (use the `async` keyword instead of `sync` if you want to run the actions in parallel). Note how we used the `summary` from the `get_current_weather` action in `tts.say`. The variables returned by the previous actions become part of the _context_ for the next action. In particular, what is contained in the `output` is expanded into local variables that you can reuse through the `${}` notation.
You can also access the original event from the context. The context is just a key-value map that contains all the variables relevant to the executed procedure, and you can use the `${}` notation to wrap any Python snippet to manipulate it and access it
Now configure an event hook to trigger the procedure through an assistant command:
```yaml
event.hook.WeatherAssistantCommand:
if:
type: platypush.message.event.assistant.SpeechRecognizedEvent
phrase: "how is the weather"
then:
action: procedure.SayWeatherForecast
```
You can also pass arguments to a procedure:
```yaml
procedure.sync.LogEvent:
-
action: shell.exec
args:
cmd: echo "Event received on ${hostname} ${event}" >> /your/log/file
event.hook.OnEvent:
if:
type: platypush.message.event.Event
then:
action: procedure.LogEvent
args:
hostname: your_host
```
## For loops
You can declare `for` loops in a procedure to iterate over values. Let's still use the weather forecast plugin to get an hourly forecast. That call will return a list of values under `data` in the response. We want to log those values to a file:
```yaml
procedure.sync.LogHourlyForecast:
-
action: weather.forecast.get_hourly_forecast
- for hour_data in ${data}:
-
action: shell.exec
args:
cmd: echo "${hour_data['time']}: ${hour_data['summary']}" >> /your/log/file
```
`for` will execute the nested actions synchronously - use the `fork` keyword instead if you want to run the nested action in parallel.
## If conditions
You can also set up `if` conditions in your procedure, to only execute some actions if some conditions are met (you can use snippets of Python code for expressing the conditions):
```yaml
procedure.sync.LogHourlyForecast:
- action: weather.forecast.get_current_weather
- if ${summary == "Clear"}:
-
action: tts.say
args:
phrase: The weather is good outside
```
The syntax also supports an `else` statement that will be executed if the condition is not matched:
```yaml
procedure.sync.LogHourlyForecast:
- action: weather.forecast.get_current_weather
- if ${summary == "Clear"}:
-
action: tts.say
args:
phrase: The weather is clear outside
- else:
-
action: tts.say
args:
phrase: The weather is not clear outside
```
And you can also build a more complex logic with nested `if-else` statements like in a real programming language.
You've now got all the basic ingredients to configure your own plugins and write you own rules. You can proceed looking at some examples with the available:
* [Plugins](plugins)
* [Backends](backends)
# Backends
Backend configurations start with `backend.` in the `config.yaml`.
## HTTP service configuration
Already covered in [quick start](#quick-start).
## PushBullet configuration
You will need:
* Your PushBullet access token (create one [here](https://www.pushbullet.com/#settings/account));
* The name of the (virtual) PushBullet device used to listen for events (create one [here](https://www.pushbullet.com/#devices)).
```yaml
backend.pushbullet:
token: PUSHBULLET_TOKEN
device: platypush
```
## Apache Kafka configuration
This would be a sample snippet for an Apache Kafka configuration:
```yaml
backend.kafka:
server: server:9092 # Kafka server and port
topic: platypush # Topic prefix. Note: platypush will create a topic for each device named <topic>.<device_id>
```
## Google Assistant configuration
Follow the steps on the [Google Assistant SDK](https://github.com/googlesamples/assistant-sdk-python/tree/master/google-assistant-sdk) page and get the assistant sample running on your machine.
Afterwards, you can enable custom speech-triggered actions on Platypush by just enabling the assistant backend:
```yaml
backend.assistant.google:
disabled: False
```
## Flic buttons configuration
[Flic buttons](https://flic.io/) are a quite cool and useful accessory. You can pair them with your phone over Bluetooth and they can trigger anything on your device - play music on Spotify, start a timer, trigger a Tasker task, snooze alarms, trigger fake phone calls...
A [beta SDK](https://github.com/50ButtonsEach/fliclib-linux-hci) is available as well that allows you to pair the buttons to any bluetooth device, not necessarily running the Flic app.
Install the SDK and run the `flicd` server on your machine. You can then enable the Flic plugin:
```yaml
backend.button.flic:
server: localhost
```
By the way, the Flic app only supports a maximum of three events per button - short press, long press and double press. With the Platypush plugin you can trigger countless actions by configuring multiple combinations of short and long presses - provided that you can remember them.
# Mobile notification mirroring
If you enable the PushBullet backend and [notification mirroring](https://docs.pushbullet.com/#mirrored-notifications) on your Android or iOS device, the PushBullet backend will be able to post events to Platypush if you receive notifications on your mobile, and you can react on those. You can for instance configure Platypush to log specific Whatsapp messages on a local db, read out the headlines of your favourite news app when you receive a push, connect custom Tasker events through [AutoNotification](https://play.google.com/store/apps/details?id=com.joaomgcd.autonotification&hl=en), mirror the notifications on a display connected to a RaspberryPi, and so on.
Example configuration that logs all the notifications mirrored from your device on a MySQL database:
```yaml
event.hook.OnMobileNotification:
if:
type: platypush.message.event.pushbullet.PushbulletEvent
push_type: mirror
then:
-
action: db.insert
args:
engine: mysql+mysqlconnector://user:password@localhost/notifications
encoding: utf-8
table: notifications
records:
-
source_device_id: ${source_device_iden}
source_user_id: ${source_user_iden}
package_name: ${package_name}
application_name: ${application_name}
title: ${title}
body: ${body}
icon: ${icon}
```
# Include directive
Your config.yaml file can easily become quite large if you have customized to automate all of your voice assistant, buttons, lights, database and media control events.
To keep the configuration neat and manageable, and/or keep some configuration parts shared among multiple nodes, you may want to use the built-in `include` directive to import some pieces of configuration from other files.
```yaml
include:
- include/backends.yaml
- include/assistant_common.yaml
```
Now you can take a more in-depth to look to the available:
- [Plugins](plugins)
- [Backends](backends)

@ -1,112 +0,0 @@
A very robust and scalable way to run Platypush is to build a Docker container image out of it.
The project comes with `platydock`, that will be installed in your prefix upon Platypush installation. You can use it to build, remove, start, stop and list Platypush container images.
Note that both `docker` and `platypush` need to be installed on your host system for these steps to work.
Example:
1. Create your own `config.yaml` file for a Platypush instance:
```yaml
device_id:
# NOTE: It's mandatory to specify a device_id when building
# a Platypush container. Containers will have their hostname
# dynamycally set by Docker and therefore won't be a reliable default
my-image
logging:
# Log to container stdout/stderr
level: INFO
main.db:
engine: sqlite:////usr/local/share/platypush/platypush.db
backend.pushbullet:
token: YOUR_TOKEN
device: platypush/your_device
backend.redis:
# Redis and Platypush can't run in the same Docker container, but platydock will
# take care of pulling a Redis docker image and connect it to your container.
redis_args:
host: redis
backend.mqtt:
host: YOUR_MQTT_HOST
backend.tcp:
port: 3333
backend.websocket:
port: 8765
backend.http:
port: 8008
redis:
host: redis
tts.google:
language: en-US
ifttt:
ifttt_key: YOUR_IFTTT_KEY
autoremote:
devices:
OnePlus6:
key: KEY_1
PixelC:
key: KEY_2
```
2. Build a Docker image out of the configuration file using `platydock`:
```shell
platydock build -c /path/to/config.yaml
```
Note that `platydock` will inspect your configuration file and automatically identify required dependencies and exposed ports.
3. Start the new image:
```shell
platydock start my-image
```
4. After it's started up, if everything went smooth, you should already be able to send messages to the new container over the available exposed service. For example, over JSON-RPC:
```shell
curl -XPOST -H 'Content-Type: application/json' \
-d '{"type":"request", "action":"shell.exec", "args": {"cmd":"hostname"}}' \
http://localhost:8008/execute | jq
```
You can also easily inspect the application logs:
```shell
docker logs my-image
```
And you can also pass extra options to `platydock start`, that will be transparently passed to `docker run`, for instance `--add-host=`, `--device=` etc. Note that the exposed ports are automatically added to the container based on the configuration and by default will be exposed to the same port number on the host (e.g. 8008 on the container is published to 8008 on the host). You can override it through the `-p` option passed to `platypush start` (e.g. `-p 18008:8008` will map the port 8008 on the container to 18008 on the host).
Also note that `platypush start` will start both your Platypush container and the support Redis container.
5. Check the installed Platypush images:
```shell
platydock ls [filter]
```
6. Stop a running instance:
```shell
platydock stop my-image
```
7. Remove an image:
```shell
platydock rm my-image
```

@ -1,95 +0,0 @@
You can run a Platypush instance in a [Python virtual environment](https://docs.python-guide.org/dev/virtualenvs/). There are several advantages for this option:
* You won't have to install dependencies at system level and you can easily create instances in user space
* The available tools will take care for the required dependencies depending on your configuration. Just write your `config.yaml` and Platypush will figure out which dependencies are required by the plugins you wish to use
* You can easily copy environments to other machines, as all the required dependencies and configuration for the application to run will all be inside of the `~/.local/share/platypush/<my-device>` folder
* You can create multiple instances on the same machine and easily start, stop and remove them. Note however that this option will use more disk space compared to a system installation if you decide to use virtual environments: each environment will install its own set of dependencies instead of relying on those available on the system.
The project comes with `platyvenv`, a script that should be installed in your bin path upon Platypush installation.
You can use it to build, remove, start, stop and list Platypush virtual environments.
Note that both `python-venv` and `platypush` need to be installed on your host system for these steps to work.
Example:
1. Create your own `config.yaml` file for a Platypush instance:
```yaml
device_id:
my-device
logging:
level: INFO
main.db:
engine: sqlite:////home/user/.local/share/platypush/venv/my-device/usr/share/db/platypush.db
backend.pushbullet:
token: YOUR_TOKEN
device: platypush/your_device
backend.redis:
redis_args:
host: localhost
port: 6397
backend.mqtt:
host: YOUR_MQTT_HOST
backend.tcp:
port: 3333
backend.websocket:
port: 8765
backend.http:
port: 8008
tts.google:
language: en-US
ifttt:
ifttt_key: YOUR_IFTTT_KEY
autoremote:
devices:
OnePlus6:
key: KEY_1
PixelC:
key: KEY_2
```
2. Build a virtual environment out of the configuration file using `platyvenv`:
```shell
platyvenv build -c /path/to/config.yaml
```
Note that `platyvenv` will inspect your configuration file, automatically identify the required dependencies and install them in your virtual environment.
3. Start the new environment:
```shell
platyvenv start my-device
```
The output of the Platypush service is printed on the terminal.
4. After it's started up, if everything went smooth, you should already be able to send messages to the new environment over the available exposed service. For example, over JSON-RPC:
```shell
curl -XPOST -H 'Content-Type: application/json' \
-d '{"type":"request", "action":"shell.exec", "args": {"cmd":"hostname"}}' \
http://localhost:8008/execute | jq
```
6. Stop a running instance:
```shell
platyvenv stop my-device
```
7. Remove an environment:
```shell
platyvenv rm my-device
```

73
The-Web-interface.md Normal file

@ -0,0 +1,73 @@
# The Web interface
## Other Web panels
Besides the built-in panels that we've already seen in the other sections,
Several integrations add their own feature-rich panels to the Web view, turning
Platypush into a gateway to all of your services - from Zigbee sensors, to
media players and services, to your music cloud, and more.
For example, the music view is available to most of the `music` plugins.
![Screenshot of one of the music
panels](https://platypush-static.s3.nl-ams.scw.cloud/screenshots/music-panel-screenshot-1.png)
![Screenshot of the Snapcast panel, which can be used to synchronize your music
streams across multiple
devices](https://platypush-static.s3.nl-ams.scw.cloud/screenshots/snapcast-panel-screenshot-1.png)
Another example is the camera panel, to monitor your cameras, get stand-alone
feed URLs, and take photos. This becomes available in the UI if you enable at
least a `camera` plugin.
![Camera panel screenshot
1](https://platypush-static.s3.nl-ams.scw.cloud/screenshots/Camera-panel-screenshot-1.png)
If you enabled at least one local `media` plugin (like `media.vlc`,
`media.mplayer` etc.) then you'll also unlock the media UI, which allows you to
index, search, view and cast media files under the configured `media_dirs`, and
it also integrates with other configured/supported backends such as YouTube,
Plex and Jellyfin.
![Media panel screenshot
1](https://platypush-static.s3.nl-ams.scw.cloud/screenshots/Media-panel-screenshot-1.png)
## Dashboards
The web service also provides means for the user to create [custom
dashboards](https://git.platypush.tech/platypush/platypush/src/branch/master/examples/conf/dashboard.xml)
that can be used to show information from multiple sources on a large screen.
![Screenshot of a Platypush dashboard, showing a calendar widget, the current
music state, weather, news from the RSS integration, and a carousel of custom
pictures.](https://blog.platypush.tech/img/dashboard-1.png)
## PWA support
Note that having the web application served over SSL is a requirement for the
PWA (progressive web app) to work. The Platypush PWA allows you to install a
Platypush native-like client on your mobile devices if you don't want to use the
full Android app.
## Mobile app
An [official Android
app](https://f-droid.org/en/packages/tech.platypush.platypush/) is provided on
the F-Droid store. It allows to easily discover and manage multiple Platypush
services on a network through the web interface, and it easily brings the power
of Platypush to your fingertips.
## Browser extension
A [browser extension](https://git.platypush.tech/platypush/platypush-webext) is
available for [Chrome](https://git.platypush.tech/platypush/platypush-webext)
and [Firefox](https://addons.mozilla.org/en-US/firefox/addon/platypush/).
The browser extension allows you to run Platypush actions and procedures
directly from your browser, associate keybindings with them, so you can run
your favourite routines with a few keystrokes anywhere in your browser, and
provides an advanced API to interact with the Web pages you visit - for
example, you can build an action that gets the content of a page you're
visiting and uses Platypush to distill it in readable format, or send the URL
to another service.

39
Variables.md Normal file

@ -0,0 +1,39 @@
# Variables
You can persist custom state across runs through _variables_.
Variables will be stored on the application database and can model any kind of
state that you want to share across different pieces of automation.
For example, you can set a `AT_HOME` variable in the previous
`procedure.at_home` - and, conversely, you may want to unset it if you have a
`procedure.outside_home` when that procedure is called.
```python
from platypush import Variable, procedure
@procedure
def at_home():
# ...
Variable('AT_HOME').set(True)
@procedure
def outside_home():
# ...
Variable('AT_HOME').set(False)
```
Within another procedure or hook, for example, you may want to turn on the
lights only if you're at home:
```python
from platypush import Variable, procedure, run
@procedure
def my_procedure():
# ...
at_home = Variable('AT_HOME')
if at_home.get():
run('light.hue.on')
```

@ -1,62 +0,0 @@
You can also write your own backends, where a backend is nothing but a thread that listens for messages on a certain channel and pokes the main app whenever it receives one.
1. Create your backend directory under `platypush/backend` (e.g. `voicemail`)
2. In the case above, `platypush.backend.voicemail` will be your package name.
3. Create an `__init__.py` under `platypush/backend/voicemail`.
4. If your module is `voicemail`, then its main class should be named `VoicemailBackend`.
5. The configuration for your module will be read from a section named `backend.voicemail` from your `config.yaml`. Its values will be passed over the backend constructor arguments.
6. Implement the `run` method. Since a backend is a thread that polls for new messages on a channel, this will be the thread main method. `send_message` should call `self.on_message` at the end to post a new message to the application.
7. Implement the `send_message` method. This method will be called whenever the application needs to send a new message through `send_request` and `send_response`. You should never call `send_message` directly.
The `__init__.py` will look like this:
```python
from .. import Backend
class VoicemailBackend(Backend)
def __init__(self, phone)
super().__init__()
self.phone = phone
self.voicemail = Voicemail(...)
def send_message(self, msg):
self.voicemail.save_msg(msg)
def run(self):
while True:
msg = self.voicemail.poll()
self.on_message(msg)
```
8. Add a `manifest.yaml` file to the same folder. Example template:
```yaml
manifest:
type: backend
package: platypush.backend.voicemail
# Events launched by the extension
events:
platypush.message.event.voicemail.OnVoicemailMessage: When a message is received
# Installation requirements
install:
# List of system-wide requirements on Debian/Ubuntu and derived
apt:
- sudo
# List of system-wide requirements on Arch Linux and derived
pacman:
- sudo
# List of pip dependencies
pip:
- requests
# Extra commands to run after the dependencies are installed
exec:
- sudo systemctl enable my-service
```

@ -1,114 +0,0 @@
Writing your own `platypush` plugin, that would execute your own custom logic whenever a bullet with your plugin name is received, is a very simple task.
1. Create your plugin directory under `platypush/plugins` (e.g. `light/batsignal`).
2. In the case above, `platypush.plugins.light.batsignal` will be your package name.
3. Create an `__init__.py` under `platypush/plugins/light/batsignal`.
4. If your module is `light/batsignal`, then its main class should be named `LightBatsignalPlugin`.
5. The configuration for your module will be read from a section named `light.batsignal` from your `config.yaml`. Its values will passed over the plugin constructor arguments.
The `__init__.py` will look like this:
```python
import batman
# Decorator used to identify class methods to be exposed as plugin actions
from platypush.plugins import action
from platypush.plugins.light import LightPlugin
class LightBatsignalPlugin(LightPlugin):
def __init__(self, intensity=100):
super().__init__()
self.batsignal = batman.Batsignal(intensity)
@action
def status(self):
return {'status': self.batsignal.status()}
@action
def on(self, urgent=False):
if urgent:
self.batsignal.notify_robin()
self.batsignal.on()
return self.status()
@action
def off(self):
self.batsignal.off()
return self.status()
@action
def toggle(self):
if self.batsignal.status().on:
self.batsignal.off()
else:
self.batsignal.on()
return self.status()
```
6. Create a `manifest.yaml` file in the same directory that defines your plugin. Template:
```yaml
manifest:
type: plugin
package: platypush.plugins.light.batsignal
# Events launched by the extension
events:
platypush.message.event.batsignal.SignalOnEvent: When the signal goes on
platypush.message.event.batsignal.SignalOffEvent: When the signal goes off
# Installation requirements
install:
# List of system-wide requirements on Debian/Ubuntu and derived
apt:
- sudo
# List of system-wide requirements on Arch Linux and derived
pacman:
- sudo
# List of pip dependencies
pip:
- requests
# Extra commands to run after the dependencies are installed
exec:
- sudo systemctl enable my-service
```
8. Rebuild and reinstall `platypush` if required and relaunch it.
9. Test your new plugin by sending some requests to it:
* Via cURL:
```shell
curl -XPOST -H 'Content-Type: application/json' \
-d '{"type":"request", "target":"hostname", "action":"light.batsignal.on"}' \
http://hostname:8008/execute
```
* Via TCP backend:
```shell
echo -n '{"type":"request", "target":"hostname", "action":"light.batsignal.on"}' | nc hostname 3333
```
* Via Redis backend:
```shell
echo "RPUSH platypush_bus_mq '{\"type\":\"request\",\"target\":\"turing\",\"action\":\"light.batsignal.on\"}'" | redis-cli
```
* Via websocket backend:
```shell
wscat -w 0 -c 'ws://turing:8765' -x '{"type":"request", "target":"hostname", "action":"light.batsignal.on"}'
```
Or you can even experiment and connect [Node-Red](https://nodered.org) components and flows to trigger your custom action.

1
index.md Symbolic link

@ -0,0 +1 @@
Home.md