forked from platypush/platypush
Completed documentation for plugins
This commit is contained in:
parent
1cbef67f2c
commit
b876f17f81
23 changed files with 661 additions and 16 deletions
6
docs/source/platypush/plugins/pushbullet.rst
Normal file
6
docs/source/platypush/plugins/pushbullet.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
``platypush.plugins.pushbullet``
|
||||||
|
================================
|
||||||
|
|
||||||
|
.. automodule:: platypush.plugins.pushbullet
|
||||||
|
:members:
|
||||||
|
|
6
docs/source/platypush/plugins/redis.rst
Normal file
6
docs/source/platypush/plugins/redis.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
``platypush.plugins.redis``
|
||||||
|
===========================
|
||||||
|
|
||||||
|
.. automodule:: platypush.plugins.redis
|
||||||
|
:members:
|
||||||
|
|
6
docs/source/platypush/plugins/serial.rst
Normal file
6
docs/source/platypush/plugins/serial.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
``platypush.plugins.serial``
|
||||||
|
============================
|
||||||
|
|
||||||
|
.. automodule:: platypush.plugins.serial
|
||||||
|
:members:
|
||||||
|
|
6
docs/source/platypush/plugins/shell.rst
Normal file
6
docs/source/platypush/plugins/shell.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
``platypush.plugins.shell``
|
||||||
|
===========================
|
||||||
|
|
||||||
|
.. automodule:: platypush.plugins.shell
|
||||||
|
:members:
|
||||||
|
|
6
docs/source/platypush/plugins/switch.rst
Normal file
6
docs/source/platypush/plugins/switch.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
``platypush.plugins.switch``
|
||||||
|
============================
|
||||||
|
|
||||||
|
.. automodule:: platypush.plugins.switch
|
||||||
|
:members:
|
||||||
|
|
6
docs/source/platypush/plugins/switch.switchbot.rst
Normal file
6
docs/source/platypush/plugins/switch.switchbot.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
``platypush.plugins.switch.switchbot``
|
||||||
|
======================================
|
||||||
|
|
||||||
|
.. automodule:: platypush.plugins.switch.switchbot
|
||||||
|
:members:
|
||||||
|
|
6
docs/source/platypush/plugins/switch.wemo.rst
Normal file
6
docs/source/platypush/plugins/switch.wemo.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
``platypush.plugins.switch.wemo``
|
||||||
|
=================================
|
||||||
|
|
||||||
|
.. automodule:: platypush.plugins.switch.wemo
|
||||||
|
:members:
|
||||||
|
|
6
docs/source/platypush/plugins/tts.rst
Normal file
6
docs/source/platypush/plugins/tts.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
``platypush.plugins.tts``
|
||||||
|
=========================
|
||||||
|
|
||||||
|
.. automodule:: platypush.plugins.tts
|
||||||
|
:members:
|
||||||
|
|
6
docs/source/platypush/plugins/variable.rst
Normal file
6
docs/source/platypush/plugins/variable.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
``platypush.plugins.variable``
|
||||||
|
==============================
|
||||||
|
|
||||||
|
.. automodule:: platypush.plugins.variable
|
||||||
|
:members:
|
||||||
|
|
6
docs/source/platypush/plugins/video.omxplayer.rst
Normal file
6
docs/source/platypush/plugins/video.omxplayer.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
``platypush.plugins.video.omxplayer``
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
.. automodule:: platypush.plugins.video.omxplayer
|
||||||
|
:members:
|
||||||
|
|
6
docs/source/platypush/plugins/weather.forecast.rst
Normal file
6
docs/source/platypush/plugins/weather.forecast.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
``platypush.plugins.weather.forecast``
|
||||||
|
======================================
|
||||||
|
|
||||||
|
.. automodule:: platypush.plugins.weather.forecast
|
||||||
|
:members:
|
||||||
|
|
|
@ -26,4 +26,15 @@ Plugins
|
||||||
platypush/plugins/midi.rst
|
platypush/plugins/midi.rst
|
||||||
platypush/plugins/mqtt.rst
|
platypush/plugins/mqtt.rst
|
||||||
platypush/plugins/music.mpd.rst
|
platypush/plugins/music.mpd.rst
|
||||||
|
platypush/plugins/pushbullet.rst
|
||||||
|
platypush/plugins/redis.rst
|
||||||
|
platypush/plugins/serial.rst
|
||||||
|
platypush/plugins/shell.rst
|
||||||
|
platypush/plugins/switch.rst
|
||||||
|
platypush/plugins/switch.switchbot.rst
|
||||||
|
platypush/plugins/switch.wemo.rst
|
||||||
|
platypush/plugins/tts.rst
|
||||||
|
platypush/plugins/variable.rst
|
||||||
|
platypush/plugins/video.omxplayer.rst
|
||||||
|
platypush/plugins/weather.forecast.rst
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,24 @@ from platypush.plugins import Plugin
|
||||||
|
|
||||||
|
|
||||||
class PushbulletPlugin(Plugin):
|
class PushbulletPlugin(Plugin):
|
||||||
|
"""
|
||||||
|
This plugin allows you to send pushes and files to your PushBullet account.
|
||||||
|
Note: This plugin will only work if the :mod:`platypush.backend.pushbullet`
|
||||||
|
backend is configured.
|
||||||
|
|
||||||
|
Requires:
|
||||||
|
|
||||||
|
* **requests** (``pip install requests``)
|
||||||
|
"""
|
||||||
|
|
||||||
def send_push(self, **kwargs):
|
def send_push(self, **kwargs):
|
||||||
|
"""
|
||||||
|
Send a push.
|
||||||
|
|
||||||
|
:param kwargs: Push arguments, see https://docs.pushbullet.com/#create-push
|
||||||
|
:type kwargs: dict
|
||||||
|
"""
|
||||||
|
|
||||||
pushbullet = get_backend('pushbullet')
|
pushbullet = get_backend('pushbullet')
|
||||||
resp = requests.post('https://api.pushbullet.com/v2/ephemerals',
|
resp = requests.post('https://api.pushbullet.com/v2/ephemerals',
|
||||||
data=json.dumps({
|
data=json.dumps({
|
||||||
|
@ -27,6 +44,13 @@ class PushbulletPlugin(Plugin):
|
||||||
|
|
||||||
|
|
||||||
def send_file(self, filename):
|
def send_file(self, filename):
|
||||||
|
"""
|
||||||
|
Send a file.
|
||||||
|
|
||||||
|
:param filename: Path to the local file
|
||||||
|
:type filename: str
|
||||||
|
"""
|
||||||
|
|
||||||
pushbullet = get_backend('pushbullet')
|
pushbullet = get_backend('pushbullet')
|
||||||
resp = requests.post('https://api.pushbullet.com/v2/upload-request',
|
resp = requests.post('https://api.pushbullet.com/v2/upload-request',
|
||||||
data=json.dumps({'file_name': os.path.basename(filename)}),
|
data=json.dumps({'file_name': os.path.basename(filename)}),
|
||||||
|
|
|
@ -5,7 +5,31 @@ from platypush.plugins import Plugin
|
||||||
|
|
||||||
|
|
||||||
class RedisPlugin(Plugin):
|
class RedisPlugin(Plugin):
|
||||||
|
"""
|
||||||
|
Plugin to send messages on Redis queues.
|
||||||
|
|
||||||
|
Requires:
|
||||||
|
|
||||||
|
* **redis** (``pip install redis``)
|
||||||
|
"""
|
||||||
|
|
||||||
def send_message(self, queue, msg, *args, **kwargs):
|
def send_message(self, queue, msg, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Send a message to a Redis queu.
|
||||||
|
|
||||||
|
:param queue: Queue name
|
||||||
|
:type queue: str
|
||||||
|
|
||||||
|
:param msg: Message to be sent
|
||||||
|
:type msg: str, bytes, list, dict, Message object
|
||||||
|
|
||||||
|
:param args: Args passed to the Redis constructor (see https://redis-py.readthedocs.io/en/latest/#redis.Redis)
|
||||||
|
:type args: list
|
||||||
|
|
||||||
|
:param kwargs: Kwargs passed to the Redis constructor (see https://redis-py.readthedocs.io/en/latest/#redis.Redis)
|
||||||
|
:type kwargs: dict
|
||||||
|
"""
|
||||||
|
|
||||||
redis = Redis(*args, **kwargs)
|
redis = Redis(*args, **kwargs)
|
||||||
redis.rpush(queue, msg)
|
redis.rpush(queue, msg)
|
||||||
return Response(output={'state': 'ok'})
|
return Response(output={'state': 'ok'})
|
||||||
|
|
|
@ -17,6 +17,14 @@ class SerialPlugin(Plugin):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, device, baud_rate=9600, *args, **kwargs):
|
def __init__(self, device, baud_rate=9600, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
:param device: Device path (e.g. ``/dev/ttyUSB0`` or ``/dev/ttyACM0``)
|
||||||
|
:type device: str
|
||||||
|
|
||||||
|
:param baud_rate: Serial baud rate (default: 9600)
|
||||||
|
:type baud_rate: int
|
||||||
|
"""
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.device = device
|
self.device = device
|
||||||
|
@ -53,6 +61,10 @@ class SerialPlugin(Plugin):
|
||||||
return output.decode().strip()
|
return output.decode().strip()
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
|
"""
|
||||||
|
Reads JSON data from the serial device and returns it as a message
|
||||||
|
"""
|
||||||
|
|
||||||
ser = serial.Serial(self.device, self.baud_rate)
|
ser = serial.Serial(self.device, self.baud_rate)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -5,7 +5,20 @@ from platypush.message.response import Response
|
||||||
from .. import Plugin
|
from .. import Plugin
|
||||||
|
|
||||||
class ShellPlugin(Plugin):
|
class ShellPlugin(Plugin):
|
||||||
|
"""
|
||||||
|
Plugin to run custom shell commands.
|
||||||
|
"""
|
||||||
|
|
||||||
def exec(self, cmd):
|
def exec(self, cmd):
|
||||||
|
"""
|
||||||
|
Execute a command.
|
||||||
|
|
||||||
|
:param cmd: Command to execute
|
||||||
|
:type cmd: str
|
||||||
|
|
||||||
|
:returns: A response object where the ``output`` field will contain the command output as a string, and the ``errors`` field will contain whatever was sent to stderr.
|
||||||
|
"""
|
||||||
|
|
||||||
output = None
|
output = None
|
||||||
errors = []
|
errors = []
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,24 @@
|
||||||
from .. import Plugin
|
from .. import Plugin
|
||||||
|
|
||||||
class SwitchPlugin(Plugin):
|
class SwitchPlugin(Plugin):
|
||||||
|
"""
|
||||||
|
Abstract class for interacting with switch devices
|
||||||
|
"""
|
||||||
|
|
||||||
def on(self, args):
|
def on(self, args):
|
||||||
|
""" Turn the device on """
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def off(self, args):
|
def off(self, args):
|
||||||
|
""" Turn the device off """
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def toggle(self, args):
|
def toggle(self, args):
|
||||||
|
""" Toggle the device status (on/off) """
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def status(self):
|
def status(self):
|
||||||
|
""" Get the device state """
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -80,15 +80,38 @@ class Driver(object):
|
||||||
|
|
||||||
class SwitchSwitchbotPlugin(SwitchPlugin):
|
class SwitchSwitchbotPlugin(SwitchPlugin):
|
||||||
"""
|
"""
|
||||||
|
Plugin to interact with a Switchbot (https://www.switch-bot.com/) device and
|
||||||
|
programmatically control buttons.
|
||||||
|
|
||||||
NOTE: since the interaction with the Switchbot requires root privileges
|
NOTE: since the interaction with the Switchbot requires root privileges
|
||||||
(in order to scan on the bluetooth interface or setting gattlib in random),
|
(in order to scan on the bluetooth interface or setting gattlib in random),
|
||||||
this plugin just wraps the module into a `sudo` flavor, since running
|
this plugin just wraps the module into a `sudo` flavor, since running
|
||||||
Platypush with root privileges should be considered as a very bad idea.
|
Platypush with root privileges should be considered as a very bad idea.
|
||||||
Make sure that your user has sudo privileges for running this bit of code.
|
Make sure that your user has sudo privileges for running this plugin.
|
||||||
|
|
||||||
|
Requires:
|
||||||
|
|
||||||
|
* **pybluez** (``pip install pybluez``)
|
||||||
|
* **gattlib** (``pip install gattlib``)
|
||||||
|
* **libboost** (on Debian ```apt-get install libboost-python-dev libboost-thread-dev``)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, bt_interface=None, connect_timeout=None,
|
def __init__(self, bt_interface=None, connect_timeout=None,
|
||||||
scan_timeout=None, devices={}, *args, **kwargs):
|
scan_timeout=None, devices={}, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
:param bt_interface: Bluetooth interface to use (e.g. hci0) default: first available one
|
||||||
|
:type bt_interface: str
|
||||||
|
|
||||||
|
:param connecct_timeout: Timeout for the conncection to the Switchbot device - default: None
|
||||||
|
:type connect_timeout: float
|
||||||
|
|
||||||
|
:param scan_timeout: Timeout for the scan operations - default: None
|
||||||
|
:type scan_timeout: float
|
||||||
|
|
||||||
|
:param devices: Devices to control, as a BMAC address -> name map
|
||||||
|
:type devices: dict
|
||||||
|
"""
|
||||||
|
|
||||||
self.bt_interface = bt_interface
|
self.bt_interface = bt_interface
|
||||||
self.connect_timeout = connect_timeout if connect_timeout else 5
|
self.connect_timeout = connect_timeout if connect_timeout else 5
|
||||||
self.scan_timeout = scan_timeout if scan_timeout else 2
|
self.scan_timeout = scan_timeout if scan_timeout else 2
|
||||||
|
@ -118,15 +141,34 @@ class SwitchSwitchbotPlugin(SwitchPlugin):
|
||||||
|
|
||||||
|
|
||||||
def press(self, device):
|
def press(self, device):
|
||||||
|
"""
|
||||||
|
Send a press button command to a device
|
||||||
|
|
||||||
|
:param device: Device name or address
|
||||||
|
:type device: str
|
||||||
|
"""
|
||||||
return self._run(device)
|
return self._run(device)
|
||||||
|
|
||||||
def on(self, device):
|
def on(self, device):
|
||||||
|
"""
|
||||||
|
Send a press-on button command to a device
|
||||||
|
|
||||||
|
:param device: Device name or address
|
||||||
|
:type device: str
|
||||||
|
"""
|
||||||
return self._run(device, 'on')
|
return self._run(device, 'on')
|
||||||
|
|
||||||
def off(self, device):
|
def off(self, device):
|
||||||
|
"""
|
||||||
|
Send a press-off button command to a device
|
||||||
|
|
||||||
|
:param device: Device name or address
|
||||||
|
:type device: str
|
||||||
|
"""
|
||||||
return self._run(device, 'off')
|
return self._run(device, 'off')
|
||||||
|
|
||||||
def scan(self):
|
def scan(self):
|
||||||
|
""" Scan for available Switchbot devices nearby """
|
||||||
output = None
|
output = None
|
||||||
errors = []
|
errors = []
|
||||||
|
|
||||||
|
@ -145,5 +187,6 @@ class SwitchSwitchbotPlugin(SwitchPlugin):
|
||||||
|
|
||||||
return Response(output=output, errors=errors)
|
return Response(output=output, errors=errors)
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,20 @@ from platypush.message.response import Response
|
||||||
from .. import SwitchPlugin
|
from .. import SwitchPlugin
|
||||||
|
|
||||||
class SwitchWemoPlugin(SwitchPlugin):
|
class SwitchWemoPlugin(SwitchPlugin):
|
||||||
|
"""
|
||||||
|
Plugin to control a Belkin WeMo smart switch
|
||||||
|
(https://www.belkin.com/us/Products/home-automation/c/wemo-home-automation/)
|
||||||
|
|
||||||
|
Requires:
|
||||||
|
|
||||||
|
* **ouimeaux** (``pip install ouimeaux``)
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, discovery_seconds=3, *args, **kwargs):
|
def __init__(self, discovery_seconds=3, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
:param discovery_seconds: Discovery time when scanning for devices (default: 3)
|
||||||
|
:type discovery_seconds: int
|
||||||
|
"""
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.discovery_seconds=discovery_seconds
|
self.discovery_seconds=discovery_seconds
|
||||||
|
@ -15,11 +28,34 @@ class SwitchWemoPlugin(SwitchPlugin):
|
||||||
self.refresh_devices()
|
self.refresh_devices()
|
||||||
|
|
||||||
def refresh_devices(self):
|
def refresh_devices(self):
|
||||||
|
""" Update the list of available devices """
|
||||||
self.logger.info('Starting WeMo discovery')
|
self.logger.info('Starting WeMo discovery')
|
||||||
self.env.discover(seconds=self.discovery_seconds)
|
self.env.discover(seconds=self.discovery_seconds)
|
||||||
self.devices = self.env.devices
|
self.devices = self.env.devices
|
||||||
|
|
||||||
def get_devices(self):
|
def get_devices(self):
|
||||||
|
"""
|
||||||
|
Get the list of available devices
|
||||||
|
:returns: The list of devices.
|
||||||
|
|
||||||
|
Example output::
|
||||||
|
|
||||||
|
output = {
|
||||||
|
"devices": [
|
||||||
|
{
|
||||||
|
"host": "192.168.1.123",
|
||||||
|
"name": "Switch 1",
|
||||||
|
"state": 1,
|
||||||
|
"model": "Belkin Plugin Socket 1.0",
|
||||||
|
"serialnumber": "123456ABCDEF"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
# ...
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
"""
|
||||||
self.refresh_devices()
|
self.refresh_devices()
|
||||||
return Response(
|
return Response(
|
||||||
output = { 'devices': [
|
output = { 'devices': [
|
||||||
|
@ -49,12 +85,30 @@ class SwitchWemoPlugin(SwitchPlugin):
|
||||||
return Response(output=json.dumps(resp))
|
return Response(output=json.dumps(resp))
|
||||||
|
|
||||||
def on(self, device):
|
def on(self, device):
|
||||||
|
"""
|
||||||
|
Turn a switch on
|
||||||
|
|
||||||
|
:param device: Device name
|
||||||
|
:type device: str
|
||||||
|
"""
|
||||||
return self._exec('on', device)
|
return self._exec('on', device)
|
||||||
|
|
||||||
def off(self, device):
|
def off(self, device):
|
||||||
|
"""
|
||||||
|
Turn a switch off
|
||||||
|
|
||||||
|
:param device: Device name
|
||||||
|
:type device: str
|
||||||
|
"""
|
||||||
return self._exec('off', device)
|
return self._exec('off', device)
|
||||||
|
|
||||||
def toggle(self, device):
|
def toggle(self, device):
|
||||||
|
"""
|
||||||
|
Toggle the state of a switch (on/off)
|
||||||
|
|
||||||
|
:param device: Device name
|
||||||
|
:type device: str
|
||||||
|
"""
|
||||||
return self._exec('toggle', device)
|
return self._exec('toggle', device)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,14 +6,28 @@ from platypush.message.response import Response
|
||||||
from .. import Plugin
|
from .. import Plugin
|
||||||
|
|
||||||
class TtsPlugin(Plugin):
|
class TtsPlugin(Plugin):
|
||||||
""" Default Text-to-Speech plugin. It leverages Google Translate and
|
"""
|
||||||
requires mplayer """
|
Default Text-to-Speech plugin. It leverages Google Translate.
|
||||||
|
|
||||||
|
Requires:
|
||||||
|
|
||||||
|
* **mplayer** - see your distribution docs on how to install the mplayer package
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, lang='en-gb'):
|
def __init__(self, lang='en-gb'):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.lang=lang
|
self.lang=lang
|
||||||
|
|
||||||
def say(self, phrase, lang=None):
|
def say(self, phrase, lang=None):
|
||||||
|
"""
|
||||||
|
Say a phrase
|
||||||
|
|
||||||
|
:param phrase: Phrase to say
|
||||||
|
:type phrase: str
|
||||||
|
|
||||||
|
:param lang: Language code
|
||||||
|
:type lang: str
|
||||||
|
"""
|
||||||
if lang is None: lang=self.lang
|
if lang is None: lang=self.lang
|
||||||
output = None
|
output = None
|
||||||
errors = []
|
errors = []
|
||||||
|
@ -34,5 +48,6 @@ class TtsPlugin(Plugin):
|
||||||
|
|
||||||
return Response(output=output, errors=errors)
|
return Response(output=output, errors=errors)
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
||||||
|
|
|
@ -13,14 +13,37 @@ class VariablePlugin(Plugin):
|
||||||
self._variables = {}
|
self._variables = {}
|
||||||
|
|
||||||
def get(self, name, default_value=None):
|
def get(self, name, default_value=None):
|
||||||
|
"""
|
||||||
|
Get the value of a variable by name.
|
||||||
|
|
||||||
|
:param name: Variable name
|
||||||
|
:type name: str
|
||||||
|
|
||||||
|
:param default_value: What will be returned if the variable is not defined (default: None)
|
||||||
|
|
||||||
|
:returns: A map in the format ``{"<name>":"<value>"}``
|
||||||
|
"""
|
||||||
|
|
||||||
return Response(output={name: self._variables.get(name, default_value)})
|
return Response(output={name: self._variables.get(name, default_value)})
|
||||||
|
|
||||||
def set(self, **kwargs):
|
def set(self, **kwargs):
|
||||||
|
"""
|
||||||
|
Set a variable or a set of variables.
|
||||||
|
|
||||||
|
:param kwargs: Key-value list of variables to set (e.g. ``foo='bar', answer=42``)
|
||||||
|
"""
|
||||||
|
|
||||||
for (name, value) in kwargs.items():
|
for (name, value) in kwargs.items():
|
||||||
self._variables[name] = value
|
self._variables[name] = value
|
||||||
return Response(output=kwargs)
|
return Response(output=kwargs)
|
||||||
|
|
||||||
def unset(self, name):
|
def unset(self, name):
|
||||||
|
"""
|
||||||
|
Unset a variable by name if it's set
|
||||||
|
|
||||||
|
:param name: Name of the variable to remove
|
||||||
|
:type name: str
|
||||||
|
"""
|
||||||
if name in self._variables:
|
if name in self._variables:
|
||||||
del self._variables[name]
|
del self._variables[name]
|
||||||
return Response(output={'status':'ok'})
|
return Response(output={'status':'ok'})
|
||||||
|
|
|
@ -19,6 +19,22 @@ from platypush.message.event.video import VideoPlayEvent, VideoPauseEvent, \
|
||||||
from .. import Plugin
|
from .. import Plugin
|
||||||
|
|
||||||
class VideoOmxplayerPlugin(Plugin):
|
class VideoOmxplayerPlugin(Plugin):
|
||||||
|
"""
|
||||||
|
Plugin to control video and media playback on your Raspberry Pi or
|
||||||
|
ARM-compatible device using OMXPlayer.
|
||||||
|
|
||||||
|
It can play local files, remote URLs, YouTube URLs and it supports torrents
|
||||||
|
search, download and play.
|
||||||
|
|
||||||
|
Requires:
|
||||||
|
|
||||||
|
* **omxplayer** installed on your system (see your distro instructions)
|
||||||
|
* **omxplayer-wrapper** (``pip install omxplayer-wrapper``)
|
||||||
|
* **python-libtorrent** (``pip install python-libtorrent``), optional for Torrent support
|
||||||
|
* **youtube-dl** installed on your system (see your distro instructions), optional for YouTube support
|
||||||
|
"""
|
||||||
|
|
||||||
|
""" Supported video extensions """
|
||||||
video_extensions = {
|
video_extensions = {
|
||||||
'.avi', '.flv', '.wmv', '.mov', '.mp4', '.m4v', '.mpg', '.mpeg',
|
'.avi', '.flv', '.wmv', '.mov', '.mp4', '.m4v', '.mpg', '.mpeg',
|
||||||
'.rm', '.swf', '.vob', '.mkv'
|
'.rm', '.swf', '.vob', '.mkv'
|
||||||
|
@ -28,6 +44,20 @@ class VideoOmxplayerPlugin(Plugin):
|
||||||
torrent_state = {}
|
torrent_state = {}
|
||||||
|
|
||||||
def __init__(self, args=[], media_dirs=[], download_dir=None, torrent_ports=[], *argv, **kwargs):
|
def __init__(self, args=[], media_dirs=[], download_dir=None, torrent_ports=[], *argv, **kwargs):
|
||||||
|
"""
|
||||||
|
:param args: Arguments that will be passed to the OMXPlayer constructor (e.g. subtitles, volume, start position, window size etc.) see https://github.com/popcornmix/omxplayer#synopsis and http://python-omxplayer-wrapper.readthedocs.io/en/latest/omxplayer/#omxplayer.player.OMXPlayer
|
||||||
|
:type args: list
|
||||||
|
|
||||||
|
:param media_dirs: Directories that will be scanned for media files when a search is performed (default: none)
|
||||||
|
:type media_dirs: list
|
||||||
|
|
||||||
|
:param download_dir: Directory where the videos/torrents will be downloaded (default: none)
|
||||||
|
:type download_dir: str
|
||||||
|
|
||||||
|
:param torrent_ports: Torrent ports to listen on (default: 6881 and 6891)
|
||||||
|
:type torrent_ports: list[int]
|
||||||
|
"""
|
||||||
|
|
||||||
super().__init__(*argv, **kwargs)
|
super().__init__(*argv, **kwargs)
|
||||||
|
|
||||||
self.args = args
|
self.args = args
|
||||||
|
@ -54,6 +84,17 @@ class VideoOmxplayerPlugin(Plugin):
|
||||||
self.torrent_ports = torrent_ports if torrent_ports else self.default_torrent_ports
|
self.torrent_ports = torrent_ports if torrent_ports else self.default_torrent_ports
|
||||||
|
|
||||||
def play(self, resource):
|
def play(self, resource):
|
||||||
|
"""
|
||||||
|
Play a resource.
|
||||||
|
|
||||||
|
:param resource: Resource to play. Supported types:
|
||||||
|
|
||||||
|
* Local files (format: ``file://<path>/<file>``)
|
||||||
|
* Remote videos (format: ``https://<url>/<resource>``)
|
||||||
|
* YouTube videos (format: ``https://www.youtube.com/watch?v=<id>``)
|
||||||
|
* Torrents (format: ``magnet:?<magnet_uri>``)
|
||||||
|
"""
|
||||||
|
|
||||||
if resource.startswith('youtube:') \
|
if resource.startswith('youtube:') \
|
||||||
or resource.startswith('https://www.youtube.com/watch?v='):
|
or resource.startswith('https://www.youtube.com/watch?v='):
|
||||||
resource = self._get_youtube_content(resource)
|
resource = self._get_youtube_content(resource)
|
||||||
|
@ -90,9 +131,11 @@ class VideoOmxplayerPlugin(Plugin):
|
||||||
return self.status()
|
return self.status()
|
||||||
|
|
||||||
def pause(self):
|
def pause(self):
|
||||||
|
""" Pause the playback """
|
||||||
if self.player: self.player.play_pause()
|
if self.player: self.player.play_pause()
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
|
""" Stop the playback """
|
||||||
if self.player:
|
if self.player:
|
||||||
self.player.stop()
|
self.player.stop()
|
||||||
self.player.quit()
|
self.player.quit()
|
||||||
|
@ -101,26 +144,31 @@ class VideoOmxplayerPlugin(Plugin):
|
||||||
return self.status()
|
return self.status()
|
||||||
|
|
||||||
def voldown(self):
|
def voldown(self):
|
||||||
|
""" Volume down by 10% """
|
||||||
if self.player:
|
if self.player:
|
||||||
self.player.set_volume(max(-6000, self.player.volume()-1000))
|
self.player.set_volume(max(-6000, self.player.volume()-1000))
|
||||||
return self.status()
|
return self.status()
|
||||||
|
|
||||||
def volup(self):
|
def volup(self):
|
||||||
|
""" Volume up by 10% """
|
||||||
if self.player:
|
if self.player:
|
||||||
self.player.set_volume(min(0, self.player.volume()+1000))
|
self.player.set_volume(min(0, self.player.volume()+1000))
|
||||||
return self.status()
|
return self.status()
|
||||||
|
|
||||||
def back(self):
|
def back(self):
|
||||||
|
""" Back by 30 seconds """
|
||||||
if self.player:
|
if self.player:
|
||||||
self.player.seek(-30)
|
self.player.seek(-30)
|
||||||
return self.status()
|
return self.status()
|
||||||
|
|
||||||
def forward(self):
|
def forward(self):
|
||||||
|
""" Forward by 30 seconds """
|
||||||
if self.player:
|
if self.player:
|
||||||
self.player.seek(+30)
|
self.player.seek(+30)
|
||||||
return self.status()
|
return self.status()
|
||||||
|
|
||||||
def next(self):
|
def next(self):
|
||||||
|
""" Play the next track/video """
|
||||||
if self.player:
|
if self.player:
|
||||||
self.player.stop()
|
self.player.stop()
|
||||||
|
|
||||||
|
@ -132,48 +180,103 @@ class VideoOmxplayerPlugin(Plugin):
|
||||||
|
|
||||||
|
|
||||||
def hide_subtitles(self):
|
def hide_subtitles(self):
|
||||||
|
""" Hide the subtitles """
|
||||||
if self.player: self.player.hide_subtitles()
|
if self.player: self.player.hide_subtitles()
|
||||||
return self.status()
|
return self.status()
|
||||||
|
|
||||||
def hide_video(self):
|
def hide_video(self):
|
||||||
|
""" Hide the video """
|
||||||
if self.player: self.player.hide_video()
|
if self.player: self.player.hide_video()
|
||||||
return self.status()
|
return self.status()
|
||||||
|
|
||||||
def is_playing(self):
|
def is_playing(self):
|
||||||
|
"""
|
||||||
|
:returns: True if it's playing, False otherwise
|
||||||
|
"""
|
||||||
|
|
||||||
if self.player: return self.player.is_playing()
|
if self.player: return self.player.is_playing()
|
||||||
else: return False
|
else: return False
|
||||||
|
|
||||||
def load(self, source, pause=False):
|
def load(self, resource, pause=False):
|
||||||
if self.player: self.player.load(source, pause)
|
"""
|
||||||
|
Load a resource/video in the player.
|
||||||
|
|
||||||
|
:param pause: If set, load the video in paused mode (default: False)
|
||||||
|
:type pause: bool
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.player: self.player.load(resource, pause)
|
||||||
return self.status()
|
return self.status()
|
||||||
|
|
||||||
def metadata(self):
|
def metadata(self):
|
||||||
|
""" Get the metadata of the current video """
|
||||||
if self.player: return Response(output=self.player.metadata())
|
if self.player: return Response(output=self.player.metadata())
|
||||||
return self.status()
|
return self.status()
|
||||||
|
|
||||||
def mute(self):
|
def mute(self):
|
||||||
|
""" Mute the player """
|
||||||
if self.player: self.player.mute()
|
if self.player: self.player.mute()
|
||||||
return self.status()
|
return self.status()
|
||||||
|
|
||||||
def unmute(self):
|
def unmute(self):
|
||||||
|
""" Unmute the player """
|
||||||
if self.player: self.player.unmute()
|
if self.player: self.player.unmute()
|
||||||
return self.status()
|
return self.status()
|
||||||
|
|
||||||
def seek(self, relative_position):
|
def seek(self, relative_position):
|
||||||
|
"""
|
||||||
|
Seek backward/forward by the specified number of seconds
|
||||||
|
|
||||||
|
:param relative_position: Number of seconds relative to the current cursor
|
||||||
|
:type relative_position: int
|
||||||
|
"""
|
||||||
|
|
||||||
if self.player: self.player.seek(relative_position)
|
if self.player: self.player.seek(relative_position)
|
||||||
return self.status()
|
return self.status()
|
||||||
|
|
||||||
def set_position(self, position):
|
def set_position(self, position):
|
||||||
|
"""
|
||||||
|
Seek backward/forward to the specified absolute position
|
||||||
|
|
||||||
|
:param position: Number of seconds from the start
|
||||||
|
:type position: int
|
||||||
|
"""
|
||||||
|
|
||||||
if self.player: self.player.set_seek(position)
|
if self.player: self.player.set_seek(position)
|
||||||
return self.status()
|
return self.status()
|
||||||
|
|
||||||
def set_volume(self, volume):
|
def set_volume(self, volume):
|
||||||
|
"""
|
||||||
|
Set the volume
|
||||||
|
|
||||||
|
:param volume: Volume value between 0 and 100
|
||||||
|
:type volume: int
|
||||||
|
"""
|
||||||
|
|
||||||
# Transform a [0,100] value to an OMXPlayer volume in [-6000,0]
|
# Transform a [0,100] value to an OMXPlayer volume in [-6000,0]
|
||||||
volume = 60.0*volume - 6000
|
volume = 60.0*volume - 6000
|
||||||
if self.player: self.player.set_volume(volume)
|
if self.player: self.player.set_volume(volume)
|
||||||
return self.status()
|
return self.status()
|
||||||
|
|
||||||
def status(self):
|
def status(self):
|
||||||
|
"""
|
||||||
|
Get the current player state.
|
||||||
|
|
||||||
|
:returns: A dictionary containing the current state.
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
output = {
|
||||||
|
"source": "https://www.youtube.com/watch?v=7L9KkZoNZkA",
|
||||||
|
"state": "play",
|
||||||
|
"volume": 80,
|
||||||
|
"elapsed": 123,
|
||||||
|
"duration": 300,
|
||||||
|
"width": 800,
|
||||||
|
"height": 600
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
state = PlayerState.STOP.value
|
state = PlayerState.STOP.value
|
||||||
|
|
||||||
if self.player:
|
if self.player:
|
||||||
|
@ -233,6 +336,22 @@ class VideoOmxplayerPlugin(Plugin):
|
||||||
self.player.stopEvent += self.on_stop()
|
self.player.stopEvent += self.on_stop()
|
||||||
|
|
||||||
def search(self, query, types=None, queue_results=False, autoplay=False):
|
def search(self, query, types=None, queue_results=False, autoplay=False):
|
||||||
|
"""
|
||||||
|
Perform a video search.
|
||||||
|
|
||||||
|
:param query: Query string, video name or partial name
|
||||||
|
:type query: str
|
||||||
|
|
||||||
|
:param types: Video types to search (default: ``["youtube", "file", "torrent"]``)
|
||||||
|
:type types: list
|
||||||
|
|
||||||
|
:param queue_results: Append the results to the current playing queue (default: False)
|
||||||
|
:type queue_results: bool
|
||||||
|
|
||||||
|
:param autoplay: Play the first result of the search (default: False)
|
||||||
|
:type autoplay: bool
|
||||||
|
"""
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
if types is None:
|
if types is None:
|
||||||
types = { 'youtube', 'file', 'torrent' }
|
types = { 'youtube', 'file', 'torrent' }
|
||||||
|
@ -360,6 +479,13 @@ class VideoOmxplayerPlugin(Plugin):
|
||||||
return Response(output=results)
|
return Response(output=results)
|
||||||
|
|
||||||
def download_torrent(self, magnet):
|
def download_torrent(self, magnet):
|
||||||
|
"""
|
||||||
|
Download a torrent to ``download_dir`` by Magnet URI
|
||||||
|
|
||||||
|
:param magnet: Magnet URI
|
||||||
|
:type magnet: str
|
||||||
|
"""
|
||||||
|
|
||||||
import libtorrent as lt
|
import libtorrent as lt
|
||||||
|
|
||||||
if not self.download_dir:
|
if not self.download_dir:
|
||||||
|
|
|
@ -3,27 +3,253 @@ from platypush.plugins.http.request import HttpRequestPlugin
|
||||||
|
|
||||||
|
|
||||||
class WeatherForecastPlugin(HttpRequestPlugin):
|
class WeatherForecastPlugin(HttpRequestPlugin):
|
||||||
""" Plugin for getting weather updates through Darksky API """
|
"""
|
||||||
|
Plugin for getting weather updates through Darksky API
|
||||||
|
|
||||||
|
Requires:
|
||||||
|
|
||||||
|
* **requests** (``pip install requests``)
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, darksky_token, lat, long, units='si', **kwargs):
|
def __init__(self, darksky_token, lat, long, units='si', **kwargs):
|
||||||
""" Supported unit types: ca, uk2, us, si """
|
"""
|
||||||
|
:param darksky_token: Your token for using the darksky API, see https://darksky.net/dev
|
||||||
|
:type darksky_token: str
|
||||||
|
|
||||||
|
:param lat: Default forecast latitude
|
||||||
|
:type lat: float
|
||||||
|
|
||||||
|
:param long: Default forecast longitude
|
||||||
|
:type long: float
|
||||||
|
|
||||||
|
:param units: Weather units (default: "si").
|
||||||
|
|
||||||
|
Supported units:
|
||||||
|
|
||||||
|
* **si** (international system)
|
||||||
|
* **us** (US imperial units)
|
||||||
|
* **uk** (UK imperial units)
|
||||||
|
* **ca** (Canada imperial units)
|
||||||
|
|
||||||
|
:type units: str
|
||||||
|
"""
|
||||||
|
|
||||||
super().__init__(method='get', output='json')
|
super().__init__(method='get', output='json')
|
||||||
|
self.darksky_token = darksky_token
|
||||||
|
self.units = units
|
||||||
|
self.lat = lat
|
||||||
|
self.long = long
|
||||||
self.latest_bulletin = {}
|
self.latest_bulletin = {}
|
||||||
self.url = 'https://api.darksky.net/forecast/{}/{},{}?units={}'. \
|
|
||||||
format(darksky_token, lat, long, units)
|
|
||||||
|
|
||||||
def get_current_weather(self, **kwargs):
|
def _get_url(self, lat=None, long=None):
|
||||||
response = self.get(self.url)
|
return 'https://api.darksky.net/forecast/{}/{},{}?units={}'. \
|
||||||
print(response)
|
format(self.darksky_token, (lat or self.lat), (long or self.long),
|
||||||
|
self.units)
|
||||||
|
|
||||||
|
def get_current_weather(self, lat=None, long=None, **kwargs):
|
||||||
|
"""
|
||||||
|
Get the current weather.
|
||||||
|
|
||||||
|
:param lat: Weather latitude (default: configured latitude)
|
||||||
|
:type lat: float
|
||||||
|
|
||||||
|
:param long: Weather longitude (default: configured longitude)
|
||||||
|
:type long: float
|
||||||
|
|
||||||
|
:returns: A dictionary containing the current weather object.
|
||||||
|
|
||||||
|
Example output::
|
||||||
|
|
||||||
|
output = {
|
||||||
|
"time": 1529947892,
|
||||||
|
"summary": "Mostly Cloudy",
|
||||||
|
"icon": "partly-cloudy-day",
|
||||||
|
"precipIntensity": 0.0483,
|
||||||
|
"precipProbability": 0.04,
|
||||||
|
"precipType": "rain",
|
||||||
|
"temperature": 27.94,
|
||||||
|
"apparentTemperature": 29.6,
|
||||||
|
"dewPoint": 20.01,
|
||||||
|
"humidity": 0.62,
|
||||||
|
"pressure": 1009.34,
|
||||||
|
"windSpeed": 1.83,
|
||||||
|
"windGust": 5.49,
|
||||||
|
"windBearing": 192,
|
||||||
|
"cloudCover": 0.66,
|
||||||
|
"uvIndex": 0,
|
||||||
|
"visibility": 16.09,
|
||||||
|
"ozone": 273.74
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
response = self.get(self._get_url(lat, long))
|
||||||
return Response(output=response.output['currently'])
|
return Response(output=response.output['currently'])
|
||||||
|
|
||||||
def get_hourly_forecast(self, **kwargs):
|
def get_hourly_forecast(self, lat=None, long=None, **kwargs):
|
||||||
response = self.get(self.url)
|
"""
|
||||||
|
Get the hourly forecast.
|
||||||
|
|
||||||
|
:param lat: Weather latitude (default: configured latitude)
|
||||||
|
:type lat: float
|
||||||
|
|
||||||
|
:param long: Weather longitude (default: configured longitude)
|
||||||
|
:type long: float
|
||||||
|
|
||||||
|
:returns: A forecast object.
|
||||||
|
|
||||||
|
Example output::
|
||||||
|
|
||||||
|
output = {
|
||||||
|
"summary": "Partly cloudy starting tomorrow morning, continuing until tomorrow evening.",
|
||||||
|
"icon": "partly-cloudy-day",
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"time": 1529946000,
|
||||||
|
"summary": "Clear",
|
||||||
|
"icon": "clear-day",
|
||||||
|
"precipIntensity": 0,
|
||||||
|
"precipProbability": 0,
|
||||||
|
"temperature": 18.94,
|
||||||
|
"apparentTemperature": 18.94,
|
||||||
|
"dewPoint": 11.99,
|
||||||
|
"humidity": 0.64,
|
||||||
|
"pressure": 1025.53,
|
||||||
|
"windSpeed": 5.1,
|
||||||
|
"windGust": 6.22,
|
||||||
|
"windBearing": 329,
|
||||||
|
"cloudCover": 0.14,
|
||||||
|
"uvIndex": 1,
|
||||||
|
"visibility": 14.19,
|
||||||
|
"ozone": 334.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 1529949600,
|
||||||
|
"summary": "Clear",
|
||||||
|
"icon": "clear-day",
|
||||||
|
"precipIntensity": 0,
|
||||||
|
"precipProbability": 0,
|
||||||
|
"temperature": 18.41,
|
||||||
|
"apparentTemperature": 18.41,
|
||||||
|
"dewPoint": 11.12,
|
||||||
|
"humidity": 0.63,
|
||||||
|
"pressure": 1025.54,
|
||||||
|
"windSpeed": 4.6,
|
||||||
|
"windGust": 6.18,
|
||||||
|
"windBearing": 340,
|
||||||
|
"cloudCover": 0.07,
|
||||||
|
"uvIndex": 1,
|
||||||
|
"visibility": 16.09,
|
||||||
|
"ozone": 333.53
|
||||||
|
},
|
||||||
|
# ...
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
response = self.get(self._get_url(lat, long))
|
||||||
return Response(output=response.output['hourly'])
|
return Response(output=response.output['hourly'])
|
||||||
|
|
||||||
def get_daily_forecast(self, **kwargs):
|
def get_daily_forecast(self, lat=None, long=None, **kwargs):
|
||||||
response = self.get(self.url)
|
"""
|
||||||
|
Get the daily forecast.
|
||||||
|
|
||||||
|
:param lat: Weather latitude (default: configured latitude)
|
||||||
|
:type lat: float
|
||||||
|
|
||||||
|
:param long: Weather longitude (default: configured longitude)
|
||||||
|
:type long: float
|
||||||
|
|
||||||
|
:returns: A forecast object.
|
||||||
|
|
||||||
|
Example output::
|
||||||
|
|
||||||
|
"output": {
|
||||||
|
"summary": "Light rain on Sunday, with high temperatures rising to 28°C on Sunday.",
|
||||||
|
"icon": "rain",
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"time": 1529877600,
|
||||||
|
"summary": "Mostly cloudy until afternoon.",
|
||||||
|
"icon": "partly-cloudy-day",
|
||||||
|
"sunriseTime": 1529896835,
|
||||||
|
"sunsetTime": 1529957280,
|
||||||
|
"moonPhase": 0.42,
|
||||||
|
"precipIntensity": 0,
|
||||||
|
"precipIntensityMax": 0.0051,
|
||||||
|
"precipIntensityMaxTime": 1529888400,
|
||||||
|
"precipProbability": 0,
|
||||||
|
"temperatureHigh": 20.04,
|
||||||
|
"temperatureHighTime": 1529931600,
|
||||||
|
"temperatureLow": 10.68,
|
||||||
|
"temperatureLowTime": 1529982000,
|
||||||
|
"apparentTemperatureHigh": 20.04,
|
||||||
|
"apparentTemperatureHighTime": 1529931600,
|
||||||
|
"apparentTemperatureLow": 10.68,
|
||||||
|
"apparentTemperatureLowTime": 1529982000,
|
||||||
|
"dewPoint": 12.18,
|
||||||
|
"humidity": 0.77,
|
||||||
|
"pressure": 1025.16,
|
||||||
|
"windSpeed": 3.84,
|
||||||
|
"windGust": 6.51,
|
||||||
|
"windGustTime": 1529881200,
|
||||||
|
"windBearing": 336,
|
||||||
|
"cloudCover": 0.5,
|
||||||
|
"uvIndex": 6,
|
||||||
|
"uvIndexTime": 1529928000,
|
||||||
|
"visibility": 14.08,
|
||||||
|
"ozone": 331.24,
|
||||||
|
"temperatureMin": 13.89,
|
||||||
|
"temperatureMinTime": 1529960400,
|
||||||
|
"temperatureMax": 20.04,
|
||||||
|
"temperatureMaxTime": 1529931600,
|
||||||
|
"apparentTemperatureMin": 13.89,
|
||||||
|
"apparentTemperatureMinTime": 1529960400,
|
||||||
|
"apparentTemperatureMax": 20.04,
|
||||||
|
"apparentTemperatureMaxTime": 1529931600
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": 1529964000,
|
||||||
|
"summary": "Partly cloudy throughout the day.",
|
||||||
|
"icon": "partly-cloudy-day",
|
||||||
|
"sunriseTime": 1529983261,
|
||||||
|
"sunsetTime": 1530043677,
|
||||||
|
"moonPhase": 0.45,
|
||||||
|
"precipIntensity": 0,
|
||||||
|
"precipIntensityMax": 0,
|
||||||
|
"precipProbability": 0,
|
||||||
|
"temperatureHigh": 20.95,
|
||||||
|
"temperatureHighTime": 1530018000,
|
||||||
|
"temperatureLow": 11.47,
|
||||||
|
"temperatureLowTime": 1530064800,
|
||||||
|
"apparentTemperatureHigh": 20.95,
|
||||||
|
"apparentTemperatureHighTime": 1530018000,
|
||||||
|
"apparentTemperatureLow": 11.47,
|
||||||
|
"apparentTemperatureLowTime": 1530064800,
|
||||||
|
"dewPoint": 10.19,
|
||||||
|
"humidity": 0.69,
|
||||||
|
"pressure": 1026.14,
|
||||||
|
"windSpeed": 3.67,
|
||||||
|
"windGust": 7.13,
|
||||||
|
"windGustTime": 1530036000,
|
||||||
|
"windBearing": 4,
|
||||||
|
"cloudCover": 0.3,
|
||||||
|
"uvIndex": 5,
|
||||||
|
"uvIndexTime": 1530010800,
|
||||||
|
"visibility": 16.09,
|
||||||
|
"ozone": 328.59,
|
||||||
|
"temperatureMin": 10.68,
|
||||||
|
"temperatureMinTime": 1529982000,
|
||||||
|
"temperatureMax": 20.95,
|
||||||
|
"temperatureMaxTime": 1530018000,
|
||||||
|
"apparentTemperatureMin": 10.68,
|
||||||
|
"apparentTemperatureMinTime": 1529982000,
|
||||||
|
"apparentTemperatureMax": 20.95,
|
||||||
|
"apparentTemperatureMaxTime": 1530018000
|
||||||
|
},
|
||||||
|
# ...
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
response = self.get(self._get_url(lat, long))
|
||||||
return Response(output=response.output['daily'])
|
return Response(output=response.output['daily'])
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue