Skip to content

Instantly share code, notes, and snippets.

@runningman84
Last active July 31, 2017 13:24
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save runningman84/5464ec4dc39b10efd10828ca7cef25d0 to your computer and use it in GitHub Desktop.
Save runningman84/5464ec4dc39b10efd10828ca7cef25d0 to your computer and use it in GitHub Desktop.
musiccast component
state output:
media_player.box playing device_id: 00A0DEDFFF
entity_picture: /api/media_player_proxy/media_player.box?token=a61e8aad579a1a58701f3ba722491e8605290ea7e4be7f88b771af6b7a57198c&cache=a50da
friendly_name: Box
is_volume_muted: false
media_album_name: Märchen
media_artist: toksi
media_content_type: music
media_title: Märchen
model_name: WX-030
source: spotify
source_list: napster,spotify,juke,tidal,deezer,airplay,mc_link,server,net_radio,bluetooth
supported_media_commands: 0
"""
Support for Musiccast Receivers.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/media_player.yamaha/
"""
import logging
import voluptuous as vol
from homeassistant.components.media_player import (
SUPPORT_TURN_OFF, SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET,
SUPPORT_SELECT_SOURCE, SUPPORT_PLAY_MEDIA, SUPPORT_PAUSE, SUPPORT_STOP,
SUPPORT_NEXT_TRACK, SUPPORT_PREVIOUS_TRACK, SUPPORT_PLAY,
MEDIA_TYPE_MUSIC,
MediaPlayerDevice, PLATFORM_SCHEMA)
from homeassistant.const import (CONF_NAME, CONF_HOST, STATE_OFF, STATE_ON,
STATE_PLAYING, STATE_PAUSED, STATE_IDLE)
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['pyamaha==0.1']
_LOGGER = logging.getLogger(__name__)
SUPPORT_YAMAHA = SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \
SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_SELECT_SOURCE | SUPPORT_PLAY
CONF_SOURCE_NAMES = 'source_names'
CONF_SOURCE_IGNORE = 'source_ignore'
CONF_ZONE_IGNORE = 'zone_ignore'
DEFAULT_NAME = 'Musiccast Receiver'
KNOWN = 'yamaha_known_receivers'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_HOST): cv.string,
vol.Optional(CONF_SOURCE_IGNORE, default=[]):
vol.All(cv.ensure_list, [cv.string]),
vol.Optional(CONF_ZONE_IGNORE, default=[]):
vol.All(cv.ensure_list, [cv.string]),
vol.Optional(CONF_SOURCE_NAMES, default={}): {cv.string: cv.string},
})
from pyamaha import RESPONSE_CODE
from pyamaha import Device, System, Zone, NetUSB, Tuner
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Musiccast platform."""
name = config.get(CONF_NAME)
host = config.get(CONF_HOST)
source_ignore = config.get(CONF_SOURCE_IGNORE)
source_names = config.get(CONF_SOURCE_NAMES)
# zone_ignore = config.get(CONF_ZONE_IGNORE)
if discovery_info:
_LOGGER.debug('%s', discovery_info)
host = discovery_info.get('host')
device = Device(host)
add_devices(
[MusiccastDevice(name, device, source_ignore, source_names)])
return True
device = Device(host)
add_devices([MusiccastDevice(name, device, source_ignore, source_names)])
class MusiccastDevice(MediaPlayerDevice):
"""Representation of a Musiccast device."""
def __init__(self, name, device, source_ignore, source_names):
"""Initialize the Musiccast Receiver."""
self._device = device
self._muted = False
self._volume = 0
self._volume_min = 0
self._volume_max = 0
self._volume_step = 1
self._pwstate = STATE_OFF
self._zone = 'main'
self._current_source = None
self._current_module = None
self._module_mapping = {}
self._source_list = None
self._source_ignore = source_ignore or []
self._source_names = source_names or {}
self._reverse_mapping = None
self._play_status = None
self._media_artist = None
self._media_album = None
self._media_track = None
self._media_albumart_url = None
self._name = name
self._model_name = None
self._device_id = None
self._system_id = None
self._system_version = None
self._api_version = None
self._netmodule_version = None
self.update()
def update(self):
"""Get the latest details from the device."""
try:
self._update_sytem_device_info()
self._update_sytem_features()
self._update_zone()
if(self._current_module == 'netusb'):
self._update_netusb()
elif(self._current_module == 'tuner'):
self._update_tuner()
else:
self._reset_media_info()
_LOGGER.debug("Device is running unknown module %s",
self._current_module)
self.schedule_update_ha_state()
except Exception:
_LOGGER.warning(
"Failed to execute %s on %s", "update", self._name)
self._pwstate = STATE_OFF
def _update_sytem_device_info(self):
res = self._proxy_request(System.get_device_info())
self._model_name = res['model_name']
self._device_id = res['device_id']
self._system_id = res['system_id']
self._system_version = res['system_version']
self._api_version = res['api_version']
self._netmodule_version = res['netmodule_version']
self._device_id = res['device_id']
def _update_sytem_features(self):
res = self._proxy_request(System.get_features())
for item in res["system"]["input_list"]:
self._module_mapping[item['id']] = item['play_info_type']
self._source_list = res["zone"][0]["input_list"]
for range_step in res["zone"][0]["range_step"]:
if range_step['id'] != 'volume':
continue
self._volume_min = range_step['min']
self._volume_max = range_step['max']
if range_step['step'] != None:
self._volume_step = range_step['step']
def _update_zone(self):
res = self._proxy_request(Zone.get_status(self._zone))
if self._current_source != res['input']:
self._reset_media_info()
self._current_module = self._module_mapping[res['input']]
self._current_source = res['input']
self._volume = res['volume']
if res['power'] == 'standby':
self._pwstate = STATE_IDLE
else:
self._pwstate = STATE_ON
def _update_netusb(self):
res = self._proxy_request(NetUSB.get_play_info())
if res['artist'] != '':
self._media_artist = res['artist']
else:
self._media_artist = None
if res['album'] != '':
self._media_album = res['album']
else:
self._media_album = None
if res['track'] != '':
self._media_track = res['track']
else:
self._media_track = None
if res['albumart_url'] != '':
self._media_albumart_url = res['albumart_url']
else:
self._media_albumart_url = None
if res['playback'] == 'pause':
self._pwstate = STATE_PAUSED
elif res['playback'] == 'play':
self._pwstate = STATE_PLAYING
else:
self._pwstate = STATE_ON
def _update_tuner(self):
res = self._proxy_request(Tuner.get_play_info())
if res['rds']['program_service'] != '':
self._media_artist = res['rds']['program_service']
else:
self._media_artist = None
self._pwstate = STATE_PLAYING
def _reset_media_info(self):
self._media_artist = None
self._media_album = None
self._media_track = None
self._media_albumart_url = None
def _proxy_request(self, *args):
if isinstance(args[0], str):
# get request
_LOGGER.debug("Sending get request to {}:\n{}".format(
self._device.ip, args[0]))
res = self._device.request(args[0])
else:
# post request
_LOGGER.debug("Sending post request to {}:\n{}".format(
self._device.ip, args[0]))
res = self._device.request(*(args[0]))
try:
res_json = res.json()
_LOGGER.debug("Got response from {}:\n{}".format(
self._device.ip, res_json))
response_code = res_json['response_code']
except Exception:
raise('Could not parse response')
if response_code > 0:
raise Exception(RESPONSE_CODE[response_code])
return res_json
@property
def name(self):
"""Return the name of the device."""
name = self._name
# if self._zone != "Main_Zone":
# Zone will be one of Main_Zone, Zone_2, Zone_3
# name += " " + self._zone.replace('_', ' ')
return name
@property
def state(self):
"""Return the state of the device."""
return self._pwstate
@property
def volume_level(self):
"""Volume level of the media player (0..1)."""
if self._volume > 0:
return round(self._volume / self._volume_max, 2)
else:
return 0.0
@property
def is_volume_muted(self):
"""Boolean if volume is currently muted."""
return self._muted
@property
def source(self):
"""Return the current input source."""
return self._current_source
@property
def source_list(self):
"""List of available input sources."""
return self._source_list
@property
def supported_features(self):
"""Flag media player features that are supported."""
return SUPPORT_YAMAHA
def turn_off(self):
"""Turn off media player."""
self._proxy_request(Zone.set_power(self._zone, "toggle"))
self._pwstate = STATE_OFF
def set_volume_level(self, volume):
"""Set volume level, range 0..1."""
receiver_vol = int(volume * self._volume_max)
_LOGGER.debug("Setting zone {} volume to {} with step {}".format(
self._zone, receiver_vol, self._volume_step))
self._proxy_request(Zone.set_volume(
self._zone, receiver_vol, self._volume_step))
self._volume = receiver_vol
def mute_volume(self, mute):
"""Mute (true) or unmute (false) media player."""
self._proxy_request(Zone.set_mute(self._zone, mute))
self._receiver.mute = mute
def turn_on(self):
"""Turn the media player on."""
self._proxy_request(Zone.set_power(self._zone, "on"))
def media_play(self):
"""Send play commmand."""
self._proxy_request(NetUSB.set_playback("play"))
def media_pause(self):
"""Send pause command."""
self._proxy_request(NetUSB.set_playback("play_pause"))
def media_stop(self):
"""Send stop command."""
self._proxy_request(NetUSB.set_playback("stop"))
def media_previous_track(self):
"""Send previous track command."""
self._proxy_request(NetUSB.set_playback("previous"))
def media_next_track(self):
"""Send next track command."""
self._proxy_request(NetUSB.set_playback("next"))
def select_source(self, source):
"""Select input source."""
self._proxy_request(Zone.set_input(
self._zone, source, 'autoplay_disabled'))
self._current_source = source
def play_media(self, media_type, media_id, **kwargs):
"""Play media from an ID.
This exposes a pass through for various input sources in the
Musiccast to direct play certain kinds of media. media_type is
treated as the input type that we are setting, and media id is
specific to it.
"""
# if media_type == "NET RADIO":
# self._receiver.net_radio(media_id)
@property
def media_artist(self):
"""Artist of current playing media."""
return self._media_artist
@property
def media_album_name(self):
"""Album of current playing media."""
return self._media_album
@property
def media_image_url(self):
"""Album of current playing media."""
if self._media_albumart_url is not None:
return "http://{}{}".format(self._device.ip, self._media_albumart_url)
@property
def media_content_type(self):
"""Content type of current playing media."""
return MEDIA_TYPE_MUSIC
# if self.source() in ['av1', 'av2', 'av3', 'av4', 'hdmi1', 'hdmi2', 'hdmi3', 'hdmi4']:
# return MEDIA_TYPE_VIDEO
# else:
# return MEDIA_TYPE_MUSIC
@property
def media_title(self):
"""Artist of current playing media."""
return self._media_track
@property
def device_state_attributes(self):
"""Return the state attributes."""
return {
'model_name': self._model_name,
'device_id': self._device_id,
'device_ip': self._device.ip,
'system_id': self._system_id,
'system_version': self._system_version,
'api_version': self._api_version,
'netmodule_version': self._netmodule_version
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment