[#61] Plugins actions refactoring

- Using `@action` annotation to indicate methods that are allowed to be
executed as actions

- The output and errors of an action are automatically wrapped into a
`Response` object without any response build required on the plugin side
This commit is contained in:
Fabio Manganiello 2018-07-06 02:08:38 +02:00
parent 81a81312e3
commit 66d78c8615
42 changed files with 386 additions and 216 deletions

View File

@ -60,6 +60,7 @@ class Config(object):
self._config = self._read_config_file(self._cfgfile) self._config = self._read_config_file(self._cfgfile)
if 'token' in self._config: if 'token' in self._config:
self._config['token'] = self._config['token']
self._config['token_hash'] = get_hash(self._config['token']) self._config['token_hash'] = get_hash(self._config['token'])
if 'workdir' not in self._config: if 'workdir' not in self._config:

View File

@ -86,6 +86,11 @@ class EventAction(Request):
if 'target' not in action: if 'target' not in action:
action['target'] = action['origin'] action['target'] = action['origin']
token = Config.get('token')
if token:
action['token'] = token
return super().build(action) return super().build(action)

View File

@ -193,7 +193,7 @@ class Request(Message):
raise RuntimeError('Response processed with errors: {}'.format(response)) raise RuntimeError('Response processed with errors: {}'.format(response))
logger.info('Processed response from plugin {}: {}'. logger.info('Processed response from plugin {}: {}'.
format(plugin, response)) format(plugin, str(response)))
except Exception as e: except Exception as e:
# Retry mechanism # Retry mechanism
response = Response(output=None, errors=[str(e), traceback.format_exc()]) response = Response(output=None, errors=[str(e), traceback.format_exc()])

View File

@ -1,13 +1,26 @@
import sys import sys
import logging import logging
import traceback
from platypush.config import Config from platypush.config import Config
from platypush.message.response import Response from platypush.message.response import Response
from platypush.utils import get_decorators
def action(f): def action(f):
def _execute_action(*args, **kwargs): def _execute_action(*args, **kwargs):
return f(*args, **kwargs) output = None
errors = []
try:
output = f(*args, **kwargs)
except Exception as e:
if isinstance(args[0], Plugin):
args[0].logger.exception(e)
errors.append(str(e) + '\n' + traceback.format_exc())
return Response(output=output, errors=errors)
return _execute_action return _execute_action
@ -19,7 +32,13 @@ class Plugin(object):
if 'logging' in kwargs: if 'logging' in kwargs:
self.logger.setLevel(getattr(logging, kwargs['logging'].upper())) self.logger.setLevel(getattr(logging, kwargs['logging'].upper()))
self.registered_actions = set(get_decorators(self.__class__).get('action', []))
def run(self, method, *args, **kwargs): def run(self, method, *args, **kwargs):
if method not in self.registered_actions:
raise RuntimeError('{} is not a registered action on {}'.format(
method, self.__class__.__name__))
return getattr(self, method)(*args, **kwargs) return getattr(self, method)(*args, **kwargs)

View File

@ -3,9 +3,7 @@
""" """
from platypush.context import get_backend from platypush.context import get_backend
from platypush.message.response import Response from platypush.plugins import Plugin, action
from platypush.plugins import Plugin
class AssistantGooglePlugin(Plugin): class AssistantGooglePlugin(Plugin):
""" """
@ -14,21 +12,25 @@ class AssistantGooglePlugin(Plugin):
backend to programmatically control the conversation status. backend to programmatically control the conversation status.
""" """
def __init__(*args, **kwargs):
super().__init__(*args, **kwargs)
@action
def start_conversation(self): def start_conversation(self):
""" """
Programmatically start a conversation with the assistant Programmatically start a conversation with the assistant
""" """
assistant = get_backend('assistant.google') assistant = get_backend('assistant.google')
assistant.start_conversation() assistant.start_conversation()
return Response(output='', errors=[])
@action
def stop_conversation(self): def stop_conversation(self):
""" """
Programmatically stop a running conversation with the assistant Programmatically stop a running conversation with the assistant
""" """
assistant = get_backend('assistant.google') assistant = get_backend('assistant.google')
assistant.stop_conversation() assistant.stop_conversation()
return Response(output='', errors=[])
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et:

View File

@ -3,9 +3,7 @@
""" """
from platypush.context import get_backend from platypush.context import get_backend
from platypush.message.response import Response from platypush.plugins import Plugin, action
from platypush.plugins import Plugin
class AssistantGooglePushtotalkPlugin(Plugin): class AssistantGooglePushtotalkPlugin(Plugin):
""" """
@ -14,21 +12,24 @@ class AssistantGooglePushtotalkPlugin(Plugin):
:mod:`platypush.backend.assistant.google.pushtotalk` backend. :mod:`platypush.backend.assistant.google.pushtotalk` backend.
""" """
def __init__(*args, **kwargs):
super().__init__(*args, **kwargs)
@action
def start_conversation(self): def start_conversation(self):
""" """
Programmatically start a conversation with the assistant Programmatically start a conversation with the assistant
""" """
assistant = get_backend('assistant.google.pushtotalk') assistant = get_backend('assistant.google.pushtotalk')
assistant.start_conversation() assistant.start_conversation()
return Response(output='', errors=[])
@action
def stop_conversation(self): def stop_conversation(self):
""" """
Programmatically stop a running conversation with the assistant Programmatically stop a running conversation with the assistant
""" """
assistant = get_backend('assistant.google.pushtotalk') assistant = get_backend('assistant.google.pushtotalk')
assistant.stop_conversation() assistant.stop_conversation()
return Response(output='', errors=[])
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et:

View File

@ -7,8 +7,7 @@ import importlib
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
from platypush.plugins import Plugin from platypush.plugins import Plugin, action
from platypush.message.response import Response
class CalendarInterface: class CalendarInterface:
@ -64,6 +63,7 @@ class CalendarPlugin(Plugin, CalendarInterface):
self.calendars.append(getattr(module, class_name)(**calendar)) self.calendars.append(getattr(module, class_name)(**calendar))
@action
def get_upcoming_events(self, max_results=10): def get_upcoming_events(self, max_results=10):
""" """
Get a list of upcoming events merging all the available calendars. Get a list of upcoming events merging all the available calendars.
@ -116,7 +116,7 @@ class CalendarPlugin(Plugin, CalendarInterface):
else event['start']['date'] + 'T00:00:00+00:00' else event['start']['date'] + 'T00:00:00+00:00'
))[:max_results] ))[:max_results]
return Response(output=events) return events
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et:

View File

@ -9,8 +9,7 @@ import pytz
from icalendar import Calendar, Event from icalendar import Calendar, Event
from platypush.message.response import Response from platypush.plugins import Plugin, action
from platypush.plugins import Plugin
from platypush.plugins.calendar import CalendarInterface from platypush.plugins.calendar import CalendarInterface
@ -61,6 +60,7 @@ class IcalCalendarPlugin(Plugin, CalendarInterface):
} }
@action
def get_upcoming_events(self, max_results=10, only_participating=True): def get_upcoming_events(self, max_results=10, only_participating=True):
""" """
Get the upcoming events. See Get the upcoming events. See
@ -90,7 +90,7 @@ class IcalCalendarPlugin(Plugin, CalendarInterface):
else: else:
self.logger.error("HTTP error while getting {}: {}".format(self.url, response)) self.logger.error("HTTP error while getting {}: {}".format(self.url, response))
return Response(output=events) return events
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et:

View File

@ -3,9 +3,7 @@
""" """
from platypush.context import get_backend from platypush.context import get_backend
from platypush.message.response import Response from platypush.plugins import Plugin, action
from platypush.plugins import Plugin
class CameraPiPlugin(Plugin): class CameraPiPlugin(Plugin):
@ -15,22 +13,26 @@ class CameraPiPlugin(Plugin):
to programmatically control the status. to programmatically control the status.
""" """
def __init__(*args, **kwargs):
super().__init__(*args, **kwargs)
@action
def start_recording(self): def start_recording(self):
""" """
Start recording Start recording
""" """
camera = get_backend('camera.pi') camera = get_backend('camera.pi')
camera.send_camera_action(camera.CameraAction.START_RECORDING) camera.send_camera_action(camera.CameraAction.START_RECORDING)
return Response(output={'status':'ok'})
@action
def stop_recording(self): def stop_recording(self):
""" """
Stop recording Stop recording
""" """
camera = get_backend('camera.pi') camera = get_backend('camera.pi')
camera.send_camera_action(camera.CameraAction.STOP_RECORDING) camera.send_camera_action(camera.CameraAction.STOP_RECORDING)
return Response(output={'status':'ok'})
@action
def take_picture(self, image_file): def take_picture(self, image_file):
""" """
Take a picture. Take a picture.
@ -40,7 +42,7 @@ class CameraPiPlugin(Plugin):
""" """
camera = get_backend('camera.pi') camera = get_backend('camera.pi')
camera.send_camera_action(camera.CameraAction.TAKE_PICTURE, image_file=image_file) camera.send_camera_action(camera.CameraAction.TAKE_PICTURE, image_file=image_file)
return Response(output={'image_file':image_file}) return {'image_file': image_file}
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et:

View File

@ -4,9 +4,7 @@
from sqlalchemy import create_engine, Table, MetaData from sqlalchemy import create_engine, Table, MetaData
from platypush.message.response import Response from platypush.plugins import Plugin, action
from .. import Plugin
class DbPlugin(Plugin): class DbPlugin(Plugin):
""" """
@ -40,6 +38,7 @@ class DbPlugin(Plugin):
else: else:
return self.engine return self.engine
@action
def execute(self, statement, engine=None, *args, **kwargs): def execute(self, statement, engine=None, *args, **kwargs):
""" """
Executes a raw SQL statement. Executes a raw SQL statement.
@ -65,9 +64,8 @@ class DbPlugin(Plugin):
result = connection.execute(statement) result = connection.execute(statement)
connection.commit() connection.commit()
return Response()
@action
def select(self, query, engine=None, *args, **kwargs): def select(self, query, engine=None, *args, **kwargs):
""" """
Returns rows (as a list of hashes) given a query. Returns rows (as a list of hashes) given a query.
@ -119,9 +117,10 @@ class DbPlugin(Plugin):
for row in result.fetchall() for row in result.fetchall()
] ]
return Response(output=rows) return rows
@action
def insert(self, table, records, engine=None, *args, **kwargs): def insert(self, table, records, engine=None, *args, **kwargs):
""" """
Inserts records (as a list of hashes) into a table. Inserts records (as a list of hashes) into a table.
@ -169,8 +168,6 @@ class DbPlugin(Plugin):
insert = table.insert().values(**record) insert = table.insert().values(**record)
engine.execute(insert) engine.execute(insert)
return Response()
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et:

View File

@ -9,7 +9,7 @@ import os
from apiclient import discovery from apiclient import discovery
from platypush.message.response import Response from platypush.plugins import action
from platypush.plugins.google import GooglePlugin from platypush.plugins.google import GooglePlugin
from platypush.plugins.calendar import CalendarInterface from platypush.plugins.calendar import CalendarInterface
@ -25,6 +25,7 @@ class GoogleCalendarPlugin(GooglePlugin, CalendarInterface):
super().__init__(scopes=self.scopes, *args, **kwargs) super().__init__(scopes=self.scopes, *args, **kwargs)
@action
def get_upcoming_events(self, max_results=10): def get_upcoming_events(self, max_results=10):
""" """
Get the upcoming events. See Get the upcoming events. See

View File

@ -17,7 +17,7 @@ from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText from email.mime.text import MIMEText
from platypush.message.response import Response from platypush.plugins import action
from platypush.plugins.google import GooglePlugin from platypush.plugins.google import GooglePlugin
@ -32,6 +32,7 @@ class GoogleMailPlugin(GooglePlugin):
super().__init__(scopes=self.scopes, *args, **kwargs) super().__init__(scopes=self.scopes, *args, **kwargs)
@action
def compose(self, sender, to, subject, body, files=None): def compose(self, sender, to, subject, body, files=None):
""" """
Compose a message. Compose a message.
@ -91,9 +92,10 @@ class GoogleMailPlugin(GooglePlugin):
message = (service.users().messages().send( message = (service.users().messages().send(
userId='me', body=body).execute()) userId='me', body=body).execute())
return Response(output=message) return message
@action
def get_labels(self): def get_labels(self):
""" """
Returns the available labels on the GMail account Returns the available labels on the GMail account
@ -101,7 +103,7 @@ class GoogleMailPlugin(GooglePlugin):
service = self._get_service() service = self._get_service()
results = service.users().labels().list(userId='me').execute() results = service.users().labels().list(userId='me').execute()
labels = results.get('labels', []) labels = results.get('labels', [])
return Response(output=labels) return labels
def _get_service(self): def _get_service(self):

View File

@ -5,7 +5,7 @@
import json import json
import requests import requests
from platypush.message.response import Response from platypush.plugins import action
from platypush.plugins.google import GooglePlugin from platypush.plugins.google import GooglePlugin
@ -26,6 +26,7 @@ class GoogleMapsPlugin(GooglePlugin):
self.api_key = api_key self.api_key = api_key
@action
def get_address_from_latlng(self, latitude, longitude): def get_address_from_latlng(self, latitude, longitude):
""" """
Get an address information given lat/long Get an address information given lat/long
@ -65,7 +66,7 @@ class GoogleMapsPlugin(GooglePlugin):
elif component_type == 'postal_code': elif component_type == 'postal_code':
address['postal_code'] = addr_component['long_name'] address['postal_code'] = addr_component['long_name']
return Response(output=address) return address
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et:

View File

@ -5,8 +5,7 @@
import threading import threading
import time import time
from platypush.message.response import Response from platypush.plugins import Plugin, action
from platypush.plugins import Plugin
class GpioPlugin(Plugin): class GpioPlugin(Plugin):
@ -17,6 +16,10 @@ class GpioPlugin(Plugin):
* **RPi.GPIO** (`pip install RPi.GPIO`) * **RPi.GPIO** (`pip install RPi.GPIO`)
""" """
def __init__(*args, **kwargs):
super().__init__(*args, **kwargs)
@action
def write(self, pin, val): def write(self, pin, val):
""" """
Write a byte value to a pin. Write a byte value to a pin.
@ -44,12 +47,13 @@ class GpioPlugin(Plugin):
gpio.setup(pin, gpio.OUT) gpio.setup(pin, gpio.OUT)
gpio.output(pin, val) gpio.output(pin, val)
return Response(output={ return {
'pin': pin, 'pin': pin,
'val': val, 'val': val,
'method': 'write', 'method': 'write',
}) }
@action
def read(self, pin): def read(self, pin):
""" """
Reads a value from a PIN. Reads a value from a PIN.
@ -74,11 +78,11 @@ class GpioPlugin(Plugin):
gpio.setup(pin, gpio.IN) gpio.setup(pin, gpio.IN)
val = gpio.input(pin) val = gpio.input(pin)
return Response(output={ return {
'pin': pin, 'pin': pin,
'val': val, 'val': val,
'method': 'read', 'method': 'read',
}) }
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et:

View File

@ -1,4 +1,4 @@
from platypush.plugins import Plugin from platypush.plugins import Plugin, action
class GpioSensorPlugin(Plugin): class GpioSensorPlugin(Plugin):
@ -10,6 +10,7 @@ class GpioSensorPlugin(Plugin):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@action
def get_measurement(self, *args, **kwargs): def get_measurement(self, *args, **kwargs):
""" """
Implemented by the subclasses. Implemented by the subclasses.
@ -27,6 +28,7 @@ class GpioSensorPlugin(Plugin):
""" """
raise NotImplementedError('get_measurement should be implemented in a derived class') raise NotImplementedError('get_measurement should be implemented in a derived class')
@action
def get_data(self, *args, **kwargs): def get_data(self, *args, **kwargs):
""" """
Alias for ``get_measurement`` Alias for ``get_measurement``

View File

@ -1,7 +1,7 @@
import threading import threading
import time import time
from platypush.message.response import Response from platypush.plugins import action
from platypush.plugins.gpio.sensor import GpioSensorPlugin from platypush.plugins.gpio.sensor import GpioSensorPlugin
@ -39,6 +39,7 @@ class GpioSensorDistancePlugin(GpioSensorPlugin):
gpio.output(self.trigger_pin, False) gpio.output(self.trigger_pin, False)
@action
def get_measurement(self): def get_measurement(self):
""" """
Extends :func:`.GpioSensorPlugin.get_measurement` Extends :func:`.GpioSensorPlugin.get_measurement`

View File

@ -1,8 +1,8 @@
import enum import enum
import time import time
from platypush.plugins import action
from platypush.plugins.gpio.sensor import GpioSensorPlugin from platypush.plugins.gpio.sensor import GpioSensorPlugin
from platypush.message.response import Response
class MCP3008Mode(enum.Enum): class MCP3008Mode(enum.Enum):
@ -125,6 +125,7 @@ class GpioSensorMcp3008Plugin(GpioSensorPlugin):
return (value * self.Vdd) / 1023.0 if value is not None else None return (value * self.Vdd) / 1023.0 if value is not None else None
@action
def get_measurement(self): def get_measurement(self):
""" """
Returns a measurement from the sensors connected to the MCP3008 device. Returns a measurement from the sensors connected to the MCP3008 device.
@ -164,7 +165,7 @@ class GpioSensorMcp3008Plugin(GpioSensorPlugin):
else: else:
values[i] = value values[i] = value
return Response(output=values) return values
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et:

View File

@ -2,8 +2,7 @@ import enum
import threading import threading
import time import time
from platypush.message.response import Response from platypush.plugins import Plugin, action
from platypush.plugins import Plugin
from platypush.context import get_plugin from platypush.context import get_plugin
from platypush.config import Config from platypush.config import Config
@ -95,7 +94,7 @@ class GpioZeroborgPlugin(Plugin):
value = None value = None
while value is None: while value is None:
value = plugin.get_measurement() value = plugin.get_measurement().output
if time.time() - measure_start_time > timeout: if time.time() - measure_start_time > timeout:
return None return None
@ -127,6 +126,7 @@ class GpioZeroborgPlugin(Plugin):
return direction return direction
@action
def drive(self, direction): def drive(self, direction):
""" """
Drive the motors in a certain direction. Drive the motors in a certain direction.
@ -184,9 +184,10 @@ class GpioZeroborgPlugin(Plugin):
self._drive_thread = threading.Thread(target=_run) self._drive_thread = threading.Thread(target=_run)
self._drive_thread.start() self._drive_thread.start()
return Response(output={'status': 'running', 'direction': direction}) return {'status': 'running', 'direction': direction}
@action
def stop(self): def stop(self):
""" """
Turns off the motors Turns off the motors
@ -199,7 +200,7 @@ class GpioZeroborgPlugin(Plugin):
self.zb.MotorsOff() self.zb.MotorsOff()
self.zb.ResetEpo() self.zb.ResetEpo()
return Response(output={'status':'stopped'}) return {'status':'stopped'}
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et:

View File

@ -1,8 +1,6 @@
import requests import requests
from platypush.message.response import Response from platypush.plugins import Plugin, action
from platypush.plugins import Plugin
class HttpRequestPlugin(Plugin): class HttpRequestPlugin(Plugin):
""" """
@ -41,6 +39,9 @@ class HttpRequestPlugin(Plugin):
} }
""" """
def __init__(*args, **kwargs):
super().__init__(*args, **kwargs)
def _exec(self, method, url, output='text', **kwargs): def _exec(self, method, url, output='text', **kwargs):
""" Available output types: text (default), json, binary """ """ Available output types: text (default), json, binary """
@ -51,9 +52,10 @@ class HttpRequestPlugin(Plugin):
if output == 'json': output = response.json() if output == 'json': output = response.json()
if output == 'binary': output = response.content if output == 'binary': output = response.content
return Response(output=output, errors=[]) return output
@action
def get(self, url, **kwargs): def get(self, url, **kwargs):
""" """
Perform a GET request Perform a GET request
@ -68,6 +70,7 @@ class HttpRequestPlugin(Plugin):
return self._exec(method='get', url=url, **kwargs) return self._exec(method='get', url=url, **kwargs)
@action
def post(self, url, **kwargs): def post(self, url, **kwargs):
""" """
Perform a POST request Perform a POST request
@ -82,6 +85,7 @@ class HttpRequestPlugin(Plugin):
return self._exec(method='post', url=url, **kwargs) return self._exec(method='post', url=url, **kwargs)
@action
def head(self, url, **kwargs): def head(self, url, **kwargs):
""" """
Perform an HTTP HEAD request Perform an HTTP HEAD request
@ -96,6 +100,7 @@ class HttpRequestPlugin(Plugin):
return self._exec(method='head', url=url, **kwargs) return self._exec(method='head', url=url, **kwargs)
@action
def put(self, url, **kwargs): def put(self, url, **kwargs):
""" """
Perform a PUT request Perform a PUT request
@ -110,6 +115,7 @@ class HttpRequestPlugin(Plugin):
return self._exec(method='put', url=url, **kwargs) return self._exec(method='put', url=url, **kwargs)
@action
def delete(self, url, **kwargs): def delete(self, url, **kwargs):
""" """
Perform a DELETE request Perform a DELETE request
@ -124,6 +130,7 @@ class HttpRequestPlugin(Plugin):
return self._exec(method='delete', url=url, **kwargs) return self._exec(method='delete', url=url, **kwargs)
@action
def options(self, url, **kwargs): def options(self, url, **kwargs):
""" """
Perform an HTTP OPTIONS request Perform an HTTP OPTIONS request

View File

@ -1,6 +1,7 @@
import datetime import datetime
import dateutil.parser import dateutil.parser
from platypush.plugins import action
from platypush.plugins.http.request import HttpRequestPlugin from platypush.plugins.http.request import HttpRequestPlugin
class HttpRequestOtaBookingPlugin(HttpRequestPlugin): class HttpRequestOtaBookingPlugin(HttpRequestPlugin):
@ -12,6 +13,7 @@ class HttpRequestOtaBookingPlugin(HttpRequestPlugin):
self.timeout = timeout self.timeout = timeout
@action
def get_reservations(self, day='today'): def get_reservations(self, day='today'):
url = 'https://hub-api.booking.com/v1/hotels/{}/reservations' \ url = 'https://hub-api.booking.com/v1/hotels/{}/reservations' \
.format(self.hotel_id) .format(self.hotel_id)

View File

@ -1,9 +1,7 @@
import pylast import pylast
import time import time
from platypush.message.response import Response from platypush.plugins import Plugin, action
from .. import Plugin
class LastfmPlugin(Plugin): class LastfmPlugin(Plugin):
""" """
@ -42,6 +40,7 @@ class LastfmPlugin(Plugin):
password_hash = pylast.md5(self.password)) password_hash = pylast.md5(self.password))
@action
def scrobble(self, artist, title, album=None, **kwargs): def scrobble(self, artist, title, album=None, **kwargs):
""" """
Scrobble a track to Last.FM Scrobble a track to Last.FM
@ -61,8 +60,6 @@ class LastfmPlugin(Plugin):
timestamp = int(time.time()), timestamp = int(time.time()),
) )
return Response()
def update_now_playing(self, artist, title, album=None, **kwargs): def update_now_playing(self, artist, title, album=None, **kwargs):
""" """
@ -82,8 +79,6 @@ class LastfmPlugin(Plugin):
album = album, album = album,
) )
return Response()
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et:

View File

@ -1,22 +1,26 @@
from .. import Plugin from platypush.plugins import Plugin, action
class LightPlugin(Plugin): class LightPlugin(Plugin):
""" """
Abstract plugin to interface your logic with lights/bulbs. Abstract plugin to interface your logic with lights/bulbs.
""" """
@action
def on(self): def on(self):
""" Turn the light on """ """ Turn the light on """
raise NotImplementedError() raise NotImplementedError()
@action
def off(self): def off(self):
""" Turn the light off """ """ Turn the light off """
raise NotImplementedError() raise NotImplementedError()
@action
def toggle(self): def toggle(self):
""" Toggle the light status (on/off) """ """ Toggle the light status (on/off) """
raise NotImplementedError() raise NotImplementedError()
@action
def status(self): def status(self):
""" Get the light status """ """ Get the light status """
raise NotImplementedError() raise NotImplementedError()

View File

@ -8,9 +8,9 @@ from redis.exceptions import TimeoutError as QueueTimeoutError
from phue import Bridge from phue import Bridge
from platypush.context import get_backend from platypush.context import get_backend
from platypush.message.response import Response from platypush.plugins import action
from platypush.plugins.light import LightPlugin
from .. import LightPlugin
class LightHuePlugin(LightPlugin): class LightHuePlugin(LightPlugin):
""" """
@ -75,6 +75,7 @@ class LightHuePlugin(LightPlugin):
for g in groups: for g in groups:
self.lights.extend([l.name for l in g.lights]) self.lights.extend([l.name for l in g.lights])
@action
def connect(self): def connect(self):
""" """
Connect to the configured Hue bridge. If the device hasn't been paired Connect to the configured Hue bridge. If the device hasn't been paired
@ -100,6 +101,7 @@ class LightHuePlugin(LightPlugin):
self.logger.info('Bridge already connected') self.logger.info('Bridge already connected')
@action
def get_scenes(self): def get_scenes(self):
""" """
Get the available scenes on the devices. Get the available scenes on the devices.
@ -129,9 +131,10 @@ class LightHuePlugin(LightPlugin):
} }
""" """
return Response(output=self.bridge.get_scene()) return self.bridge.get_scene()
@action
def get_lights(self): def get_lights(self):
""" """
Get the configured lights. Get the configured lights.
@ -170,9 +173,10 @@ class LightHuePlugin(LightPlugin):
} }
""" """
return Response(output=self.bridge.get_light()) return self.bridge.get_light()
@action
def get_groups(self): def get_groups(self):
""" """
Get the list of configured light groups. Get the list of configured light groups.
@ -222,7 +226,7 @@ class LightHuePlugin(LightPlugin):
} }
""" """
return Response(output=self.bridge.get_group()) return self.bridge.get_group()
def _exec(self, attr, *args, **kwargs): def _exec(self, attr, *args, **kwargs):
@ -257,8 +261,8 @@ class LightHuePlugin(LightPlugin):
self.bridge = None self.bridge = None
raise e raise e
return Response(output='ok')
@action
def set_light(self, light, **kwargs): def set_light(self, light, **kwargs):
""" """
Set a light (or lights) property. Set a light (or lights) property.
@ -281,8 +285,8 @@ class LightHuePlugin(LightPlugin):
self.connect() self.connect()
self.bridge.set_light(light, **kwargs) self.bridge.set_light(light, **kwargs)
return Response(output='ok')
@action
def set_group(self, group, **kwargs): def set_group(self, group, **kwargs):
""" """
Set a group (or groups) property. Set a group (or groups) property.
@ -305,8 +309,8 @@ class LightHuePlugin(LightPlugin):
self.connect() self.connect()
self.bridge.set_group(group, **kwargs) self.bridge.set_group(group, **kwargs)
return Response(output='ok')
@action
def on(self, lights=[], groups=[]): def on(self, lights=[], groups=[]):
""" """
Turn lights/groups on. Turn lights/groups on.
@ -317,6 +321,7 @@ class LightHuePlugin(LightPlugin):
return self._exec('on', True, lights=lights, groups=groups) return self._exec('on', True, lights=lights, groups=groups)
@action
def off(self, lights=[], groups=[]): def off(self, lights=[], groups=[]):
""" """
Turn lights/groups off. Turn lights/groups off.
@ -327,6 +332,7 @@ class LightHuePlugin(LightPlugin):
return self._exec('on', False, lights=lights, groups=groups) return self._exec('on', False, lights=lights, groups=groups)
@action
def bri(self, value, lights=[], groups=[]): def bri(self, value, lights=[], groups=[]):
""" """
Set lights/groups brightness. Set lights/groups brightness.
@ -339,6 +345,7 @@ class LightHuePlugin(LightPlugin):
return self._exec('bri', int(value) % (self.MAX_BRI+1), return self._exec('bri', int(value) % (self.MAX_BRI+1),
lights=lights, groups=groups) lights=lights, groups=groups)
@action
def sat(self, value, lights=[], groups=[]): def sat(self, value, lights=[], groups=[]):
""" """
Set lights/groups saturation. Set lights/groups saturation.
@ -351,6 +358,7 @@ class LightHuePlugin(LightPlugin):
return self._exec('sat', int(value) % (self.MAX_SAT+1), return self._exec('sat', int(value) % (self.MAX_SAT+1),
lights=lights, groups=groups) lights=lights, groups=groups)
@action
def hue(self, value, lights=[], groups=[]): def hue(self, value, lights=[], groups=[]):
""" """
Set lights/groups color hue. Set lights/groups color hue.
@ -363,6 +371,7 @@ class LightHuePlugin(LightPlugin):
return self._exec('hue', int(value) % (self.MAX_HUE+1), return self._exec('hue', int(value) % (self.MAX_HUE+1),
lights=lights, groups=groups) lights=lights, groups=groups)
@action
def scene(self, name, lights=[], groups=[]): def scene(self, name, lights=[], groups=[]):
""" """
Set a scene by name. Set a scene by name.
@ -374,6 +383,7 @@ class LightHuePlugin(LightPlugin):
return self._exec('scene', name=name, lights=lights, groups=groups) return self._exec('scene', name=name, lights=lights, groups=groups)
@action
def is_animation_running(self): def is_animation_running(self):
""" """
:returns: True if there is an animation running, false otherwise. :returns: True if there is an animation running, false otherwise.
@ -381,6 +391,7 @@ class LightHuePlugin(LightPlugin):
return self.animation_thread is not None return self.animation_thread is not None
@action
def stop_animation(self): def stop_animation(self):
""" """
Stop a running animation if any Stop a running animation if any
@ -389,6 +400,7 @@ class LightHuePlugin(LightPlugin):
if self.animation_thread and self.animation_thread.is_alive(): if self.animation_thread and self.animation_thread.is_alive():
self.redis.rpush(self.ANIMATION_CTRL_QUEUE_NAME, 'STOP') self.redis.rpush(self.ANIMATION_CTRL_QUEUE_NAME, 'STOP')
@action
def animate(self, animation, duration=None, def animate(self, animation, duration=None,
hue_range=[0, MAX_HUE], sat_range=[0, MAX_SAT], hue_range=[0, MAX_HUE], sat_range=[0, MAX_SAT],
bri_range=[MAX_BRI-1, MAX_BRI], lights=None, groups=None, bri_range=[MAX_BRI-1, MAX_BRI], lights=None, groups=None,
@ -529,7 +541,6 @@ class LightHuePlugin(LightPlugin):
self.stop_animation() self.stop_animation()
self.animation_thread = Thread(target=_animate_thread, args=(lights,)) self.animation_thread = Thread(target=_animate_thread, args=(lights,))
self.animation_thread.start() self.animation_thread.start()
return Response(output='ok')
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et:

View File

@ -2,10 +2,9 @@ import re
import subprocess import subprocess
from platypush.context import get_plugin from platypush.context import get_plugin
from platypush.message.response import Response
from platypush.plugins.media import PlayerState from platypush.plugins.media import PlayerState
from .. import Plugin from platypush.plugins import Plugin, action
class MediaCtrlPlugin(Plugin): class MediaCtrlPlugin(Plugin):
""" """
@ -76,9 +75,9 @@ class MediaCtrlPlugin(Plugin):
return None return None
@action
def play(self, url): def play(self, url):
(type, resource) = self._get_type_and_resource_by_url(url) (type, resource) = self._get_type_and_resource_by_url(url)
response = Response(output='', errors = [])
plugin_name = None plugin_name = None
if type == 'mpd': if type == 'mpd':
@ -99,11 +98,13 @@ class MediaCtrlPlugin(Plugin):
self.url = resource self.url = resource
return self.plugin.play(resource) return self.plugin.play(resource)
@action
def pause(self): def pause(self):
plugin = self._get_playing_plugin() plugin = self._get_playing_plugin()
if plugin: return plugin.pause() if plugin: return plugin.pause()
@action
def stop(self): def stop(self):
plugin = self._get_playing_plugin() plugin = self._get_playing_plugin()
if plugin: if plugin:
@ -112,36 +113,43 @@ class MediaCtrlPlugin(Plugin):
return ret return ret
@action
def voldown(self): def voldown(self):
plugin = self._get_playing_plugin() plugin = self._get_playing_plugin()
if plugin: return plugin.voldown() if plugin: return plugin.voldown()
@action
def volup(self): def volup(self):
plugin = self._get_playing_plugin() plugin = self._get_playing_plugin()
if plugin: return plugin.volup() if plugin: return plugin.volup()
@action
def back(self): def back(self):
plugin = self._get_playing_plugin() plugin = self._get_playing_plugin()
if plugin: return plugin.back() if plugin: return plugin.back()
@action
def forward(self): def forward(self):
plugin = self._get_playing_plugin() plugin = self._get_playing_plugin()
if plugin: return plugin.forward() if plugin: return plugin.forward()
@action
def next(self): def next(self):
plugin = self._get_playing_plugin() plugin = self._get_playing_plugin()
if plugin: return plugin.next() if plugin: return plugin.next()
@action
def previous(self): def previous(self):
plugin = self._get_playing_plugin() plugin = self._get_playing_plugin()
if plugin: return plugin.previous() if plugin: return plugin.previous()
@action
def status(self): def status(self):
plugin = self._get_playing_plugin() plugin = self._get_playing_plugin()
if plugin: return plugin.status() if plugin: return plugin.status()

View File

@ -1,8 +1,7 @@
import rtmidi import rtmidi
import time import time
from platypush.message.response import Response from platypush.plugins import Plugin, action
from platypush.plugins import Plugin
class MidiPlugin(Plugin): class MidiPlugin(Plugin):
@ -39,6 +38,7 @@ class MidiPlugin(Plugin):
format(self.device_name)) format(self.device_name))
@action
def send_message(self, values, *args, **kwargs): def send_message(self, values, *args, **kwargs):
""" """
:param values: Values is expected to be a list containing the MIDI command code and the command parameters - see reference at https://ccrma.stanford.edu/~craig/articles/linuxmidi/misc/essenmidi.html :param values: Values is expected to be a list containing the MIDI command code and the command parameters - see reference at https://ccrma.stanford.edu/~craig/articles/linuxmidi/misc/essenmidi.html
@ -70,9 +70,9 @@ class MidiPlugin(Plugin):
""" """
self.midiout.send_message(values, *args, **kwargs) self.midiout.send_message(values, *args, **kwargs)
return Response(output={'status':'ok'})
@action
def play_note(self, note, velocity, duration=0): def play_note(self, note, velocity, duration=0):
""" """
Play a note with selected velocity and duration. Play a note with selected velocity and duration.
@ -96,6 +96,7 @@ class MidiPlugin(Plugin):
self._played_notes.remove(note) self._played_notes.remove(note)
@action
def release_note(self, note): def release_note(self, note):
""" """
Release a played note. Release a played note.
@ -108,6 +109,7 @@ class MidiPlugin(Plugin):
self._played_notes.remove(note) self._played_notes.remove(note)
@action
def release_all_notes(self): def release_all_notes(self):
""" """
Release all the notes being played. Release all the notes being played.

View File

@ -2,8 +2,7 @@ import json
import paho.mqtt.publish as publisher import paho.mqtt.publish as publisher
from platypush.message import Message from platypush.message import Message
from platypush.message.response import Response from platypush.plugins import Plugin, action
from platypush.plugins import Plugin
class MqttPlugin(Plugin): class MqttPlugin(Plugin):
@ -12,6 +11,7 @@ class MqttPlugin(Plugin):
with the MQTT protocol, see http://mqtt.org/ with the MQTT protocol, see http://mqtt.org/
""" """
@action
def send_message(self, topic, msg, host, port=1883, *args, **kwargs): def send_message(self, topic, msg, host, port=1883, *args, **kwargs):
""" """
Sends a message to a topic/channel. Sends a message to a topic/channel.
@ -35,7 +35,6 @@ class MqttPlugin(Plugin):
except: pass except: pass
publisher.single(topic, str(msg), hostname=host, port=port) publisher.single(topic, str(msg), hostname=host, port=port)
return Response(output={'state': 'ok'})
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et:

View File

@ -1,33 +1,47 @@
from .. import Plugin from platypush.plugins import Plugin, action
class MusicPlugin(Plugin): class MusicPlugin(Plugin):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@action
def play(self): def play(self):
raise NotImplementedError() raise NotImplementedError()
@action
def pause(self): def pause(self):
raise NotImplementedError() raise NotImplementedError()
@action
def stop(self): def stop(self):
raise NotImplementedError() raise NotImplementedError()
@action
def next(self): def next(self):
raise NotImplementedError() raise NotImplementedError()
@action
def previous(self): def previous(self):
raise NotImplementedError() raise NotImplementedError()
@action
def setvol(self, vol): def setvol(self, vol):
raise NotImplementedError() raise NotImplementedError()
@action
def add(self, content): def add(self, content):
raise NotImplementedError() raise NotImplementedError()
@action
def playlistadd(self, playlist): def playlistadd(self, playlist):
raise NotImplementedError() raise NotImplementedError()
@action
def clear(self): def clear(self):
raise NotImplementedError() raise NotImplementedError()
@action
def status(self): def status(self):
raise NotImplementedError() raise NotImplementedError()

View File

@ -1,9 +1,8 @@
import mpd import mpd
import re import re
from platypush.message.response import Response from platypush.plugins import action
from platypush.plugins.music import MusicPlugin
from .. import MusicPlugin
class MusicMpdPlugin(MusicPlugin): class MusicMpdPlugin(MusicPlugin):
""" """
@ -29,6 +28,7 @@ class MusicMpdPlugin(MusicPlugin):
:type port: int :type port: int
""" """
super().__init__()
self.host = host self.host = host
self.port = port self.port = port
self.client = mpd.MPDClient(use_unicode=True) self.client = mpd.MPDClient(use_unicode=True)
@ -36,8 +36,9 @@ class MusicMpdPlugin(MusicPlugin):
def _exec(self, method, *args, **kwargs): def _exec(self, method, *args, **kwargs):
getattr(self.client, method)(*args, **kwargs) getattr(self.client, method)(*args, **kwargs)
return self.status() return self.status().output
@action
def play(self, resource=None): def play(self, resource=None):
""" """
Play a resource by path/URI Play a resource by path/URI
@ -51,6 +52,7 @@ class MusicMpdPlugin(MusicPlugin):
self.add(resource) self.add(resource)
return self._exec('play') return self._exec('play')
@action
def play_pos(self, pos): def play_pos(self, pos):
""" """
Play a track in the current playlist by position number Play a track in the current playlist by position number
@ -61,6 +63,7 @@ class MusicMpdPlugin(MusicPlugin):
return self._exec('play', pos) return self._exec('play', pos)
@action
def pause(self): def pause(self):
""" Pause playback """ """ Pause playback """
@ -68,31 +71,38 @@ class MusicMpdPlugin(MusicPlugin):
if status == 'play': return self._exec('pause') if status == 'play': return self._exec('pause')
else: return self._exec('play') else: return self._exec('play')
@action
def pause_if_playing(self): def pause_if_playing(self):
""" Pause playback only if it's playing """ """ Pause playback only if it's playing """
status = self.status().output['state'] status = self.status().output['state']
if status == 'play': return self._exec('pause') if status == 'play':
else: return Response(output={}) return self._exec('pause')
@action
def play_if_paused(self): def play_if_paused(self):
""" Play only if it's paused (resume) """ """ Play only if it's paused (resume) """
status = self.status().output['state'] status = self.status().output['state']
if status == 'pause': return self._exec('play') if status == 'pause':
else: return Response(output={}) return self._exec('play')
@action
def stop(self): def stop(self):
""" Stop playback """ """ Stop playback """
return self._exec('stop') return self._exec('stop')
@action
def play_or_stop(self): def play_or_stop(self):
""" Play or stop (play state toggle) """ """ Play or stop (play state toggle) """
status = self.status().output['state'] status = self.status().output['state']
if status == 'play': return self._exec('stop') if status == 'play':
else: return self._exec('play') return self._exec('stop')
else:
return self._exec('play')
@action
def playid(self, track_id): def playid(self, track_id):
""" """
Play a track by ID Play a track by ID
@ -103,14 +113,17 @@ class MusicMpdPlugin(MusicPlugin):
return self._exec('playid', track_id) return self._exec('playid', track_id)
@action
def next(self): def next(self):
""" Play the next track """ """ Play the next track """
return self._exec('next') return self._exec('next')
@action
def previous(self): def previous(self):
""" Play the previous track """ """ Play the previous track """
return self._exec('previous') return self._exec('previous')
@action
def setvol(self, vol): def setvol(self, vol):
""" """
Set the volume Set the volume
@ -120,6 +133,7 @@ class MusicMpdPlugin(MusicPlugin):
""" """
return self._exec('setvol', vol) return self._exec('setvol', vol)
@action
def volup(self, delta=10): def volup(self, delta=10):
""" """
Turn up the volume Turn up the volume
@ -134,6 +148,7 @@ class MusicMpdPlugin(MusicPlugin):
self.setvol(str(new_volume)) self.setvol(str(new_volume))
return self.status() return self.status()
@action
def voldown(self, delta=10): def voldown(self, delta=10):
""" """
Turn down the volume Turn down the volume
@ -148,6 +163,7 @@ class MusicMpdPlugin(MusicPlugin):
self.setvol(str(new_volume)) self.setvol(str(new_volume))
return self.status() return self.status()
@action
def random(self, value=None): def random(self, value=None):
""" """
Set shuffle mode Set shuffle mode
@ -161,6 +177,7 @@ class MusicMpdPlugin(MusicPlugin):
value = 1 if value == 0 else 0 value = 1 if value == 0 else 0
return self._exec('random', value) return self._exec('random', value)
@action
def repeat(self, value=None): def repeat(self, value=None):
""" """
Set repeat mode Set repeat mode
@ -174,6 +191,7 @@ class MusicMpdPlugin(MusicPlugin):
value = 1 if value == 0 else 0 value = 1 if value == 0 else 0
return self._exec('repeat', value) return self._exec('repeat', value)
@action
def add(self, resource): def add(self, resource):
""" """
Add a resource (track, album, artist, folder etc.) to the current playlist Add a resource (track, album, artist, folder etc.) to the current playlist
@ -184,6 +202,7 @@ class MusicMpdPlugin(MusicPlugin):
return self._exec('add', resource) return self._exec('add', resource)
@action
def load(self, playlist): def load(self, playlist):
""" """
Load and play a playlist by name Load and play a playlist by name
@ -195,10 +214,12 @@ class MusicMpdPlugin(MusicPlugin):
self._exec('load', playlist) self._exec('load', playlist)
return self.play() return self.play()
@action
def clear(self): def clear(self):
""" Clear the current playlist """ """ Clear the current playlist """
return self._exec('clear') return self._exec('clear')
@action
def seekcur(self, value): def seekcur(self, value):
""" """
Seek to the specified position Seek to the specified position
@ -209,16 +230,19 @@ class MusicMpdPlugin(MusicPlugin):
return self._exec('seekcur', value) return self._exec('seekcur', value)
@action
def forward(self): def forward(self):
""" Go forward by 15 seconds """ """ Go forward by 15 seconds """
return self._exec('seekcur', '+15') return self._exec('seekcur', '+15')
@action
def back(self): def back(self):
""" Go backward by 15 seconds """ """ Go backward by 15 seconds """
return self._exec('seekcur', '-15') return self._exec('seekcur', '-15')
@action
def status(self): def status(self):
""" """
:returns: The current state. :returns: The current state.
@ -245,8 +269,9 @@ class MusicMpdPlugin(MusicPlugin):
} }
""" """
return Response(output=self.client.status()) return self.client.status()
@action
def currentsong(self): def currentsong(self):
""" """
:returns: The currently played track. :returns: The currently played track.
@ -277,8 +302,9 @@ class MusicMpdPlugin(MusicPlugin):
track['artist'] = m.group(1) track['artist'] = m.group(1)
track['title'] = m.group(2) track['title'] = m.group(2)
return Response(output=track) return track
@action
def playlistinfo(self): def playlistinfo(self):
""" """
:returns: The tracks in the current playlist as a list of dicts. :returns: The tracks in the current playlist as a list of dicts.
@ -315,8 +341,9 @@ class MusicMpdPlugin(MusicPlugin):
] ]
""" """
return Response(output=self.client.playlistinfo()) return self.client.playlistinfo()
@action
def listplaylists(self): def listplaylists(self):
""" """
:returns: The playlists available on the server as a list of dicts. :returns: The playlists available on the server as a list of dicts.
@ -338,17 +365,18 @@ class MusicMpdPlugin(MusicPlugin):
] ]
""" """
return Response(output=sorted(self.client.listplaylists(), return sorted(self.client.listplaylists(), key=lambda p: p['playlist'])
key=lambda p: p['playlist']))
@action
def lsinfo(self, uri=None): def lsinfo(self, uri=None):
""" """
Returns the list of playlists and directories on the server Returns the list of playlists and directories on the server
""" """
output = self.client.lsinfo(uri) if uri else self.client.lsinfo() output = self.client.lsinfo(uri) if uri else self.client.lsinfo()
return Response(output=output) return output
@action
def plchanges(self, version): def plchanges(self, version):
""" """
Show what has changed on the current playlist since a specified playlist Show what has changed on the current playlist since a specified playlist
@ -360,8 +388,9 @@ class MusicMpdPlugin(MusicPlugin):
:returns: A list of dicts representing the songs being added since the specified version :returns: A list of dicts representing the songs being added since the specified version
""" """
return Response(output=self.client.plchanges(version)) return self.client.plchanges(version)
@action
def searchaddplaylist(self, name): def searchaddplaylist(self, name):
""" """
Search and add a playlist by (partial or full) name Search and add a playlist by (partial or full) name
@ -379,10 +408,9 @@ class MusicMpdPlugin(MusicPlugin):
self.client.clear() self.client.clear()
self.client.load(playlists[0]) self.client.load(playlists[0])
self.client.play() self.client.play()
return Response(output={'playlist': playlists[0]}) return {'playlist': playlists[0]}
return Response(output={})
@action
def find(self, filter, *args, **kwargs): def find(self, filter, *args, **kwargs):
""" """
Find in the database/library by filter. Find in the database/library by filter.
@ -392,9 +420,9 @@ class MusicMpdPlugin(MusicPlugin):
:returns: list[dict] :returns: list[dict]
""" """
return Response( return self.client.find(*filter, *args, **kwargs)
output=self.client.find(*filter, *args, **kwargs))
@action
def findadd(self, filter, *args, **kwargs): def findadd(self, filter, *args, **kwargs):
""" """
Find in the database/library by filter and add to the current playlist. Find in the database/library by filter and add to the current playlist.
@ -404,9 +432,9 @@ class MusicMpdPlugin(MusicPlugin):
:returns: list[dict] :returns: list[dict]
""" """
return Response( return self.client.findadd(*filter, *args, **kwargs)
output=self.client.findadd(*filter, *args, **kwargs))
@action
def search(self, filter, *args, **kwargs): def search(self, filter, *args, **kwargs):
""" """
Free search by filter. Free search by filter.
@ -422,8 +450,9 @@ class MusicMpdPlugin(MusicPlugin):
items = sorted(items, key=lambda item: items = sorted(items, key=lambda item:
0 if item['file'].startswith('spotify:') else 1) 0 if item['file'].startswith('spotify:') else 1)
return Response(output=items) return items
@action
def searchadd(self, filter, *args, **kwargs): def searchadd(self, filter, *args, **kwargs):
""" """
Free search by filter and add the results to the current playlist. Free search by filter and add the results to the current playlist.
@ -433,8 +462,7 @@ class MusicMpdPlugin(MusicPlugin):
:returns: list[dict] :returns: list[dict]
""" """
return Response( return self.client.searchadd(*filter, *args, **kwargs)
output=self.client.searchadd(*filter, *args, **kwargs))
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et:

View File

@ -3,8 +3,7 @@ import os
import requests import requests
from platypush.context import get_backend from platypush.context import get_backend
from platypush.message.response import Response from platypush.plugins import Plugin, action
from platypush.plugins import Plugin
class PushbulletPlugin(Plugin): class PushbulletPlugin(Plugin):
@ -18,6 +17,7 @@ class PushbulletPlugin(Plugin):
* **requests** (``pip install requests``) * **requests** (``pip install requests``)
""" """
@action
def send_push(self, **kwargs): def send_push(self, **kwargs):
""" """
Send a push. Send a push.
@ -40,9 +40,8 @@ class PushbulletPlugin(Plugin):
raise Exception('Pushbullet push failed with status {}: {}'. raise Exception('Pushbullet push failed with status {}: {}'.
format(resp.status_code, resp.json())) format(resp.status_code, resp.json()))
return Response(output={'status':'ok'})
@action
def send_file(self, filename): def send_file(self, filename):
""" """
Send a file. Send a file.
@ -83,11 +82,11 @@ class PushbulletPlugin(Plugin):
raise Exception('Pushbullet file push failed with status {}'. raise Exception('Pushbullet file push failed with status {}'.
format(resp.status_code)) format(resp.status_code))
return Response(output={ return {
'filename': r['file_name'], 'filename': r['file_name'],
'type': r['file_type'], 'type': r['file_type'],
'url': r['file_url'] 'url': r['file_url']
}) }
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et:

View File

@ -1,7 +1,6 @@
from redis import Redis from redis import Redis
from platypush.message.response import Response from platypush.plugins import Plugin, action
from platypush.plugins import Plugin
class RedisPlugin(Plugin): class RedisPlugin(Plugin):
@ -13,6 +12,7 @@ class RedisPlugin(Plugin):
* **redis** (``pip install redis``) * **redis** (``pip install redis``)
""" """
@action
def send_message(self, queue, msg, *args, **kwargs): def send_message(self, queue, msg, *args, **kwargs):
""" """
Send a message to a Redis queu. Send a message to a Redis queu.
@ -32,7 +32,6 @@ class RedisPlugin(Plugin):
redis = Redis(*args, **kwargs) redis = Redis(*args, **kwargs)
redis.rpush(queue, msg) redis.rpush(queue, msg)
return Response(output={'state': 'ok'})
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et:

View File

@ -1,9 +1,7 @@
import json import json
import serial import serial
from platypush.message.response import Response from platypush.plugins import Plugin, action
from .. import Plugin
class SerialPlugin(Plugin): class SerialPlugin(Plugin):
@ -60,6 +58,7 @@ class SerialPlugin(Plugin):
return output.decode().strip() return output.decode().strip()
@action
def get_data(self): def get_data(self):
""" """
Reads JSON data from the serial device and returns it as a message Reads JSON data from the serial device and returns it as a message
@ -78,7 +77,7 @@ class SerialPlugin(Plugin):
self.logger.warning('Invalid JSON message from {}: {}'. self.logger.warning('Invalid JSON message from {}: {}'.
format(self.device, data)) format(self.device, data))
return Response(output=data) return data
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et:

View File

@ -1,14 +1,15 @@
import subprocess import subprocess
from platypush.message.response import Response from platypush.message.response import Response
from platypush.plugins import Plugin, action
from .. import Plugin
class ShellPlugin(Plugin): class ShellPlugin(Plugin):
""" """
Plugin to run custom shell commands. Plugin to run custom shell commands.
""" """
@action
def exec(self, cmd): def exec(self, cmd):
""" """
Execute a command. Execute a command.
@ -19,16 +20,12 @@ class ShellPlugin(Plugin):
: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. :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
errors = []
try: try:
output = subprocess.check_output( return subprocess.check_output(
cmd, stderr=subprocess.STDOUT, shell=True).decode('utf-8') cmd, stderr=subprocess.STDOUT, shell=True).decode('utf-8')
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
errors = [e.output.decode('utf-8')] raise RuntimeError(e.output.decode('utf-8'))
return Response(output=output, errors=errors)
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et:

View File

@ -1,22 +1,29 @@
from .. import Plugin from platypush.plugins import Plugin, action
class SwitchPlugin(Plugin): class SwitchPlugin(Plugin):
""" """
Abstract class for interacting with switch devices Abstract class for interacting with switch devices
""" """
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@action
def on(self, args): def on(self, args):
""" Turn the device on """ """ Turn the device on """
raise NotImplementedError() raise NotImplementedError()
@action
def off(self, args): def off(self, args):
""" Turn the device off """ """ Turn the device off """
raise NotImplementedError() raise NotImplementedError()
@action
def toggle(self, args): def toggle(self, args):
""" Toggle the device status (on/off) """ """ Toggle the device status (on/off) """
raise NotImplementedError() raise NotImplementedError()
@action
def status(self): def status(self):
""" Get the device state """ """ Get the device state """
raise NotImplementedError() raise NotImplementedError()

View File

@ -4,9 +4,8 @@ import time
from bluetooth.ble import DiscoveryService, GATTRequester from bluetooth.ble import DiscoveryService, GATTRequester
from platypush.message.response import Response from platypush.plugins import action
from platypush.plugins.switch import SwitchPlugin
from .. import SwitchPlugin
class Scanner(object): class Scanner(object):
service_uuid = '1bc5d5a5-0200b89f-e6114d22-000da2cb' service_uuid = '1bc5d5a5-0200b89f-e6114d22-000da2cb'
@ -112,6 +111,7 @@ class SwitchSwitchbotPlugin(SwitchPlugin):
:type devices: dict :type devices: dict
""" """
super().__init__(*args, **kwargs)
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
@ -119,15 +119,12 @@ class SwitchSwitchbotPlugin(SwitchPlugin):
def _run(self, device, command=None): def _run(self, device, command=None):
output = None
errors = []
try: try:
# XXX this requires sudo and it's executed in its own process # XXX this requires sudo and it's executed in its own process
# because the Switchbot plugin requires root privileges to send # because the Switchbot plugin requires root privileges to send
# raw bluetooth messages on the interface. Make sure that the user # raw bluetooth messages on the interface. Make sure that the user
# that runs platypush has the right permissions to run this with sudo # that runs platypush has the right permissions to run this with sudo
output = subprocess.check_output(( return subprocess.check_output((
'sudo python3 -m platypush.plugins.switch.switchbot ' + 'sudo python3 -m platypush.plugins.switch.switchbot ' +
'--device {} ' + '--device {} ' +
('--interface {} '.format(self.bt_interface) if self.bt_interface else '') + ('--interface {} '.format(self.bt_interface) if self.bt_interface else '') +
@ -135,11 +132,10 @@ class SwitchSwitchbotPlugin(SwitchPlugin):
('--{} '.format(command) if command else '')).format(device), ('--{} '.format(command) if command else '')).format(device),
stderr=subprocess.STDOUT, shell=True).decode('utf-8') stderr=subprocess.STDOUT, shell=True).decode('utf-8')
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
errors = [e.output.decode('utf-8')] raise RuntimeError(e.output.decode('utf-8'))
return Response(output=output, errors=errors)
@action
def press(self, device): def press(self, device):
""" """
Send a press button command to a device Send a press button command to a device
@ -149,6 +145,7 @@ class SwitchSwitchbotPlugin(SwitchPlugin):
""" """
return self._run(device) return self._run(device)
@action
def on(self, device): def on(self, device):
""" """
Send a press-on button command to a device Send a press-on button command to a device
@ -158,6 +155,7 @@ class SwitchSwitchbotPlugin(SwitchPlugin):
""" """
return self._run(device, 'on') return self._run(device, 'on')
@action
def off(self, device): def off(self, device):
""" """
Send a press-off button command to a device Send a press-off button command to a device
@ -167,25 +165,17 @@ class SwitchSwitchbotPlugin(SwitchPlugin):
""" """
return self._run(device, 'off') return self._run(device, 'off')
@action
def scan(self): def scan(self):
""" Scan for available Switchbot devices nearby """ """ Scan for available Switchbot devices nearby """
output = None
errors = []
try: try:
print('sudo python3 -m platypush.plugins.switch.switchbot --scan ' + return subprocess.check_output(
('--interface {} '.format(self.bt_interface) if self.bt_interface else '') +
('--scan-timeout {} '.format(self.scan_timeout) if self.scan_timeout else ''))
output = subprocess.check_output(
'sudo python3 -m platypush.plugins.switch.switchbot --scan ' + 'sudo python3 -m platypush.plugins.switch.switchbot --scan ' +
('--interface {} '.format(self.bt_interface) if self.bt_interface else '') + ('--interface {} '.format(self.bt_interface) if self.bt_interface else '') +
('--scan-timeout {} '.format(self.scan_timeout) if self.scan_timeout else ''), ('--scan-timeout {} '.format(self.scan_timeout) if self.scan_timeout else ''),
stderr=subprocess.STDOUT, shell=True).decode('utf-8') stderr=subprocess.STDOUT, shell=True).decode('utf-8')
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
errors = [e.output.decode('utf-8')] raise RuntimeError(e.output.decode('utf-8'))
return Response(output=output, errors=errors)
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et:

View File

@ -1,6 +1,6 @@
from pyHS100 import Discover from pyHS100 import Discover
from platypush.message.response import Response from platypush.plugins import action
from platypush.plugins.switch import SwitchPlugin from platypush.plugins.switch import SwitchPlugin
@ -17,6 +17,8 @@ class SwitchTplinkPlugin(SwitchPlugin):
_ip_to_dev = {} _ip_to_dev = {}
_alias_to_dev = {} _alias_to_dev = {}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def _scan(self): def _scan(self):
devices = Discover.discover() devices = Discover.discover()
@ -46,6 +48,7 @@ class SwitchTplinkPlugin(SwitchPlugin):
raise RuntimeError('Device {} not found'.format(device)) raise RuntimeError('Device {} not found'.format(device))
@action
def status(self): def status(self):
""" """
:returns: The available device over the network as a :returns: The available device over the network as a
@ -61,8 +64,9 @@ class SwitchTplinkPlugin(SwitchPlugin):
} for (ip, dev) in self._scan().items() } for (ip, dev) in self._scan().items()
} } } }
return Response(output=devices) return devices
@action
def on(self, device): def on(self, device):
""" """
Turn on a device Turn on a device
@ -73,9 +77,10 @@ class SwitchTplinkPlugin(SwitchPlugin):
device = self._get_device(device) device = self._get_device(device)
device.turn_on() device.turn_on()
return Response(output={'status':'on'}) return {'status':'on'}
@action
def off(self, device): def off(self, device):
""" """
Turn off a device Turn off a device
@ -86,9 +91,10 @@ class SwitchTplinkPlugin(SwitchPlugin):
device = self._get_device(device) device = self._get_device(device)
device.turn_off() device.turn_off()
return Response(output={'status':'off'}) return {'status':'off'}
@action
def toggle(self, device): def toggle(self, device):
""" """
Toggle the state of a device (on/off) Toggle the state of a device (on/off)
@ -104,7 +110,7 @@ class SwitchTplinkPlugin(SwitchPlugin):
else: else:
device.turn_on() device.turn_on()
return Response(output={'status': 'off' if device.is_off else 'on'}) return {'status': 'off' if device.is_off else 'on'}
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et:

View File

@ -1,9 +1,9 @@
import json import json
from ouimeaux.environment import Environment, UnknownDevice from ouimeaux.environment import Environment, UnknownDevice
from platypush.message.response import Response from platypush.plugins import action
from platypush.plugins.switch import SwitchPlugin
from .. import SwitchPlugin
class SwitchWemoPlugin(SwitchPlugin): class SwitchWemoPlugin(SwitchPlugin):
""" """
@ -20,8 +20,8 @@ class SwitchWemoPlugin(SwitchPlugin):
:param discovery_seconds: Discovery time when scanning for devices (default: 3) :param discovery_seconds: Discovery time when scanning for devices (default: 3)
:type discovery_seconds: int :type discovery_seconds: int
""" """
super().__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self.discovery_seconds=discovery_seconds self.discovery_seconds=discovery_seconds
self.env = Environment() self.env = Environment()
self.env.start() self.env.start()
@ -33,6 +33,7 @@ class SwitchWemoPlugin(SwitchPlugin):
self.env.discover(seconds=self.discovery_seconds) self.env.discover(seconds=self.discovery_seconds)
self.devices = self.env.devices self.devices = self.env.devices
@action
def get_devices(self): def get_devices(self):
""" """
Get the list of available devices Get the list of available devices
@ -57,8 +58,8 @@ class SwitchWemoPlugin(SwitchPlugin):
} }
""" """
self.refresh_devices() self.refresh_devices()
return Response( return {
output = { 'devices': [ 'devices': [
{ {
'host': dev.host, 'host': dev.host,
'name': dev.name, 'name': dev.name,
@ -67,8 +68,8 @@ class SwitchWemoPlugin(SwitchPlugin):
'serialnumber': dev.serialnumber, 'serialnumber': dev.serialnumber,
} }
for (name, dev) in self.devices.items() for (name, dev) in self.devices.items()
] } ]
) }
def _exec(self, method, device, *args, **kwargs): def _exec(self, method, device, *args, **kwargs):
if device not in self.devices: if device not in self.devices:
@ -81,9 +82,9 @@ class SwitchWemoPlugin(SwitchPlugin):
dev = self.devices[device] dev = self.devices[device]
getattr(dev, method)(*args, **kwargs) getattr(dev, method)(*args, **kwargs)
resp = {'device': device, 'state': dev.get_state()} return {'device': device, 'state': dev.get_state()}
return Response(output=json.dumps(resp))
@action
def on(self, device): def on(self, device):
""" """
Turn a switch on Turn a switch on
@ -93,6 +94,7 @@ class SwitchWemoPlugin(SwitchPlugin):
""" """
return self._exec('on', device) return self._exec('on', device)
@action
def off(self, device): def off(self, device):
""" """
Turn a switch off Turn a switch off
@ -102,6 +104,7 @@ class SwitchWemoPlugin(SwitchPlugin):
""" """
return self._exec('off', device) return self._exec('off', device)
@action
def toggle(self, device): def toggle(self, device):
""" """
Toggle the state of a switch (on/off) Toggle the state of a switch (on/off)

View File

@ -2,8 +2,7 @@ import subprocess
import urllib.parse import urllib.parse
from platypush.message.response import Response from platypush.message.response import Response
from platypush.plugins import Plugin, action
from .. import Plugin
class TtsPlugin(Plugin): class TtsPlugin(Plugin):
""" """
@ -18,6 +17,7 @@ class TtsPlugin(Plugin):
super().__init__() super().__init__()
self.lang=lang self.lang=lang
@action
def say(self, phrase, lang=None): def say(self, phrase, lang=None):
""" """
Say a phrase Say a phrase
@ -41,12 +41,10 @@ class TtsPlugin(Plugin):
}))] }))]
try: try:
output = subprocess.check_output( return subprocess.check_output(
cmd, stderr=subprocess.STDOUT, shell=True).decode('utf-8') cmd, stderr=subprocess.STDOUT, shell=True).decode('utf-8')
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
errors = [e.output.decode('utf-8')] raise RuntimeError(e.output.decode('utf-8'))
return Response(output=output, errors=errors)
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et:

View File

@ -1,5 +1,4 @@
from platypush.message.response import Response from platypush.plugins import Plugin, action
from platypush.plugins import Plugin
class VariablePlugin(Plugin): class VariablePlugin(Plugin):
@ -12,6 +11,7 @@ class VariablePlugin(Plugin):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self._variables = {} self._variables = {}
@action
def get(self, name, default_value=None): def get(self, name, default_value=None):
""" """
Get the value of a variable by name. Get the value of a variable by name.
@ -24,8 +24,9 @@ class VariablePlugin(Plugin):
:returns: A map in the format ``{"<name>":"<value>"}`` :returns: A map in the format ``{"<name>":"<value>"}``
""" """
return Response(output={name: self._variables.get(name, default_value)}) return {name: self._variables.get(name, default_value)}
@action
def set(self, **kwargs): def set(self, **kwargs):
""" """
Set a variable or a set of variables. Set a variable or a set of variables.
@ -35,8 +36,9 @@ class VariablePlugin(Plugin):
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 kwargs
@action
def unset(self, name): def unset(self, name):
""" """
Unset a variable by name if it's set Unset a variable by name if it's set
@ -46,9 +48,6 @@ class VariablePlugin(Plugin):
""" """
if name in self._variables: if name in self._variables:
del self._variables[name] del self._variables[name]
return Response(output={'status':'ok'})
else:
return Response(output={'status':'not_found'})
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et:

View File

@ -12,11 +12,11 @@ from omxplayer import OMXPlayer
from platypush.context import get_backend from platypush.context import get_backend
from platypush.plugins.media import PlayerState from platypush.plugins.media import PlayerState
from platypush.message.response import Response
from platypush.message.event.video import VideoPlayEvent, VideoPauseEvent, \ from platypush.message.event.video import VideoPlayEvent, VideoPauseEvent, \
VideoStopEvent, NewPlayingVideoEvent VideoStopEvent, NewPlayingVideoEvent
from .. import Plugin from platypush.plugins import Plugin, action
class VideoOmxplayerPlugin(Plugin): class VideoOmxplayerPlugin(Plugin):
""" """
@ -83,6 +83,7 @@ class VideoOmxplayerPlugin(Plugin):
self.videos_queue = [] self.videos_queue = []
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
@action
def play(self, resource): def play(self, resource):
""" """
Play a resource. Play a resource.
@ -105,9 +106,7 @@ class VideoOmxplayerPlugin(Plugin):
self.videos_queue = resources self.videos_queue = resources
resource = self.videos_queue.pop(0) resource = self.videos_queue.pop(0)
else: else:
error = 'Unable to download torrent {}'.format(resource) raise RuntimeError('Unable to download torrent {}'.format(resource))
self.logger.warning(error)
return Response(errors=[error])
self.logger.info('Playing {}'.format(resource)) self.logger.info('Playing {}'.format(resource))
@ -130,10 +129,12 @@ class VideoOmxplayerPlugin(Plugin):
return self.status() return self.status()
@action
def pause(self): def pause(self):
""" Pause the playback """ """ Pause the playback """
if self.player: self.player.play_pause() if self.player: self.player.play_pause()
@action
def stop(self): def stop(self):
""" Stop the playback """ """ Stop the playback """
if self.player: if self.player:
@ -143,30 +144,35 @@ class VideoOmxplayerPlugin(Plugin):
return self.status() return self.status()
@action
def voldown(self): def voldown(self):
""" Volume down by 10% """ """ 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()
@action
def volup(self): def volup(self):
""" Volume up by 10% """ """ 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()
@action
def back(self): def back(self):
""" Back by 30 seconds """ """ Back by 30 seconds """
if self.player: if self.player:
self.player.seek(-30) self.player.seek(-30)
return self.status() return self.status()
@action
def forward(self): def forward(self):
""" Forward by 30 seconds """ """ Forward by 30 seconds """
if self.player: if self.player:
self.player.seek(+30) self.player.seek(+30)
return self.status() return self.status()
@action
def next(self): def next(self):
""" Play the next track/video """ """ Play the next track/video """
if self.player: if self.player:
@ -176,19 +182,19 @@ class VideoOmxplayerPlugin(Plugin):
video = self.videos_queue.pop(0) video = self.videos_queue.pop(0)
return self.play(video) return self.play(video)
return Response(output={'status': 'no media'}, errors = []) @action
def hide_subtitles(self): def hide_subtitles(self):
""" Hide the subtitles """ """ Hide the subtitles """
if self.player: self.player.hide_subtitles() if self.player: self.player.hide_subtitles()
return self.status() return self.status()
@action
def hide_video(self): def hide_video(self):
""" Hide the video """ """ Hide the video """
if self.player: self.player.hide_video() if self.player: self.player.hide_video()
return self.status() return self.status()
@action
def is_playing(self): def is_playing(self):
""" """
:returns: True if it's playing, False otherwise :returns: True if it's playing, False otherwise
@ -197,6 +203,7 @@ class VideoOmxplayerPlugin(Plugin):
if self.player: return self.player.is_playing() if self.player: return self.player.is_playing()
else: return False else: return False
@action
def load(self, resource, pause=False): def load(self, resource, pause=False):
""" """
Load a resource/video in the player. Load a resource/video in the player.
@ -208,21 +215,26 @@ class VideoOmxplayerPlugin(Plugin):
if self.player: self.player.load(resource, pause) if self.player: self.player.load(resource, pause)
return self.status() return self.status()
@action
def metadata(self): def metadata(self):
""" Get the metadata of the current video """ """ Get the metadata of the current video """
if self.player: return Response(output=self.player.metadata()) if self.player:
return self.player.metadata()
return self.status() return self.status()
@action
def mute(self): def mute(self):
""" Mute the player """ """ Mute the player """
if self.player: self.player.mute() if self.player: self.player.mute()
return self.status() return self.status()
@action
def unmute(self): def unmute(self):
""" Unmute the player """ """ Unmute the player """
if self.player: self.player.unmute() if self.player: self.player.unmute()
return self.status() return self.status()
@action
def seek(self, relative_position): def seek(self, relative_position):
""" """
Seek backward/forward by the specified number of seconds Seek backward/forward by the specified number of seconds
@ -234,6 +246,7 @@ class VideoOmxplayerPlugin(Plugin):
if self.player: self.player.seek(relative_position) if self.player: self.player.seek(relative_position)
return self.status() return self.status()
@action
def set_position(self, position): def set_position(self, position):
""" """
Seek backward/forward to the specified absolute position Seek backward/forward to the specified absolute position
@ -245,6 +258,7 @@ class VideoOmxplayerPlugin(Plugin):
if self.player: self.player.set_seek(position) if self.player: self.player.set_seek(position)
return self.status() return self.status()
@action
def set_volume(self, volume): def set_volume(self, volume):
""" """
Set the volume Set the volume
@ -258,6 +272,7 @@ class VideoOmxplayerPlugin(Plugin):
if self.player: self.player.set_volume(volume) if self.player: self.player.set_volume(volume)
return self.status() return self.status()
@action
def status(self): def status(self):
""" """
Get the current player state. Get the current player state.
@ -285,7 +300,7 @@ class VideoOmxplayerPlugin(Plugin):
elif state == 'stopped': state = PlayerState.STOP.value elif state == 'stopped': state = PlayerState.STOP.value
elif state == 'paused': state = PlayerState.PAUSE.value elif state == 'paused': state = PlayerState.PAUSE.value
return Response(output=json.dumps({ return {
'source': self.player.get_source(), 'source': self.player.get_source(),
'state': state, 'state': state,
'volume': self.player.volume(), 'volume': self.player.volume(),
@ -293,12 +308,13 @@ class VideoOmxplayerPlugin(Plugin):
'duration': self.player.duration(), 'duration': self.player.duration(),
'width': self.player.width(), 'width': self.player.width(),
'height': self.player.height(), 'height': self.player.height(),
})) }
else: else:
return Response(output=json.dumps({ return {
'state': PlayerState.STOP.value 'state': PlayerState.STOP.value
})) }
@action
def send_message(self, msg): def send_message(self, msg):
try: try:
redis = get_backend('redis') redis = get_backend('redis')
@ -311,16 +327,19 @@ class VideoOmxplayerPlugin(Plugin):
redis.send_message(msg) redis.send_message(msg)
@action
def on_play(self): def on_play(self):
def _f(player): def _f(player):
self.send_message(VideoPlayEvent(video=self.player.get_source())) self.send_message(VideoPlayEvent(video=self.player.get_source()))
return _f return _f
@action
def on_pause(self): def on_pause(self):
def _f(player): def _f(player):
self.send_message(VideoPauseEvent(video=self.player.get_source())) self.send_message(VideoPauseEvent(video=self.player.get_source()))
return _f return _f
@action
def on_stop(self): def on_stop(self):
def _f(player): def _f(player):
self.send_message(VideoStopEvent()) self.send_message(VideoStopEvent())
@ -335,6 +354,7 @@ class VideoOmxplayerPlugin(Plugin):
self.player.pauseEvent += self.on_pause() self.player.pauseEvent += self.on_pause()
self.player.stopEvent += self.on_stop() self.player.stopEvent += self.on_stop()
@action
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. Perform a video search.
@ -376,7 +396,7 @@ class VideoOmxplayerPlugin(Plugin):
elif autoplay: elif autoplay:
self.play(results[0]['url']) self.play(results[0]['url'])
return Response(output=results) return results
@classmethod @classmethod
def _is_video_file(cls, filename): def _is_video_file(cls, filename):
@ -388,6 +408,7 @@ class VideoOmxplayerPlugin(Plugin):
return is_video return is_video
@action
def file_search(self, query): def file_search(self, query):
results = [] results = []
query_tokens = [_.lower() for _ in re.split('\s+', query.strip())] query_tokens = [_.lower() for _ in re.split('\s+', query.strip())]
@ -413,8 +434,9 @@ class VideoOmxplayerPlugin(Plugin):
'title': f, 'title': f,
}) })
return Response(output=results) return results
@action
def youtube_search(self, query): def youtube_search(self, query):
self.logger.info('Searching YouTube for "{}"'.format(query)) self.logger.info('Searching YouTube for "{}"'.format(query))
@ -439,7 +461,7 @@ class VideoOmxplayerPlugin(Plugin):
self.logger.info('{} YouTube video results for the search query "{}"' self.logger.info('{} YouTube video results for the search query "{}"'
.format(len(results), query)) .format(len(results), query))
return Response(output=results) return results
@classmethod @classmethod
@ -476,8 +498,9 @@ class VideoOmxplayerPlugin(Plugin):
for _ in json.loads(request.read())['MovieList'] for _ in json.loads(request.read())['MovieList']
] ]
return Response(output=results) return results
@action
def download_torrent(self, magnet): def download_torrent(self, magnet):
""" """
Download a torrent to ``download_dir`` by Magnet URI Download a torrent to ``download_dir`` by Magnet URI
@ -538,9 +561,10 @@ class VideoOmxplayerPlugin(Plugin):
time.sleep(5) time.sleep(5)
return Response(output=files) return files
@action
def get_torrent_state(self): def get_torrent_state(self):
return self.torrent_state return self.torrent_state

View File

@ -3,10 +3,8 @@ import urllib3
import urllib.request import urllib.request
import urllib.parse import urllib.parse
from platypush.plugins import Plugin, action
from platypush.plugins.media import PlayerState from platypush.plugins.media import PlayerState
from platypush.message.response import Response
from .. import Plugin
class VideoTorrentcastPlugin(Plugin): class VideoTorrentcastPlugin(Plugin):
def __init__(self, server='localhost', port=9090, *args, **kwargs): def __init__(self, server='localhost', port=9090, *args, **kwargs):
@ -14,6 +12,7 @@ class VideoTorrentcastPlugin(Plugin):
self.port = port self.port = port
self.state = PlayerState.STOP.value self.state = PlayerState.STOP.value
@action
def play(self, url): def play(self, url):
request = urllib.request.urlopen( request = urllib.request.urlopen(
'http://{}:{}/play/'.format(self.server, self.port), 'http://{}:{}/play/'.format(self.server, self.port),
@ -23,24 +22,27 @@ class VideoTorrentcastPlugin(Plugin):
) )
self.state = PlayerState.PLAY.value self.state = PlayerState.PLAY.value
return Response(output=request.read()) return request.read()
@action
def pause(self): def pause(self):
http = urllib3.PoolManager() http = urllib3.PoolManager()
request = http.request('POST', request = http.request('POST',
'http://{}:{}/pause/'.format(self.server, self.port)) 'http://{}:{}/pause/'.format(self.server, self.port))
self.state = PlayerState.PAUSE.value self.state = PlayerState.PAUSE.value
return Response(output=request.read()) return request.read()
@action
def stop(self): def stop(self):
http = urllib3.PoolManager() http = urllib3.PoolManager()
request = http.request('POST', request = http.request('POST',
'http://{}:{}/stop/'.format(self.server, self.port)) 'http://{}:{}/stop/'.format(self.server, self.port))
self.state = PlayerState.STOP.value self.state = PlayerState.STOP.value
return Response(output=request.read()) return request.read()
@action
def search(self, query): def search(self, query):
request = urllib.request.urlopen(urllib.request.Request( request = urllib.request.urlopen(urllib.request.Request(
'https://api.apidomain.info/list?' + urllib.parse.urlencode({ 'https://api.apidomain.info/list?' + urllib.parse.urlencode({
@ -56,8 +58,9 @@ class VideoTorrentcastPlugin(Plugin):
) )
results = json.loads(request.read()) results = json.loads(request.read())
return Response(output=results) return results
@action
def search_and_play(self, query): def search_and_play(self, query):
response = self.search(query) response = self.search(query)
if not response.output['MovieList']: if not response.output['MovieList']:
@ -71,13 +74,20 @@ class VideoTorrentcastPlugin(Plugin):
return self.play(magnet) return self.play(magnet)
def voldown(self): return Response(output='Unsupported method') @action
def volup(self): return Response(output='Unsupported method') def voldown(self): raise NotImplementedError()
def back(self): return Response(output='Unsupported method')
def forward(self): return Response(output='Unsupported method')
def status(self): @action
return Response(output={ 'state': self.state }) def volup(self): raise NotImplementedError()
@action
def back(self): raise NotImplementedError()
@action
def forward(self): raise NotImplementedError()
@action
def status(self): return { 'state': self.state }
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et:

View File

@ -1,4 +1,4 @@
from platypush.message.response import Response from platypush.plugins import action
from platypush.plugins.http.request import HttpRequestPlugin from platypush.plugins.http.request import HttpRequestPlugin
@ -46,6 +46,7 @@ class WeatherForecastPlugin(HttpRequestPlugin):
format(self.darksky_token, (lat or self.lat), (long or self.long), format(self.darksky_token, (lat or self.lat), (long or self.long),
self.units) self.units)
@action
def get_current_weather(self, lat=None, long=None, **kwargs): def get_current_weather(self, lat=None, long=None, **kwargs):
""" """
Get the current weather. Get the current weather.
@ -83,8 +84,9 @@ class WeatherForecastPlugin(HttpRequestPlugin):
""" """
response = self.get(self._get_url(lat, long)) response = self.get(self._get_url(lat, long))
return Response(output=response.output['currently']) return response.output['currently']
@action
def get_hourly_forecast(self, lat=None, long=None, **kwargs): def get_hourly_forecast(self, lat=None, long=None, **kwargs):
""" """
Get the hourly forecast. Get the hourly forecast.
@ -146,8 +148,9 @@ class WeatherForecastPlugin(HttpRequestPlugin):
""" """
response = self.get(self._get_url(lat, long)) response = self.get(self._get_url(lat, long))
return Response(output=response.output['hourly']) return response.output['hourly']
@action
def get_daily_forecast(self, lat=None, long=None, **kwargs): def get_daily_forecast(self, lat=None, long=None, **kwargs):
""" """
Get the daily forecast. Get the daily forecast.
@ -250,7 +253,7 @@ class WeatherForecastPlugin(HttpRequestPlugin):
""" """
response = self.get(self._get_url(lat, long)) response = self.get(self._get_url(lat, long))
return Response(output=response.output['daily']) return response.output['daily']
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et:

View File

@ -1,6 +1,8 @@
import ast
import errno import errno
import hashlib import hashlib
import importlib import importlib
import inspect
import logging import logging
import os import os
import signal import signal
@ -67,8 +69,32 @@ def clear_timeout():
def get_hash(s): def get_hash(s):
""" Get the SHA256 hash hexdigest of a string input """
return hashlib.sha256(s.encode('utf-8')).hexdigest() return hashlib.sha256(s.encode('utf-8')).hexdigest()
def get_decorators(cls):
target = cls
decorators = {}
def visit_FunctionDef(node):
# decorators[node.name] = []
for n in node.decorator_list:
name = ''
if isinstance(n, ast.Call):
name = n.func.attr if isinstance(n.func, ast.Attribute) else n.func.id
else:
name = n.attr if isinstance(n, ast.Attribute) else n.id
decorators[name] = decorators.get(name, [])
# decorators[node.name].append(name)
decorators[name].append(node.name)
node_iter = ast.NodeVisitor()
node_iter.visit_FunctionDef = visit_FunctionDef
node_iter.visit(ast.parse(inspect.getsource(target)))
return decorators
# vim:sw=4:ts=4:et: # vim:sw=4:ts=4:et: