{"action":"created","comment":{"url":"https://api.github.com/repos/balloob/home-assistant/pulls/comments/49420473","id":49420473,"diff_hunk":"@@ -0,0 +1,433 @@\n+\"\"\"\n+homeassistant.components.media_player.universal\n+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n+Combines multiple media players into one for a universal controller.\n+\n+For more details about this platform, please refer to the documentation at\n+https://home-assistant.io/components/media_player.universal/\n+\"\"\"\n+\n+# pylint: disable=import-error\n+from copy import copy\n+import logging\n+\n+from homeassistant.helpers.event import track_state_change\n+\n+from homeassistant.const import (\n+    STATE_IDLE, STATE_ON, STATE_OFF, CONF_NAME,\n+    ATTR_ENTITY_ID, ATTR_ENTITY_PICTURE,\n+    SERVICE_TURN_OFF, SERVICE_TURN_ON,\n+    SERVICE_VOLUME_UP, SERVICE_VOLUME_DOWN, SERVICE_VOLUME_SET,\n+    SERVICE_VOLUME_MUTE,\n+    SERVICE_MEDIA_PLAY_PAUSE, SERVICE_MEDIA_PLAY, SERVICE_MEDIA_PAUSE,\n+    SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_MEDIA_SEEK)\n+\n+from homeassistant.components.media_player import (\n+    MediaPlayerDevice, DOMAIN,\n+    SUPPORT_VOLUME_BUTTONS, SUPPORT_VOLUME_SET, SUPPORT_VOLUME_MUTE,\n+    SUPPORT_TURN_ON, SUPPORT_TURN_OFF,\n+    SERVICE_PLAY_MEDIA, SERVICE_YOUTUBE_VIDEO,\n+    ATTR_SUPPORTED_MEDIA_COMMANDS, ATTR_MEDIA_VOLUME_MUTED,\n+    ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_CONTENT_TYPE, ATTR_MEDIA_DURATION,\n+    ATTR_MEDIA_TITLE, ATTR_MEDIA_ARTIST, ATTR_MEDIA_ALBUM_NAME,\n+    ATTR_MEDIA_TRACK, ATTR_MEDIA_SERIES_TITLE, ATTR_MEDIA_ALBUM_ARTIST,\n+    ATTR_MEDIA_SEASON, ATTR_MEDIA_EPISODE, ATTR_MEDIA_CHANNEL,\n+    ATTR_MEDIA_PLAYLIST, ATTR_APP_ID, ATTR_APP_NAME, ATTR_MEDIA_VOLUME_LEVEL,\n+    ATTR_MEDIA_SEEK_POSITION)\n+\n+ATTR_ACTIVE_CHILD = 'active_child'\n+\n+CONF_ATTRS = 'attributes'\n+CONF_CHILDREN = 'children'\n+CONF_COMMANDS = 'commands'\n+CONF_PLATFORM = 'platform'\n+CONF_SERVICE = 'service'\n+CONF_SERVICE_DATA = 'service_data'\n+CONF_STATE = 'state'\n+\n+OFF_STATES = [STATE_IDLE, STATE_OFF]\n+REQUIREMENTS = []\n+_LOGGER = logging.getLogger(__name__)\n+\n+\n+# pylint: disable=unused-argument\n+def setup_platform(hass, config, add_devices, discovery_info=None):\n+    \"\"\" sets up the universal media players \"\"\"\n+    if not validate_config(config):\n+        return\n+\n+    player = UniversalMediaPlayer(hass,\n+                                  config[CONF_NAME],\n+                                  config[CONF_CHILDREN],\n+                                  config[CONF_COMMANDS],\n+                                  config[CONF_ATTRS])\n+\n+    add_devices([player])\n+\n+\n+def validate_config(config):\n+    \"\"\" validate universal media player configuration \"\"\"\n+    del config[CONF_PLATFORM]\n+\n+    # validate name\n+    if CONF_NAME not in config:\n+        _LOGGER.error('Universal Media Player configuration requires name')\n+        return False\n+\n+    validate_children(config)\n+    validate_commands(config)\n+    validate_attributes(config)\n+\n+    del_keys = []\n+    for key in config:\n+        if key not in [CONF_NAME, CONF_CHILDREN, CONF_COMMANDS, CONF_ATTRS]:\n+            _LOGGER.warning(\n+                'Universal Media Player (%s) unrecognized parameter %s',\n+                config[CONF_NAME], key)\n+            del_keys.append(key)\n+    for key in del_keys:\n+        del config[key]\n+\n+    return True\n+\n+\n+def validate_children(config):\n+    \"\"\" validate children \"\"\"\n+    if CONF_CHILDREN not in config:\n+        _LOGGER.info(\n+            'No children under Universal Media Player (%s)', config[CONF_NAME])\n+        config[CONF_CHILDREN] = []\n+    elif not isinstance(config[CONF_CHILDREN], list):\n+        _LOGGER.warning(\n+            'Universal Media Player (%s) children not list in config. '\n+            'They will be ignored.',\n+            config[CONF_NAME])\n+        config[CONF_CHILDREN] = []\n+\n+\n+def validate_commands(config):\n+    \"\"\" validate commands \"\"\"\n+    if CONF_COMMANDS not in config:\n+        config[CONF_COMMANDS] = {}\n+    elif not isinstance(config[CONF_COMMANDS], dict):\n+        _LOGGER.warning(\n+            'Universal Media Player (%s) specified commands not dict in '\n+            'config. They will be ignored.',\n+            config[CONF_NAME])\n+        config[CONF_COMMANDS] = {}\n+\n+\n+def validate_attributes(config):\n+    \"\"\" validate attributes \"\"\"\n+    if CONF_ATTRS not in config:\n+        config[CONF_ATTRS] = {}\n+    elif not isinstance(config[CONF_ATTRS], dict):\n+        _LOGGER.warning(\n+            'Universal Media Player (%s) specified attributes '\n+            'not dict in config. They will be ignored.',\n+            config[CONF_NAME])\n+        config[CONF_ATTRS] = {}\n+\n+    for key, val in config[CONF_ATTRS].items():\n+        config[CONF_ATTRS][key] = val.split('|', 1)\n+\n+\n+class UniversalMediaPlayer(MediaPlayerDevice):\n+    \"\"\" Represents a universal media player in HA \"\"\"\n+    # pylint: disable=too-many-public-methods\n+\n+    def __init__(self, hass, name, children, commands, attributes):\n+        # pylint: disable=too-many-arguments\n+        self.hass = hass\n+        self._name = name\n+        self._children = children\n+        self._cmds = commands\n+        self._attrs = attributes\n+\n+        track_state_change(hass, self.dependencies, self.update_state)\n+\n+    def _entity_lkp(self, entity_id=None, state_attr=None, state_as_obj=True):\n+        \"\"\" Looks up an entity state from hass \"\"\"\n+        if entity_id is None:\n+            return\n+\n+        state_obj = self.hass.states.get(entity_id)\n+\n+        if state_obj is None:\n+            return\n+\n+        if state_attr:\n+            return state_obj.attributes.get(state_attr)\n+        if state_as_obj:\n+            return state_obj\n+        return state_obj.state\n+\n+    def _override_or_child_attr(self, attr_name):\n+        \"\"\" returns either the override or the active child for attr_name \"\"\"\n+        if attr_name in self._attrs:\n+            return self._entity_lkp(*self._attrs[attr_name],\n+                                    state_as_obj=False)\n+\n+        return self._child_attr(attr_name)\n+\n+    def _child_attr(self, attr_name):\n+        \"\"\" returns the active child's attr \"\"\"\n+        active_child = self.active_child_state\n+        return active_child.attributes.get(attr_name) if active_child else None\n+\n+    def _override_or_child_service(self, service_name, **service_data):\n+        \"\"\" calls either a specified or active child's service \"\"\"\n+        if service_name in self._cmds:\n+            cmd_data = self._cmds[service_name]\n+            domain, service = cmd_data[CONF_SERVICE].split('.', 1)\n+            self.hass.services.call(domain, service,\n+                                    cmd_data[CONF_SERVICE_DATA],\n+                                    blocking=True)\n+            return\n+\n+        self._child_service(service_name, **service_data)\n+\n+    def _child_service(self, service_name, **service_data):\n+        \"\"\" calls the active child's specified service \"\"\"\n+        active_child = self.active_child_state\n+        service_data[ATTR_ENTITY_ID] = active_child.entity_id\n+\n+        self.hass.services.call(DOMAIN, service_name, service_data,\n+                                blocking=True)\n+\n+    @property\n+    def dependencies(self):\n+        \"\"\" List of entity ids of entities that the mp depends on for state \"\"\"\n+        depend = copy(self._children)\n+        for entity in self._attrs.values():\n+            depend.append(entity[0])\n+        return depend\n+\n+    @property\n+    def master_state(self):\n+        \"\"\" gets the master state from entity or none \"\"\"\n+        if CONF_STATE in self._attrs:\n+            master_state = self._entity_lkp(*self._attrs[CONF_STATE],\n+                                            state_as_obj=False)\n+            return master_state if master_state else STATE_OFF\n+        else:\n+            return None\n+\n+    @property\n+    def children(self):\n+        \"\"\" Gets children and their current states \"\"\"\n+        return {child: self._entity_lkp(child) for child in self._children}\n+\n+    @property\n+    def active_child_state(self):\n+        \"\"\" The state of the active child or None \"\"\"\n+        for child_state in self.children.values():\n+            if child_state and child_state.state not in OFF_STATES:\n+                return child_state\n+\n+    @property\n+    def name(self):\n+        \"\"\" name of universal player \"\"\"\n+        return self._name\n+\n+    @property\n+    def state(self):\n+        \"\"\"\n+        Current state of media player\n+\n+        Off if master state is off\n+        ELSE Status of first active child\n+        ELSE master state or off\n+        \"\"\"\n+        master_state = self.master_state  # avoid multiple lookups\n+        if master_state == STATE_OFF:\n+            return STATE_OFF\n+\n+        active_child = self.active_child_state\n+        if active_child:\n+            return active_child.state\n+\n+        return master_state if master_state else STATE_OFF\n+\n+    @property\n+    def volume_level(self):\n+        \"\"\" Volume level of entity specified in attributes or active child \"\"\"\n+        return self._child_attr(ATTR_MEDIA_VOLUME_LEVEL)\n+\n+    @property\n+    def is_volume_muted(self):\n+        \"\"\" boolean if volume is muted \"\"\"\n+        return self._override_or_child_attr(ATTR_MEDIA_VOLUME_MUTED) \\\n+            in [True, STATE_ON]\n+\n+    @property\n+    def media_content_id(self):\n+        \"\"\" Content ID of current playing media. \"\"\"\n+        return self._child_attr(ATTR_MEDIA_CONTENT_ID)\n+\n+    @property\n+    def media_content_type(self):\n+        \"\"\" Content type of current playing media. \"\"\"\n+        return self._child_attr(ATTR_MEDIA_CONTENT_TYPE)\n+\n+    @property\n+    def media_duration(self):\n+        \"\"\" Duration of current playing media in seconds. \"\"\"\n+        return self._child_attr(ATTR_MEDIA_DURATION)\n+\n+    @property\n+    def media_image_url(self):\n+        \"\"\" Image url of current playing media. \"\"\"\n+        return self._child_attr(ATTR_ENTITY_PICTURE)\n+\n+    @property\n+    def media_title(self):\n+        \"\"\" Title of current playing media. \"\"\"\n+        return self._child_attr(ATTR_MEDIA_TITLE)\n+\n+    @property\n+    def media_artist(self):\n+        \"\"\" Artist of current playing media. (Music track only) \"\"\"\n+        return self._child_attr(ATTR_MEDIA_ARTIST)\n+\n+    @property\n+    def media_album_name(self):\n+        \"\"\" Album name of current playing media. (Music track only) \"\"\"\n+        return self._child_attr(ATTR_MEDIA_ALBUM_NAME)\n+\n+    @property\n+    def media_album_artist(self):\n+        \"\"\" Album arist of current playing media. (Music track only) \"\"\"\n+        return self._child_attr(ATTR_MEDIA_ALBUM_ARTIST)\n+\n+    @property\n+    def media_track(self):\n+        \"\"\" Track number of current playing media. (Music track only) \"\"\"\n+        return self._child_attr(ATTR_MEDIA_TRACK)\n+\n+    @property\n+    def media_series_title(self):\n+        \"\"\" Series title of current playing media. (TV Show only)\"\"\"\n+        return self._child_attr(ATTR_MEDIA_SERIES_TITLE)\n+\n+    @property\n+    def media_season(self):\n+        \"\"\" Season of current playing media. (TV Show only) \"\"\"\n+        return self._child_attr(ATTR_MEDIA_SEASON)\n+\n+    @property\n+    def media_episode(self):\n+        \"\"\" Episode of current playing media. (TV Show only) \"\"\"\n+        return self._child_attr(ATTR_MEDIA_EPISODE)\n+\n+    @property\n+    def media_channel(self):\n+        \"\"\" Channel currently playing. \"\"\"\n+        return self._child_attr(ATTR_MEDIA_CHANNEL)\n+\n+    @property\n+    def media_playlist(self):\n+        \"\"\" Title of Playlist currently playing. \"\"\"\n+        return self._child_attr(ATTR_MEDIA_PLAYLIST)\n+\n+    @property\n+    def app_id(self):\n+        \"\"\"  ID of the current running app. \"\"\"\n+        return self._child_attr(ATTR_APP_ID)\n+\n+    @property\n+    def app_name(self):\n+        \"\"\"  Name of the current running app. \"\"\"\n+        return self._child_attr(ATTR_APP_NAME)\n+\n+    @property\n+    def supported_media_commands(self):\n+        \"\"\" Flags of media commands that are supported. \"\"\"\n+        flags = self._child_attr(ATTR_SUPPORTED_MEDIA_COMMANDS) or 0\n+\n+        if SERVICE_TURN_ON in self._cmds:\n+            flags |= SUPPORT_TURN_ON\n+        if SERVICE_TURN_OFF in self._cmds:\n+            flags |= SUPPORT_TURN_OFF\n+\n+        if any([cmd in self._cmds for cmd in [SERVICE_VOLUME_UP,\n+                                              SERVICE_VOLUME_DOWN]]):\n+            flags |= SUPPORT_VOLUME_BUTTONS\n+            flags &= ~SUPPORT_VOLUME_SET\n+\n+        if SERVICE_VOLUME_MUTE in self._cmds and \\\n+                ATTR_MEDIA_VOLUME_MUTED in self._attrs:\n+            flags |= SUPPORT_VOLUME_MUTE\n+\n+        return flags\n+\n+    @property\n+    def device_state_attributes(self):\n+        \"\"\" Extra attributes a device wants to expose. \"\"\"\n+        active_child = self.active_child_state\n+        return {ATTR_ACTIVE_CHILD: active_child.entity_id} \\\n+            if active_child else {}\n+\n+    def turn_on(self):\n+        \"\"\" turn the media player on. \"\"\"\n+        self._override_or_child_service(SERVICE_TURN_ON)\n+\n+    def turn_off(self):\n+        \"\"\" turn the media player off. \"\"\"\n+        self._override_or_child_service(SERVICE_TURN_OFF)\n+\n+    def mute_volume(self, is_volume_muted):\n+        \"\"\" mute the volume. \"\"\"\n+        data = {ATTR_MEDIA_VOLUME_MUTED: is_volume_muted}\n+        self._override_or_child_service(SERVICE_VOLUME_MUTE, **data)\n+\n+    def set_volume_level(self, volume_level):\n+        \"\"\" set volume level, range 0..1. \"\"\"\n+        data = {ATTR_MEDIA_VOLUME_LEVEL: volume_level}\n+        self._child_service(SERVICE_VOLUME_SET, **data)\n+\n+    def media_play(self):\n+        \"\"\" Send play commmand. \"\"\"\n+        self._child_service(SERVICE_MEDIA_PLAY)\n+\n+    def media_pause(self):\n+        \"\"\" Send pause command. \"\"\"\n+        self._child_service(SERVICE_MEDIA_PAUSE)\n+\n+    def media_previous_track(self):\n+        \"\"\" Send previous track command. \"\"\"\n+        self._child_service(SERVICE_MEDIA_PREVIOUS_TRACK)\n+\n+    def media_next_track(self):\n+        \"\"\" Send next track command. \"\"\"\n+        self._child_service(SERVICE_MEDIA_NEXT_TRACK)\n+\n+    def media_seek(self, position):\n+        \"\"\" Send seek command. \"\"\"\n+        data = {ATTR_MEDIA_SEEK_POSITION: position}\n+        self._child_service(SERVICE_MEDIA_SEEK, **data)\n+\n+    def play_youtube(self, media_id):\n+        \"\"\" Plays a YouTube media. \"\"\"\n+        self._child_service(SERVICE_YOUTUBE_VIDEO, media_id=media_id)","path":"homeassistant/components/media_player/universal.py","position":null,"original_position":412,"commit_id":"122724270b22fd3ac7952cbe30921656c4229279","original_commit_id":"5335b54f6213db5dfa081a2ee74a01fff6ac3d61","user":{"login":"rmkraus","id":4956442,"avatar_url":"https://avatars.githubusercontent.com/u/4956442?v=3","gravatar_id":"","url":"https://api.github.com/users/rmkraus","html_url":"https://github.com/rmkraus","followers_url":"https://api.github.com/users/rmkraus/followers","following_url":"https://api.github.com/users/rmkraus/following{/other_user}","gists_url":"https://api.github.com/users/rmkraus/gists{/gist_id}","starred_url":"https://api.github.com/users/rmkraus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rmkraus/subscriptions","organizations_url":"https://api.github.com/users/rmkraus/orgs","repos_url":"https://api.github.com/users/rmkraus/repos","events_url":"https://api.github.com/users/rmkraus/events{/privacy}","received_events_url":"https://api.github.com/users/rmkraus/received_events","type":"User","site_admin":false},"body":"I couldn't find ATTR constants and I think the cast component hardcoded those parameter names as well. Would you like me to correct this for cast and universal?","created_at":"2016-01-12T06:52:24Z","updated_at":"2016-01-12T06:52:24Z","html_url":"https://github.com/balloob/home-assistant/pull/871#discussion_r49420473","pull_request_url":"https://api.github.com/repos/balloob/home-assistant/pulls/871","_links":{"self":{"href":"https://api.github.com/repos/balloob/home-assistant/pulls/comments/49420473"},"html":{"href":"https://github.com/balloob/home-assistant/pull/871#discussion_r49420473"},"pull_request":{"href":"https://api.github.com/repos/balloob/home-assistant/pulls/871"}}},"pull_request":{"url":"https://api.github.com/repos/balloob/home-assistant/pulls/871","id":55558472,"html_url":"https://github.com/balloob/home-assistant/pull/871","diff_url":"https://github.com/balloob/home-assistant/pull/871.diff","patch_url":"https://github.com/balloob/home-assistant/pull/871.patch","issue_url":"https://api.github.com/repos/balloob/home-assistant/issues/871","number":871,"state":"open","locked":false,"title":"Universal media player","user":{"login":"rmkraus","id":4956442,"avatar_url":"https://avatars.githubusercontent.com/u/4956442?v=3","gravatar_id":"","url":"https://api.github.com/users/rmkraus","html_url":"https://github.com/rmkraus","followers_url":"https://api.github.com/users/rmkraus/followers","following_url":"https://api.github.com/users/rmkraus/following{/other_user}","gists_url":"https://api.github.com/users/rmkraus/gists{/gist_id}","starred_url":"https://api.github.com/users/rmkraus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rmkraus/subscriptions","organizations_url":"https://api.github.com/users/rmkraus/orgs","repos_url":"https://api.github.com/users/rmkraus/repos","events_url":"https://api.github.com/users/rmkraus/events{/privacy}","received_events_url":"https://api.github.com/users/rmkraus/received_events","type":"User","site_admin":false},"body":"Created the initial implementation of the Universal Media Player. This is not quite done, but I wanted to get feedback before I wrapped things up.\r\n\r\nCurrently, the configuration for this component looks like this:\r\n```yaml\r\nmedia_player:\r\n  platform: universal\r\n  name: MEDIA PLAYER NAME\r\n  children:\r\n    - media_player.cast\r\n    - media_player.roku\r\n  commands:\r\n    turn_on:\r\n       service: SERVICE\r\n       service_data: SERVICE_DATA\r\n    turn_off: \r\n       service: SERVICE\r\n       service_data: SERVICE_DATA\r\n    volume_up: \r\n       service: SERVICE\r\n       service_data: SERVICE_DATA\r\n    volume_down: \r\n       service: SERVICE\r\n       service_data: SERVICE_DATA\r\n    volume_mute: \r\n       service: SERVICE\r\n       service_data: SERVICE_DATA\r\n  attributes:\r\n    is_volume_muted: ENTITY_ID|ATTRIBUTE\r\n    state: ENTITY_ID|ATTRIBUTE \r\n```\r\nwhere `commands` and `attributes` are all optional.\r\n\r\nAt its core, it is just a collection of child media players. It will report the status from and operate the first one in the list that is active (not off and not idle). To allow better control of external devices, the volume and power commands can be overwritten. If turn_on and turn_off are overwritten, the state attribute can be overwritten in some instances. The following is the logic used to determine the state if it is \"overwritten\":\r\n\r\n```\r\n     off if master state is off                                                  \r\nELSE Status of first active child if there is an active child\r\nELSE master state if defined\r\nELSE off\r\n```\r\n\r\nThis will still require an update to the frontend to add volume buttons with the support flag. I would also like to have this component update itself when any one of its children is updated. Other than that, this is basically it.","created_at":"2016-01-10T05:39:26Z","updated_at":"2016-01-12T06:52:24Z","closed_at":null,"merged_at":null,"merge_commit_sha":"d9f41dfe6be53fa86be3bc992fdc40872780cf55","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/balloob/home-assistant/pulls/871/commits","review_comments_url":"https://api.github.com/repos/balloob/home-assistant/pulls/871/comments","review_comment_url":"https://api.github.com/repos/balloob/home-assistant/pulls/comments{/number}","comments_url":"https://api.github.com/repos/balloob/home-assistant/issues/871/comments","statuses_url":"https://api.github.com/repos/balloob/home-assistant/statuses/122724270b22fd3ac7952cbe30921656c4229279","head":{"label":"rmkraus:universal-media-player","ref":"universal-media-player","sha":"122724270b22fd3ac7952cbe30921656c4229279","user":{"login":"rmkraus","id":4956442,"avatar_url":"https://avatars.githubusercontent.com/u/4956442?v=3","gravatar_id":"","url":"https://api.github.com/users/rmkraus","html_url":"https://github.com/rmkraus","followers_url":"https://api.github.com/users/rmkraus/followers","following_url":"https://api.github.com/users/rmkraus/following{/other_user}","gists_url":"https://api.github.com/users/rmkraus/gists{/gist_id}","starred_url":"https://api.github.com/users/rmkraus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rmkraus/subscriptions","organizations_url":"https://api.github.com/users/rmkraus/orgs","repos_url":"https://api.github.com/users/rmkraus/repos","events_url":"https://api.github.com/users/rmkraus/events{/privacy}","received_events_url":"https://api.github.com/users/rmkraus/received_events","type":"User","site_admin":false},"repo":{"id":46207614,"name":"home-assistant","full_name":"rmkraus/home-assistant","owner":{"login":"rmkraus","id":4956442,"avatar_url":"https://avatars.githubusercontent.com/u/4956442?v=3","gravatar_id":"","url":"https://api.github.com/users/rmkraus","html_url":"https://github.com/rmkraus","followers_url":"https://api.github.com/users/rmkraus/followers","following_url":"https://api.github.com/users/rmkraus/following{/other_user}","gists_url":"https://api.github.com/users/rmkraus/gists{/gist_id}","starred_url":"https://api.github.com/users/rmkraus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rmkraus/subscriptions","organizations_url":"https://api.github.com/users/rmkraus/orgs","repos_url":"https://api.github.com/users/rmkraus/repos","events_url":"https://api.github.com/users/rmkraus/events{/privacy}","received_events_url":"https://api.github.com/users/rmkraus/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/rmkraus/home-assistant","description":"Open-source home automation platform running on Python 3","fork":true,"url":"https://api.github.com/repos/rmkraus/home-assistant","forks_url":"https://api.github.com/repos/rmkraus/home-assistant/forks","keys_url":"https://api.github.com/repos/rmkraus/home-assistant/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rmkraus/home-assistant/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rmkraus/home-assistant/teams","hooks_url":"https://api.github.com/repos/rmkraus/home-assistant/hooks","issue_events_url":"https://api.github.com/repos/rmkraus/home-assistant/issues/events{/number}","events_url":"https://api.github.com/repos/rmkraus/home-assistant/events","assignees_url":"https://api.github.com/repos/rmkraus/home-assistant/assignees{/user}","branches_url":"https://api.github.com/repos/rmkraus/home-assistant/branches{/branch}","tags_url":"https://api.github.com/repos/rmkraus/home-assistant/tags","blobs_url":"https://api.github.com/repos/rmkraus/home-assistant/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rmkraus/home-assistant/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rmkraus/home-assistant/git/refs{/sha}","trees_url":"https://api.github.com/repos/rmkraus/home-assistant/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rmkraus/home-assistant/statuses/{sha}","languages_url":"https://api.github.com/repos/rmkraus/home-assistant/languages","stargazers_url":"https://api.github.com/repos/rmkraus/home-assistant/stargazers","contributors_url":"https://api.github.com/repos/rmkraus/home-assistant/contributors","subscribers_url":"https://api.github.com/repos/rmkraus/home-assistant/subscribers","subscription_url":"https://api.github.com/repos/rmkraus/home-assistant/subscription","commits_url":"https://api.github.com/repos/rmkraus/home-assistant/commits{/sha}","git_commits_url":"https://api.github.com/repos/rmkraus/home-assistant/git/commits{/sha}","comments_url":"https://api.github.com/repos/rmkraus/home-assistant/comments{/number}","issue_comment_url":"https://api.github.com/repos/rmkraus/home-assistant/issues/comments{/number}","contents_url":"https://api.github.com/repos/rmkraus/home-assistant/contents/{+path}","compare_url":"https://api.github.com/repos/rmkraus/home-assistant/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rmkraus/home-assistant/merges","archive_url":"https://api.github.com/repos/rmkraus/home-assistant/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rmkraus/home-assistant/downloads","issues_url":"https://api.github.com/repos/rmkraus/home-assistant/issues{/number}","pulls_url":"https://api.github.com/repos/rmkraus/home-assistant/pulls{/number}","milestones_url":"https://api.github.com/repos/rmkraus/home-assistant/milestones{/number}","notifications_url":"https://api.github.com/repos/rmkraus/home-assistant/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rmkraus/home-assistant/labels{/name}","releases_url":"https://api.github.com/repos/rmkraus/home-assistant/releases{/id}","created_at":"2015-11-15T06:43:09Z","updated_at":"2016-01-09T20:26:01Z","pushed_at":"2016-01-12T06:50:09Z","git_url":"git://github.com/rmkraus/home-assistant.git","ssh_url":"git@github.com:rmkraus/home-assistant.git","clone_url":"https://github.com/rmkraus/home-assistant.git","svn_url":"https://github.com/rmkraus/home-assistant","homepage":"https://home-assistant.io","size":28026,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"dev"}},"base":{"label":"balloob:dev","ref":"dev","sha":"ad931eac60bef00620cc58be9918fab0cc5de389","user":{"login":"balloob","id":1444314,"avatar_url":"https://avatars.githubusercontent.com/u/1444314?v=3","gravatar_id":"","url":"https://api.github.com/users/balloob","html_url":"https://github.com/balloob","followers_url":"https://api.github.com/users/balloob/followers","following_url":"https://api.github.com/users/balloob/following{/other_user}","gists_url":"https://api.github.com/users/balloob/gists{/gist_id}","starred_url":"https://api.github.com/users/balloob/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/balloob/subscriptions","organizations_url":"https://api.github.com/users/balloob/orgs","repos_url":"https://api.github.com/users/balloob/repos","events_url":"https://api.github.com/users/balloob/events{/privacy}","received_events_url":"https://api.github.com/users/balloob/received_events","type":"User","site_admin":false},"repo":{"id":12888993,"name":"home-assistant","full_name":"balloob/home-assistant","owner":{"login":"balloob","id":1444314,"avatar_url":"https://avatars.githubusercontent.com/u/1444314?v=3","gravatar_id":"","url":"https://api.github.com/users/balloob","html_url":"https://github.com/balloob","followers_url":"https://api.github.com/users/balloob/followers","following_url":"https://api.github.com/users/balloob/following{/other_user}","gists_url":"https://api.github.com/users/balloob/gists{/gist_id}","starred_url":"https://api.github.com/users/balloob/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/balloob/subscriptions","organizations_url":"https://api.github.com/users/balloob/orgs","repos_url":"https://api.github.com/users/balloob/repos","events_url":"https://api.github.com/users/balloob/events{/privacy}","received_events_url":"https://api.github.com/users/balloob/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/balloob/home-assistant","description":"Open-source home automation platform running on Python 3","fork":false,"url":"https://api.github.com/repos/balloob/home-assistant","forks_url":"https://api.github.com/repos/balloob/home-assistant/forks","keys_url":"https://api.github.com/repos/balloob/home-assistant/keys{/key_id}","collaborators_url":"https://api.github.com/repos/balloob/home-assistant/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/balloob/home-assistant/teams","hooks_url":"https://api.github.com/repos/balloob/home-assistant/hooks","issue_events_url":"https://api.github.com/repos/balloob/home-assistant/issues/events{/number}","events_url":"https://api.github.com/repos/balloob/home-assistant/events","assignees_url":"https://api.github.com/repos/balloob/home-assistant/assignees{/user}","branches_url":"https://api.github.com/repos/balloob/home-assistant/branches{/branch}","tags_url":"https://api.github.com/repos/balloob/home-assistant/tags","blobs_url":"https://api.github.com/repos/balloob/home-assistant/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/balloob/home-assistant/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/balloob/home-assistant/git/refs{/sha}","trees_url":"https://api.github.com/repos/balloob/home-assistant/git/trees{/sha}","statuses_url":"https://api.github.com/repos/balloob/home-assistant/statuses/{sha}","languages_url":"https://api.github.com/repos/balloob/home-assistant/languages","stargazers_url":"https://api.github.com/repos/balloob/home-assistant/stargazers","contributors_url":"https://api.github.com/repos/balloob/home-assistant/contributors","subscribers_url":"https://api.github.com/repos/balloob/home-assistant/subscribers","subscription_url":"https://api.github.com/repos/balloob/home-assistant/subscription","commits_url":"https://api.github.com/repos/balloob/home-assistant/commits{/sha}","git_commits_url":"https://api.github.com/repos/balloob/home-assistant/git/commits{/sha}","comments_url":"https://api.github.com/repos/balloob/home-assistant/comments{/number}","issue_comment_url":"https://api.github.com/repos/balloob/home-assistant/issues/comments{/number}","contents_url":"https://api.github.com/repos/balloob/home-assistant/contents/{+path}","compare_url":"https://api.github.com/repos/balloob/home-assistant/compare/{base}...{head}","merges_url":"https://api.github.com/repos/balloob/home-assistant/merges","archive_url":"https://api.github.com/repos/balloob/home-assistant/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/balloob/home-assistant/downloads","issues_url":"https://api.github.com/repos/balloob/home-assistant/issues{/number}","pulls_url":"https://api.github.com/repos/balloob/home-assistant/pulls{/number}","milestones_url":"https://api.github.com/repos/balloob/home-assistant/milestones{/number}","notifications_url":"https://api.github.com/repos/balloob/home-assistant/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/balloob/home-assistant/labels{/name}","releases_url":"https://api.github.com/repos/balloob/home-assistant/releases{/id}","created_at":"2013-09-17T07:29:48Z","updated_at":"2016-01-11T21:52:11Z","pushed_at":"2016-01-12T06:50:10Z","git_url":"git://github.com/balloob/home-assistant.git","ssh_url":"git@github.com:balloob/home-assistant.git","clone_url":"https://github.com/balloob/home-assistant.git","svn_url":"https://github.com/balloob/home-assistant","homepage":"https://home-assistant.io","size":35809,"stargazers_count":2555,"watchers_count":2555,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":381,"mirror_url":null,"open_issues_count":114,"forks":381,"open_issues":114,"watchers":2555,"default_branch":"dev"}},"_links":{"self":{"href":"https://api.github.com/repos/balloob/home-assistant/pulls/871"},"html":{"href":"https://github.com/balloob/home-assistant/pull/871"},"issue":{"href":"https://api.github.com/repos/balloob/home-assistant/issues/871"},"comments":{"href":"https://api.github.com/repos/balloob/home-assistant/issues/871/comments"},"review_comments":{"href":"https://api.github.com/repos/balloob/home-assistant/pulls/871/comments"},"review_comment":{"href":"https://api.github.com/repos/balloob/home-assistant/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/balloob/home-assistant/pulls/871/commits"},"statuses":{"href":"https://api.github.com/repos/balloob/home-assistant/statuses/122724270b22fd3ac7952cbe30921656c4229279"}}}}