diff --git a/docs/source/platypush/plugins/pushbullet.rst b/docs/source/platypush/plugins/pushbullet.rst new file mode 100644 index 0000000000..00fc164dac --- /dev/null +++ b/docs/source/platypush/plugins/pushbullet.rst @@ -0,0 +1,6 @@ +``platypush.plugins.pushbullet`` +================================ + +.. automodule:: platypush.plugins.pushbullet + :members: + diff --git a/docs/source/platypush/plugins/redis.rst b/docs/source/platypush/plugins/redis.rst new file mode 100644 index 0000000000..96197f2b95 --- /dev/null +++ b/docs/source/platypush/plugins/redis.rst @@ -0,0 +1,6 @@ +``platypush.plugins.redis`` +=========================== + +.. automodule:: platypush.plugins.redis + :members: + diff --git a/docs/source/platypush/plugins/serial.rst b/docs/source/platypush/plugins/serial.rst new file mode 100644 index 0000000000..6b42abd25a --- /dev/null +++ b/docs/source/platypush/plugins/serial.rst @@ -0,0 +1,6 @@ +``platypush.plugins.serial`` +============================ + +.. automodule:: platypush.plugins.serial + :members: + diff --git a/docs/source/platypush/plugins/shell.rst b/docs/source/platypush/plugins/shell.rst new file mode 100644 index 0000000000..cfb551641e --- /dev/null +++ b/docs/source/platypush/plugins/shell.rst @@ -0,0 +1,6 @@ +``platypush.plugins.shell`` +=========================== + +.. automodule:: platypush.plugins.shell + :members: + diff --git a/docs/source/platypush/plugins/switch.rst b/docs/source/platypush/plugins/switch.rst new file mode 100644 index 0000000000..4920d4e9ff --- /dev/null +++ b/docs/source/platypush/plugins/switch.rst @@ -0,0 +1,6 @@ +``platypush.plugins.switch`` +============================ + +.. automodule:: platypush.plugins.switch + :members: + diff --git a/docs/source/platypush/plugins/switch.switchbot.rst b/docs/source/platypush/plugins/switch.switchbot.rst new file mode 100644 index 0000000000..f434e8ebf8 --- /dev/null +++ b/docs/source/platypush/plugins/switch.switchbot.rst @@ -0,0 +1,6 @@ +``platypush.plugins.switch.switchbot`` +====================================== + +.. automodule:: platypush.plugins.switch.switchbot + :members: + diff --git a/docs/source/platypush/plugins/switch.wemo.rst b/docs/source/platypush/plugins/switch.wemo.rst new file mode 100644 index 0000000000..f01dff5567 --- /dev/null +++ b/docs/source/platypush/plugins/switch.wemo.rst @@ -0,0 +1,6 @@ +``platypush.plugins.switch.wemo`` +================================= + +.. automodule:: platypush.plugins.switch.wemo + :members: + diff --git a/docs/source/platypush/plugins/tts.rst b/docs/source/platypush/plugins/tts.rst new file mode 100644 index 0000000000..d79f2ab43f --- /dev/null +++ b/docs/source/platypush/plugins/tts.rst @@ -0,0 +1,6 @@ +``platypush.plugins.tts`` +========================= + +.. automodule:: platypush.plugins.tts + :members: + diff --git a/docs/source/platypush/plugins/variable.rst b/docs/source/platypush/plugins/variable.rst new file mode 100644 index 0000000000..aaf4eca4d9 --- /dev/null +++ b/docs/source/platypush/plugins/variable.rst @@ -0,0 +1,6 @@ +``platypush.plugins.variable`` +============================== + +.. automodule:: platypush.plugins.variable + :members: + diff --git a/docs/source/platypush/plugins/video.omxplayer.rst b/docs/source/platypush/plugins/video.omxplayer.rst new file mode 100644 index 0000000000..e60bd9abea --- /dev/null +++ b/docs/source/platypush/plugins/video.omxplayer.rst @@ -0,0 +1,6 @@ +``platypush.plugins.video.omxplayer`` +===================================== + +.. automodule:: platypush.plugins.video.omxplayer + :members: + diff --git a/docs/source/platypush/plugins/weather.forecast.rst b/docs/source/platypush/plugins/weather.forecast.rst new file mode 100644 index 0000000000..0763f87651 --- /dev/null +++ b/docs/source/platypush/plugins/weather.forecast.rst @@ -0,0 +1,6 @@ +``platypush.plugins.weather.forecast`` +====================================== + +.. automodule:: platypush.plugins.weather.forecast + :members: + diff --git a/docs/source/plugins.rst b/docs/source/plugins.rst index 4a6073ed5a..d8966e8d22 100644 --- a/docs/source/plugins.rst +++ b/docs/source/plugins.rst @@ -26,4 +26,15 @@ Plugins platypush/plugins/midi.rst platypush/plugins/mqtt.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 diff --git a/platypush/plugins/pushbullet.py b/platypush/plugins/pushbullet.py index 1d4eac3d0d..d9c02d64d2 100644 --- a/platypush/plugins/pushbullet.py +++ b/platypush/plugins/pushbullet.py @@ -8,7 +8,24 @@ from platypush.plugins import 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): + """ + Send a push. + + :param kwargs: Push arguments, see https://docs.pushbullet.com/#create-push + :type kwargs: dict + """ + pushbullet = get_backend('pushbullet') resp = requests.post('https://api.pushbullet.com/v2/ephemerals', data=json.dumps({ @@ -27,6 +44,13 @@ class PushbulletPlugin(Plugin): def send_file(self, filename): + """ + Send a file. + + :param filename: Path to the local file + :type filename: str + """ + pushbullet = get_backend('pushbullet') resp = requests.post('https://api.pushbullet.com/v2/upload-request', data=json.dumps({'file_name': os.path.basename(filename)}), diff --git a/platypush/plugins/redis.py b/platypush/plugins/redis.py index 6c40a9ad47..8a65258d42 100644 --- a/platypush/plugins/redis.py +++ b/platypush/plugins/redis.py @@ -5,7 +5,31 @@ from platypush.plugins import Plugin class RedisPlugin(Plugin): + """ + Plugin to send messages on Redis queues. + + Requires: + + * **redis** (``pip install redis``) + """ + 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.rpush(queue, msg) return Response(output={'state': 'ok'}) diff --git a/platypush/plugins/serial/__init__.py b/platypush/plugins/serial/__init__.py index 85738a306b..0083fde69d 100644 --- a/platypush/plugins/serial/__init__.py +++ b/platypush/plugins/serial/__init__.py @@ -17,6 +17,14 @@ class SerialPlugin(Plugin): """ 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) self.device = device @@ -53,6 +61,10 @@ class SerialPlugin(Plugin): return output.decode().strip() 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) try: diff --git a/platypush/plugins/shell/__init__.py b/platypush/plugins/shell/__init__.py index 27bc8d098d..41ab1a170a 100644 --- a/platypush/plugins/shell/__init__.py +++ b/platypush/plugins/shell/__init__.py @@ -5,7 +5,20 @@ from platypush.message.response import Response from .. import Plugin class ShellPlugin(Plugin): + """ + Plugin to run custom shell commands. + """ + 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 errors = [] diff --git a/platypush/plugins/switch/__init__.py b/platypush/plugins/switch/__init__.py index 00b59bc6c6..24316f383a 100644 --- a/platypush/plugins/switch/__init__.py +++ b/platypush/plugins/switch/__init__.py @@ -1,16 +1,24 @@ from .. import Plugin class SwitchPlugin(Plugin): + """ + Abstract class for interacting with switch devices + """ + def on(self, args): + """ Turn the device on """ raise NotImplementedError() def off(self, args): + """ Turn the device off """ raise NotImplementedError() def toggle(self, args): + """ Toggle the device status (on/off) """ raise NotImplementedError() def status(self): + """ Get the device state """ raise NotImplementedError() diff --git a/platypush/plugins/switch/switchbot/__init__.py b/platypush/plugins/switch/switchbot/__init__.py index b468e673fb..b8b00e6ef9 100644 --- a/platypush/plugins/switch/switchbot/__init__.py +++ b/platypush/plugins/switch/switchbot/__init__.py @@ -80,15 +80,38 @@ class Driver(object): 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 (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 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, 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.connect_timeout = connect_timeout if connect_timeout else 5 self.scan_timeout = scan_timeout if scan_timeout else 2 @@ -118,15 +141,34 @@ class SwitchSwitchbotPlugin(SwitchPlugin): 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) 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') 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') def scan(self): + """ Scan for available Switchbot devices nearby """ output = None errors = [] @@ -145,5 +187,6 @@ class SwitchSwitchbotPlugin(SwitchPlugin): return Response(output=output, errors=errors) + # vim:sw=4:ts=4:et: diff --git a/platypush/plugins/switch/wemo/__init__.py b/platypush/plugins/switch/wemo/__init__.py index 7355a7cd07..7fae85fd4d 100644 --- a/platypush/plugins/switch/wemo/__init__.py +++ b/platypush/plugins/switch/wemo/__init__.py @@ -6,7 +6,20 @@ from platypush.message.response import Response from .. import 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): + """ + :param discovery_seconds: Discovery time when scanning for devices (default: 3) + :type discovery_seconds: int + """ super().__init__(*args, **kwargs) self.discovery_seconds=discovery_seconds @@ -15,11 +28,34 @@ class SwitchWemoPlugin(SwitchPlugin): self.refresh_devices() def refresh_devices(self): + """ Update the list of available devices """ self.logger.info('Starting WeMo discovery') self.env.discover(seconds=self.discovery_seconds) self.devices = self.env.devices 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() return Response( output = { 'devices': [ @@ -49,12 +85,30 @@ class SwitchWemoPlugin(SwitchPlugin): return Response(output=json.dumps(resp)) def on(self, device): + """ + Turn a switch on + + :param device: Device name + :type device: str + """ return self._exec('on', device) def off(self, device): + """ + Turn a switch off + + :param device: Device name + :type device: str + """ return self._exec('off', 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) diff --git a/platypush/plugins/tts/__init__.py b/platypush/plugins/tts/__init__.py index d0a3cc9d80..0e9161276c 100644 --- a/platypush/plugins/tts/__init__.py +++ b/platypush/plugins/tts/__init__.py @@ -6,14 +6,28 @@ from platypush.message.response import Response from .. import 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'): super().__init__() self.lang=lang 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 output = None errors = [] @@ -34,5 +48,6 @@ class TtsPlugin(Plugin): return Response(output=output, errors=errors) + # vim:sw=4:ts=4:et: diff --git a/platypush/plugins/variable.py b/platypush/plugins/variable.py index a92471cc61..8c183514f0 100644 --- a/platypush/plugins/variable.py +++ b/platypush/plugins/variable.py @@ -13,14 +13,37 @@ class VariablePlugin(Plugin): self._variables = {} 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 ``{"":""}`` + """ + return Response(output={name: self._variables.get(name, default_value)}) 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(): self._variables[name] = value return Response(output=kwargs) 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: del self._variables[name] return Response(output={'status':'ok'}) diff --git a/platypush/plugins/video/omxplayer.py b/platypush/plugins/video/omxplayer.py index 34a904f019..2f60d8de58 100644 --- a/platypush/plugins/video/omxplayer.py +++ b/platypush/plugins/video/omxplayer.py @@ -19,6 +19,22 @@ from platypush.message.event.video import VideoPlayEvent, VideoPauseEvent, \ from .. import 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 = { '.avi', '.flv', '.wmv', '.mov', '.mp4', '.m4v', '.mpg', '.mpeg', '.rm', '.swf', '.vob', '.mkv' @@ -28,6 +44,20 @@ class VideoOmxplayerPlugin(Plugin): torrent_state = {} 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) self.args = args @@ -54,6 +84,17 @@ class VideoOmxplayerPlugin(Plugin): self.torrent_ports = torrent_ports if torrent_ports else self.default_torrent_ports def play(self, resource): + """ + Play a resource. + + :param resource: Resource to play. Supported types: + + * Local files (format: ``file:///``) + * Remote videos (format: ``https:///``) + * YouTube videos (format: ``https://www.youtube.com/watch?v=``) + * Torrents (format: ``magnet:?``) + """ + if resource.startswith('youtube:') \ or resource.startswith('https://www.youtube.com/watch?v='): resource = self._get_youtube_content(resource) @@ -90,9 +131,11 @@ class VideoOmxplayerPlugin(Plugin): return self.status() def pause(self): + """ Pause the playback """ if self.player: self.player.play_pause() def stop(self): + """ Stop the playback """ if self.player: self.player.stop() self.player.quit() @@ -101,26 +144,31 @@ class VideoOmxplayerPlugin(Plugin): return self.status() def voldown(self): + """ Volume down by 10% """ if self.player: self.player.set_volume(max(-6000, self.player.volume()-1000)) return self.status() def volup(self): + """ Volume up by 10% """ if self.player: self.player.set_volume(min(0, self.player.volume()+1000)) return self.status() def back(self): + """ Back by 30 seconds """ if self.player: self.player.seek(-30) return self.status() def forward(self): + """ Forward by 30 seconds """ if self.player: self.player.seek(+30) return self.status() def next(self): + """ Play the next track/video """ if self.player: self.player.stop() @@ -132,48 +180,103 @@ class VideoOmxplayerPlugin(Plugin): def hide_subtitles(self): + """ Hide the subtitles """ if self.player: self.player.hide_subtitles() return self.status() def hide_video(self): + """ Hide the video """ if self.player: self.player.hide_video() return self.status() def is_playing(self): + """ + :returns: True if it's playing, False otherwise + """ + if self.player: return self.player.is_playing() else: return False - def load(self, source, pause=False): - if self.player: self.player.load(source, pause) + def load(self, resource, pause=False): + """ + 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() def metadata(self): + """ Get the metadata of the current video """ if self.player: return Response(output=self.player.metadata()) return self.status() def mute(self): + """ Mute the player """ if self.player: self.player.mute() return self.status() def unmute(self): + """ Unmute the player """ if self.player: self.player.unmute() return self.status() 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) return self.status() 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) return self.status() 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] volume = 60.0*volume - 6000 if self.player: self.player.set_volume(volume) return self.status() 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 if self.player: @@ -233,6 +336,22 @@ class VideoOmxplayerPlugin(Plugin): self.player.stopEvent += self.on_stop() 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 = [] if types is None: types = { 'youtube', 'file', 'torrent' } @@ -360,6 +479,13 @@ class VideoOmxplayerPlugin(Plugin): return Response(output=results) 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 if not self.download_dir: diff --git a/platypush/plugins/weather/forecast.py b/platypush/plugins/weather/forecast.py index bacb9bdec0..85b9e62747 100644 --- a/platypush/plugins/weather/forecast.py +++ b/platypush/plugins/weather/forecast.py @@ -3,27 +3,253 @@ from platypush.plugins.http.request import 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): - """ 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') + self.darksky_token = darksky_token + self.units = units + self.lat = lat + self.long = long self.latest_bulletin = {} - self.url = 'https://api.darksky.net/forecast/{}/{},{}?units={}'. \ - format(darksky_token, lat, long, units) - def get_current_weather(self, **kwargs): - response = self.get(self.url) - print(response) + def _get_url(self, lat=None, long=None): + return 'https://api.darksky.net/forecast/{}/{},{}?units={}'. \ + 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']) - def get_hourly_forecast(self, **kwargs): - response = self.get(self.url) + def get_hourly_forecast(self, lat=None, long=None, **kwargs): + """ + 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']) - def get_daily_forecast(self, **kwargs): - response = self.get(self.url) + def get_daily_forecast(self, lat=None, long=None, **kwargs): + """ + 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'])