diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3f2fd110a1d..f646766a231 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,7 +18,7 @@ For help on building your component, please see the [developer documentation](ht After you finish adding support for your device: - Update the supported devices in the `README.md` file. - - Add any new dependencies to `requirements.txt`. + - Add any new dependencies to `requirements_all.txt`. There is no ordering right now, so just add it to the end. - Update the `.coveragerc` file. - Provide some documentation for [home-assistant.io](https://home-assistant.io/). The documentation is handled in a separate [git repository](https://github.com/balloob/home-assistant.io). - Make sure all your code passes Pylint and flake8 (PEP8 and some more) validation. To generate reports, run `pylint homeassistant > pylint.txt` and `flake8 homeassistant --exclude bower_components,external > flake8.txt`. diff --git a/homeassistant/components/device_tracker/demo.py b/homeassistant/components/device_tracker/demo.py new file mode 100644 index 00000000000..e8cf906be9e --- /dev/null +++ b/homeassistant/components/device_tracker/demo.py @@ -0,0 +1,50 @@ +""" +homeassistant.components.device_tracker.demo +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Demo platform for the device tracker. + +device_tracker: + platform: demo +""" +import random + +from homeassistant.components.device_tracker import DOMAIN + + +def setup_scanner(hass, config, see): + """ Set up a demo tracker. """ + + def offset(): + """ Return random offset. """ + return (random.randrange(500, 2000)) / 2e5 * random.choice((-1, 1)) + + def random_see(dev_id, name): + """ Randomize a sighting. """ + see( + dev_id=dev_id, + host_name=name, + gps=(hass.config.latitude + offset(), + hass.config.longitude + offset()), + gps_accuracy=random.randrange(50, 150), + battery=random.randrange(10, 90) + ) + + def observe(call=None): + """ Observe three entities. """ + random_see('demo_paulus', 'Paulus') + random_see('demo_anne_therese', 'Anne Therese') + + observe() + + see( + dev_id='demo_home_boy', + host_name='Home Boy', + gps=[hass.config.latitude - 0.00002, hass.config.longitude + 0.00002], + gps_accuracy=20, + battery=53 + ) + + hass.services.register(DOMAIN, 'demo', observe) + + return True diff --git a/homeassistant/components/device_tracker/owntracks.py b/homeassistant/components/device_tracker/owntracks.py index e2591ffcc26..9ef227909e1 100644 --- a/homeassistant/components/device_tracker/owntracks.py +++ b/homeassistant/components/device_tracker/owntracks.py @@ -8,6 +8,7 @@ device_tracker: platform: owntracks """ import json +import logging import homeassistant.components.mqtt as mqtt @@ -24,18 +25,29 @@ def setup_scanner(hass, config, see): # Docs on available data: # http://owntracks.org/booklet/tech/json/#_typelocation - - parts = topic.split('/') try: data = json.loads(payload) except ValueError: # If invalid JSON + logging.getLogger(__name__).error( + 'Unable to parse payload as JSON: %s', payload) return + if data.get('_type') != 'location': return - dev_id = '{}_{}'.format(parts[1], parts[2]) - see(dev_id=dev_id, host_name=parts[1], gps=(data['lat'], data['lon']), - gps_accuracy=data['acc'], battery=data['batt']) + + parts = topic.split('/') + kwargs = { + 'dev_id': '{}_{}'.format(parts[1], parts[2]), + 'host_name': parts[1], + 'gps': (data['lat'], data['lon']), + } + if 'acc' in data: + kwargs['gps_accuracy'] = data['acc'] + if 'batt' in data: + kwargs['battery'] = data['batt'] + + see(**kwargs) mqtt.subscribe(hass, LOCATION_TOPIC, owntracks_location_update, 1) diff --git a/homeassistant/components/discovery.py b/homeassistant/components/discovery.py index 6a780693f25..450019022e1 100644 --- a/homeassistant/components/discovery.py +++ b/homeassistant/components/discovery.py @@ -19,7 +19,7 @@ from homeassistant.const import ( DOMAIN = "discovery" DEPENDENCIES = [] -REQUIREMENTS = ['netdisco==0.4'] +REQUIREMENTS = ['netdisco==0.4.1'] SCAN_INTERVAL = 300 # seconds diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index 902b14e38b3..419e48d55b5 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -21,7 +21,8 @@ _LOGGER = logging.getLogger(__name__) FRONTEND_URLS = [ - URL_ROOT, '/logbook', '/history', '/devService', '/devState', '/devEvent'] + URL_ROOT, '/logbook', '/history', '/map', '/devService', '/devState', + '/devEvent'] STATES_URL = re.compile(r'/states(/([a-zA-Z\._\-0-9/]+)|)') diff --git a/homeassistant/components/frontend/version.py b/homeassistant/components/frontend/version.py index 5d1b9696feb..5f913eae674 100644 --- a/homeassistant/components/frontend/version.py +++ b/homeassistant/components/frontend/version.py @@ -1,2 +1,2 @@ """ DO NOT MODIFY. Auto-generated by build_frontend script """ -VERSION = "5f35285bc502e3f69f564240fee04baa" +VERSION = "3a3ed81f9d66bf24e17f1d02b8403335" diff --git a/homeassistant/components/frontend/www_static/frontend.html b/homeassistant/components/frontend/www_static/frontend.html index 8933710b823..9277184b8a2 100644 --- a/homeassistant/components/frontend/www_static/frontend.html +++ b/homeassistant/components/frontend/www_static/frontend.html @@ -2277,7 +2277,490 @@ http://nicolasgallagher.com/micro-clearfix-hack/ .pika-table abbr { border-bottom: none; cursor: help; -}- \ No newline at end of file + } \ No newline at end of file diff --git a/homeassistant/components/frontend/www_static/home-assistant-polymer b/homeassistant/components/frontend/www_static/home-assistant-polymer index 68f6c6ae5d3..6989009b2d5 160000 --- a/homeassistant/components/frontend/www_static/home-assistant-polymer +++ b/homeassistant/components/frontend/www_static/home-assistant-polymer @@ -1 +1 @@ -Subproject commit 68f6c6ae5d37a1f0fcd1c36a8803581f9367ac5f +Subproject commit 6989009b2d59e39fd39b3025ff5899877f618bd3 diff --git a/homeassistant/components/frontend/www_static/images/leaflet/layers-2x.png b/homeassistant/components/frontend/www_static/images/leaflet/layers-2x.png new file mode 100644 index 00000000000..a2cf7f9efef Binary files /dev/null and b/homeassistant/components/frontend/www_static/images/leaflet/layers-2x.png differ diff --git a/homeassistant/components/frontend/www_static/images/leaflet/layers.png b/homeassistant/components/frontend/www_static/images/leaflet/layers.png new file mode 100644 index 00000000000..bca0a0e4296 Binary files /dev/null and b/homeassistant/components/frontend/www_static/images/leaflet/layers.png differ diff --git a/homeassistant/components/frontend/www_static/images/leaflet/marker-icon-2x.png b/homeassistant/components/frontend/www_static/images/leaflet/marker-icon-2x.png new file mode 100644 index 00000000000..0015b6495fa Binary files /dev/null and b/homeassistant/components/frontend/www_static/images/leaflet/marker-icon-2x.png differ diff --git a/homeassistant/components/frontend/www_static/images/leaflet/marker-icon.png b/homeassistant/components/frontend/www_static/images/leaflet/marker-icon.png new file mode 100644 index 00000000000..e2e9f757f51 Binary files /dev/null and b/homeassistant/components/frontend/www_static/images/leaflet/marker-icon.png differ diff --git a/homeassistant/components/frontend/www_static/images/leaflet/marker-shadow.png b/homeassistant/components/frontend/www_static/images/leaflet/marker-shadow.png new file mode 100644 index 00000000000..d1e773c715a Binary files /dev/null and b/homeassistant/components/frontend/www_static/images/leaflet/marker-shadow.png differ diff --git a/homeassistant/components/light/tellstick.py b/homeassistant/components/light/tellstick.py index f5e74029ced..19ce1a06d4a 100644 --- a/homeassistant/components/light/tellstick.py +++ b/homeassistant/components/light/tellstick.py @@ -12,6 +12,7 @@ from tellcore.library import DirectCallbackDispatcher REQUIREMENTS = ['tellcore-py==1.0.4'] +# pylint: disable=unused-argument def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Find and return Tellstick lights. """ @@ -36,7 +37,6 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): if switch.methods(tellcore_constants.TELLSTICK_DIM): lights.append(TellstickLight(switch)) - # pylint: disable=unused-argument def _device_event_callback(id_, method, data, cid): """ Called from the TelldusCore library to update one device """ for light_device in lights: diff --git a/homeassistant/components/media_player/plex.py b/homeassistant/components/media_player/plex.py new file mode 100644 index 00000000000..a43916f10e3 --- /dev/null +++ b/homeassistant/components/media_player/plex.py @@ -0,0 +1,188 @@ +""" +homeassistant.components.media_player.plex +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Provides an interface to the Plex API + +Configuration: + +To use Plex add something like this to your configuration: + +media_player: + platform: plex + name: plex_server + user: plex + password: my_secure_password + +Variables: + +name +*Required +The name of the backend device (Under Plex Media Server > settings > server). + +user +*Required +The Plex username + +password +*Required +The Plex password +""" + +import logging + +from homeassistant.components.media_player import ( + MediaPlayerDevice, SUPPORT_PAUSE, SUPPORT_PREVIOUS_TRACK, + SUPPORT_NEXT_TRACK, MEDIA_TYPE_TVSHOW, MEDIA_TYPE_VIDEO) +from homeassistant.const import ( + STATE_IDLE, STATE_PLAYING, STATE_PAUSED, STATE_UNKNOWN) + +REQUIREMENTS = ['https://github.com/miniconfig/python-plexapi/archive/' + '437e36dca3b7780dc0cb73941d662302c0cd2fa9.zip' + '#python-plexapi==1.0.2'] + +_LOGGER = logging.getLogger(__name__) + +SUPPORT_PLEX = SUPPORT_PAUSE | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK + +# pylint: disable=abstract-method +# pylint: disable=unused-argument + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """ Sets up the plex platform. """ + from plexapi.myplex import MyPlexUser + name = config.get('name', '') + user = config.get('user', '') + password = config.get('password', '') + plexuser = MyPlexUser.signin(user, password) + plexserver = plexuser.getResource(name).connect() + dev = plexserver.clients() + for device in dev: + if "PlayStation" not in device.name: + add_devices([PlexClient(device.name, plexserver)]) + + +class PlexClient(MediaPlayerDevice): + """ Represents a Plex device. """ + + # pylint: disable=too-many-public-methods + + def __init__(self, name, plexserver): + self.client = plexserver.client(name) + self._name = name + self._media = None + self.update() + self.server = plexserver + + @property + def name(self): + """ Returns the name of the device. """ + return self._name + + @property + def state(self): + """ Returns the state of the device. """ + if self._media is None: + return STATE_IDLE + else: + state = self._media.get('state') + if state == 'playing': + return STATE_PLAYING + elif state == 'paused': + return STATE_PAUSED + return STATE_UNKNOWN + + def update(self): + timeline = self.client.timeline() + for timeline_item in timeline: + if timeline_item.get('state') in ('playing', 'paused'): + self._media = timeline_item + + @property + def media_content_id(self): + """ Content ID of current playing media. """ + if self._media is not None: + return self._media.get('ratingKey') + + @property + def media_content_type(self): + """ Content type of current playing media. """ + if self._media is None: + return None + media_type = self.server.library.getByKey( + self.media_content_id).type + if media_type == 'episode': + return MEDIA_TYPE_TVSHOW + elif media_type == 'movie': + return MEDIA_TYPE_VIDEO + return None + + @property + def media_duration(self): + """ Duration of current playing media in seconds. """ + if self._media is not None: + total_time = self._media.get('duration') + return total_time + + @property + def media_image_url(self): + """ Image url of current playing media. """ + if self._media is not None: + return self.server.library.getByKey(self.media_content_id).thumbUrl + return None + + @property + def media_title(self): + """ Title of current playing media. """ + # find a string we can use as a title + if self._media is not None: + return self.server.library.getByKey(self.media_content_id).title + + @property + def media_season(self): + """ Season of curent playing media. (TV Show only) """ + if self._media is not None: + show_season = self.server.library.getByKey( + self.media_content_id).season().index + return show_season + return None + + @property + def media_series_title(self): + """ Series title of current playing media. (TV Show only)""" + if self._media is not None: + series_title = self.server.library.getByKey( + self.media_content_id).show().title + return series_title + return None + + @property + def media_episode(self): + """ Episode of current playing media. (TV Show only) """ + if self._media is not None: + show_episode = self.server.library.getByKey( + self.media_content_id).index + return show_episode + return None + + @property + def supported_media_commands(self): + """ Flags of media commands that are supported. """ + return SUPPORT_PLEX + + def media_play(self): + """ media_play media player. """ + self.client.play() + + def media_pause(self): + """ media_pause media player. """ + self.client.pause() + + def media_next_track(self): + """ Send next track command. """ + self.client.skipNext() + + def media_previous_track(self): + """ Send previous track command. """ + self.client.skipPrevious() diff --git a/homeassistant/components/switch/tellstick.py b/homeassistant/components/switch/tellstick.py index 42ce7af9926..96b10a0a977 100644 --- a/homeassistant/components/switch/tellstick.py +++ b/homeassistant/components/switch/tellstick.py @@ -48,7 +48,6 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): switches.append( TellstickSwitchDevice(switch, signal_repetitions)) - # pylint: disable=unused-argument def _device_event_callback(id_, method, data, cid): """ Called from the TelldusCore library to update one device """ for switch_device in switches: diff --git a/requirements_all.txt b/requirements_all.txt index 19cc04016a8..77f725ed4f0 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -86,7 +86,7 @@ https://github.com/theolind/pymysensors/archive/35b87d880147a34107da0d40cb815d75 pynetgear==0.3 # Netdisco (discovery) -netdisco==0.4 +netdisco==0.4.1 # Wemo (switch.wemo) pywemo==0.3 @@ -133,3 +133,6 @@ https://github.com/balloob/home-assistant-vera-api/archive/a8f823066ead6c7da6fb5 # Sonos bindings (media_player.sonos) SoCo==0.11.1 + +# PlexAPI (media_player.plex) +https://github.com/miniconfig/python-plexapi/archive/437e36dca3b7780dc0cb73941d662302c0cd2fa9.zip#python-plexapi==1.0.2 diff --git a/script/home-assistant@.service b/script/home-assistant@.service new file mode 100644 index 00000000000..983844a95a3 --- /dev/null +++ b/script/home-assistant@.service @@ -0,0 +1,14 @@ +# This is a simple service file for systems with systemd to tun HA as user. +# +[Unit] +Description=Home Assistant for %i +After=network.target + +[Service] +Type=simple +User=%i +WorkingDirectory=%h +ExecStart=/usr/bin/hass --config %h/.homeassistant/ + +[Install] +WantedBy=multi-user.target diff --git a/script/lint b/script/lint index c8b65f99677..75667ef88a4 100755 --- a/script/lint +++ b/script/lint @@ -13,7 +13,7 @@ PYLINT_STATUS=$? if [ $FLAKE8_STATUS -eq 0 ] then - exit $FLAKE8_STATUS -else exit $PYLINT_STATUS +else + exit $FLAKE8_STATUS fi