Quick start
In this first example, let's suppose that you want to control your smart lights (we'll use the Philips Hue 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 through the Google SDK, all you need is a microphone plugged into your device).
-
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. -
Download the
phue
Python package (pip install phue
), required by the Hue plugin -
Add the Hue configuration to your file:
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
- Also, enable the web server backend, we'll use it to test our plugin through
curl
:
backend.http:
port: 8008
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 start Platypush:
$ platypush
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:
curl -XPOST -H 'Content-Type: application/json' -d '
{
"username": "your-user",
"password": "your-pass"
}
' http://localhost:8008/auth
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.
Note: if you have authentication issues with the Hue bridge, press the physical connect button on the bridge and restart the application.
- Try to send commands to your lights:
curl -XPOST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $PP_TOKEN" \
-d '{"type":"request", "action":"light.hue.on"}' \
http://localhost:8008/execute
Your lights should have turned on - congratulations!
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:
{
"id": "response_id",
"type": "response",
"target": "target",
"origin": "origin",
"response": {
"output": "The response output",
"errors": ["The response errors"]
}
}
- Another way to set an authentication token is by creating a global token in your
config.yaml
:
token: your_authentication_token
If configured, the calls to the service will require this bearer token to be provided either:
- 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.
The web interface will also require basic HTTP authentication through this token.
- 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.
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.
- Check out the plugin docs 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
:
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
- Connect the action to your smart assistant now. Follow the steps on the Google website to get the assistant SDK installed and the credential file ready. Then configure your assistant backend:
backend.assistant.google:
device_model_id: your_model_id # From your Google project configuration
credentials_file: /path/to/your/credentials.json
-
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.
-
Configure an event hook to run the "lights on" action whenever you say "turn on the lights":
event.hook.LightsOnAssistantCommand:
if:
type: platypush.message.event.assistant.SpeechRecognizedEvent
phrase: "turn on the lights"
then:
action: light.hue.on
Event hooks:
- Start with the
event.hook.
string followed by a unique name - Have an
if
andthen
clause. In theif
clause you specify thetype
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 aphrase
argument that contains the phrase recognized by the assistant. If the confition matches, then the list of requests in thethen
clause will be triggered (you don't need to specifytype
, as they will always be requests, nortarget
, as it will be the local host).
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:
event.hook.ChangeSceneAssistantCommand:
if:
type: platypush.message.event.assistant.SpeechRecognizedEvent
phrase: "set the scene on ${scene}"
then:
-
action: light.hue.scene
args:
name: ${scene}
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.
You can also run multiple actions in the same hook:
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
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.
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.
Congratulation, you've set up your first rule! Check out the plugins documentation, the backends documentation and the events documentation for the list of supported plugins, backends and events.
Procedures
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.
Start by configuring the weather forecast plugin:
weather.forecast:
lat: your_latitude
long: your_longitude
darksky_token: token # Get it from https://darksky.net/dev/register
units: si
Restart Platypush and test out the plugin with a curl call:
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:
{
"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:
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:
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:
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:
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):
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:
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:
Backends
Backend configurations start with backend.
in the config.yaml
.
HTTP service configuration
Already covered in quick start.
PushBullet configuration
You will need:
- Your PushBullet access token (create one here);
- The name of the (virtual) PushBullet device used to listen for events (create one here).
backend.pushbullet:
token: PUSHBULLET_TOKEN
device: platypush
Apache Kafka configuration
This would be a sample snippet for an Apache Kafka configuration:
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 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:
backend.assistant.google:
disabled: False
Flic buttons configuration
Flic buttons 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 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:
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 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, 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:
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.
include:
- include/backends.yaml
- include/assistant_common.yaml
Now you can take a more in-depth to look to the available: