-
-
Notifications
You must be signed in to change notification settings - Fork 38k
Add a component for Sisyphus Kinetic Art Tables #14472
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
Changes from 10 commits
6211c30
001b7db
a5c2101
af35c93
bfdeddf
78463c1
b605a7e
af670bb
383566b
bf3ac4d
b325ea9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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) |
| 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 | ||
| elif self._table.state == "paused": | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replace |
||
| if self._table.is_sleeping: | ||
| return STATE_OFF | ||
|
|
||
| return STATE_PAUSED | ||
| elif self._table.state == "waiting": | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See above. |
||
| 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 | ||
| 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/ | ||
| """ | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please follow the same style for the header as the other components do. |
||
| import asyncio | ||
| import logging | ||
|
|
||
| import voluptuous as vol | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a blank line between standard library and 3rd party imports. |
||
|
|
||
| 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 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Imports should be grouped -> https://developers.home-assistant.io/docs/en/development_guidelines.html |
||
|
|
||
| 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): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. expected 2 blank lines, found 1 |
||
| """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 | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expected 2 blank lines, found 1