Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a component for Sisyphus Kinetic Art Tables #14472

Merged
merged 11 commits into from
Jul 29, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,9 @@ omit =
homeassistant/components/scsgate.py
homeassistant/components/*/scsgate.py

homeassistant/components/sisyphus.py
homeassistant/components/*/sisyphus.py

homeassistant/components/skybell.py
homeassistant/components/*/skybell.py

Expand Down
78 changes: 78 additions & 0 deletions homeassistant/components/light/sisyphus.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"""
Support for the light on the Sisyphus Kinetic Art Table.

For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/light.sisyphus/
"""
import logging

from homeassistant.const import CONF_NAME
from homeassistant.components.light import SUPPORT_BRIGHTNESS, Light
from homeassistant.components.sisyphus import DATA_SISYPHUS

_LOGGER = logging.getLogger(__name__)

DEPENDENCIES = ['sisyphus']

SUPPORTED_FEATURES = SUPPORT_BRIGHTNESS


def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up a single Sisyphus table."""
name = discovery_info[CONF_NAME]
add_devices(
[SisyphusLight(name, hass.data[DATA_SISYPHUS][name])],
update_before_add=True)


class SisyphusLight(Light):
"""Represents a Sisyphus table as a light."""

def __init__(self, name, table):
"""
Constructor.

:param name: name of the table
:param table: sisyphus-control Table object
"""
self._name = name
self._table = table

async def async_added_to_hass(self):
"""Add listeners after this object has been initialized."""
self._table.add_listener(
lambda: self.async_schedule_update_ha_state(False))

@property
def name(self):
"""Return the ame of the table."""
return self._name

@property
def is_on(self):
"""Return True if the table is on."""
return not self._table.is_sleeping

@property
def brightness(self):
"""Return the current brightness of the table's ring light."""
return self._table.brightness * 255

@property
def supported_features(self):
"""Return the features supported by the table; i.e. brightness."""
return SUPPORTED_FEATURES

async def async_turn_off(self, **kwargs):
"""Put the table to sleep."""
await self._table.sleep()
_LOGGER.debug("Sisyphus table %s: sleep")

async def async_turn_on(self, **kwargs):
"""Wake up the table if necessary, optionally changes brightness."""
if not self.is_on:
await self._table.wakeup()
_LOGGER.debug("Sisyphus table %s: wakeup")

if "brightness" in kwargs:
await self._table.set_brightness(kwargs["brightness"] / 255.0)
197 changes: 197 additions & 0 deletions homeassistant/components/media_player/sisyphus.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
"""
Support for track controls on the Sisyphus Kinetic Art Table.

For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/media_player.sisyphus/
"""
import logging

from homeassistant.components.media_player import (
SUPPORT_NEXT_TRACK,
SUPPORT_PAUSE,
SUPPORT_PLAY,
SUPPORT_PREVIOUS_TRACK,
SUPPORT_SHUFFLE_SET,
SUPPORT_TURN_OFF,
SUPPORT_TURN_ON,
SUPPORT_VOLUME_MUTE,
SUPPORT_VOLUME_SET,
MediaPlayerDevice)
from homeassistant.components.sisyphus import DATA_SISYPHUS
from homeassistant.const import CONF_HOST, CONF_NAME, STATE_PLAYING, \
STATE_PAUSED, STATE_IDLE, STATE_OFF

_LOGGER = logging.getLogger(__name__)

DEPENDENCIES = ['sisyphus']

MEDIA_TYPE_TRACK = "sisyphus_track"

SUPPORTED_FEATURES = SUPPORT_VOLUME_MUTE \
| SUPPORT_VOLUME_SET \
| SUPPORT_TURN_OFF \
| SUPPORT_TURN_ON \
| SUPPORT_PAUSE \
| SUPPORT_SHUFFLE_SET \
| SUPPORT_PREVIOUS_TRACK \
| SUPPORT_NEXT_TRACK \
| SUPPORT_PLAY


# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up a media player entity for a Sisyphus table."""
name = discovery_info[CONF_NAME]
host = discovery_info[CONF_HOST]
add_devices(
[SisyphusPlayer(name, host, hass.data[DATA_SISYPHUS][name])],
update_before_add=True)


class SisyphusPlayer(MediaPlayerDevice):
"""Represents a single Sisyphus table as a media player device."""

def __init__(self, name, host, table):
"""
Constructor.

:param name: name of the table
:param host: hostname or ip address
:param table: sisyphus-control Table object
"""
self._name = name
self._host = host
self._table = table

async def async_added_to_hass(self):
"""Add listeners after this object has been initialized."""
self._table.add_listener(
lambda: self.async_schedule_update_ha_state(False))

@property
def name(self):
"""Return the name of the table."""
return self._name

@property
def state(self):
"""Return the current state of the table; sleeping maps to off."""
if self._table.state in ["homing", "playing"]:
return STATE_PLAYING
if self._table.state == "paused":
if self._table.is_sleeping:
return STATE_OFF

return STATE_PAUSED
if self._table.state == "waiting":
return STATE_IDLE

return None

@property
def volume_level(self):
"""Return the current playback speed (0..1)."""
return self._table.speed

@property
def shuffle(self):
"""Return True if the current playlist is in shuffle mode."""
return self._table.is_shuffle

async def async_set_shuffle(self, shuffle):
"""
Change the shuffle mode of the current playlist.

:param shuffle: True to shuffle, False not to
"""
await self._table.set_shuffle(shuffle)

@property
def media_playlist(self):
"""Return the name of the current playlist."""
return self._table.active_playlist.name \
if self._table.active_playlist \
else None

@property
def media_title(self):
"""Return the title of the current track."""
return self._table.active_track.name \
if self._table.active_track \
else None

@property
def media_content_type(self):
"""Return the content type currently playing; i.e. a Sisyphus track."""
return MEDIA_TYPE_TRACK

@property
def media_content_id(self):
"""Return the track ID of the current track."""
return self._table.active_track.id \
if self._table.active_track \
else None

@property
def supported_features(self):
"""Return the features supported by this table."""
return SUPPORTED_FEATURES

@property
def media_image_url(self):
"""Return the URL for a thumbnail image of the current track."""
from sisyphus_control import Track
if self._table.active_track:
return self._table.active_track.get_thumbnail_url(
Track.ThumbnailSize.LARGE)

return super.media_image_url()

async def async_turn_on(self):
"""Wake up a sleeping table."""
await self._table.wakeup()

async def async_turn_off(self):
"""Put the table to sleep."""
await self._table.sleep()

async def async_volume_down(self):
"""Slow down playback."""
await self._table.set_speed(max(0, self._table.speed - 0.1))

async def async_volume_up(self):
"""Speed up playback."""
await self._table.set_speed(min(1.0, self._table.speed + 0.1))

async def async_set_volume_level(self, volume):
"""Set playback speed (0..1)."""
await self._table.set_speed(volume)

async def async_media_play(self):
"""Start playing."""
await self._table.play()

async def async_media_pause(self):
"""Pause."""
await self._table.pause()

async def async_media_next_track(self):
"""Skip to next track."""
cur_track_index = self._get_current_track_index()

await self._table.active_playlist.play(
self._table.active_playlist.tracks[cur_track_index + 1])

async def async_media_previous_track(self):
"""Skip to previous track."""
cur_track_index = self._get_current_track_index()

await self._table.active_playlist.play(
self._table.active_playlist.tracks[cur_track_index - 1])

def _get_current_track_index(self):
for index, track in enumerate(self._table.active_playlist.tracks):
if track.id == self._table.active_track.id:
return index

return -1
84 changes: 84 additions & 0 deletions homeassistant/components/sisyphus.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
"""
Support for controlling Sisyphus Kinetic Art Tables.

For more details about this component, please refer to the documentation at
https://home-assistant.io/components/sisyphus/
"""
import asyncio
import logging

import voluptuous as vol

from homeassistant.const import (
CONF_HOST,
CONF_NAME,
EVENT_HOMEASSISTANT_STOP
)
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.discovery import async_load_platform

REQUIREMENTS = ['sisyphus-control==2.1']

_LOGGER = logging.getLogger(__name__)

DATA_SISYPHUS = 'sisyphus'
DOMAIN = 'sisyphus'

AUTODETECT_SCHEMA = vol.Schema({})

TABLE_SCHEMA = vol.Schema({
vol.Required(CONF_NAME): cv.string,
vol.Required(CONF_HOST): cv.string,
})

TABLES_SCHEMA = vol.Schema([TABLE_SCHEMA])

CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Any(AUTODETECT_SCHEMA, TABLES_SCHEMA),
}, extra=vol.ALLOW_EXTRA)


async def async_setup(hass, config):
"""Set up the sisyphus component."""
from sisyphus_control import Table
tables = hass.data.setdefault(DATA_SISYPHUS, {})
table_configs = config.get(DOMAIN)
session = async_get_clientsession(hass)

async def add_table(host, name=None):
"""Add platforms for a single table with the given hostname."""
table = await Table.connect(host, session)
if name is None:
name = table.name
tables[name] = table
_LOGGER.debug("Connected to %s at %s", name, host)

hass.async_add_job(async_load_platform(
hass, 'light', DOMAIN, {
CONF_NAME: name,
}, config
))
hass.async_add_job(async_load_platform(
hass, 'media_player', DOMAIN, {
CONF_NAME: name,
CONF_HOST: host,
}, config
))

if isinstance(table_configs, dict): # AUTODETECT_SCHEMA
for ip_address in await Table.find_table_ips(session):
await add_table(ip_address)
else: # TABLES_SCHEMA
for conf in table_configs:
await add_table(conf[CONF_HOST], conf[CONF_NAME])

async def close_tables(*args):
"""Close all table objects."""
tasks = [table.close() for table in tables.values()]
if tasks:
await asyncio.wait(tasks)

hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, close_tables)

return True
3 changes: 3 additions & 0 deletions requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1243,6 +1243,9 @@ simplepush==1.1.4
# homeassistant.components.alarm_control_panel.simplisafe
simplisafe-python==1.0.5

# homeassistant.components.sisyphus
sisyphus-control==2.1

# homeassistant.components.skybell
skybellpy==0.1.2

Expand Down