From 3b6b201428a42239895a219cdf3169b42c93bbc1 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 7 Oct 2013 00:15:47 -0700 Subject: [PATCH] Added a file downloader --- homeassistant/actors.py | 67 +++++++++++++++++++++++++++++++++++++++-- homeassistant/util.py | 7 +++++ start.py | 4 ++- 3 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 homeassistant/util.py diff --git a/homeassistant/actors.py b/homeassistant/actors.py index 04b94d8c17e..b9c79ce33e3 100644 --- a/homeassistant/actors.py +++ b/homeassistant/actors.py @@ -6,14 +6,17 @@ This module provides actors that will react to events happening within homeassis """ +import os import logging from datetime import datetime, timedelta +import re import dateutil.parser from phue import Bridge +import requests from . import track_state_change - +from .util import sanitize_filename from .observers import (STATE_CATEGORY_SUN, SUN_STATE_BELOW_HORIZON, SUN_STATE_ABOVE_HORIZON, STATE_CATEGORY_ALL_DEVICES, DEVICE_STATE_HOME, DEVICE_STATE_NOT_HOME, STATE_CATEGORY_NEXT_SUN_SETTING, track_time_change) @@ -22,6 +25,8 @@ LIGHT_TRANSITION_TIME = timedelta(minutes=15) HUE_MAX_TRANSITION_TIME = 9000 +EVENT_DOWNLOAD_FILE = "download_file" + EVENT_TURN_LIGHT_ON = "turn_light_on" EVENT_TURN_LIGHT_OFF = "turn_light_off" @@ -55,10 +60,10 @@ class LightTrigger(object): self._handle_sun_rising(None, None, None) # Listen for light on and light off events - eventbus.listen(EVENT_TURN_LIGHT_ON, lambda event: self.light_control.turn_light_on(event.data.get("light_id", None), + eventbus.listen(EVENT_TURN_LIGHT_ON, lambda event: self.light_control.turn_light_on(event.data.get("light_id", None), event.data.get("transition_seconds", None))) - eventbus.listen(EVENT_TURN_LIGHT_OFF, lambda event: self.light_control.turn_light_off(event.data.get("light_id", None), + eventbus.listen(EVENT_TURN_LIGHT_OFF, lambda event: self.light_control.turn_light_off(event.data.get("light_id", None), event.data.get("transition_seconds", None))) @@ -177,3 +182,59 @@ class HueLightControl(object): command['transitiontime'] = _hue_process_transition_time(transition_seconds) self.bridge.set_light(light_id, command) + + +def setup_file_downloader(eventbus, download_path): + """ Listens for download events to download files. """ + + logger = logging.getLogger(__name__) + + if not os.path.isdir(download_path): + logger.error("Download path {} does not exist. File Downloader not active.".format(download_path)) + return + + def download_file(event): + """ Downloads file specified in the url. """ + try: + req = requests.get(event.data['url'], stream=True) + if req.status_code == 200: + filename = None + + if 'content-disposition' in req.headers: + match = re.findall(r"filename=(\S+)", req.headers['content-disposition']) + + if len(match) > 0: + filename = match[0].strip("'\" ") + + if not filename: + filename = os.path.basename(event.data['url']).strip() + + if not filename: + filename = "ha_download" + + # Remove stuff to ruin paths + filename = sanitize_filename(filename) + + path, ext = os.path.splitext(os.path.join(download_path, filename)) + + # If file exist append a number. We test filename, filename_2, filename_3 etc.. + tries = 0 + while True: + tries += 1 + + final_path = path + ("" if tries == 1 else "_{}".format(tries)) + ext + + if not os.path.isfile(final_path): + break + + logger.info("FileDownloader:{} -> {}".format(event.data['url'], final_path)) + + with open(final_path, 'wb') as fil: + for chunk in req.iter_content(1024): + fil.write(chunk) + + except requests.exceptions.ConnectionError: + logger.exception("FileDownloader:ConnectionError occured for {}".format(event.data['url'])) + + + eventbus.listen(EVENT_DOWNLOAD_FILE, download_file) diff --git a/homeassistant/util.py b/homeassistant/util.py new file mode 100644 index 00000000000..7b1e7514706 --- /dev/null +++ b/homeassistant/util.py @@ -0,0 +1,7 @@ +""" Helper methods for various modules. """ + +import re + +def sanitize_filename(filename): + """ Sanitizes a filename by removing .. / and \\. """ + return re.sub(r"(~|(\.\.)|/|\+)", "", filename) diff --git a/start.py b/start.py index d4b002a3cc6..1dc612e196b 100644 --- a/start.py +++ b/start.py @@ -3,7 +3,7 @@ from ConfigParser import SafeConfigParser from homeassistant import StateMachine, EventBus, start_home_assistant from homeassistant.observers import TomatoDeviceScanner, DeviceTracker, track_sun -from homeassistant.actors import HueLightControl, LightTrigger +from homeassistant.actors import HueLightControl, LightTrigger, setup_file_downloader from homeassistant.httpinterface import HTTPInterface from lib.pychromecast import play_youtube_video @@ -32,6 +32,8 @@ if config.has_option("chromecast", "host"): eventbus.listen("start_fireplace", lambda event: play_youtube_video(config.get("chromecast","host"), "eyU3bRy2x44")) eventbus.listen("start_epic_sax", lambda event: play_youtube_video(config.get("chromecast","host"), "kxopViU98Xo")) +setup_file_downloader(eventbus, "downloads") + # Init HTTP interface HTTPInterface(eventbus, statemachine, config.get("common","api_password"))