A versatile and extensible platform for home and life automation with hundreds of supported integrations
Find a file
Fabio Manganiello bd5c80175f - Major refactoring.
- More consistent naming for many methods, plus added a more extensive doc.

- Refactored the entry points for the daemon and the pusher into two
  classes, easier to encapsulate and wrap into tests.

- Removed the local backend - managing the concurrency of two processes
  reading and writing on the same socket at the same time was too much,
  and its utility outside of the tests (which can have mock backends as
  well) is quite modest.

- Managing stop events on the bus. Still some work to do tho.

- Fixed several bugs.
2017-12-20 20:25:08 +01:00
platypush - Major refactoring. 2017-12-20 20:25:08 +01:00
.gitignore Ignore .pypirc 2017-12-11 20:39:46 +01:00
LICENSE.txt MIT License 2017-11-03 18:06:58 +01:00
README.md - Major refactoring. 2017-12-20 20:25:08 +01:00
setup.cfg setup.cfg 2017-12-11 20:41:50 +01:00
setup.py Version bump 2017-12-18 22:45:55 +01:00

Platypush

Execute any command or custom complex logic on your devices, wherever they are, using your PushBullet account.

Installation

pip install platypush

Manual Installation

git clone https://github.com/BlackLight/platypush
cd platypush
python setup.py install

Configuration

Copy /etc/platypush/config.example.yaml to /etc/platypush/config.yaml (system-wise settings) or ~/.config/platypush/config.yaml (user-wise settings).

Edit the file to include:

For the PushBullet backend

  • Your PushBullet access token (create one here);
  • The name of the (virtual) PushBullet device used to listen for events (create one here).

For the Apache Kafka backend

  • The host and port of the Kafka installation
  • The topic that will be used to deliver and process messages

device_id

Each target device is identified by a unique device_id in the messages sent over your account. The device_id is the hostname by default, unless changed in config.yaml.

Shell interface

platypush installs pusher, a command-line tool to send PushBullet messages to the connected devices in the format used by platypush.

Some examples:

pusher --target laptop --action shell.exec --cmd "scp /home/user/photos/*.jpg backup_host:/mnt/hd/photos"
pusher --target raspberrypi --action music.mpd.play

The logic to execute is specified by the --action option, whose format is package_name.method_name (with method_name part of the package main class).

Available plugins

  • platypush.plugins.shell: The simplest and yet most versatile plugin. Executes a remote command on the host identified by the --target device_id. Example:
pusher --target laptop --action shell.exec --cmd "scp /home/user/photos/*.jpg backup_host:/mnt/hd/photos"
  • platypush.plugins.music.mpd: Controls the playback on a mpd/mopidy music server. Requires the package mpd2 on the target machine. Example:
pusher --target raspberry --action music.mpd.play

Configure the plugin through an entry like this in your config.yaml:

music.mpd:
    host: your_mpd_host
    port: 6600
  • platypush.plugins.switch.wemo: Controls a WeMo Switch smart switch device. Requires the package ouimeaux on the target machine. Example:
pusher --target raspberry --action switch.wemo.on
  • platypush.plugins.light.hue: Controls a Philips Hue smart lights system. Requires the package phue on the target machine. Example:
pusher --target raspberry --action light.hue.scene --name "Sunset" --group "Living Room"

Writing your plugins

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:

import batman

from platypush.message.response import Response

from .. import LightPlugin

class LightBatsignalPlugin(LightPlugin):
    def __init__(self, intensity=100):
        super().__init__()
        self.batsignal = batman.Batsignal(intensity)

    def on(self, urgent=False):
        if urgent:
            self.batsignal.notify_robin()

        self.batsignal.on()
        return Response(output='ok')

    def off(self):
        self.batsignal.off()
        return Response(output='ok')

    def toggle(self):
        self.batsignal.toggle()
        return Response(output='ok')

  1. Rebuild and reinstall platypush if required and relaunch it.

  2. Test your new plugin by sending some bullets to it:

pusher --target your_pc --action light.batsignal.on --urgent 1

Writing your backends

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:

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)