Moved to manifest files for describing plugins and backends and their dependencies

This commit is contained in:
Fabio Manganiello 2021-09-16 17:53:40 +02:00
parent ac0dd95bcc
commit 3bfc5b83ef
437 changed files with 3139 additions and 1121 deletions

View File

@ -1,2 +1,3 @@
recursive-include platypush/backend/http/webapp/dist *
include platypush/plugins/http/webpage/mercury-parser.js
global-include manifest.yaml

View File

@ -6,12 +6,12 @@
# and automatically managed the required dependencies, as well as start, #
# stop and remove them #
# #
# @author: Fabio Manganiello <blacklight86@gmail.com> #
# @author: Fabio Manganiello <fabio@platypush.tech> #
# @licence: MIT #
##############################################################################
workdir=$HOME/.local/share/platypush/venv
workdir="$HOME/.local/share/platypush/venv"
function build {
cfgfile=
@ -35,88 +35,74 @@ function build {
fi
echo "Parsing configuration file"
deps=()
pip_cmd=
pkg_cmd=
includes=()
cmd_exec=()
while read -r line; do
echo ${line} | egrep '``pip install .+?``' > /dev/null 2>&1
if (( $? != 0 )); then
continue
if echo "$line" | grep -E "^pip:\s*"; then
pip_cmd="$(echo "$line" | sed -r -e 's/^pip:\s*(.*)'/\\1/)"
elif echo "$line" | grep -E "^packages:\s*"; then
pkg_cmd="$(echo "$line" | sed -r -e 's/^packages:\s*(.*)'/\\1/)"
elif echo "$line" | grep -E "^exec:\s*"; then
cmd_exec+=("$(echo "$line" | sed -r -e 's/^exec:\s*(.*)'/\\1/)")
elif echo "$line" | grep -E "^include:\s*"; then
includes+=("$(echo "$line" | sed -r -e 's/^include:\s*(.*)'/\\1/)")
elif echo "$line" | grep -E "^device_id:\s*"; then
device_id="$(echo "$line" | sed -r -e 's/^device_id:\s*(.*)'/\\1/)"
fi
dep=$(echo ${line} | sed -r -e 's/.*``pip install (.+?)``.*/\1/')
deps+=("$dep")
done <<< $(python <<EOF
done <<< "$(python <<EOF
from platypush.config import Config
from platypush.context import get_plugin, get_backend, register_backends
from platypush.utils.manifest import get_install_commands_from_conf
Config.init('`realpath ${cfgfile}`')
register_backends()
backend_config = Config.get_backends()
deps = get_install_commands_from_conf('$(realpath "${cfgfile}")')
print(f'device_id: {Config.get("device_id")}')
for name in Config.get_backends().keys():
backend = get_backend(name)
print(backend.__doc__)
if deps.get('pip'):
print(f'pip: {deps["pip"]}')
for name in Config.get_plugins().keys():
try:
plugin = get_plugin(name)
print(plugin.__doc__)
except:
pass
EOF
)
if deps.get('packages'):
print(f'packages: {deps["packages"]}')
while read -r include; do
includes+=(${include})
done <<< $(python <<EOF
from platypush.config import Config
from platypush.context import get_plugin, get_backend, register_backends
Config.init('`realpath ${cfgfile}`')
for cmd in deps.get('exec', []):
print(f'exec: {cmd}')
for include in Config._included_files:
print(include)
print(f'include: {include}')
EOF
)
)"
device_id=$(python <<EOF
from platypush.config import Config
Config.init('`realpath ${cfgfile}`')
print(Config.get('device_id'))
EOF
)
envdir=${workdir}/${device_id}
etcdir=${envdir}/etc/platypush
envdir="${workdir}/${device_id}"
etcdir="${envdir}/etc/platypush"
echo "Preparing virtual environment for device $device_id"
mkdir -p "$envdir"
mkdir -p "$etcdir"
srcdir=`dirname "$cfgfile"`
srcdir=$(dirname "$cfgfile")
for ((i=0; $i < ${#includes[@]}; i++)); do
incdir=`dirname "${includes[$i]}"`
incdir=`realpath --relative-to="$srcdir" "$incdir"`
for ((i=0; i < ${#includes[@]}; i++)); do
incdir=$(dirname "${includes[$i]}")
incdir=$(realpath --relative-to="$srcdir" "$incdir")
destdir="$etcdir/$incdir"
mkdir -p "$destdir"
cp "${includes[$i]}" "$destdir"
done
cp "$cfgfile" "$etcdir/config.yaml"
cfgfile=${etcdir}/config.yaml
cfgfile="${etcdir}/config.yaml"
python3 -m venv ${envdir}
cd ${envdir}
source ${envdir}/bin/activate
python3 -m venv "${envdir}"
cd "${envdir}" || exit 1
source "${envdir}/bin/activate"
echo "Installing required dependencies"
# shellcheck disable=SC2086
[ -n "${pkg_cmd}" ] && sudo ${pkg_cmd}
[ -n "${pip_cmd}" ] && ${pip_cmd}
for ((i=0; $i < ${#deps[@]}; i++)); do
echo ${deps[$i]}
done | sort -u | while read dep; do
pip install ${dep}
for ((i=0; i < ${#cmd_exec[@]}; i++)); do
${cmd_exec[$i]}
done
pip install --upgrade git+https://git.platypush.tech/platypush/platypush.git
@ -130,44 +116,43 @@ function start {
fi
env=$1
envdir=${workdir}/${env}
logsdir=${envdir}/var/log/platypush
rundir=${envdir}/var/run
pidfile=${rundir}/platypush.pid
cfgfile=${envdir}/etc/platypush/config.yaml
envdir="${workdir}/${env}"
logsdir="${envdir}/var/log/platypush"
rundir="${envdir}/var/run"
pidfile="${rundir}/platypush.pid"
cfgfile="${envdir}/etc/platypush/config.yaml"
if [[ ! -d "$envdir" ]]; then
echo "No such directory: $envdir" >&2
exit 1
fi
mkdir -p ${logsdir}
mkdir -p ${rundir}
mkdir -p "${logsdir}"
mkdir -p "${rundir}"
if [[ -f "$pidfile" ]]; then
pid=`cat "$pidfile"`
if ps -p ${pid} | grep platypush; then
echo "Another instance (PID $pid) is running, please stop that instance first"
if pgrep -F "${pidfile}"; then
echo "Another instance (PID $(cat "${pidfile}")) is running, please stop that instance first"
exit 1
fi
echo "A PID file was found but the process doesn't seem to be running, starting anyway"
echo "A PID file was found but the process does not seem to be running, starting anyway"
rm -f "$pidfile"
fi
python3 -m venv ${envdir}
cd ${envdir}
python3 -m venv "${envdir}"
cd "${envdir}" || exit 1
source bin/activate
bin/platypush -c "$cfgfile" -P "$pidfile" > ${logsdir}/stdout.log 2> ${logsdir}/stderr.log &
start_time=`date +'%s'`
bin/platypush -c "$cfgfile" -P "$pidfile" > "${logsdir}/stdout.log" 2> "${logsdir}/stderr.log" &
start_time=$(date +'%s')
timeout=30
while :; do
[[ -f "$pidfile" ]] && break
now=`date +'%s'`
let elapsed=$now-$start_time
if (( ${elapsed} >= ${timeout} )); then
echo "Platypush instance '$env' didn't start within $timeout seconds" >&2
now=$(date +'%s')
elapsed=$(( now-start_time ))
if (( elapsed >= timeout )); then
echo "Platypush instance '$env' did not start within $timeout seconds" >&2
exit 1
fi
@ -175,7 +160,7 @@ function start {
sleep 1
done
pid=`cat "$pidfile"`
pid=$(cat "$pidfile")
echo
echo "Platypush environment $env started with PID $pid, logs dir: $logsdir"
}
@ -187,9 +172,9 @@ function stop {
fi
env=$1
envdir=${workdir}/${env}
rundir=${envdir}/var/run
pidfile=${rundir}/platypush.pid
envdir="${workdir}/${env}"
rundir="${envdir}/var/run"
pidfile="${rundir}/platypush.pid"
if [[ ! -d "$envdir" ]]; then
echo "No such directory: $envdir" >&2
@ -197,12 +182,13 @@ function stop {
fi
if [[ ! -f "$pidfile" ]]; then
echo "No pidfile found for instance "${env}""
echo "No pidfile found for instance \"${env}\""
exit 1
fi
pid=`cat "$pidfile"`
pids="$pid `ps --no-headers -o pid= --ppid $pid`"
pid=$(cat "$pidfile")
pids="$pid $(ps --no-headers -o pid= --ppid "$pid")"
# shellcheck disable=SC2086
kill -9 ${pids}
rm -f "$pidfile"
echo "Instance '$env' with PID $pid stopped"
@ -214,9 +200,9 @@ function rme {
exit 1
fi
envdir=${workdir}/$1
rundir=${envdir}/var/run
pidfile=${rundir}/platypush.pid
envdir="${workdir}/$1"
rundir="${envdir}/var/run"
pidfile="${rundir}/platypush.pid"
if [[ ! -d "$envdir" ]]; then
echo "No such directory: $envdir" >&2
@ -224,14 +210,18 @@ function rme {
fi
if [[ -f "$pidfile" ]]; then
if ps -p `cat "$pidfile"` | grep platypush; then
echo "Another instance (PID $pidfile) is running, please stop that instance first"
if pgrep -F "${pidfile}"; then
echo "Another instance (PID $(cat "$pidfile")) is running, please stop that instance first"
exit 1
fi
echo "A PID file was found but the process doesn't seem to be running, removing anyway"
echo "A PID file was found but the process does not seem to be running, removing anyway"
fi
echo "WARNING: This operation will permanently remove the Platypush environment $1"
echo -n "Are you sure you want to continue? (y/N) "
IFS= read -r answer
echo "$answer" | grep -E '^[yY]' >/dev/null || exit 0
rm -rf "$envdir"
echo "$envdir removed"
}
@ -247,13 +237,13 @@ fi
action=$1
shift
mkdir -p ${workdir}
mkdir -p "${workdir}"
# shellcheck disable=SC2048,SC2086
case ${action} in
'build') build;;
'build') build $*;;
'start') start $*;;
'stop') stop $*;;
'rm') rme $*;;
*) usage;;
esac

View File

@ -8,10 +8,8 @@ Backends
platypush/backend/adafruit.io.rst
platypush/backend/alarm.rst
platypush/backend/assistant.rst
platypush/backend/assistant.google.rst
platypush/backend/assistant.snowboy.rst
platypush/backend/bluetooth.rst
platypush/backend/bluetooth.fileserver.rst
platypush/backend/bluetooth.pushserver.rst
platypush/backend/bluetooth.scanner.rst
@ -52,7 +50,6 @@ Backends
platypush/backend/pushbullet.rst
platypush/backend/redis.rst
platypush/backend/scard.rst
platypush/backend/sensor.rst
platypush/backend/sensor.accelerometer.rst
platypush/backend/sensor.arduino.rst
platypush/backend/sensor.battery.rst
@ -67,7 +64,6 @@ Backends
platypush/backend/sensor.mcp3008.rst
platypush/backend/sensor.motion.pwm3901.rst
platypush/backend/sensor.serial.rst
platypush/backend/stt.rst
platypush/backend/stt.deepspeech.rst
platypush/backend/stt.picovoice.hotword.rst
platypush/backend/stt.picovoice.speech.rst
@ -75,7 +71,6 @@ Backends
platypush/backend/todoist.rst
platypush/backend/travisci.rst
platypush/backend/trello.rst
platypush/backend/weather.rst
platypush/backend/weather.buienradar.rst
platypush/backend/weather.darksky.rst
platypush/backend/weather.openweathermap.rst

View File

@ -238,7 +238,7 @@ autodoc_mock_imports = ['googlesamples.assistant.grpc.audio_helpers',
'envirophat',
'gps',
'picamera',
'pwm3901',
'pmw3901',
'PIL',
'croniter',
'pyaudio',

View File

@ -9,7 +9,6 @@ Plugins
platypush/plugins/adafruit.io.rst
platypush/plugins/alarm.rst
platypush/plugins/arduino.rst
platypush/plugins/assistant.rst
platypush/plugins/assistant.echo.rst
platypush/plugins/assistant.google.rst
platypush/plugins/assistant.google.pushtotalk.rst
@ -18,14 +17,12 @@ Plugins
platypush/plugins/bluetooth.ble.rst
platypush/plugins/calendar.rst
platypush/plugins/calendar.ical.rst
platypush/plugins/camera.rst
platypush/plugins/camera.android.ipcam.rst
platypush/plugins/camera.cv.rst
platypush/plugins/camera.ffmpeg.rst
platypush/plugins/camera.gstreamer.rst
platypush/plugins/camera.ir.mlx90640.rst
platypush/plugins/camera.pi.rst
platypush/plugins/chat.rst
platypush/plugins/chat.telegram.rst
platypush/plugins/clipboard.rst
platypush/plugins/config.rst
@ -38,7 +35,6 @@ Plugins
platypush/plugins/ffmpeg.rst
platypush/plugins/file.rst
platypush/plugins/foursquare.rst
platypush/plugins/google.rst
platypush/plugins/google.calendar.rst
platypush/plugins/google.drive.rst
platypush/plugins/google.fit.rst
@ -48,7 +44,6 @@ Plugins
platypush/plugins/google.translate.rst
platypush/plugins/google.youtube.rst
platypush/plugins/gpio.rst
platypush/plugins/gpio.sensor.rst
platypush/plugins/gpio.sensor.accelerometer.rst
platypush/plugins/gpio.sensor.bme280.rst
platypush/plugins/gpio.sensor.dht.rst
@ -69,18 +64,14 @@ Plugins
platypush/plugins/inspect.rst
platypush/plugins/kafka.rst
platypush/plugins/lastfm.rst
platypush/plugins/lcd.rst
platypush/plugins/lcd.gpio.rst
platypush/plugins/lcd.i2c.rst
platypush/plugins/light.rst
platypush/plugins/light.hue.rst
platypush/plugins/linode.rst
platypush/plugins/logger.rst
platypush/plugins/luma.oled.rst
platypush/plugins/mail.rst
platypush/plugins/mail.imap.rst
platypush/plugins/mail.smtp.rst
platypush/plugins/media.rst
platypush/plugins/media.chromecast.rst
platypush/plugins/media.gstreamer.rst
platypush/plugins/media.kodi.rst
@ -95,7 +86,6 @@ Plugins
platypush/plugins/ml.cv.rst
platypush/plugins/mobile.join.rst
platypush/plugins/mqtt.rst
platypush/plugins/music.rst
platypush/plugins/music.mpd.rst
platypush/plugins/music.snapcast.rst
platypush/plugins/music.spotify.rst
@ -110,19 +100,16 @@ Plugins
platypush/plugins/qrcode.rst
platypush/plugins/redis.rst
platypush/plugins/rtorrent.rst
platypush/plugins/sensor.rst
platypush/plugins/serial.rst
platypush/plugins/shell.rst
platypush/plugins/slack.rst
platypush/plugins/smartthings.rst
platypush/plugins/sound.rst
platypush/plugins/ssh.rst
platypush/plugins/stt.rst
platypush/plugins/stt.deepspeech.rst
platypush/plugins/stt.picovoice.hotword.rst
platypush/plugins/stt.picovoice.speech.rst
platypush/plugins/sun.rst
platypush/plugins/switch.rst
platypush/plugins/switch.tplink.rst
platypush/plugins/switch.wemo.rst
platypush/plugins/switchbot.rst
@ -142,7 +129,6 @@ Plugins
platypush/plugins/user.rst
platypush/plugins/utils.rst
platypush/plugins/variable.rst
platypush/plugins/weather.rst
platypush/plugins/weather.buienradar.rst
platypush/plugins/weather.darksky.rst
platypush/plugins/weather.openweathermap.rst
@ -151,5 +137,4 @@ Plugins
platypush/plugins/zeroconf.rst
platypush/plugins/zigbee.mqtt.rst
platypush/plugins/zwave.rst
platypush/plugins/zwave._base.rst
platypush/plugins/zwave.mqtt.rst

View File

@ -1,14 +1,27 @@
import os
from platypush.backend import Backend
from platypush.context import get_plugin
from platypush.plugins import Plugin
from platypush.utils.manifest import get_manifests
def get_all_plugins():
return get_plugin('inspect').get_all_plugins().output
manifests = {mf.component_name for mf in get_manifests(Plugin)}
return {
plugin_name: plugin_info
for plugin_name, plugin_info in get_plugin('inspect').get_all_plugins().output.items()
if plugin_name in manifests
}
def get_all_backends():
return get_plugin('inspect').get_all_backends().output
manifests = {mf.component_name for mf in get_manifests(Backend)}
return {
backend_name: backend_info
for backend_name, backend_info in get_plugin('inspect').get_all_backends().output.items()
if backend_name in manifests
}
def get_all_events():

View File

@ -12,6 +12,7 @@ from threading import Thread, Event as ThreadEvent, get_ident
from typing import Optional, Dict
from platypush.bus import Bus
from platypush.common import ExtensionWithManifest
from platypush.config import Config
from platypush.context import get_backend
from platypush.message.event.zeroconf import ZeroconfServiceAddedEvent, ZeroconfServiceRemovedEvent
@ -26,7 +27,7 @@ from platypush.message.request import Request
from platypush.message.response import Response
class Backend(Thread, EventGenerator):
class Backend(Thread, EventGenerator, ExtensionWithManifest):
"""
Parent class for backends.
@ -53,6 +54,7 @@ class Backend(Thread, EventGenerator):
self._thread_name = self.__class__.__name__
EventGenerator.__init__(self)
ExtensionWithManifest.__init__(self)
Thread.__init__(self, name=self._thread_name, daemon=True)
# If no bus is specified, create an internal queue where

View File

@ -1,3 +1,5 @@
from typing import Optional
from platypush.backend import Backend
from platypush.context import get_plugin
from platypush.message.event.adafruit import ConnectedEvent, DisconnectedEvent, \
@ -30,8 +32,9 @@ class AdafruitIoBackend(Backend):
"""
super().__init__(*args, **kwargs)
from Adafruit_IO import MQTTClient
self.feeds = feeds
self._client = None
self._client: Optional[MQTTClient] = None
def _init_client(self):
if self._client:
@ -57,12 +60,12 @@ class AdafruitIoBackend(Backend):
return _handler
def on_disconnect(self):
def _handler(client):
def _handler(*_, **__):
self.bus.post(DisconnectedEvent())
return _handler
def on_message(self, msg):
def on_message(self, *_, **__):
# noinspection PyUnusedLocal
def _handler(client, feed, data):
try:
@ -83,7 +86,9 @@ class AdafruitIoBackend(Backend):
while not self.should_stop():
try:
self._init_client()
# noinspection PyUnresolvedReferences
self._client.connect()
# noinspection PyUnresolvedReferences
self._client.loop_blocking()
except Exception as e:
self.logger.exception(e)

View File

@ -0,0 +1,12 @@
manifest:
events:
platypush.message.event.adafruit.ConnectedEvent: when thebackend connects to the
Adafruit queue
platypush.message.event.adafruit.DisconnectedEvent: when thebackend disconnects
from the Adafruit queue
platypush.message.event.adafruit.FeedUpdateEvent: when anupdate event is received
on a monitored feed
install:
pip: []
package: platypush.backend.adafruit.io
type: backend

View File

@ -0,0 +1,10 @@
manifest:
events:
platypush.message.event.alarm.AlarmDismissedEvent: when an alarm is dismissed.
platypush.message.event.alarm.AlarmSnoozedEvent: when an alarm is snoozed.
platypush.message.event.alarm.AlarmStartedEvent: when an alarm starts.
platypush.message.event.alarm.AlarmTimeoutEvent: when an alarm times out.
install:
pip: []
package: platypush.backend.alarm
type: backend

View File

@ -1,3 +1,4 @@
from abc import ABC
import threading
from typing import Optional, Dict, Any, Tuple

View File

@ -0,0 +1,26 @@
manifest:
events:
platypush.message.event.assistant.AlarmEndEvent: when an alarm ends
platypush.message.event.assistant.AlarmStartedEvent: when an alarm starts
platypush.message.event.assistant.ConversationEndEvent: when a new conversation
ends
platypush.message.event.assistant.ConversationStartEvent: when a new conversation
starts
platypush.message.event.assistant.ConversationTimeoutEvent: when a conversation
times out
platypush.message.event.assistant.MicMutedEvent: when the microphone is muted.
platypush.message.event.assistant.MicUnmutedEvent: when the microphone is un-muted.
platypush.message.event.assistant.NoResponse: when a conversation returned no
response
platypush.message.event.assistant.ResponseEvent: when the assistant is speaking
a response
platypush.message.event.assistant.SpeechRecognizedEvent: when a new voice command
is recognized
platypush.message.event.assistant.TimerEndEvent: when a timer ends
platypush.message.event.assistant.TimerStartedEvent: when a timer starts
install:
pip:
- google-assistant-library
- google-assistant-sdk[samples]
package: platypush.backend.assistant.google
type: backend

View File

@ -0,0 +1,9 @@
manifest:
events:
platypush.message.event.assistant.HotwordDetectedEvent: whenever the hotword has
been detected
install:
pip:
- snowboy
package: platypush.backend.assistant.snowboy
type: backend

View File

@ -83,7 +83,7 @@ class BluetoothBackend(Backend, Server):
self.connect(connection, request)
self.bus.post(BluetoothDeviceConnectedEvent(address=address[0], port=address[1]))
elif isinstance(request, requests.Disconnect):
self.disconnect(connection)
self.disconnect(connection, request)
self.bus.post(BluetoothDeviceDisconnectedEvent(address=address[0], port=address[1]))
elif isinstance(request, requests.Put):
self.bus.post(BluetoothFilePutRequestEvent(address=address[0], port=address[1]))

View File

@ -0,0 +1,8 @@
manifest:
events: {}
install:
pip:
- pybluez
- pyobex
package: platypush.backend.bluetooth.fileserver
type: backend

View File

@ -0,0 +1,8 @@
manifest:
events: {}
install:
pip:
- pybluez
- pyobex
package: platypush.backend.bluetooth.pushserver
type: backend

View File

@ -0,0 +1,10 @@
manifest:
events:
platypush.message.event.bluetooth.BluetoothDeviceFoundEvent: when a new bluetooth
device is found.
platypush.message.event.bluetooth.BluetoothDeviceLostEvent: when a bluetooth device
is lost.
install:
pip: []
package: platypush.backend.bluetooth.scanner.ble
type: backend

View File

@ -0,0 +1,10 @@
manifest:
events:
platypush.message.event.bluetooth.BluetoothDeviceFoundEvent: when a new bluetooth
device is found.
platypush.message.event.bluetooth.BluetoothDeviceLostEvent: when a bluetooth device
is lost.
install:
pip: []
package: platypush.backend.bluetooth.scanner
type: backend

View File

@ -1,6 +1,3 @@
import importlib
from enum import Enum
from threading import Timer
from time import time
@ -17,11 +14,14 @@ class ButtonFlicBackend(Backend):
Triggers:
* :class:`platypush.message.event.button.flic.FlicButtonEvent` when a button is pressed. The event will also contain the press sequence (e.g. ``["ShortPressEvent", "LongPressEvent", "ShortPressEvent"]``)
* :class:`platypush.message.event.button.flic.FlicButtonEvent` when a button is pressed.
The event will also contain the press sequence
(e.g. ``["ShortPressEvent", "LongPressEvent", "ShortPressEvent"]``)
Requires:
* **fliclib** (https://github.com/50ButtonsEach/fliclib-linux-hci). For the backend to work properly you need to have the ``flicd`` daemon from the fliclib running, and you have to first pair the buttons with your device using any of the scanners provided by the library.
"""
_long_press_timeout = 0.3
@ -87,6 +87,7 @@ class ButtonFlicBackend(Backend):
return _f
def _on_event(self):
# noinspection PyUnusedLocal
def _f(bd_addr, channel, click_type, was_queued, time_diff):
if was_queued:
return

View File

@ -15,6 +15,7 @@ from enum import Enum
from collections import namedtuple
import struct
import itertools
import threading
class CreateConnectionChannelError(Enum):

View File

@ -0,0 +1,9 @@
manifest:
events:
platypush.message.event.button.flic.FlicButtonEvent: when a button is pressed.The
event will also contain the press sequence(e.g. ``["ShortPressEvent", "LongPressEvent",
"ShortPressEvent"]``)
install:
pip: []
package: platypush.backend.button.flic
type: backend

View File

@ -18,7 +18,6 @@ class CameraPiBackend(Backend):
Requires:
* **picamera** (``pip install picamera``)
* **redis** (``pip install redis``) for inter-process communication with the camera process
This backend is **DEPRECATED**. Use the plugin :class:`platypush.plugins.camera.pi.CameraPiPlugin` instead to run
Pi camera actions. If you want to start streaming the camera on application start then simply create an event hook

View File

@ -0,0 +1,7 @@
manifest:
events: {}
install:
pip:
- picamera
package: platypush.backend.camera.pi
type: backend

View File

@ -0,0 +1,19 @@
manifest:
events:
platypush.message.event.chat.telegram.CommandMessageEvent: when a command message
is received.
platypush.message.event.chat.telegram.ContactMessageEvent: when a contact is received.
platypush.message.event.chat.telegram.DocumentMessageEvent: when a document is
received.
platypush.message.event.chat.telegram.GroupCreatedEvent: when the bot is invited
to a new group.
platypush.message.event.chat.telegram.LocationMessageEvent: when a location is
received.
platypush.message.event.chat.telegram.PhotoMessageEvent: when a photo is received.
platypush.message.event.chat.telegram.TextMessageEvent: when a text message is
received.
platypush.message.event.chat.telegram.VideoMessageEvent: when a video is received.
install:
pip: []
package: platypush.backend.chat.telegram
type: backend

View File

@ -0,0 +1,8 @@
manifest:
events:
platypush.message.event.clipboard.ClipboardEvent: on clipboard update.
install:
pip:
- pyperclip
package: platypush.backend.clipboard
type: backend

View File

@ -0,0 +1,7 @@
manifest:
events:
platypush.message.event.covid19.Covid19UpdateEvent: when new data is available.
install:
pip: []
package: platypush.backend.covid19
type: backend

View File

@ -0,0 +1,7 @@
manifest:
events: {}
install:
pip:
- dbus-python
package: platypush.backend.dbus
type: backend

View File

@ -0,0 +1,10 @@
manifest:
events:
platypush.message.event.file.FileSystemCreateEvent: if a resource is created.
platypush.message.event.file.FileSystemDeleteEvent: if a resource is removed.
platypush.message.event.file.FileSystemModifyEvent: if a resource is modified.
install:
pip:
- watchdog
package: platypush.backend.file.monitor
type: backend

View File

@ -0,0 +1,8 @@
manifest:
events:
platypush.message.event.foursquare.FoursquareCheckinEvent: when a new check-in
occurs.
install:
pip: []
package: platypush.backend.foursquare
type: backend

View File

@ -0,0 +1,32 @@
manifest:
events:
platypush.message.event.github.GithubCommitCommentEvent: when a new commit comment
is created.
platypush.message.event.github.GithubCreateEvent: when a tag or branch is created.
platypush.message.event.github.GithubDeleteEvent: when a tag or branch is deleted.
platypush.message.event.github.GithubEvent: for any event that doesn't fall in
the above categories(``event_type`` will be set accordingly).
platypush.message.event.github.GithubForkEvent: when a user forks a repository.
platypush.message.event.github.GithubIssueCommentEvent: when new activity happens
on an issue comment.
platypush.message.event.github.GithubIssueEvent: when new repository issue activity
happens.
platypush.message.event.github.GithubMemberEvent: when new repository collaborators
activity happens.
platypush.message.event.github.GithubPublicEvent: when a repository goes public.
platypush.message.event.github.GithubPullRequestEvent: when new pull request related
activity happens.
platypush.message.event.github.GithubPullRequestReviewCommentEvent: when activity
happens on a pullrequest commit.
platypush.message.event.github.GithubPushEvent: when a new push is created.
platypush.message.event.github.GithubReleaseEvent: when a new release happens.
platypush.message.event.github.GithubSponsorshipEvent: when new sponsorship related
activity happens.
platypush.message.event.github.GithubWatchEvent: when someone stars/starts watching
a repository.
platypush.message.event.github.GithubWikiEvent: when new activity happens on a
repository wiki.
install:
pip: []
package: platypush.backend.github
type: backend

View File

@ -0,0 +1,8 @@
manifest:
events:
platypush.message.event.google.fit.GoogleFitEvent: when a newdata point is received
on one of the registered streams.
install:
pip: []
package: platypush.backend.google.fit
type: backend

View File

@ -0,0 +1,9 @@
manifest:
events:
platypush.message.event.google.pubsub.GooglePubsubMessageEvent: when a new message
is received ona subscribed topic.
install:
pip:
- google-cloud-pubsub
package: platypush.backend.google.pubsub
type: backend

View File

@ -0,0 +1,16 @@
manifest:
events:
platypush.message.event.gps.GPSDeviceEvent: when a GPS device is connected or
updated
platypush.message.event.gps.GPSUpdateEvent: when a GPS device has new data
platypush.message.event.gps.GPSVersionEvent: when a GPS device advertises its
version data
install:
pip:
- gps
pacman:
- gpsd
apt:
- gpsd
package: platypush.backend.gps
type: backend

View File

@ -4,6 +4,11 @@ import threading
from multiprocessing import Process
try:
from websockets.exceptions import ConnectionClosed
except ImportError:
from websockets import ConnectionClosed
from platypush.backend import Backend
from platypush.backend.http.app import application
from platypush.context import get_or_create_event_loop
@ -146,9 +151,7 @@ class HttpBackend(Backend):
Requires:
* **flask** (``pip install flask``)
* **redis** (``pip install redis``)
* **websockets** (``pip install websockets``)
* **python-dateutil** (``pip install python-dateutil``)
* **bcrypt** (``pip install bcrypt``)
* **magic** (``pip install python-magic``), optional, for MIME type
support if you want to enable media streaming
* **uwsgi** (``pip install uwsgi`` plus uwsgi server installed on your
@ -328,8 +331,6 @@ class HttpBackend(Backend):
def notify_web_clients(self, event):
""" Notify all the connected web clients (over websocket) of a new event """
import websockets
async def send_event(ws):
try:
self._acquire_websocket_lock(ws)
@ -345,7 +346,7 @@ class HttpBackend(Backend):
for _ws in wss:
try:
loop.run_until_complete(send_event(_ws))
except websockets.exceptions.ConnectionClosed:
except ConnectionClosed:
self.logger.warning('Websocket client {} connection lost'.format(_ws.remote_address))
self.active_websockets.remove(_ws)
if _ws.remote_address in self._websocket_locks:
@ -365,7 +366,7 @@ class HttpBackend(Backend):
try:
await websocket.recv()
except websockets.exceptions.ConnectionClosed:
except ConnectionClosed:
self.logger.info('Websocket client {} closed connection'.format(address))
self.active_websockets.remove(websocket)
if address in self._websocket_locks:

View File

@ -4,7 +4,7 @@ from platypush.backend.http.app import template_folder
from platypush.backend.http.app.routes.plugins.camera import get_photo, get_video
from platypush.backend.http.app.utils import authenticate
camera_ir_mlx90640 = Blueprint('camera.ir.mlx90640', __name__, template_folder=template_folder)
camera_ir_mlx90640 = Blueprint('camera-ir-mlx90640', __name__, template_folder=template_folder)
# Declare routes list
__routes__ = [

View File

@ -8,7 +8,7 @@ from platypush.backend.http.app.utils import authenticate, send_request
from platypush.config import Config
from platypush.plugins.camera.pi import CameraPiPlugin
camera_pi = Blueprint('camera.pi', __name__, template_folder=template_folder)
camera_pi = Blueprint('camera-pi', __name__, template_folder=template_folder)
# Declare routes list
__routes__ = [

View File

@ -2,7 +2,7 @@ import hashlib
import json
import threading
from flask import Response, render_template
from flask import Response
from platypush.backend.http.app.utils import get_remote_base_url, logger, \
send_message

View File

@ -0,0 +1,9 @@
manifest:
events: {}
install:
pip:
- flask
- bcrypt
- python-magic
package: platypush.backend.http
type: backend

View File

@ -1,5 +1,6 @@
import logging
class MediaHandler:
"""
Abstract class to manage media handlers that can be streamed over the HTTP
@ -57,7 +58,7 @@ class MediaHandler:
for attr in ['name', 'source', 'mime_type', 'url', 'subtitles',
'prefix_handlers', 'media_id']:
if hasattr(self, attr):
yield (attr, getattr(self, attr))
yield attr, getattr(self, attr)
from .file import FileHandler

View File

@ -34,7 +34,7 @@ class HttpPollBackend(Backend):
-
# Poll for updates on an RSS feed
type: platypush.backend.http.request.rss.RssUpdates
url: http://www.theguardian.com/rss/world
url: https://www.theguardian.com/rss/world
title: The Guardian - World News
poll_seconds: 120
max_entries: 10

View File

@ -0,0 +1,6 @@
manifest:
events: {}
install:
pip: []
package: platypush.backend.http.poll
type: backend

View File

@ -1,48 +0,0 @@
import datetime
import dateutil.parser
import json
from platypush.backend.http.request import JsonHttpRequest
from platypush.message.event.http.ota.booking import NewReservationEvent
class GetReservationUpdates(JsonHttpRequest):
""" Gets the reservation updates """
def __init__(self, hotel_id, token, *args, **kwargs):
self.hotel_id = hotel_id
self.token = token
self.seen_entries = set()
self.last_update = None
args = {
'method': 'get',
'url': 'https://hub-api.booking.com/v1/hotels/{}/reservations'.format(self.hotel_id),
'headers': { 'X-Booking-Auth-Token': self.token },
'params': { 'updatedSince': datetime.date.today().isoformat() }
}
super().__init__(args=args, **kwargs)
def get_new_items(self, response):
response = response.json()
entries = []
for entry in response:
update_timestamp = dateutil.parser.parse(entry['updateDate'])
last_update_timestamp = dateutil.parser.parse(self.last_update['updateDate']) \
if self.last_update else None
if self.last_update is None \
or (update_timestamp > last_update_timestamp
and not (
entry['booker']['email'] == self.last_update['booker']['email']
and entry['status'] == self.last_update['status'])):
self.last_update = entry
entries.append(entry)
return NewReservationEvent(dict(self), entries)
# vim:sw=4:ts=4:et:

View File

@ -239,7 +239,10 @@ class RssUpdates(HttpRequest):
f.write(content)
elif self.digest_format == 'pdf':
from weasyprint import HTML, CSS
try:
from weasyprint.fonts import FontConfiguration
except ImportError:
from weasyprint.document import FontConfiguration
body_style = 'body { ' + self.body_style + ' }'
font_config = FontConfiguration()

View File

@ -0,0 +1,15 @@
manifest:
events:
platypush.message.event.inotify.InotifyAccessEvent: if a resource is accessed
platypush.message.event.inotify.InotifyCloseEvent: if a resource is closed
platypush.message.event.inotify.InotifyCreateEvent: if a resource is created
platypush.message.event.inotify.InotifyDeleteEvent: if a resource is removed
platypush.message.event.inotify.InotifyModifyEvent: if a resource is modified
platypush.message.event.inotify.InotifyOpenEvent: if a resource is opened
platypush.message.event.inotify.InotifyPermissionsChangeEvent: if the permissions
of a resource are changed
install:
pip:
- inotify
package: platypush.backend.inotify
type: backend

View File

@ -0,0 +1,21 @@
manifest:
events:
platypush.message.event.joystick.JoystickAxisEvent: when an axis value of the
joystick changes.
platypush.message.event.joystick.JoystickButtonPressedEvent: when a joystick button
is pressed.
platypush.message.event.joystick.JoystickButtonReleasedEvent: when a joystick
button is released.
platypush.message.event.joystick.JoystickConnectedEvent: when the joystick is
connected.
platypush.message.event.joystick.JoystickDisconnectedEvent: when the joystick
is disconnected.
platypush.message.event.joystick.JoystickStateEvent: when the state of the joystick
(i.e. some of itsaxes or buttons values) changes.
install:
apt:
- joystick
pacman:
- jsutils
package: platypush.backend.joystick.jstest
type: backend

View File

@ -0,0 +1,16 @@
manifest:
events:
platypush.message.event.joystick.JoystickAxisEvent: when an axis value of the
joystick changes.
platypush.message.event.joystick.JoystickButtonPressedEvent: when a joystick button
is pressed.
platypush.message.event.joystick.JoystickButtonReleasedEvent: when a joystick
button is released.
platypush.message.event.joystick.JoystickConnectedEvent: when the joystick is
connected.
platypush.message.event.joystick.JoystickDisconnectedEvent: when the joystick
is disconnected.
install:
pip: []
package: platypush.backend.joystick.linux
type: backend

View File

@ -0,0 +1,8 @@
manifest:
events:
platypush.message.event.joystick.JoystickEvent: when a new joystick event is received
install:
pip:
- inputs
package: platypush.backend.joystick
type: backend

View File

@ -1,4 +1,3 @@
import json
import logging
import time

View File

@ -0,0 +1,7 @@
manifest:
events: {}
install:
pip:
- kafka
package: platypush.backend.kafka
type: backend

View File

@ -0,0 +1,8 @@
manifest:
events:
platypush.message.event.light.LightStatusChangeEvent: when thestatus of a lightbulb
changes
install:
pip: []
package: platypush.backend.light.hue
type: backend

View File

@ -0,0 +1,8 @@
manifest:
events:
platypush.message.event.linode.LinodeInstanceStatusChanged: when the status of
an instance changes.
install:
pip: []
package: platypush.backend.linode
type: backend

View File

@ -0,0 +1,8 @@
manifest:
events:
platypush.message.event.log.http.HttpLogEvent: when a new log line is created.
install:
pip:
- watchdog
package: platypush.backend.log.http
type: backend

View File

@ -0,0 +1,10 @@
manifest:
events:
platypush.message.event.mail.MailFlaggedEvent: when a message is marked as flagged/starred.
platypush.message.event.mail.MailReceivedEvent: when a new message is received.
platypush.message.event.mail.MailSeenEvent: when a message is marked as seen.
platypush.message.event.mail.MailUnflaggedEvent: when a message is marked as unflagged/unstarred.
install:
pip: []
package: platypush.backend.mail
type: backend

View File

@ -120,7 +120,7 @@ class MidiBackend(Backend):
self.logger.exception(e)
if self.midi:
self.midi.close_port(self.port_number)
self.midi.close_port()
self.midi = None

View File

@ -0,0 +1,8 @@
manifest:
events:
platypush.message.event.midi.MidiMessageEvent: when a new MIDI event is received
install:
pip:
- rtmidi
package: platypush.backend.midi
type: backend

View File

@ -0,0 +1,9 @@
manifest:
events:
platypush.message.event.mqtt.MQTTMessageEvent: when a newmessage is received on
one of the custom listeners
install:
pip:
- paho-mqtt
package: platypush.backend.mqtt
type: backend

View File

@ -2,6 +2,8 @@ import json
import re
import threading
import websocket
from platypush.backend import Backend
from platypush.message.event.music import MusicPlayEvent, MusicPauseEvent, \
MusicStopEvent, NewPlayingTrackEvent, PlaylistChangeEvent, VolumeChangeEvent, \
@ -32,7 +34,7 @@ class MusicMopidyBackend(Backend):
* :class:`platypush.message.event.music.SeekChangeEvent` if a track seek event occurs
Requires:
* **websocket-client** (``pip install websocket-client``)
* Mopidy installed and the HTTP service enabled
"""
@ -88,7 +90,6 @@ class MusicMopidyBackend(Backend):
return conv_track
def _communicate(self, msg):
import websocket
if isinstance(msg, str):
msg = json.loads(msg)
@ -242,8 +243,6 @@ class MusicMopidyBackend(Backend):
return hndl
def _connect(self):
import websocket
if not self._ws:
self._ws = websocket.WebSocketApp(self.url,
on_open=self._on_open(),

View File

@ -0,0 +1,17 @@
manifest:
events:
platypush.message.event.music.MusicPauseEvent: if the playback state changed to
pause
platypush.message.event.music.MusicPlayEvent: if the playback state changed to
play
platypush.message.event.music.MusicStopEvent: if the playback state changed to
stop
platypush.message.event.music.MuteChangeEvent: if the mute status has changed
platypush.message.event.music.NewPlayingTrackEvent: if a new track is being played
platypush.message.event.music.PlaylistChangeEvent: if the main playlist has changed
platypush.message.event.music.SeekChangeEvent: if a track seek event occurs
platypush.message.event.music.VolumeChangeEvent: if the main volume has changed
install:
pip: []
package: platypush.backend.music.mopidy
type: backend

View File

@ -0,0 +1,16 @@
manifest:
events:
platypush.message.event.music.MusicPauseEvent: if the playback state changed to
pause
platypush.message.event.music.MusicPlayEvent: if the playback state changed to
play
platypush.message.event.music.MusicStopEvent: if the playback state changed to
stop
platypush.message.event.music.NewPlayingTrackEvent: if a new track is being played
platypush.message.event.music.PlaylistChangeEvent: if the main playlist has changed
platypush.message.event.music.VolumeChangeEvent: if the main volume has changed
install:
pip:
- python-mpd2
package: platypush.backend.music.mpd
type: backend

View File

@ -0,0 +1,15 @@
manifest:
events:
platypush.message.event.music.snapcast.ClientConnectedEvent: ''
platypush.message.event.music.snapcast.ClientDisconnectedEvent: ''
platypush.message.event.music.snapcast.ClientLatencyChangeEvent: ''
platypush.message.event.music.snapcast.ClientNameChangeEvent: ''
platypush.message.event.music.snapcast.ClientVolumeChangeEvent: ''
platypush.message.event.music.snapcast.GroupMuteChangeEvent: ''
platypush.message.event.music.snapcast.GroupStreamChangeEvent: ''
platypush.message.event.music.snapcast.ServerUpdateEvent: ''
platypush.message.event.music.snapcast.StreamUpdateEvent: ''
install:
pip: []
package: platypush.backend.music.snapcast
type: backend

View File

@ -0,0 +1,21 @@
manifest:
events:
platypush.message.event.music.MusicPauseEvent: if the playback state changed to
pause
platypush.message.event.music.MusicPlayEvent: if the playback state changed to
play
platypush.message.event.music.MusicStopEvent: if the playback state changed to
stop
platypush.message.event.music.NewPlayingTrackEvent: if a new track is being played
platypush.message.event.music.VolumeChangeEvent: if the volume changes
install:
apt:
- sudo
- cargo
pacman:
- sudo
- cargo
exec:
- sudo cargo install librespot
package: platypush.backend.music.spotify
type: backend

View File

@ -0,0 +1,10 @@
manifest:
events:
platypush.message.event.nextcloud.NextCloudActivityEvent: 'when new activity occurs
on the instance.The field ``activity_type`` identifies the activity type (e.g.
``file_created``, ``file_deleted``,``file_changed``). Example in the case of
the creation of new files:'
install:
pip: []
package: platypush.backend.nextcloud
type: backend

View File

@ -0,0 +1,13 @@
manifest:
events:
platypush.message.event.nfc.NFCDeviceConnectedEvent: when an NFC reader/writer
is connected
platypush.message.event.nfc.NFCDeviceDisconnectedEvent: when an NFC reader/writer
is disconnected
platypush.message.event.nfc.NFCTagDetectedEvent: when an NFC tag is detected
platypush.message.event.nfc.NFCTagRemovedEvent: when an NFC tag is removed
install:
pip:
- ndef
package: platypush.backend.nfc
type: backend

View File

@ -17,6 +17,7 @@ class NoderedBackend(Backend):
Requires:
* **pynodered** (``pip install pynodered``)
"""
def __init__(self, port: int = 5051, *args, **kwargs):

View File

@ -0,0 +1,7 @@
manifest:
events: {}
install:
pip:
- pynodered
package: platypush.backend.nodered
type: backend

View File

@ -0,0 +1,8 @@
manifest:
events:
platypush.message.event.ping.HostDownEvent: if a host stops responding ping requests
platypush.message.event.ping.HostUpEvent: if a host starts responding ping requests
install:
pip: []
package: platypush.backend.ping
type: backend

View File

@ -21,8 +21,8 @@ class PushbulletBackend(Backend):
Requires:
* **requests** (``pip install requests``)
* **pushbullet.py** (``pip install git+https://github.com/rbrcsk/pushbullet.py``)
* **pushbullet.py** (``pip install git+https://github.com/pushbullet.py/pushbullet.py``)
"""
def __init__(self, token: str, device: str = 'Platypush', proxy_host: Optional[str] = None,
@ -52,7 +52,7 @@ class PushbulletBackend(Backend):
try:
self.device = self.pb.get_device(self.device_name)
except Exception as e:
self.logger.info('Device {} does not exist: {}. Creating it'.format(self.device_name, str(e)))
self.logger.info(f'Device {self.device_name} does not exist: {e}. Creating it')
self.device = self.pb.new_device(self.device_name)
self.pb_device_id = self.get_device_id()
@ -91,17 +91,15 @@ class PushbulletBackend(Backend):
if 'body' not in push:
return
self.logger.debug('Received push: {}'.format(push))
self.logger.debug(f'Received push: {push}')
body = push['body']
try:
body = json.loads(body)
self.on_message(body)
except Exception as e:
self.logger.debug(('Unexpected message received on the ' +
'Pushbullet backend: {}. Message: {}')
.format(str(e), body))
self.logger.debug('Unexpected message received on the ' +
f'Pushbullet backend: {e}. Message: {body}')
except Exception as e:
self.logger.exception(e)
return
@ -116,9 +114,7 @@ class PushbulletBackend(Backend):
device = self.pb.new_device(self.device_name, model='Platypush virtual device',
manufacturer='platypush', icon='system')
self.logger.info('Created Pushbullet device {}'.format(
self.device_name))
self.logger.info(f'Created Pushbullet device {self.device_name}')
return device.device_iden
def close(self):
@ -133,27 +129,33 @@ class PushbulletBackend(Backend):
self.logger.info('Pushbullet backend terminated')
def on_close(self, err=None):
def callback(*_):
self.listener = None
raise RuntimeError(err or 'Connection closed')
return callback
def on_open(self):
def callback(*_):
self.logger.info('Pushbullet service connected')
return callback
def run_listener(self):
from .listener import Listener
self.logger.info('Initializing Pushbullet backend - device_id: {}'.format(self.device_name))
self.listener = Listener(account=self.pb, on_push=self.on_push(),
on_open=self.on_open,
on_close=self.on_close,
on_error=self.on_close,
self.logger.info(f'Initializing Pushbullet backend - device_id: {self.device_name}')
self.listener = Listener(account=self.pb,
on_push=self.on_push(),
on_open=self.on_open(),
on_close=self.on_close(),
on_error=self.on_close(),
http_proxy_host=self.proxy_host,
http_proxy_port=self.proxy_port)
self.listener.run_forever()
def run(self):
# noinspection PyPackageRequirements
super().run()
initialized = False
@ -163,7 +165,7 @@ class PushbulletBackend(Backend):
initialized = True
except Exception as e:
self.logger.exception(e)
self.logger.error('Pushbullet initialization error: {}'.format(str(e)))
self.logger.error(f'Pushbullet initialization error: {e}')
time.sleep(10)
while not self.should_stop():
@ -175,5 +177,4 @@ class PushbulletBackend(Backend):
self.logger.info('Retrying connection')
# vim:sw=4:ts=4:et:

View File

@ -0,0 +1,8 @@
manifest:
events:
platypush.message.event.pushbullet.PushbulletEvent: if a new push is received
install:
pip:
- git+https://github.com/rbrcsk/pushbullet.py
package: platypush.backend.pushbullet
type: backend

View File

@ -14,11 +14,6 @@ class RedisBackend(Backend):
``platypush_bus_mq``) and posts them to the application bus. Very
useful when you have plugin whose code is executed in another process
and can't post events or requests to the application bus.
Requires:
* **redis** (``pip install redis``)
"""
def __init__(self, queue='platypush_bus_mq', redis_args=None, *args, **kwargs):

View File

@ -0,0 +1,6 @@
manifest:
events: {}
install:
pip: []
package: platypush.backend.redis
type: backend

View File

@ -1,5 +1,3 @@
import json
from platypush.backend import Backend
from platypush.message.event.scard import SmartCardDetectedEvent, SmartCardRemovedEvent
@ -41,10 +39,15 @@ class ScardBackend(Backend):
"supported types: string, list".format(
atr, type(atr)))
self.cardtype = ATRCardType( *[toBytes(atr) for atr in self.ATRs] )
self.cardtype = ATRCardType(*[self._to_bytes(atr) for atr in self.ATRs])
else:
self.cardtype = AnyCardType()
@staticmethod
def _to_bytes(data) -> bytes:
if isinstance(data, str):
data = data.encode()
return data
def run(self):
from smartcard.CardRequest import CardRequest
@ -82,6 +85,4 @@ class ScardBackend(Backend):
prev_atr = None
# vim:sw=4:ts=4:et:

View File

@ -0,0 +1,9 @@
manifest:
events:
platypush.message.event.scard.SmartCardDetectedEvent: when a smart card is detected
platypush.message.event.scard.SmartCardRemovedEvent: when a smart card is removed
install:
pip:
- pyscard
package: platypush.backend.scard
type: backend

View File

@ -17,6 +17,7 @@ class SensorBackend(Backend):
gone above a configured threshold
* :class:`platypush.message.event.sensor.SensorDataBelowThresholdEvent` if the measurements of a sensor have
gone below a configured threshold
"""
default_tolerance = 1e-7

Some files were not shown because too many files have changed in this diff Show More