Plugins

Pulls

Pulls can be divided into pulls that react on events and pulls that regularly poll for data.

So called Polling components are special pulls that - as stated earlier - regularly poll data or just execute in regular intervals.

Besides the arguments stated in the component description polls always have the following arguments to control their polling behavior.

name type opt. default description
interval str/float yes 60s You may specify duration literals such as 60 (60 secs), 1m, 1h (…) to realize a periodic polling or cron expressions e.g. */1 * * * * (every minute) to realize cron like behavior.
instant_run bool yes False If set to True the component will run as soon as pnp starts; otherwise it will run the next configured interval.

fitbit.Current

plugin type extra version
pnp.plugins.pull.fitbit.Current poll fitbit 0.13.0

Description

Requests various current metrics (steps, calories, distance, …) from the fitbit api for a specific account.

Please see Fitbit Authentication to configure to prepare your account accordingly.

Arguments

name type opt. default description
config str no n/a The configuration file that keeps your initial and refreshed authentication tokens (see Fitbit Authentication for detailed information)
resources List[str] no n/a The resources to request (see below for possible options)
system str yes None The metric system to use based on your localisation (de_DE, en_US, …). Default is your configured metric system in your fitbit account

Note

You can query the following resources:

  • activities/calories
  • activities/caloriesBMR
  • activities/steps
  • activities/distance
  • activities/floors
  • activities/elevation
  • activities/minutesSedentary
  • activities/minutesLightlyActive
  • activities/minutesFairlyActive
  • activities/minutesVeryActive
  • activities/activityCalories
  • body/bmi
  • body/fat
  • body/weight
  • foods/log/caloriesIn
  • foods/log/water
  • sleep/awakeningsCount
  • sleep/efficiency
  • sleep/minutesAfterWakeup
  • sleep/minutesAsleep
  • sleep/minutesAwake
  • sleep/minutesToFallAsleep
  • sleep/startTime
  • sleep/timeInBed

Result

Emits a map that contains the requested resources and their associated values:

{
  "activities/calories": 1216,
  "activities/caloriesBMR": 781,
  "activities/steps": 4048,
  "activities/distance": 3.02385,
  "activities/floors": 4,
  "activities/elevation": 12,
  "activities/minutes_sedentary": 127,
  "activities/minutes_lightly_active": 61,
  "activities/minutes_fairly_active": 8,
  "activities/minutes_very_active": 24,
  "activities/activity_calories": 484,
  "body/bmi": 23.086421966552734,
  "body/fat": 0.0,
  "body/weight": 74.8,
  "foods/log/calories_in": 0,
  "foods/log/water": 0.0,
  "sleep/awakenings_count": 0,
  "sleep/efficiency": 84,
  "sleep/minutes_after_wakeup": 0,
  "sleep/minutes_asleep": 369,
  "sleep/minutes_awake": 69,
  "sleep/minutes_to_fall_asleep": 0,
  "sleep/start_time": "21:50",
  "sleep/time_in_bed": 438
}

Example

# Please point your environment variable `FITBIT_AUTH` to your authentication
# configuration
- name: fitbit_current
  pull:
    plugin: pnp.plugins.pull.fitbit.Current
    args:
      config: !env FITBIT_AUTH
      instant_run: true
      interval: 5m
      resources:
        - 'activities/calories'
        - 'activities/caloriesBMR'
        - 'activities/steps'
        - 'activities/distance'
        - 'activities/floors'
        - 'activities/elevation'
        - 'activities/minutesSedentary'
        - 'activities/minutesLightlyActive'
        - 'activities/minutesFairlyActive'
        - 'activities/minutesVeryActive'
        - 'activities/activityCalories'
        - 'body/bmi'
        - 'body/fat'
        - 'body/weight'
        - 'foods/log/caloriesIn'
        - 'foods/log/water'
        - 'sleep/awakeningsCount'
        - 'sleep/efficiency'
        - 'sleep/minutesAfterWakeup'
        - 'sleep/minutesAsleep'
        - 'sleep/minutesAwake'
        - 'sleep/minutesToFallAsleep'
        - 'sleep/startTime'
        - 'sleep/timeInBed'
  push:
    - plugin: pnp.plugins.push.simple.Echo

fitbit.Devices

plugin type extra version
pnp.plugins.pull.fitbit.Devices poll fitbit 0.13.0

Description

Requests details about your fitbit devices / trackers (battery, model, …) associated to your account.

Please see Fitbit Authentication to configure to prepare your account accordingly.

Arguments

name type opt. default description
config str no n/a The configuration file that keeps your initial and refreshed authentication tokens (see Fitbit Authentication for detailed information)
system str yes None The metric system to use based on your localisation (de_DE, en_US, …). Default is your configured metric system in your fitbit account

Result

Emits a list that contains your available trackers and/or devices and their associated details:

[{
  "battery": "Empty",
  "battery_level": 10,
  "device_version": "Charge 2",
  "features": [],
  "id": "abc",
  "last_sync_time": "2018-12-23T10:47:40.000",
  "mac": "AAAAAAAAAAAA",
  "type": "TRACKER"
}, {
  "battery": "High",
  "battery_level": 95,
  "device_version": "Blaze",
  "features": [],
  "id": "xyz",
  "last_sync_time": "2019-01-02T10:48:39.000",
  "mac": "FFFFFFFFFFFF",
  "type": "TRACKER"
}]

Example

# Please point your environment variable `FITBIT_AUTH` to your authentication
# configuration

tasks:
  - name: fitbit_devices
    pull:
      plugin: pnp.plugins.pull.fitbit.Devices
      args:
        config: !env FITBIT_AUTH
        instant_run: true
        interval: 15m
    push:
      - plugin: pnp.plugins.push.simple.Echo

fitbit.Goal

plugin type extra version
pnp.plugins.pull.fitbit.Goal poll fitbit 0.13.0

Description

Requests your goals (water, steps, …) from the fitbit api.

Please see Fitbit Authentication to configure to prepare your account accordingly.

Arguments

name type opt. default description
config str no n/a The configuration file that keeps your initial and refreshed authentication tokens (see Fitbit Authentication for detailed information)
resources List[str] no n/a The resources to request (see below for possible options)
system str yes None The goals to request (see below for detailed information)

Note

You can query the following resources:

  • body/fat
  • body/weight
  • activities/daily/activeMinutes
  • activities/daily/caloriesOut
  • activities/daily/distance
  • activities/daily/floors
  • activities/daily/steps
  • activities/weekly/distance
  • activities/weekly/floors
  • activities/weekly/steps
  • foods/calories
  • foods/water

Result

Emits a map structure that consists of the requested goals:

{
  "body/fat": 15.0,
  "body/weight": 70.0,
  "activities/daily/active_minutes": 30,
  "activities/daily/calories_out": 2100,
  "activities/daily/distance": 5.0,
  "activities/daily/floors": 10,
  "activities/daily/steps": 6000,
  "activities/weekly/distance": 5.0,
  "activities/weekly/floors": 10.0,
  "activities/weekly/steps": 6000.0,
  "foods/calories": 2220,
  "foods/water": 1893
}

Example

# Please point your environment variable `FITBIT_AUTH` to your authentication
# configuration

tasks:
  - name: fitbit_goal
    pull:
      plugin: pnp.plugins.pull.fitbit.Goal
      args:
        config: !env FITBIT_AUTH
        instant_run: true
        interval: 5m
        goals:
          - body/fat
          - body/weight
          - activities/daily/activeMinutes
          - activities/daily/caloriesOut
          - activities/daily/distance
          - activities/daily/floors
          - activities/daily/steps
          - activities/weekly/distance
          - activities/weekly/floors
          - activities/weekly/steps
          - foods/calories
          - foods/water
    push:
      - plugin: pnp.plugins.push.simple.Echo

fs.FileSystemWatcher

plugin type extra version
pnp.plugins.pull.fs.FileSystemWatcher pull fswatcher < 0.10.0

Description

Watches the given directory for changes like created, moved, modified and deleted files.

Per default will recursively report any file that is touched, changed or deleted in the given path. The directory itself or subdirectories will be object to reporting too, if ignore_directories is set to False.

Arguments

name type opt. default description
path str no n/a The path to track for file / directory changes
recursive bool yes True If set to True, any subfolders of the given path will be tracked too.
patterns List[str] yes None Any file pattern (e.g. *.txt or *.txt, *.md. If set to None no filter is applied.
ignore_patterns List[str] yes None Any patterns to ignore (specify like argument patterns). If set to None, nothing will be ignored.
ignore_directories bool yes False If set to True will send events for directories when file change.
case_sensitive bool yes False If set to True, any pattern is case_sensitive, otherwise it is case insensitive.
events List[str] yes None The events to track. One or multiple of ‘moved’, ‘deleted’, ‘created’ and/or ‘modified’. If set to None all events will be reported.
load_file bool yes False If set to True the file contents will be loaded into the result.
mode str yes auto Open mode of the file (only necessary when load_file is True). Can be text, binary or auto (guessing).
base64 bool yes False If set to True the loaded file contents will be converted to base64 (only applicable when load_file is True). Argument mode will be automatically set to ‘binary’
defer_modified float yes 0.5 There might be multiple flushes of a file before it is written completely to disk. Without defer_modified each flush will raise a modified event.

Result

Example of an emitted message:

{
    "operation": "modified",
    "source": "/tmp/abc.txt",
    "is_directory": False,
    "destination": None,  # Only non-None when operation = "moved"
    "file": {  # Only present when load_file is True
        "file_name": "abc.txt",
        "content": "foo and bar",
        "read_mode": "text",
        "base64": False
    }
}

Example

tasks:
  - name: file_watcher
    pull:
      plugin: pnp.plugins.pull.fs.FileSystemWatcher
      args:
        path: "/tmp"
        ignore_directories: true
        events: [created, deleted, modified]
        load_file: false
    push:
      plugin: pnp.plugins.push.simple.Echo

fs.Size

plugin type extra version
pnp.plugins.pull.fs.Size poll none 0.17.0

Description

Periodically determines the size of the specified files or directories in bytes.

Arguments

name type opt. default description
paths List[str] no n/a List of files and/or directories to monitor their sizes in bytes.
fail_on_error bool yes True If set to true, the plugin will raise an error when a file/directory does not exists or any other file system related error occurs. Otherwise the plugin will proceed and simply report None as size.

Note

Be careful when adding directories with a large amount of files. This will be prettly slow cause the plugin will iterate over each file and determine it’s individual size.

Result

Example of an emitted message. Size is in bytes.

{
  "logs": 32899586,
  "copy": 28912
}

Example

tasks:
  - name: file_size
    pull:
      plugin: pnp.plugins.pull.fs.Size
      args:
        instant_run: true
        interval: 5s
        fail_on_error: false
        paths:
          logs: /var/log  # directory - recursively determines size
          copy: /bin/cp  # file
    push:
      plugin: pnp.plugins.push.simple.Echo

ftp.Server

plugin type extra version
pnp.plugins.pull.ftp.Server pull ftp 0.17.0

Description

Runs a ftp server on the specified port to receive and send files by ftp protocol.

Optionally sets up a simple user/password authentication mechanism.

Arguments

name type opt. default description
directory str yes None The directory to serve via ftp protocol. If not given a directory is created is created temporarily to accept incoming uploads.
port int yes 2121 The port to listen on.
user_pwd str / Tuple[str] yes None User/password combination (as a tuple/list; see example). You may specify the user only - password will be empty OR you can enable anonymous access by not providing the argument.
events List[str] yes None A list of events to subscribe to. Available events are: connect, disconnect, login, logout, file_received, file_sent, file_received_incomplete, file_sent_incomplete. By default all events are subscribed.
max_cons int yes 256 The maximum number of simultaneous connections the ftpserver will permit.
max_cons_ip int yes 5 The maximum number of simultaneous connections from the same ip. Default is 5.

Result

All emitted messages will have an event field to identify the type of the event and an - optional - data field.

The data field will contain the user for login/logout events and the file_path for file-related events.

{
  "event": "file_received",
  "data": {
    "file_path": "/private/tmp/ftp/test.txt"
  }
}

Example

tasks:
  - name: ftp_server
    pull:
      plugin: pnp.plugins.pull.ftp.Server
      args:
        directory: !env FTP_DIR
        user_pwd: [admin, root]  # user: admin, pw: root
        events:
          - file_received
          - file_sent
    push:
      - plugin: pnp.plugins.push.simple.Echo

gpio.Watcher

plugin type extra version
pnp.plugins.pull.gpio.Watcher poll gpio 0.12.0

Description

Listens for low/high state changes on the configured gpio pins.

In more detail the plugin can raise events when one of the following situations occur:

  • rising (high) of a gpio pin - multiple events may occur in a short period of time
  • falling (low) of a gpio pin - multiple events may occur in a short period of time
  • switch of gpio pin - will suppress multiple events a defined period of time (bounce time)
  • motion of gpio pin - will raise the event motion_on if the pin rises and set a timer with a configurable amount of time. Any other gpio rising events will reset the timer. When the timer expires the motion_off event is raised.

Arguments

name type opt. default description
pins List[int] no n/a The gpio pins to observe for state changes. Please see the examples section on how to configure it.
default str no n/a The default edge that is applied when not configured. Please see the examples section for further details. One of rising, falling, switch, motion

Result

Emits a dictionary that contains an entry for every sensor of the plant sensor device:

{
  "gpio_pin": 17  # The gpio pin which state has changed
  "event": rising  # One of [rising, falling, switch, motion_on, motion_off]
}

Example

tasks:
  - name: gpio
    pull:
      plugin: pnp.plugins.pull.gpio.Watcher
      args:
        default: rising
        pins:
          - 2               # No mode specified: Default mode (in this case 'rising')
          - 2               # Duplicates will get ignored
          - 3:rising        # Equal to '3' (without explicit mode)
          - 3:falling       # Get the falling event for gpio pin 3 as well
          - 4:switch        # Uses some debouncing magic and emits only one rising event
          - 5:switch(1000)  # Specify debounce in millseconds (default is 500ms)
          - 5:switch(500)   # Duplicates - even when they have other arguments - will get ignored
          - 7:motion        # Uses some delay magic to emit only one motion on and one motion off event
          - 9:motion(1m)    # Specify delay (default is 30 seconds)
    push:
      - plugin: pnp.plugins.push.simple.Echo

hass.State

plugin type extra version
pnp.plugins.pull.hass.State pull none 0.14.0

Description

Connects to the home assistant websocket api and listens for state changes. If no include or exclude is defined it will report all state changes. If include is defined only entities that match one of the specified patterns will be emitted. If exclude if defined entities that match at least one of the specified patterns will be ignored. exclude patterns overrides include patterns.

Arguments

name type opt. default description
host str no n/a Url to your home assistant instance (e.g. http://my-hass:8123)
token str no n/a Your long lived access token to access the websocket api. See below for further instructions
include List[str] yes n/a Patterns of entity state changes to include. All state changes that do not match the defined patterns will be ignored
exclude List[str] yes n/a Patterns of entity state changes to exclude. All state changes that do match the defined patterns will be ignored

Note

  • include and exclude support wildcards (e.g * and ?)
  • exclude overrides include. So you can include everything from a domain (sensor.*) but exclude individual entities.
  • Create a long lived access token: Home Assistant documentation

Result

The emitted result always contains the entity_id, new_state and old_state:

{
  "entity_id": "light.bedroom_lamp",
  "old_state": {
    "state": "off",
    "attributes": {},
    "last_changed": "2019-01-08T18:24:42.087195+00:00",
    "last_updated": "2019-01-08T18:40:40.011459+00:00"
  },
  "new_state": {
    "state": "on",
    "attributes": {},
    "last_changed": "2019-01-08T18:41:06.329699+00:00",
    "last_updated": "2019-01-08T18:41:06.329699+00:00"
  }
}

Example

tasks:
  - name: hass_state
    pull:
      plugin: pnp.plugins.pull.hass.State
      args:
        url: http://localhost:8123
        token: !env HA_TOKEN
        exclude:
          - light.lamp
        include:
          - light.*
    push:
      - plugin: pnp.plugins.push.simple.Echo

http.Server

plugin type extra version
pnp.plugins.pull.http.Server pull none < 0.10.0

Description

Creates a specific route on the builtin api server and listens to any call to that route. Any data passed to the endpoint will be tried to be parsed to a dictionary (json). If this is not possible the data will be passed as is. See sections Result for specific payload and examples.

Arguments

name type opt. default description
prefix_path str no n/a The route to create for incoming traffic on the builtin api server. See the Example section for reference.
allowed_methods List[str] yes GET List of http methods that are allowed. Default is ‘GET’.

Result

Assumes that you configured your pull with prefix_path = callme

curl -X GET "http://localhost:9999/callme/telephone/now?number=12345&priority=high" --data '{"magic": 42}'
{
  "endpoint": "telephone/now",
  "data": {"magic": 42},
  "levels": ["telephone", "now"],
  "method": "GET",
  "query": {"number": "12345", "priority": "high"},
  "is_json": True,
  "url": "http://localhost:9999/callme/telephone/now?number=12345&priority=high",
  "full_path": "/callme/telephone/now?number=12345&priority=high",
  "path": "/callme/telephone/now"
}

Example

#
# Registers the endpoint /callme to the builtin api server.
# Use curl to try it out:
#   curl -X GET "http://localhost:9999/callme/telephone/now?number=12345&priority=high" --data '{"magic": 42}'
#

api:  # You need to enable the api
  port: 9999  # Mandatory
tasks:
  - name: server
    pull:
      plugin: pnp.plugins.pull.http.Server
      args:
        prefix_path: callme  # Results into http://localhost:9999/callme
        allowed_methods:  # Specify which methods are allowed
          - GET
          - POST
    push:
      plugin: pnp.plugins.push.simple.Echo

monitor.Stats

plugin type extra version
pnp.plugins.pull.monitor.Stats poll none 0.12.0

Description

Emits every interval various metrics / statistics about the host system. Please see the Result section for available metrics.

Result

Emits a dictionary that contains an entry for every sensor of the plant sensor device:

{
  "cpu_count": 4,
  "cpu_freq": 2700,
  "cpu_temp": 0.0,
  "cpu_use": 80.0,
  "disk_use": 75.1,
  "load_1m": 2.01171875,
  "load_5m": 1.89501953125,
  "load_15m": 1.94189453125,
  "memory_use": 67.0,
  "rpi_cpu_freq_capped": 0,
  "rpi_temp_limit_throttle": 0,
  "rpi_throttle": 0,
  "rpi_under_voltage": 0,
  "swap_use": 36.1
}

Example

tasks:
  - name: stats
    pull:
      plugin: pnp.plugins.pull.monitor.Stats
      args:
        interval: 10s
        instant_run: true
    push:
      plugin: pnp.plugins.push.simple.Echo

mqtt.Subscribe

plugin type extra version
pnp.plugins.pull.mqtt.Subscribe pull none < 0.10.0

Description

Pulls messages from the specified topic from the given mosquitto mqtt broker (identified by host and port).

Arguments

name type opt. default description
host str no n/a Host where the mosquitto broker is running.
port int no n/a Port where the mosquitto broker is listening.
topic str no n/a Topic to listen for new messages. You can listen to multiple topics by using the #-wildcard (e.g. test/# will listen to all topics underneath test).

Result

The emitted message will look like this:

{
  "topic": "test/device/device1",
  "levels": ["test", "device", "device1"]
  "payload": "The actual event message"
}

Example

tasks:
  - name: mqtt
    pull:
      plugin: pnp.plugins.pull.mqtt.Subscribe
      args:
        host: localhost
        port: 1883
        topic: test/#
    push:
      plugin: pnp.plugins.push.simple.Echo

net.PortProbe

plugin type extra version
pnp.plugins.pull.net.PortProbe poll none 0.19.0

Description

Periodically establishes socket connection to check if anybody is listening on a given server on a specific port.

Arguments

name type opt. default description
port int no n/a The port to probe if a service is listening
server str yes localhost Server name or ip address
timeout float yes 1.0 Timeout for remote operations

Result

Emits a dictionary that contains an entry for every sensor of the plant sensor device:

{
  "server": "www.google.de",
  "port": 80,
  "reachable": True
}

Example

tasks:
  - name: port_probe
    pull:
      plugin: pnp.plugins.pull.net.PortProbe
      args:
        server: localhost  # Server name or ip address, default is localhost
        port: 9999  # The port to probe if somebody is listening
        interval: 5s  # Probe the port every five seconds ...
        instant_run: true  # ... and run as soon as pnp starts
    push:
      - plugin: pnp.plugins.push.simple.Echo

net.Speedtest

plugin type extra version
pnp.plugins.pull.net.Speedtest poll speedtest 0.25.0

Description

Performs a speedtest of your internet connection using speedtest.net

Arguments

No arguments

Result

{
  "download_speed_bps": 13815345.098080076,
  "download_speed_mbps": 13.82,
  "upload_speed_bps": 1633087.176468341,
  "upload_speed_mbps": 1.63,
  "ping_latency": 19.933,
  "result_image": "http://www.speedtest.net/result/10049630297.png",
  "server": {
    "name": "Deutsche Telekom",
    "host": "ham.wsqm.telekom-dienste.de:8080",
    "location": {
      "city": "Hamburg",
      "country": "Germany",
      "lat": "53.5653",
      "lon": "10.0014"
    }
  },
  "client": {
    "isp": "Vodafone DSL",
    "rating": "3.7"
  }
}

Example

tasks:
  - name: speedtest
    pull:
      plugin: pnp.plugins.pull.net.Speedtest
      args:
        num_parallel_requests: 2  # Number of parallel requests
        interval: 1h  # Run every hour
        instant_run: true  # Run as soon as pnp starts
    push:
      - plugin: pnp.plugins.push.simple.Echo

net.SSLVerify

plugin type extra version
pnp.plugins.pull.net.SSLVerify poll none 0.22.0

Description

Periodically checks if the ssl certificate of a given host is valid and how many days are remaining before the certificate will expire.

Arguments

name type opt. default description
host str no n/a The host to check for it’s SSL certificate.
timeout float yes 3.0 Timeout for remote operation.

Result

{
  # Envelope
  "host": "www.google.com",
  "payload": {
    "expires_days": 50,  # Remaining days before expiration
    "expires_at": datetime.datetime(2020, 5, 26, 9, 45, 52),  # Python datetime of expiration
    "expired": False  # True of the certificate is expired; otherwise False.
  }
}

Example

tasks:
  - name: ssl_verify
    pull:
      plugin: pnp.plugins.pull.net.SSLVerify
      args:
        host: www.google.com  # Check the ssl certificate for this host
        interval: 1m  # Check the ssl certificate every minute
        instant_run: true  # ... and run as soon as pnp starts
    push:
      - plugin: pnp.plugins.push.simple.Echo

presence.FritzBoxTracker

plugin type extra version
pnp.plugins.pull.presence.FritzBoxTracker poll fritz 0.22.0

Description

Periodically asks a Fritz!Box router for the devices that were connected in the past or right now.

Note

Extra fritz is only compatible with python 3.6 or higher

Arguments

name type opt. default description
host str yes 169.254.1.1 The IP address of your Fritz!Box.
user str yes admin The user to use.
password str yes <empty> The password to use.
offline_delay int yes 0 Defines how many intervals to wait before marking a device as not connected after the Fritz!Box reported the device as not connected anymore. This is useful for mobile devices that go temporarily to sleep and drop connection. Default is 0 -> Disconnected devices will be instantly reported as disconnected.
whitelist List[str] yes None A specific list of devices to track (identified by mac address). If not passed all devices will be fetched.

Note

By using the default values you should be able to connect to your Fritz!Box, because the necessary operation can be performed anonymously.

Result

{
  "ip": "192.168.178.2",
  "mac": "00:0a:95:9d:68:16",
  "status": True,  # True or False
  "name": "pc1"
}

Example

tasks:
  - name: fritzbox_tracker
    pull:
      plugin: pnp.plugins.pull.presence.FritzBoxTracker
      args:
        host: 169.254.1.1  # IP of your Fritz!Box. Default is 169.254.1.1
        user: admin  # User name. Default is admin
        password: ''  # Password. Default is an empty string
        offline_delay: 0  # How many intervals to wait before marking a device as not connected after the fritzbox reported so
        instant_run: true  # ... and run as soon as pnp starts
    push:
      - plugin: pnp.plugins.push.simple.Echo
tasks:
  - name: fritzbox_tracker_whitelist
    pull:
      plugin: pnp.plugins.pull.presence.FritzBoxTracker
      args:
        host: 169.254.1.1  # IP of your Fritz!Box. Default is 169.254.1.1
        user: admin  # User name. Default is admin
        password: ''  # Password. Default is an empty string
        offline_delay: 0  # How many intervals to wait before marking a device as not connected after the fritzbox reported so
        whitelist:  # A specific list of devices to track (identified by mac address)
          - B0:05:94:77:B8:3B
          - 90:CD:B6:DC:8D:61
        instant_run: true  # ... and run as soon as pnp starts
    push:
      - plugin: pnp.plugins.push.simple.Echo

sensor.DHT

plugin type extra version
pnp.plugins.pull.sensor.DHT poll dht < 0.10.0

Description

Periodically polls a dht11 or dht22 (aka am2302) for temperature and humidity readings. Polling interval is controlled by interval.

Arguments

name type opt. default description
device str yes dht22 The device to poll (one of dht22, dht11, am2302).
data_gpio int yes 17 The data gpio port where the device operates on.
humidity_offset float yes 0.0 Positive/Negative offset for humidity.
temp_offset float yes 0.0 Positive/Negative offset for temperature.

Result

{
  "humidity": 65.4  # in %
  "temperature": 23.7  # in celsius
}

Example

tasks:
  - name: dht
    pull:
      plugin: pnp.plugins.pull.sensor.DHT
      args:
        device: dht22  # Connect to a dht22
        data_gpio: 17  # DHT is connected to gpio port 17
        interval: 5m  # Polls the readings every 5 minutes
        humidity_offset: -5.0  # Subtracts 5% from the humidity reading
        temp_offset: 1.0  # Adds 1 °C to the temperature reading
        instant_run: true
    push:
      - plugin: pnp.plugins.push.simple.Echo
        selector: payload.temperature  # Temperature reading
      - plugin: pnp.plugins.push.simple.Echo
        selector: payload.humidity  # Humidity reading

sensor.MiFlora

plugin type extra version
pnp.plugins.pull.sensor.MiFlora poll miflora 0.16.0

Description

Periodically polls a Xiaomi MiFlora plant sensor for sensor readings (temperature, conductivity, light, …) via btle

Arguments

name type opt. default description
mac str no n/a

The device to poll identified by mac address.

See below for further instructions

adapter str yes hci0

The bluetooth adapter to use (if you have more than one).

Default is hci0 which is your default bluetooth device.

Note

Start a bluetooth scan to determine the MAC addresses of the sensor (look for Flower care or Flower mate entries) using this command:

$ sudo hcitool lescan
LE Scan ...
F8:04:33:AF:AB:A2 [TV] UE48JU6580
C4:D3:8C:12:4C:57 Flower mate
[...]

Result

Emits a dictionary that contains an entry for every sensor of the plant sensor device:

{
  "conductivity": 800,
  "light": 2000,
  "moisture": 42,
  "battery": 72,
  "temperature": 24.2,
  "firmaware": "3.1.9"
}

Example

- name: miflora
  pull:
    plugin: pnp.plugins.pull.sensor.MiFlora
    args:
      mac: 'C4:7C:8D:67:50:AB'  # The mac of your miflora device
      instant_run: true
  push:
    - plugin: pnp.plugins.push.simple.Echo

sensor.Sound

plugin type extra version
pnp.plugins.pull.sensor.Sound pull sound 0.15.0

Description

Listens to the microphone in realtime and searches the stream for specific sound patterns.

Practical example: I use this plugin to recognize my doorbell without tampering with the electrical device ;-)

Arguments

name type opt. default description
wav_files List[Dict] no n/a See below for a detailed description
device_index int yes None The index of the microphone device. Run pnp_record_sound --list to get the index. If not specified pyAudio will try to find a capable device
ignore_overflow bool yes true If set to True any buffer overflows due to slow realtime processing will be ignored. Otherwise an exception will be thrown and the plugin will abort.
  • wav_files

    A list of dictionaries containing the configuration for each file that contains an original sound pattern to listen for. Possible keys:

    name type opt. default description
    path str no n/a The path to the original sound file. Absolute or relative to the pnp configuration file
    mode str yes pearson Correlation/similarity method. Default is pearson. Try out which one is best for you
    offset float yes 0.0 Adjusts sensitivity for similarity. Positive means less sensitive; negative is more sensitive. You should try out 0.1 steps
    cooldown Dict yes special See below for a detailed description
  • cooldown

    Contains the cooldown configuration. Default is a cooldown period of 10 seconds and no emit of a cooldown event. Possible keys:

    name type opt. default description
    period str yes 10s Prevents the pull to emit more than one sound detection event per cool down period.
    emit_event bool yes false If set to true the end of the cooldown period will an emit as well.

Note

  • You can list your available input devices: pnp_record_sound --list
  • You can record a wav file from an input device: pnp_record_sound <out.wav> --seconds=<seconds_to_record> --index=<idx>
  • This one is _not_ pre-installed when using the docker image. Would be grateful if anyone can integrate it

Result

Will emit the event below when the correlation coefficient is above or equal the threshold. In this case the component has detected a sound that is similar to one of the given sound patterns

{
  "type": "sound"  # Type 'sound' means we detected a sound pattern
  "sound": ding,  # Name of the wav_file without path and extension. To differentiate if you have multiple patterns you listen to
  "corrcoef": 0.82,  # Correlation coefficient probably between [-1;+1] for pearson
  "threshold": 0.6  # Threshold influenced by sensitivity_offset
}

Will emit the event below when you have configured the component to send cooldown events as well.

{
  "type": "cooldown"  # Type 'cooldown' means that we previously identified a sound pattern and the cooldown has happened
  "sound": ding,  # Name of the wav_file without path and extension. To differentiate if you have multiple patterns you listen to
}

Example

tasks:
  - name: sound_detector
    pull:
      plugin: pnp.plugins.pull.sensor.Sound
      args:
        wav_files:  # The files to compare for similarity
          - path: ding.wav  # Absolute or relative (from the config) path to the wav file
            mode: std  # Use std correlation coefficient [pearson, std]; optional default is pearson
            offset: -0.5  # Adjust sensitivity. Positive means less sensitive; negative is more sensitive. Default is 0.0
          - path: doorbell.wav  # This will use default values for mode and offset (pearson, 0.0)
            cooldown:
              period: 10s  # Prevents the pull to emit more than one sound detection event every 10 seconds
              emit_event: true  # Fire an event after the actual cool down - Useful for binary_sensors to return to their 'off' state
        device_index:  # The index of the microphone devices. If not specified pyAudio will try to find a capable device
        ignore_overflow: true  # Some devices might be too slow to process the stream in realtime. Ignore any buffer overflow errors.
    push:
      - plugin: pnp.plugins.push.simple.Echo

simple.Count

plugin type extra version
pnp.plugins.pull.simple.Count poll none < 0.10.0

Description

Emits every interval seconds a counting value which runs from from_cnt to to_cnt. If to_cnt is None the counter will count to infinity (or more precise to sys.maxsize).

Arguments

name type opt. default description
from_cnt int yes 0 Starting value of the counter.
to_cnt int yes sys.maxsize End value of the counter. If not passed set to “infinity” (precise: sys.maxsize)

Result

Counter value (int).

Example

tasks:
  - name: count
    pull:
      plugin: pnp.plugins.pull.simple.Count
      args:
        interval: 1s
        from_cnt: 1
        to_cnt: 10
    push:
      plugin: pnp.plugins.push.simple.Echo

simple.Cron

plugin type extra version
pnp.plugins.pull.simple.Cron pull none 0.16.0

Description

Execute push-components based on time constraints configured by cron-like expressions.

This plugin basically wraps cronex to parse cron expressions and to check if any job is pending. See the documentation of cronex for a guide on featured/supported cron expressions.

Arguments

name type opt. default description
exppresions List[str] no n/a Cron like expressions to configure the scheduler.

Result

Imagine your cron expressions looks like this: */1 * * * * every minute. The pull will emit the text every minute every minute.

Example

tasks:
  - name: cron
    pull:
      plugin: pnp.plugins.pull.simple.Cron
      args:
        expressions:
          - "*/1 * * * * every minute"
          - "0 15 * * * 3pm"
          - "0 0 * * * midnight every day"
          - "0 16 * * 1-5 every weekday @ 4pm"
    push:
      plugin: pnp.plugins.push.simple.Echo

simple.Repeat

plugin type extra version
pnp.plugins.pull.simple.Repeat poll none < 0.10.0

Description

Emits every interval seconds the same repeat.

Arguments

name type opt. default description
repeat Any no n/a The object to emit.

Result

Emits the repeat-object as it is.

Example

tasks:
  - name: repeat
    pull:
      plugin: pnp.plugins.pull.simple.Repeat
      args:
        repeat: "Hello World"  # Repeats 'Hello World'
        interval: 1s  # Every second
    push:
      plugin: pnp.plugins.push.simple.Echo

simple.RunOnce

plugin type extra version
pnp.plugins.pull.simple.RunOnce pull none 0.23.0

Description

Takes a valid plugins.pull.Polling component and immediately executes it and ventures down the given plugins.push components. If no component to wrap is given it will simple execute the push chain.

Arguments

name type opt. default description
poll plugin yes None The polling component you want to run once. If not passed the push chain will be executed.

Result

Emits the payload of the polling component if given. Otherwise an empty dictionary will be returned.

Example

tasks:
  - name: run_once
    pull:
      plugin: pnp.plugins.pull.simple.RunOnce
    push:
      plugin: pnp.plugins.push.simple.Echo
tasks:
  - name: run_once_wrapped
    pull:
      plugin: pnp.plugins.pull.simple.RunOnce
      args:
        poll:
          plugin: pnp.plugins.pull.monitor.Stats
    push:
      plugin: pnp.plugins.push.simple.Echo

zway.ZwayPoll

plugin type extra version
pnp.plugins.pull.zway.ZwayPoll poll none < 0.10.0

Description

Pulls the specified json content from the zway rest api. The content is specified by the url, e.g. http://<host>:8083/ZWaveAPI/Run/devices will pull all devices and serve the result as a json.

Specify the polling interval by setting the argument interval. User / password combination is required when your api is protected against guest access (by default it is).

Use multiple pushes and the related selectors to extract the required content like temperature readings (see the examples section for guidance).

Arguments

name type opt. default description
url str no n/a The url to poll periodically.
user str no n/a Authentication user name.
password str no n/a Authentication password.

Note

Below are some common selector examples to fetch various metrics from various devices

Fibaro Motion Sensor

  • Temperature payload[deviceid].instances[0].commandClasses[49].data[1].val.value
  • Luminescence payload[deviceid].instances[0].commandClasses[49].data[3].val.value

Fibaro Wallplug

  • Meter payload[deviceid].instances[0].commandClasses[50].data[0].val.value

Thermostat (Danfoss / other should work as well)

  • Setpoint payload[deviceid].instances[0].commandClasses[67].data[1].val.value

Battery operated devices

  • Battery level payload[deviceid].instances[0].commandClasses[128].data.last.value

Result

Emits the content of the fetched url as it is.

Example

# Please make sure to adjust url and device ids
# Username and Password are injected from environment variables:
#     export ZWAY_USER=admin
#     export ZWAY_PASSWORD=secret_one
tasks:
  - name: zway
    pull:
      plugin: pnp.plugins.pull.zway.ZwayPoll
      args:
        url: "http://smarthome:8083/ZWaveAPI/Run/devices"
        interval: 5s
        user: !env ZWAY_USER
        password: !env ZWAY_PASSWORD
    push:
      - plugin: pnp.plugins.push.simple.Echo
        # Temperature of fibaro motion sensor
        # You can access the returned json like you would inquire the zway-api
        selector: payload[19].instances[0].commandClasses[49].data[1].val.value
      - plugin: pnp.plugins.push.simple.Echo
        # Luminiscence of fibaro motion sensor
        selector: payload[19].instances[0].commandClasses[49].data[3].val.value

Pushes

Like a pull a push does support args to initialize the instance of a push. Besides that you can optionally pass a selector to transform the incoming payload and set the unwrap option to invoke a push for each element of an iterable.

See also

Some pushes do support the envelope feature to alter the arguments for a push during runtime: Envelope

fs.FileDump

plugin type extra version
pnp.plugins.push.fs.FileDump push none < 0.10.0

Description

This push dumps the given payload to a file to the specified directory. If argument file_name is None, a name will be generated based on the current datetime (%Y%m%d-%H%M%S). If file_name is not passed (or None) you should pass extension to specify the extension of the generated file name. Argument binary_mode controls whether the dump is binary (mode=wb) or text (mode=w).

Arguments

name type opt. default env description
directory str yes . (cwd) no The target directory to store the dumps.
file_name str yes None yes The name of the file to dump. If not passed a file name will be automatically generated.
extension str yes .dump yes The extension to use when the file name is automatically generated.
binary_mode bool yes False no If set to True the file will be written in binary mode (wb); otherwise in text mode (w).

Result

Will return an absolute path to the file created.

Example

tasks:
  - name: file_dump
    pull:
      plugin: pnp.plugins.pull.simple.Repeat
      args:
        repeat: "Hello World"
    push:
      plugin: pnp.plugins.push.fs.FileDump
      args:
        directory: !env WATCH_DIR
        file_name: null  # Auto-generated file (timestamp)
        extension: ".txt"  # Extension of auto-generated file
        binary_mode: false  # text mode
      deps:
        - plugin: pnp.plugins.push.simple.Echo
tasks:
  - name: file_dump
    pull:
      plugin: pnp.plugins.pull.simple.Repeat
      args:
        repeat: "Hello World"
    push:
      plugin: pnp.plugins.push.fs.FileDump
      # Override `file_name` and `extension` via envelope.
      # Instead of an auto generated file, the file '/tmp/hello-world.hello' will be dumped.
      selector:
        data: "lambda data: data"
        file_name: hello-world
        extension: .hello
      args:
        directory: !env WATCH_DIR
        file_name: null  # Auto-generated file (timestamp)
        extension: ".txt"  # Extension of auto-generated file
        binary_mode: false  # text mode
      deps:
        - plugin: pnp.plugins.push.simple.Echo

fs.Zipper

plugin type extra version
pnp.plugins.push.fs.Zipper push none 0.21.0

Description

The push expects a directory or a file path to be passed as the payload. As long it’s a valid path it will zip the directory or the single file and return the absolute path to the created zip file.

Note

You can use a so called .zipignore file to exclude files and directories from zipping. It works - mostly - like a .gitignore file. To use a .zipignore file you have to put it in the root the folder you want to zip. An example .zipignore looks like this:

__pycache__/
*.log

This example will ignore all folder called __pycache__ and all files with the extension .log

Arguments

name type opt. default env description
source str yes n/a yes Specifies the source directory or file to zip. If not passed the source can be specified by the envelope at runtime.
out_path str yes tmp no Specifies the path to the general output path where all target zip files should be generated. If not passed the systems temp directory is used.
archive_name str yes below yes Explicitly specifies the name of the resulting archive.

The default of archive_name will be either the original file name (if you zip a single file) resp. the name of the zipped directory (if you zip a directory). In both cases the extension .zip will be added. If you do not want an extension, you have to provide the archive_name.

Result

Will return an absolute path to the zip file created.

Example

tasks:
  - name: zipper
    pull:
      plugin: pnp.plugins.pull.simple.Cron
      args:
        expressions:
          - "*/1 * * * * /path/to/backup"
    push:
      plugin: pnp.plugins.push.fs.Zipper
      args:
        out_path: !env BACKUP_DIR
      deps:
        plugin: pnp.plugins.push.simple.Echo

The next example is useful for dynamically adjusting the archive name to generate unique names for storing multiple backups:

tasks:
  - name: zipper
    pull:
      plugin: pnp.plugins.pull.simple.Cron
      args:
        expressions:
          - "*/1 * * * * /tmp/backup_folder"
    push:
      plugin: pnp.plugins.push.fs.Zipper
      args:
        out_path: !env BACKUP_DIR
      selector:
        archive_name: "lambda payload: '{}_{}'.format(now().isoformat(), 'backup.zip')"
        data: "lambda payload: payload"
      deps:
        plugin: pnp.plugins.push.simple.Echo

hass.Service

plugin type extra version
pnp.plugins.push.hass.Service push none 0.16.0

Description

Calls a home assistant service providing the payload as service-data.

Arguments

name type opt. default env description
url str no n/a no The url to your home assistant instance (e.g. http://hass:8123)
token str no n/a no The long live access token to get access to home assistant.
domain str no n/a no The domain of the service to call.
service str no n/a no The name of the service to call.
timeout int|float yes 5.0 no Tell the request to stop waiting for a response after given number of seconds.

Note

Create a long lived access token: Home Assistant documentation

Result

Returns the payload as-is for better chaining (this plugin can’t add any useful information).

Example

# Calls the frontend.set_theme service to oscillate between a "light" and a "dark" theme
tasks:
  - name: hass_service
    pull:
      plugin: pnp.plugins.pull.simple.Count
      args:
        interval: 10s
    push:
      plugin: pnp.plugins.push.hass.Service
      selector:
        name: "lambda i: 'clear' if i % 2 == 0 else 'dark'"
      args:
        url: http://localhost:8123
        token: !env HA_TOKEN
        domain: frontend
        service: set_theme
# Calls the notify.notify service to send a message with the actual counter
tasks:
  - name: hass_service
    pull:
      plugin: pnp.plugins.pull.simple.Count
      args:
        interval: 10s
    push:
      plugin: pnp.plugins.push.hass.Service
      selector:
        message: "lambda i: 'Counter: ' + str(i)"
      args:
        url: http://localhost:8123
        token: !env HA_TOKEN
        domain: notify
        service: notify

http.Call

plugin type extra version
pnp.plugins.push.http.Call push none 0.21.0

Description

Makes a request to a http resource.

Arguments

name type opt. default env description
url str no n/a yes Request url.
method str yes GET yes The http method to use for the request. Must be a valid http method (GET, POST, …).
fail_on_error bool yes False yes If True the push will fail on a http status code <> 2xx. This leads to an error message recorded into the logs and no further execution of any dependencies.
provide_response bool yes False no If True the push will not return the payload as it is, but instead provide the response status_code, fetched url content and a flag if the url content is a json response. This is useful for other push instances in the dependency chain.

Result

Will return the payload as it is for easy chaining of dependencies. If provide_response is True the push will return a dictionary that looks like this:

{
  "status_code": 200,
  "data": "fetched url content",
  "is_json": False
}

Example

# Simple example calling the built-in rest server
# Oscillates between http method GET and POST. Depending on the fact if the counter is even or not.
api:
  port: 9999
tasks:
  - name: http_call
    pull:
      plugin: pnp.plugins.pull.simple.Count
      args:
        interval: 5s
    push:
      plugin: pnp.plugins.push.http.Call
      selector:
        data:
          counter: "lambda data: data"
        method: "lambda data: 'POST' if int(data) % 2 == 0 else 'GET'"
      args:
        url: http://localhost:9999/counter
  - name: rest_server
    pull:
      plugin: pnp.plugins.pull.http.Server
      args:
        prefix_path: counter
        allowed_methods:
          - GET
          - POST
    push:
      plugin: pnp.plugins.push.simple.Echo
# Demonstrates the use of `provide_response` set to True.
# Call will return a response object to dependent push instances.
api:
  port: 9999
tasks:
  - name: http_call
    pull:
      plugin: pnp.plugins.pull.simple.Count
      args:
        interval: 5s
    push:
      plugin: pnp.plugins.push.http.Call
      args:
        url: http://localhost:9999/counter
        provide_response: true
      deps:
        plugin: pnp.plugins.push.simple.Echo
  - name: rest_server
    pull:
      plugin: pnp.plugins.pull.http.Server
      args:
        prefix_path: counter
        allowed_methods:
          - GET
    push:
      plugin: pnp.plugins.push.simple.Nop

ml.FaceR

plugin type extra version
pnp.plugins.push.ml.FaceR push faceR < 0.10.0

Description

FaceR (short one for face recognition) tags known faces in images. Output is the image with all faces tagged whether with the known name or an unknown_label. Default for unknown ones is Unknown.

Known faces can be ingested either by a directory of known faces (known_faces_dir) or by mapping of known_faces (dictionary: name -> [list of face files]).

The payload passed to the push method is expected to be a valid byte array that represents an image in memory. Please see the example section for loading physical files into memory.

Note

This one is not pre-installed when using the docker image. Would be grateful if anyone can integrate it

Arguments

name type opt. default env description
known_faces Dict[str, str] yes None no Mapping of a person’s name to a list of images that contain the person’s face.
known_faces_dir str yes None no A directory containing images with known persons (file_name -> person’s name).
unknown_label str yes Unknown no Tag label of unknown faces.
lazy bool yes False no If set to True the face encodings will be loaded when the first push is executed (lazy); otherwise the encodings are loaded when the plugin is initialized (during __init__).

Note

You need to specify either known_faces or known_faces_dir

Result

Will return a dictionary that contains the bytes of the tagged image (key tagged_image) and metadata (no_of_faces, known_faces)

{
  'tagged_image': <bytes of tagged image>
  'no_of_faces': 2
  'known_faces': ['obama']
}

Example

tasks:
  - name: faceR
    pull:
      plugin: pnp.plugins.pull.fs.FileSystemWatcher
      args:
        path: "/tmp/camera"
        recursive: true
        patterns: "*.jpg"
        ignore_directories: true
        case_sensitive: false
        events: [created]
        load_file: true
        mode: binary
        base64: false
    push:
      plugin: pnp.plugins.push.ml.FaceR
      args:
        known_faces_dir: "/tmp/faces"
        unknown_label: "don't know him"
        lazy: true

mqtt.Discovery

plugin type extra version
pnp.plugins.push.mqtt.Discovery push none 0.13.0

Description

Pushes an entity to home assistant by publishing it to an mqtt broker. The entity will be enabled to be auto discovered by home assistant.

Please see the home assistant docs about mqtt auto discovery.

The mqtt topic is structured like this:

<discovery_prefix>/<component>/[<node_id>/]<object_id>/[config|state]

You may also publish attributes besides your state. attributes can be passed by the envelope via runtime. Please see the examples section for further reference.

Arguments

name type opt. default env description
discovery_prefix str no n/a no The prefix for the topic.
component str no n/a no The component / type of the entity, e.g. sensor, light, …
config Dict no n/a no A dictionary of configuration items to configure the entity (e.g. icon -> mdi:soccer)
object_id str yes None yes The ID of the device. This is only to allow for separate topics for each device and is not used for the entity_id.
node_id str yes None yes A non-interpreted structuring entity to structure the MQTT topic.

Note

Inside the config section you can reference some variables to make the configuration easier. The following variables can be referenced via the dictmentor syntax "{{var::<variable>}}":

  • discovery_prefix
  • component
  • object_id
  • node_id
  • base_topic
  • config_topic
  • state_topic
  • json_attributes_topic

Please see the examples section on how to use that.

Result

Returns the payload as-is for better chaining (this plugin can’t add any useful information).

Example

tasks:
  - name: fitbit_steps
    pull:
      plugin: pnp.plugins.pull.fitbit.Current
      args:
        config: !env FITBIT_AUTH
        instant_run: true
        interval: 5m
        resources:
          - activities/steps
    push:
      - plugin: pnp.plugins.push.mqtt.Discovery
        selector: "data.get('activities/steps')"
        args:
          host: localhost
          discovery_prefix: homeassistant
          component: sensor
          object_id: fitbit_steps
          config:
            name: "{{var::object_id}}"
            icon: "mdi:soccer"
name: service_probing
pull:
  plugin: pnp.plugins.pull.net.PortProbe
  args:
    server: server  # Server name or ip address, default is localhost
    port: 3000  # The port to probe if somebody is listening
    timeout: 5
    interval: 2m  # Probe the port every five seconds ...
    instant_run: true  # ... and run as soon as pnp starts
push:
  - plugin: pnp.plugins.push.mqtt.Discovery
    selector:
      data: "lambda data: 'OFF' if data.get('reachable') else 'ON'"
      object_id: "service"
      attributes:
        friendly_name: My Service
        icon: mdi:monitor-dashboard
    args:
      host: !env MQTT_HOST
      discovery_prefix: !env MQTT_BASE_TOPIC
      component: binary_sensor
      config:
        name: "{{var::object_id}}"
        device_class: problem

mqtt.Publish

plugin type extra version
pnp.plugins.push.mqtt.Publish push none < 0.10.0

Description

Will push the given payload to a mqtt broker e.g. mosquitto. The broker is specified by host and port. In addition a topic needs to be specified were the payload will be pushed to (e.g. home/living/thermostat).

The payload will be pushed as it is. No transformation is applied. If you need to some transformations, use the selector.

Arguments

name type opt. default env description
host str no n/a no The host where the mqtt broker is running.
port int yes 1883 no The port where the mqtt broker is listening
topic Dict yes None yes The topic to subscribe to. If set to None the envelope of the payload has to contain a topic key or the push will fail. If both exists the topic from the envelope will overrule the __init__ one.
retain bool yes False no If set to True will mark the message as retained. See the mosquitto man page for further guidance: https://mosquitto.org/man/mqtt-7.html.
multi bool yes False no If set to True the payload is expected to be a dictionary. Each item of that dictionary will be send individually to the broker. The key of the item will be appended to the configured topic.

Result

For chaining of pushes the payload is simply returned as it is.

Example

# Demonstrates the basic mqtt.Publish

tasks:
  - name: mqtt
    pull:
      plugin: pnp.plugins.pull.simple.Count
    push:
      # Will push the counter to the 'home/counter/state' topic
      plugin: pnp.plugins.push.mqtt.Publish
      args:
        host: localhost
        topic: home/counter/state
        port: 1883
        retain: true
# Demonstrates the topic override via envelope

tasks:
  - name: mqtt
    pull:
      plugin: pnp.plugins.pull.simple.Count
      args:
        interval: 1s
    push:
      plugin: pnp.plugins.push.mqtt.Publish
      # Lets override the topic via envelope mechanism
      # Will publish even counts on topic 'even' and uneven counts on 'uneven'
      selector:
        data: "lambda data: data"
        topic: "lambda data: 'test/even' if int(data) % 2 == 0 else 'test/uneven'"
      args:
        host: localhost
        port: 1883
# Demonstrates the use of multi push

tasks:
  - name: mqtt
    pull:
      # Periodically gets metrics about your system
      plugin: pnp.plugins.pull.monitor.Stats
      args:
        instant_run: true
        interval: 10s
    push:
      # Push them to the mqtt
      plugin: pnp.plugins.push.mqtt.Publish
      args:
        host: localhost
        topic: devices/localhost/
        port: 1883
        retain: true
        # Each item of the payload-dict (cpu_count, cpu_usage, ...) will be pushed to the broker as multiple items.
        # The key of the item will be appended to the topic, e.g. `devices/localhost/cpu_count`.
        # The value of the item is the actual payload.
        multi: true

notify.Slack

plugin type extra version
pnp.plugins.push.notify.Slack push none 0.20.0

Description

Sends a message to a given Slack channel.

You can specify the channel, the name of the poster, the icon of the poster and a list of users to ping.

Arguments

name type opt. default env description
api_key str no n/a no The api key of your slack oauth token.
channel str no n/a yes The channel to post the message to.
username str yes PnP yes The username of the message poster.
emoji str yes :robot: yes The emoji of the message poster.
ping_users List[str] yes None yes A list of users to ping when the message is posted. By default no one is ping’d.

Result

Will return the payload as it is for easy chaining of dependencies.

Example

tasks:
  - name: slack
    pull:
      plugin: pnp.plugins.pull.simple.Count  # Let's count
      args:
        wait: 10
    push:
      - plugin: pnp.plugins.push.notify.Slack
        selector:
          data: "lambda data: 'This is the counter: {}'.format(data)"
          # You can override the channel if necessary
          # channel: "lambda data: 'test_even' if int(data) % 2 == 0 else 'test_odd'"
          # You can override the username if necessary
          # username: the_new_user
          # You can override the emoji if necessary
          # emoji: ':see_no_evil:'
          # You can override the ping users if necessary
          # ping_users:
          #   - clone_dede
        args:
          api_key: !env SLACK_API_KEY  # Your slack api key.
          channel: test  # The channel to post to. Mandatory. Overridable by envelope.
          username: slack_tester  # The username to show. Default is PnP. Overridable by envelope
          emoji: ':pig:'  # The emoji to use. Default is :robot: . Overridable by envelope
          ping_users:  # The users you want to ping when the message is send. Overridable by envelope
            - dede

simple.Echo

plugin type extra version
pnp.plugins.push.simple.Echo push none < 0.10.0

Description

Simply log the passed payload to the default logging instance.

Result

Will return the payload as it is for easy chaining of dependencies.

Example

tasks:
  - name: echo
    pull:
      plugin: pnp.plugins.pull.simple.Count
      args:
        interval: 1s
        from_cnt: 1
        to_cnt: 10
    push:
      plugin: pnp.plugins.push.simple.Echo

simple.Execute

plugin type extra version
pnp.plugins.push.simple.Execute push none 0.12.0

Description

Executes a command with given arguments in a shell of the operating system. Both command and args may include placeholders (e.g. {{placeholder}}) which are injected at runtime by passing the specified payload after selector transformation. Please see the examples section for further details.

Will return the exit code of the command and optionally the output from stdout and stderr.

Arguments

name type opt. default env description
command str no n/a no The command to execute. May contain placeholders.
args List[str] yes [] no The arguments to pass to the command. Default is no arguments. May contain placeholders.
cwd str yes special no Specifies where to execute the command (working directory). Default is the folder where the invoked pnp configuration file is located.
timeout str|float yes 5s no Specifies how long the worker should wait for the command to finish.
capture bool yes False no If True stdout and stderr output is captured, otherwise not.

Result

Returns a dictionary that contains the return_code and optionally the output from stdout and stderr whether capture is set or not. The output is a list of lines.

{
  "return_code": 0
  "stdout": ["hello", "dude!"]
  "stderr": []
}

Example

tasks:
  - name: execute
    pull:
      plugin: pnp.plugins.pull.simple.Count
      args:
        interval: 1s
        from_cnt: 1
    push:
      plugin: pnp.plugins.push.simple.Execute
      selector:
        command: echo
        count: "lambda data: str(data)"
        labels:
          prefix: "The actual count is"
          iter: iterations
      args:
        command: "{{command}}"  # The command to execute (passed by selector)
        args:
          - "{{labels.prefix}}"
          - "{{count}}"  # The named argument passed at runtime by selector
          - "{{labels.iter}}"
        timeout: 2s
        cwd:  # None -> pnp-configuration directory
        capture: true  # Capture stdout and stderr
      deps:
        - plugin: pnp.plugins.push.simple.Echo
# How to escape " correctly

tasks:
  - name: execute
    pull:
      plugin: pnp.plugins.pull.simple.Count
      args:
        interval: 1s
        from_cnt: 1
    push:
      plugin: pnp.plugins.push.simple.Execute
      selector:
        command: echo
        salutation: "\"hello you\""
      args:
        command: "{{command}}"  # The command to execute (passed by selector)
        args:
          - "{{salutation}}"
        timeout: 2s
        cwd:  # None -> pnp-configuration directory
        capture: true  # Capture stdout and stderr
      deps:
        - plugin: pnp.plugins.push.simple.Echo
# Capturing multiline output
tasks:
  - name: execute
    pull:
      plugin: pnp.plugins.pull.simple.Count
      args:
        interval: 1s
        from_cnt: 1
    push:
      plugin: pnp.plugins.push.simple.Execute
      selector:
        command: cat multiline.txt
      args:
        command: "{{command}}"  # The command to execute (passed by selector)
        cwd:  # None -> pnp-configuration directory
        capture: true  # Capture stdout and stderr
      deps:
        - plugin: pnp.plugins.push.simple.Echo

simple.Nop

plugin type extra version
pnp.plugins.push.simple.Nop push none < 0.10.0

Description

Executes no operation at all. A call to push(…) just returns the payload. This push is useful when you only need the power of the selector for dependent pushes.

See the example section for an example.

Nop = No operation OR No push ;-)

Result

Will return the payload as it is for easy chaining of dependencies.

Example

tasks:
  - name: nop
    pull:
      plugin: pnp.plugins.pull.simple.Repeat
      args:
        interval: 1s
        repeat:
          - 1
          - 2
          - 3
    push:
      plugin: pnp.plugins.push.simple.Nop
      selector: "data + [4]"
      deps:
        plugin: pnp.plugins.push.simple.Echo
        unwrap: true

simple.Wait

plugin type extra version
pnp.plugins.push.simple.Wait push none 0.19.0

Description

Performs a sleep operation and waits for some time to pass by.

Arguments

name type opt. default env description
wait_for float|str no n/a no The time to wait for before proceeding. You can pass literals such as 5s, 1m; ints such as 1, 2, 3 or floats such as 0.5. In the end everything will be converted to it’s float representation (1 => 1.0; 5s => 5.0; 1m => 60.0; 0.5 => 0.5)

Result

Will return the payload as it is for easy chaining of dependencies.

Example

tasks:
  - name: wait
    pull:
      plugin: pnp.plugins.pull.simple.Count  # Let's count
      args:
        interval: 1s
    push:
      - plugin: pnp.plugins.push.simple.Echo
        selector: "'START WAITING: {}'.format(payload)"
        deps:
          - plugin: pnp.plugins.push.simple.Wait
            args:
              wait_for: 3s
            deps:
              - plugin: pnp.plugins.push.simple.Echo
                selector: "'END WAITING: {}'.format(payload)"

storage.Dropbox

plugin type extra version
pnp.plugins.push.storage.Dropbox push dropbox 0.12.0

Description

Uploads a provided file to the configured dropbox account.

Arguments

name type opt. default env description
api_key str no n/a no The api key to your dropbox account/app.
target_file_name str yes None yes The file path on the server where to upload the file to. If not specified you have to specify this argument during runtime by setting it in the envelope.
created_shared_link bool yes True no If set to True, the push will create a publicly available link to your uploaded file.

Result

Returns a dictionary that contains metadata information about your uploaded file. If you uploaded a file named 42.txt, your result will be similar to the one below:

{
  "name": "42.txt",
  "id": "HkdashdasdOOOOOadss",
  "content_hash": "aljdhfjdahfafuhu489",
  "size": 42,
  "path": "/42.txt",
  "shared_link": "http://someserver/tosomestuff/asdasd?dl=1",
  "raw_link": "http://someserver/tosomestuff/asdasd?raw=1"
}

shared_link is the one that is publicly available (but only if you know the link). Same for raw_link, but this link will return the raw file (without the dropbox overhead).

Both are None if create_shared_link is set to False.

Example

tasks:
  - name: dropbox
    pull:
      plugin: pnp.plugins.pull.fs.FileSystemWatcher
      args:
        path: "/tmp"
        ignore_directories: true
        events:
          - created
          - modified
        load_file: false
    push:
      - plugin: pnp.plugins.push.storage.Dropbox
        args:
          api_key: !env DROPBOX_API_KEY
          create_shared_link: true  # Create a publicly available link
        selector:
          data: "lambda data: data.source"  # Absolute path to file
          target_file_name: "lambda data: basename(data.source)"  # File name only

timedb.InfluxPush

plugin type extra version
pnp.plugins.push.timedb.InfluxPush push none < 0.10.0

Description

Pushes the given payload to an influx database using the line protocol. You have to specify host, port, user, password and the database.

The protocol is basically a string that will be augmented at push-time with data from the payload. E.g. {payload.metric},room={payload.location} value={payload.value} assumes that payload contains metric, location and value.

Arguments

name type opt. default env description
host str no n/a no The host where influx service is running.
port int no n/a no The port where the influx service is listening on.
user str no n/a no Username to use for authentication.
password str no n/a no Related password.
database str no n/a no The database to store the measurement.
protocol str no n/a no Line protocol template (augmented with payload-data).

Result

For the ability to chain multiple pushes together the payload is simply returned as is.

Example

tasks:
  - name: influx_push
    pull:
      plugin: pnp.plugins.pull.mqtt.Subscribe
      args:
        host: mqtt
        topic: home/#
    push:
      plugin: pnp.plugins.push.timedb.InfluxPush
      selector:
        data: "lambda data: data"
      args:
        host: influxdb
        port: 8086
        user: root
        password: secret
        database: home
        # This assumes that your topics are structured like this:
        # home/<room e.g. living>/<sensor e.g. humidity>
        protocol: "{payload.levels[2]},room={payload.levels[1]} value={payload.payload}"

UDFs

New in version 0.14.0.

All udfs do share the following base arguments:

name type opt. default description
throttle str/float yes None If set to a valid duration literal (e.g. 5m) the return value of the called functions will be cached for the given amount of time.

Note

Please note that even when an udf does not require arguments, you anyway have to specify the args: section. Otherwise it will be interpreted as a regular function and not as a UDF.

...
udfs:
  - name: fsize
    args:
tasks:
  ...

hass.State

plugin type extra version
pnp.plugins.udf.hass.State udf none 0.14.0

Description

Fetches the state of an entity from home assistant by a rest-api call.

Arguments

name type opt. default description
url str no n/a The url to your home assistant instance (e.g. http://hass:8123)
token str no n/a The long lived access token to get access to home assistant
timeout float yes 5.0 Tell the request to abort the waiting for a response after given number of seconds

Note

Create a long lived access token: Home Assistant documentation

Call Arguments

name type opt. default description
entity_id str no n/a The entity to fetch the state
attribute str yes None Optionally you can fetch the state of one of the entity attributes. Not passed will fetch the state of the entity

Result

Returns the current state of the entity or one of it’s attributes. If the entity is not known to home assistant an exception is raised. In case of an attribute does not exists, None will be returned instead to signal it’s absence.

Example

udfs:
  # Defines the udf. name is the actual alias you can call in selector expressions.
  - name: hass_state
    plugin: pnp.plugins.udf.hass.State
    args:
      url: http://localhost:8123
      token: !env HA_TOKEN
tasks:
  - name: hass_state
    pull:
      plugin: pnp.plugins.pull.simple.Repeat
      args:
        repeat: "Hello World"  # Repeats 'Hello World'
        interval: 1s  # Every second
    push:
      - plugin: pnp.plugins.push.simple.Echo
        # Will only print the data when attribute azimuth of the sun component is above 200
        selector: "'azimuth is greater than 200' if hass_state('sun.sun', attribute='azimuth') > 200.0 else SUPPRESS"
      - plugin: pnp.plugins.push.simple.Echo
        # Will only print the data when the state of the sun component is above 'above_horizon'
        selector: "'above_horizon' if hass_state('sun.sun') == 'above_horizon' else SUPPRESS"

simple.Counter

plugin type extra version
pnp.plugins.udf.simple.Counter udf none 0.14.0

Description

Memories a counter value which is increased everytime you call the udf.

Arguments

name type opt. default description
init int yes 0 The initialization value of the counter.

Result

Returns the current counter.

Example

udfs:
  # Defines the udf. name is the actual alias you can call in selector expressions.
  - name: counter
    plugin: pnp.plugins.udf.simple.Counter
    args:
tasks:
  - name: countme
    pull:
      plugin: pnp.plugins.pull.simple.Repeat
      args:
        repeat: "Hello World"  # Repeats 'Hello World'
        interval: 1s  # Every second
    push:
      - plugin: pnp.plugins.push.simple.Echo
        selector:
          data: "lambda data: data"
          count: "lambda data: counter()"  # Calls the udf

simple.FormatSize

plugin type extra version
pnp.plugins.udf.simple.FormatSize udf none 0.14.0

Description

Returns the size of a file (or whatever) as a human readable size (e.g. bytes, KB, MB, GB, TB, PB). The input is expected to be at byte scale.

Call Arguments

name type opt. default description
size_in_bytes int|float no n/a The size in bytes to format to a human readable format.

Result

Returns the argument in a human readable size format.

Example

udfs:
  # Defines the udf. name is the actual alias you can call in selector expressions.
  - name: fsize
    plugin: pnp.plugins.udf.simple.FormatSize
    args:
tasks:
  - name: format_size
    pull:
      plugin: pnp.plugins.pull.fs.Size
      args:
        instant_run: true
        interval: 5s
        fail_on_error: false
        paths:
          logs: /var/log  # directory - recursively determines size
          copy: /bin/cp  # file
    push:
      plugin: pnp.plugins.push.simple.Nop
      selector: "[(k ,v) for k, v in data.items()]"  # Transform the dictionary into a list
      deps:
        plugin: pnp.plugins.push.simple.Echo
        unwrap: true  # Call the push for each individual item in the list
        selector:
          object: "lambda d: d[0]"
          data: "lambda d: fsize(d[1])"

simple.Memory

plugin type extra version
pnp.plugins.udf.simple.Memory udf none 0.14.0

Description

Returns a previously memorized value when called.

Arguments

name type opt. default description
init Any yes None The initial memory of the plugin. When not set initially the first call will return the value of new_memory, if specified; otherwise None.

Call Arguments

name type opt. default description
new_memory Any no None After emitting the current memorized value the current memory is overwritten by this value. Will only be overwritten if the parameter is specified.

Result

Returns the memorized value.

Example

udfs:
  - name: mem
    plugin: pnp.plugins.udf.simple.Memory
    args:
tasks:
  - name: countme
    pull:
      plugin: pnp.plugins.pull.simple.Count
      args:
        from_cnt: 1
        interval: 1s  # Every second
    push:
      - plugin: pnp.plugins.push.simple.Echo
        # Will memorize every uneven count
        selector: "mem() if data % 2 == 0 else mem(new_memory=data)"
udfs:
  - name: mem
    plugin: pnp.plugins.udf.simple.Memory
    args:
tasks:
  - name: countme
    pull:
      plugin: pnp.plugins.pull.simple.Count
      args:
        from_cnt: 1
        interval: 1s  # Every second
    push:
      - plugin: pnp.plugins.push.simple.Echo
        # Will memorize every uneven count
        selector: "mem() if data % 2 == 0 else mem(new_memory=data)"

Appendix

Fitbit Authentication

To request data from the fitbit account it is necessary to create an app. Go to dev.fitbit.com. Under Manage go to Register an App.

For the application website and organization website, name it anything starting with http:// or https://. Secondly, make sure the OAuth 2.0 Application Type is Personal. Lastly, make sure the Callback URL is http://127.0.0.1:8080/ in order to get our Fitbit API to connect properly. After that, click on the agreement box and submit. You will be redirected to a page that contains your Client ID and your Client Secret.

Next we need to acquire your initial access- and refresh-token.

git clone https://github.com/orcasgit/python-fitbit.git
cd python-fitbit
python3 -m venv venv
source venv/bin/activate
pip install -r dev.txt
./gather_keys_oauth2.py <client_id> <client_secret>

You will be redirected to your browser and asked to login to your fitbit account. Next you can restrict the app to certain data. If everything is fine, your console window should print your access- and refresh-token and also expires_at.

Put your client_id, client_secret, access_token, refresh_token and expires_at to a yaml file and use this file-path as the config argument of this plugin. Please see the example below:

access_token: <access_token>
client_id: <client_id>
client_secret: <client_secret>
expires_at: <expires_at>
refresh_token: <refresh_token>

That’s it. If your token expires it will be refreshed automatically by the plugin.