README.md 24.6 KB
Newer Older
Fabio Manganiello's avatar
Fabio Manganiello committed
1
Platypush
2
3
=========

4
5
[![Build Status](https://ci.platypush.tech/status.svg)](https://ci.platypush.tech/latest.log)
[![Documentation Status](https://ci.platypush.tech/docs/status.svg)](https://ci.platypush.tech/docs/latest.log)
6
[![pip version](https://img.shields.io/pypi/v/platypush.svg?style=flat)](https://pypi.python.org/pypi/platypush/)
7
8
[![License](https://img.shields.io/github/license/BlackLight/platypush.svg)](https://git.platypush.tech/platypush/platypush/-/blob/master/LICENSE.txt)
[![Last Commit](https://img.shields.io/github/last-commit/BlackLight/platypush.svg)](https://git.platypush.tech/platypush/platypush/-/commits/master/)
9
[![Join chat on Matrix](https://img.shields.io/matrix/:platypush?server_fqdn=matrix.platypush.tech)](https://matrix.to/#/#platypush:matrix.platypush.tech)
10
[![Contributions](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://git.platypush.tech/platypush/platypush/-/blob/master/CONTRIBUTING.md)
Fabio Manganiello's avatar
Fabio Manganiello committed
11
12
[![Language grade: Python](https://img.shields.io/lgtm/grade/python/g/BlackLight/platypush.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/BlackLight/platypush/context:python)
[![Language grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/BlackLight/platypush.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/BlackLight/platypush/context:javascript)
13

14
15
16
17
18
19
20
21
22
23
24
25
26
<!-- toc -->

- [Architecture](#architecture)
  * [Plugins](#plugins)
  * [Actions](#actions)
  * [Backends](#backends)
  * [Events](#events)
  * [Hooks](#hooks)
  * [Procedures](#procedures)
  * [Cronjobs](#cronjobs)
  * [The web interface](#the-web-interface)
- [Installation](#installation)
  * [System installation](#system-installation)
27
28
29
    + [Install through `pip`](#install-through-pip)
    + [Install through a system package manager](#install-through-a-system-package-manager)
    + [Install from sources](#install-from-sources)
30
31
32
33
34
35
36
37
38
39
40
  * [Installing the dependencies for your extensions](#installing-the-dependencies-for-your-extensions)
    + [Install via `extras` name](#install-via-extras-name)
    + [Install via `manifest.yaml`](#install-via-manifestyaml)
    + [Check the instructions reported in the documentation](#check-the-instructions-reported-in-the-documentation)
  * [Virtual environment installation](#virtual-environment-installation)
  * [Docker installation](#docker-installation)
- [Mobile app](#mobile-app)
- [Tests](#tests)
- [Funding](#funding)

<!-- tocstop -->
41

42
- Recommended read: [**Getting started with Platypush**](https://blog.platypush.tech/article/Ultimate-self-hosted-automation-with-Platypush).
fabio-eiq's avatar
fabio-eiq committed
43

44
45
46
- The [blog](https://blog.platypush.tech) is in general a good place to get
  more insights on what you can build with it and inspiration about possible
  usages.
47

48
49
- The [wiki](https://git.platypush.tech/platypush/platypush/-/wikis/home) also
  contains many resources on getting started.
50

51
52
- Extensive documentation for all the available integrations and messages [is
  available](https://docs.platypush.tech/).
53

54
55
- If you have issues/feature requests/enhancement ideas please [create an
  issue](https://git.platypush.tech/platypush/platypush/-/issues).
Fabio Manganiello's avatar
Fabio Manganiello committed
56

57
58
- A [Reddit channel](https://www.reddit.com/r/platypush) is also available for
  more general questions.
59

60
61
62
- A [Matrix instance](https://matrix.to/#/#platypush:matrix.platypush.tech) is
  also available if you are looking for more interactive support.

63
---
64

65
66
Platypush is a general-purpose extensible platform for automation and
integration across multiple services and devices.
Fabio Manganiello's avatar
Fabio Manganiello committed
67

68
69
70
71
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.
72

73
74
75
76
77
It takes some concepts from [IFTTT](https://ifttt.com),
[Tasker](https://tasker.joaoapps.com/), [Microsoft
Flow](https://flow.microsoft.com), [PushBullet](https://pushbullet.com) and
[Home Assistant](https://www.home-assistant.io/) to provide an environment
where the user can easily connect things together.
78

79
80
81
82
83
84
Its ideal home is a single-board computer like a RaspberryPi that you can
configure to orchestrate any home automation and cloud automation in your own
living room or garage, but it can easily run on any device that can run a
Python interpreter, and the bar for the hardware requirements is very low as
well - I use it to run pieces of automation on devices as powerful as a
RaspberryPi Zero or an old Nokia N900 with Linux.
Fabio Manganiello's avatar
Fabio Manganiello committed
85

86
87
You can use Platypush to do things like:

88
89
90
91
92
- [Control your smart home lights](https://blog.platypush.tech/article/Ultimate-self-hosted-automation-with-Platypush)
- [Control your music and synchronize it to multiple devices](https://blog.platypush.tech/article/Build-your-open-source-multi-room-and-multi-provider-sound-server-with-Platypush-Mopidy-and-Snapcast)
- [Create custom and privacy-secure voice assistants that run custom hooks on your phrases](https://blog.platypush.tech/article/Build-custom-voice-assistants)
- Build integrations between [sensors](https://docs.platypush.tech/en/latest/platypush/backend/sensor.html),
  [cameras](https://docs.platypush.tech/en/latest/platypush/plugins/camera.pi.html),
93
94
95
96
  [microphones](https://docs.platypush.tech/en/latest/platypush/plugins/sound.html)
  and [machine learning
  models](https://docs.platypush.tech/en/latest/platypush/plugins/tensorflow.html)
  to create smart
97
98
99
100
101
102
103
104
105
106
107
  pieces of automation for e.g.
  [people detection](https://blog.platypush.tech/article/Detect-people-with-a-RaspberryPi-a-thermal-camera-Platypush-and-a-pinch-of-machine-learning)
  or [sound detection](https://blog.platypush.tech/article/Create-your-smart-baby-monitor-with-Platypush-and-Tensorflow)
- [Get events from your Google or Facebook calendars](https://docs.platypush.tech/en/latest/platypush/plugins/calendar.html)
- [Read data from your sensors and trigger custom events whenever they go above or below some custom thresholds](https://blog.platypush.tech/article/How-to-build-your-personal-infrastructure-for-data-collection-and-visualization)
- [Control and automate a self-built robot](https://docs.platypush.tech/en/latest/platypush/plugins/gpio.zeroborg.html)
- [Deliver automated newsletters from custom RSS digests](https://blog.platypush.tech/article/Deliver-customized-newsletters-from-RSS-feeds-with-Platypush)
- [Synchronize the clipboards on your devices](https://docs.platypush.tech/en/latest/platypush/plugins/clipboard.html)
- [Control your smart switches](https://docs.platypush.tech/en/latest/platypush/plugins/switch.html)
- [Implement automated custom text-to-speech routines](https://docs.platypush.tech/en/latest/platypush/plugins/tts.html)
- [Build any kind of interactions and automation routines with your Android device using Tasker](https://blog.platypush.tech/article/How-to-build-your-personal-infrastructure-for-data-collection-and-visualization)
108
109
110
- Play [local
  videos](https://docs.platypush.tech/en/latest/platypush/plugins/media.mpv.html),
  YouTube videos and torrent media from any device and service, to any device
111
112
- [Get weather forecast events for your location and build automation routines on them](https://docs.platypush.tech/en/latest/platypush/plugins/weather.darksky.html)
- [Create a custom single hub for Zigbee and Z-Wave smart devices](https://blog.platypush.tech/article/Transform-a-RaspberryPi-into-a-universal-Zigbee-and-Z-Wave-bridge)
113
114
- Build your own web dashboard with calendar, weather, news and music controls
  (basically, anything that has a Platypush web widget)
115
- ...and much more (basically, anything that comes with a [Platypush plugin](https://docs.platypush.tech/en/latest/plugins.html))
116

117
118
## Architecture

119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
The architecture of Platypush consists of a few simple pieces, orchestrated by
a configuration file stored by default under
[`~/.config/platypush/config.yaml`](https://git.platypush.tech/platypush/platypush/-/blob/master/examples/conf/config.yaml):

### Plugins

[Full list](https://docs.platypush.tech/en/latest/plugins.html)

Plugins are integrations that do things - like [modify
files](https://docs.platypush.tech/en/latest/platypush/plugins/file.html),
[train and evaluate machine learning
models](https://docs.platypush.tech/en/latest/platypush/plugins/tensorflow.html),
[control
cameras](https://docs.platypush.tech/en/latest/platypush/plugins/camera.pi.html),
[read
sensors](https://docs.platypush.tech/en/latest/platypush/plugins/gpio.sensor.dht.html),
[parse a web
page](https://docs.platypush.tech/en/latest/platypush/plugins/http.webpage.html),
[control
lights](https://docs.platypush.tech/en/latest/platypush/plugins/light.hue.html),
[send
emails](https://docs.platypush.tech/en/latest/platypush/plugins/mail.smtp.html),
[control
Chromecasts](https://docs.platypush.tech/en/latest/platypush/plugins/media.chromecast.html),
[run voice
queries](https://docs.platypush.tech/en/latest/platypush/plugins/assistant.google.html),
[handle torrent
transfers](https://docs.platypush.tech/en/latest/platypush/plugins/torrent.html)
or control
[Zigbee](https://docs.platypush.tech/en/latest/platypush/plugins/zigbee.mqtt.html)
or [Z-Wave](https://docs.platypush.tech/en/latest/platypush/plugins/zwave.html)
devices.

The configuration of a plugin matches one-on-one that of its documented class
constructor, so it's very straightforward to write a configuration for a plugin
by reading its documentation:
155
156
157
158
159
160
161
162
163
164
165

```yaml
light.hue:
  # Groups that will be controlled by default
  groups:
    - Living Room
    - Hall
```

### Actions

166
167
168
169
170
Plugins expose *actions*, that match one-on-one the plugin class methods
denoted by `@action`, so it's very straightforward to invoke plugin actions by
just reading the plugin documentation. They can be invoked directly from your
own scripts or they can be sent to the platform through any supported channel
as simple JSON messages:
171
172
173
174
175
176
177
178
179
180
181

```json
{
  "type": "request",
  "action": "light.hue.on",
  "args": {
    "lights": ["Entrance Bulb"]
  }
}
```

182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
### Backends

[Full list](https://docs.platypush.tech/en/latest/backends.html)

They are background services that either listen for messages on channels (like
an [HTTP
backend](https://docs.platypush.tech/en/latest/platypush/backend/http.html), an
[MQTT
instance](https://docs.platypush.tech/en/latest/platypush/backend/mqtt.html), a
[Kafka
instance](https://docs.platypush.tech/en/latest/platypush/backend/kafka.html),
a [Websocket
service](https://docs.platypush.tech/en/latest/platypush/backend/websocket.html),
[Pushbullet](https://docs.platypush.tech/en/latest/platypush/backend/pushbullet.html)
etc.) or monitor a device or a service for events (like a
[sensor](https://docs.platypush.tech/en/latest/platypush/backend/sensor.html),
a custom [voice
assistant](https://docs.platypush.tech/en/latest/platypush/backend/assistant.google.html),
a bridge running on a
[Zigbee](https://docs.platypush.tech/en/latest/platypush/backend/zigbee.mqtt.html)
or
[Z-Wave](https://docs.platypush.tech/en/latest/platypush/backend/zwave.html),
an [NFC card
reader](https://docs.platypush.tech/en/latest/platypush/backend/nfc.html), a
[MIDI
device](https://docs.platypush.tech/en/latest/platypush/backend/midi.html), a
[Telegram
channel](https://docs.platypush.tech/en/latest/platypush/backend/chat.telegram.html),
a [Bluetooth
scanner](https://docs.platypush.tech/en/latest/platypush/backend/bluetooth.scanner.ble.html)
etc.).

If a backend supports the execution of requests (e.g. HTTP, MQTT, Kafka,
Websocket and TCP) then you can send requests to these services in JSON format.
For example, in the case of the HTTP backend:
217
218

```shell
219
    # Get a token
220
221
222
223
224
225
curl -XPOST -H 'Content-Type: application/json' -d '
  {
    "username": "$YOUR_USER",
    "password": "$YOUR_PASSWORD"
  }' http://host:8008/auth

226
    # Execute a request
227

228
229
curl -XPOST -H 'Content-Type: application/json' \
    -H "Authorization: Bearer $YOUR_TOKEN" -d '
230
231
232
233
234
235
236
237
238
  {
    "type": "request",
    "action": "tts.say",
    "args": {
      "text": "This is a test"
    }
  }' http://host:8008/execute
```

239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
### Events

[Full list](https://docs.platypush.tech/en/latest/events.html)

When a certain event occurs (e.g. a JSON request is received, or a [Bluetooth
device is
connected](https://docs.platypush.tech/en/latest/platypush/events/bluetooth.html#platypush.message.event.bluetooth.BluetoothDeviceConnectedEvent),
or a [Flic button is
pressed](https://docs.platypush.tech/en/latest/platypush/events/button.flic.html#platypush.message.event.button.flic.FlicButtonEvent),
or some [speech is detected on the voice assistant
service](https://docs.platypush.tech/en/latest/platypush/events/assistant.html#platypush.message.event.assistant.SpeechRecognizedEvent),
or an [RSS feed has new
items](https://docs.platypush.tech/en/latest/platypush/events/http.rss.html#platypush.message.event.http.rss.NewFeedEvent),
or a [new email is
received](https://docs.platypush.tech/en/latest/platypush/events/mail.html#platypush.message.event.mail.MailReceivedEvent),
or a [new track is
played](https://docs.platypush.tech/en/latest/platypush/events/music.html#platypush.message.event.music.NewPlayingTrackEvent),
or an [NFC tag is
detected](https://docs.platypush.tech/en/latest/platypush/events/nfc.html#platypush.message.event.nfc.NFCTagDetectedEvent),
or [new sensor data is
available](https://docs.platypush.tech/en/latest/platypush/events/sensor.html#platypush.message.event.sensor.SensorDataChangeEvent),
or [a value of a Zigbee device
changes](https://docs.platypush.tech/en/latest/platypush/events/zigbee.mqtt.html#platypush.message.event.zigbee.mqtt.ZigbeeMqttDevicePropertySetEvent),
etc.), the associated backend will trigger an
[event](https://docs.platypush.tech/en/latest/events.html).
264
265
266

### Hooks

267
268
269
270
Event hooks are custom pieces of logic that will be run when a certain event is
triggered. Hooks are the glue that connects events to actions, exposing a
paradigm similar to IFTTT (_if a certain event happens then run these
actions_). They can declared as:
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293

- Sections of the [`config.yaml`](https://git.platypush.tech/platypush/platypush/-/blob/master/examples/conf/config.yaml).
  Example:

```yaml
event.hook.SearchSongVoiceCommand:
  if:
    type: platypush.message.event.assistant.SpeechRecognizedEvent
    phrase: "play ${title} by ${artist}"
  then:
    - action: music.mpd.clear
    - action: music.mpd.search
      args:
        filter:
          artist: ${artist}
          title: ${title}

    - if ${len(output)}:
      - action: music.mpd.play
        args:
          resource: ${output[0]['file']}
```

294
295
- Stand-alone Python scripts stored under `~/.config/platypush/scripts` and
  will be dynamically imported at start time.
296
  [Example](https://git.platypush.tech/platypush/platypush/-/blob/master/examples/conf/hook.py):
297

298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
```python
from platypush.event.hook import hook
from platypush.utils import run
from platypush.message.event.assistant import SpeechRecognizedEvent

@hook(SpeechRecognizedEvent, phrase='play ${title} by ${artist}')
def on_music_play_command(event, title=None, artist=None, **context):
  results = run('music.mpd.search', filter={
    'artist': artist,
    'title': title,
  })

  if results:
    run('music.mpd.play', results[0]['file'])
```

314
315
### Procedures

316
317
318
319
320
321
322
Procedures are pieces of custom logic that can be executed as atomic actions
using `procedure.<name>` as an action name.

They can be defined either in the `config.yaml` or as Python scripts stored
under `~/.config/platypush/scripts` - provided that the procedure is also
imported in `~/.config/platypush/scripts/__init__.py` so it can be discovered
by the service.
323

324
325
326
YAML example for a procedure that can be executed when we arrive home and turns
on the lights if the luminosity is lower that a certain thresholds, says a
welcome home message using the TTS engine and starts playing the music:
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348

```yaml
procedure.at_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
    - if ${int(light or 0) < 110}:
        - action: light.hue.on

    # Say a welcome home message
    - action: tts.google.say
      args:
        text: Welcome home

    # Play the music
    - action: music.mpd.play
```

Python example:

```python
349
    # Content of ~/.config/platypush/scripts/home.py
350
351
352
353
354
355
356
357
358
359
360
361
362
from platypush.procedure import procedure
from platypush.utils import run

@procedure
def at_home(**context):
  sensor_data = run('gpio.sensor.ltr559.get_data')
  if sensor_data['light'] < 110:
    run('light.hue.on')

  run('tts.google.say', text='Welcome home')
  run('music.mpd.play')
```

363
364
In either case, you can easily trigger the at-home procedure by sending an
action request message to a backend - for example, over the HTTP backend:
365
366

```shell
367
368
curl -XPOST -H 'Content-Type: application/json' \
    -H "Authorization: Bearer $YOUR_TOKEN" -d '
369
370
371
372
373
374
  {
    "type": "request",
    "action": "procedure.at_home"
  }' http://host:8008/execute
```

375
376
### Cronjobs

377
378
379
380
Cronjobs are pieces of logic that will be run at regular intervals, expressed
in crontab-compatible syntax. They can be defined either in the `config.yaml`
or as Python scripts stored under `~/.config/platypush/scripts` as functions
labelled by the `@cron` decorator.
381

382
383
384
Note that seconds are also supported (unlike the standard crontab definition),
but, for back-compatibility with the standard crontab format, they are at the
end of the cron expression, so the expression is actually in the format
385
386
`<minute> <hour> <day_of_month> <month> <day_of_week> <second>`.

387
388
YAML example for a cronjob that is executed every 30 seconds and checks if a
Bluetooth device is nearby:
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406

```yaml
cron.check_bt_device:
  cron_expression: '* * * * * */30'
  actions:
    - action: bluetooth.lookup_name
      args:
        addr: XX:XX:XX:XX:XX:XX

    - if ${name}:
        - action: procedure.on_device_on
    - else:
        - action: procedure.on_device_off
```

Python example:

```python
407
    # Content of ~/.config/platypush/scripts/bt_cron.py
408
409
410
411
412
413
414
415
416
417
418
419
from platypush.cron import cron
from platypush.utils import run

@cron('* * * * * */30')
def check_bt_device(**context):
  name = run('bluetooth.lookup_name').get('name')
  if name:
    # on_device_on logic here
  else:
    # on_device_off logic here
```

420
421
### The web interface

422
423
424
425
426
427
428
429
If
[`backend.http`](https://docs.platypush.tech/en/latest/platypush/backend/http.html)
is enabled then a web interface will be provided by default on
`http://host:8008/`. Besides using the `/execute` endpoint for running
requests, the built-in web server also provides a full-featured interface that
groups together the controls for most of the plugins - e.g. sensors, switches,
music controls and search, media library and torrent management, lights,
Zigbee/Z-Wave devices and so on. The UI is responsive and mobile-friendly.
430

431
432
433
The web service also provides means for the user to create [custom
dashboards](https://git.platypush.tech/platypush/platypush/-/blob/master/examples/conf/dashboard.xml)
that can be used to show information from multiple sources on a large screen.
434
435
436
437
438
439
440
441

## Installation

### System installation

Platypush uses Redis to deliver and store requests and temporary messages:

```yaml
442
    # Example for Debian-based distributions
443
444
[sudo] apt-get install redis-server

445
    # Enable and start the service
446
447
448
449
[sudo] systemctl enable redis
[sudo] systemctl start redis
```

450
#### Install through `pip`
451
452
453
454
455

```shell
[sudo] pip3 install platypush
```

456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
#### Install through a system package manager

Note: currently only Arch Linux and derived distributions are supported.

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`:

```shell
yay platypush
# Or
yay platypush-git
```

The Arch Linux packages on AUR are automatically updated upon new git commits
or tags.

#### Install from sources
477
478
479
480

```shell
git clone https://git.platypush.tech/platypush/platypush.git
cd platypush
481
482
[sudo] pip install .
    # Or
483
484
485
[sudo] python3 setup.py install
```

486
### Installing the dependencies for your extensions
487

488
489
490
After installing the base platform, you may want to check the dependencies and
configuration required by the extensions that you wish to use. There are a few
ways to check the dependencies required by an extension:
491

492
493
494
495
496
497
498
499
500
501
502
503
504
#### Install via `extras` name

All the extensions that require extra dependencies are listed in the
[`extras_require` section under
`setup.py`](https://git.platypush.tech/platypush/platypush/-/blob/master/setup.py#L72).

#### Install via `manifest.yaml`

All the plugins and backends have a `manifest.yaml` file in their source folder.
Any extra dependencies are listed there

If you followed the `extras` or `manifest.yaml` way to discover the
dependencies, then you can install them in two ways:
505
506
507
508
509
510
511
512
513
514
515
516
517
518

1. `pip` installation:

```shell
[sudo] pip3 install 'platypush[extra1,extra2,extra3]'
```

2. Sources installation:

```shell
cd $DIR_TO_PLATYPUSH
[sudo] pip3 install '.[extra1,extra2,extra3]'
```

519
#### Check the instructions reported in the documentation
520

521
522
523
524
525
526
527
528
529
If you follow this route then simply run the commands listed in the
[plugin/backend documentation](https://docs.platypush.tech) to get the
dependencies installed.

After installing the dependencies, create a configuration file under
`~/.config/platypush/config.yaml` (the application can load the configuration
from another location through the `-c` option) containing the configuration of
the backend and plugins that you want to use, and add any hooks and procedures
for your use case.
530
531
532
533
534
535
536
537

You can then start the service by simply running:

```shell
platypush
```

It's advised to run it as a systemd service though - simply copy the provided
538
539
540
541
[`.service`
file](https://git.platypush.tech/platypush/platypush/-/blob/master/examples/systemd/platypush.service)
to `~/.config/systemd/user`, check if the path of `platypush` matches the path
where it's installed on your system, and start the service via `systemctl`:
542
543
544
545
546

```shell
systemctl --user start platypush
```

547
### Virtual environment installation
548

549
550
551
552
Platypush provides a script named `platyvenv` that can parse a `config.yaml`
and automatically create a virtual environment (under
`~/.local/share/platypush/venv/<device_id>`) with all the dependencies required
by the configured integrations.
553
554
555

1. Create the environment from a configuration file:

556
557
558
    ```shell
    platyvenv build -c /path/to/config.yaml
    ```
559
560
561

2. Start the service from the virtual environment:

562
563
564
565
    ```shell
        # device_id matches either the hostname or the device_id in config.yaml
    platyvenv start device_id
    ```
566
567
568

3. Stop the instance:

569
570
571
    ```shell
    platyvenv stop device_id
    ```
572
573
574

4. Remove the instance:

575
576
577
578
579
    ```shell
    platyvenv rm device_id
    ```

[Wiki instructions](https://git.platypush.tech/platypush/platypush/-/wikis/Run-platypush-in-a-virtual-environment)
580

581
### Docker installation
582

583
584
585
You can also install Platypush in a container - the application provides a
script named `platydock` that automatically creates a container instance from a
`config.yaml`:
586
587
588

1. Create the container from a configuration file:

589
590
591
    ```shell
    platydock build -c /path/to/config.yaml
    ```
592
593
594

2. Start the container:

595
596
597
598
    ```shell
        # device_id matches either the hostname or the device_id in config.yaml
    platydock start device_id
    ```
599
600
601

3. Stop the instance:

602
603
604
    ```shell
    platydock stop device_id
    ```
605
606
607

4. Remove the instance:

608
609
610
611
612
613
614
615
616
    ```shell
    platydock rm device_id
    ```

Note that both the virtual environment and Docker container option offer the
possibility to include extra YAML configuration files in the main `config.yaml`
through the `include` directive (as long as they are in the same folder as the
main `config.yaml`), as well as external Python scripts in a `scripts`
directory in the same folder as the `config.yaml`.
Fabio Manganiello's avatar
Fabio Manganiello committed
617

618
[Wiki instructions](https://git.platypush.tech/platypush/platypush/-/wikis/Run-platypush-in-a-container)
Fabio Manganiello's avatar
Fabio Manganiello committed
619

620
621
## Mobile app

622
623
624
625
626
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.
627

628
629
## Tests

630
631
To run the tests simply run `pytest` either from the project root folder or the
`tests/` folder. Or run the following command from the project root folder:
632
633
634
635

```shell
python -m tests
```
636

Fabio Manganiello's avatar
Fabio Manganiello committed
637
638
---

Fabio Manganiello's avatar
Fabio Manganiello committed
639
640
641
642
## Funding

If you use and love Platypush, please consider [buying me a coffee/beer](https://paypal.me/fabiomanganiello).

643
644
I've been working on Platypush all by myself in my spare time for the past few
years, and I've made sure that it remains open and free.
Fabio Manganiello's avatar
Fabio Manganiello committed
645

646
647
648
If you like this product, please consider supporting - I'm definitely not
planning to get rich with this project, but I'd love to have at least the costs
for the server covered by users.
Fabio Manganiello's avatar
Fabio Manganiello committed
649
650

Issues and requests opened by donors will also be given priority over others.