New platform media_player/anthemav (#5146)
* Initial commit of anthemav platform. It loads but has no purpose. * Now presents a card in the UI but the values aren't real * Mute and volume polling/setting work now * Source lists and selection works now. * Reduce debug logging verbosity * Support power on/off and skip polling for details if power is off * Add some static tables to decode numerics from telnet commands * Add stub for unsupported media_play * New style anthemav uses native asyncio structure * Add device callback for asyncio * This is ugly but it works * Simplify async setup and abstract class data retrieval * Implement commands (power on and power off for now) * Add support for scan_interval and set default to 120 seconds * Pass-through to package handlers for volume and input selection * Slight restructuring to satisfy anthemav 0.9 * Load anthemav package from pypi now that it's registered * Proper app_name from a/v info * Mispelled word * media_player/anthemav initial commit of platform requirements * Philio 3-in-1 Gen 4 zwave sensor needs the no-off-event workaround. (#5120) * Add print_config_parameter service to Z-Wave (#5121) * Add print_config_param service to z-wave * Add print_config_parameter service to z-wave * Add print_config_parameter service to z-wave * Fix typos * Fix typos * Fix typo * Conform to Python/project style requirements * Making pylint happy * Bring pip requirements in agreement with the code * Bungled previous update * Remove unnecessady SCAN_INTERVAL logic I was unawre that this is performed as part of the normal platform behavior and it's unnecessary for a platform to independently implement this logic. * Refactor code based on @armills PR requests * Re-add media_play stub to avoid traceback * Align with platform reqirements * Remove references to SCAN_INTERVAL and clean up _lookup logic * Add DEFAULT_PORT assignment * Code style changes and removal of vestigial structures * CONF_NAME handling changes to allow local override to default from device * Address PR feedback from @balloob * Remove media_play function override It's no longer necesary for the platform to implement a stub media_play function override now that the Add SUPPORT_PLAY flag #5181 issue has been resolved and merged into the dev branch. * Rename callback function to async_ for clarity * Use async routines for platform methods * Convert update callback to coroutine for conformity Underlying anthemav library now properly supports coroutine callbacks instead of normal functions. Converted the platform callback to a coroutine for conformance with async operation for the device. Special thanks to @pvizeli and @armills for their invaluable remedial Python instruction! * Further callback refinements Altered the nature of callback handling based on suggestions from @pvizeli * True not needed for local push update_ha_state * Small style fixpull/5447/head
parent
3da25c227f
commit
1a82adb054
|
@ -201,6 +201,7 @@ omit =
|
|||
homeassistant/components/light/yeelight.py
|
||||
homeassistant/components/light/zengge.py
|
||||
homeassistant/components/lirc.py
|
||||
homeassistant/components/media_player/anthemav.py
|
||||
homeassistant/components/media_player/aquostv.py
|
||||
homeassistant/components/media_player/braviatv.py
|
||||
homeassistant/components/media_player/cast.py
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
"""
|
||||
Support for Anthem Network Receivers and Processors.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/media_player.anthemav/
|
||||
"""
|
||||
import logging
|
||||
import asyncio
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.media_player import (
|
||||
PLATFORM_SCHEMA, SUPPORT_TURN_OFF, SUPPORT_TURN_ON, SUPPORT_SELECT_SOURCE,
|
||||
SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET, MediaPlayerDevice)
|
||||
from homeassistant.const import (
|
||||
CONF_NAME, CONF_HOST, CONF_PORT, STATE_OFF, STATE_ON, STATE_UNKNOWN,
|
||||
EVENT_HOMEASSISTANT_STOP)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
REQUIREMENTS = ['anthemav==1.1.7']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DOMAIN = 'anthemav'
|
||||
|
||||
DEFAULT_PORT = 14999
|
||||
|
||||
SUPPORT_ANTHEMAV = SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \
|
||||
SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_SELECT_SOURCE
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_HOST): cv.string,
|
||||
vol.Optional(CONF_NAME): cv.string,
|
||||
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
|
||||
})
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
||||
"""Set up our socket to the AVR."""
|
||||
import anthemav
|
||||
|
||||
host = config.get(CONF_HOST)
|
||||
port = config.get(CONF_PORT)
|
||||
name = config.get(CONF_NAME)
|
||||
device = None
|
||||
|
||||
_LOGGER.info('Provisioning Anthem AVR device at %s:%d', host, port)
|
||||
|
||||
def async_anthemav_update_callback(message):
|
||||
"""Receive notification from transport that new data exists."""
|
||||
_LOGGER.info('Received update calback from AVR: %s', message)
|
||||
hass.async_add_job(device.async_update_ha_state())
|
||||
|
||||
avr = yield from anthemav.Connection.create(
|
||||
host=host, port=port, loop=hass.loop,
|
||||
update_callback=async_anthemav_update_callback)
|
||||
|
||||
device = AnthemAVR(avr, name)
|
||||
|
||||
_LOGGER.debug('dump_devicedata: '+device.dump_avrdata)
|
||||
_LOGGER.debug('dump_conndata: '+avr.dump_conndata)
|
||||
_LOGGER.debug('dump_rawdata: '+avr.protocol.dump_rawdata)
|
||||
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, device.avr.close)
|
||||
yield from async_add_devices([device])
|
||||
|
||||
|
||||
class AnthemAVR(MediaPlayerDevice):
|
||||
"""Entity reading values from Anthem AVR protocol."""
|
||||
|
||||
def __init__(self, avr, name):
|
||||
""""Initialize entity with transport."""
|
||||
super().__init__()
|
||||
self.avr = avr
|
||||
self._name = name
|
||||
|
||||
def _lookup(self, propname, dval=None):
|
||||
return getattr(self.avr.protocol, propname, dval)
|
||||
|
||||
@property
|
||||
def supported_media_commands(self):
|
||||
"""Return flag of media commands that are supported."""
|
||||
return SUPPORT_ANTHEMAV
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
"""No polling needed."""
|
||||
return False
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return name of device."""
|
||||
return self._name or self._lookup('model')
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return state of power on/off."""
|
||||
pwrstate = self._lookup('power')
|
||||
|
||||
if pwrstate is True:
|
||||
return STATE_ON
|
||||
elif pwrstate is False:
|
||||
return STATE_OFF
|
||||
else:
|
||||
return STATE_UNKNOWN
|
||||
|
||||
@property
|
||||
def is_volume_muted(self):
|
||||
"""Return boolean reflecting mute state on device."""
|
||||
return self._lookup('mute', False)
|
||||
|
||||
@property
|
||||
def volume_level(self):
|
||||
"""Return volume level from 0 to 1."""
|
||||
return self._lookup('volume_as_percentage', 0.0)
|
||||
|
||||
@property
|
||||
def media_title(self):
|
||||
"""Return current input name (closest we have to media title)."""
|
||||
return self._lookup('input_name', 'No Source')
|
||||
|
||||
@property
|
||||
def app_name(self):
|
||||
"""Return details about current video and audio stream."""
|
||||
return self._lookup('video_input_resolution_text', '') + ' ' \
|
||||
+ self._lookup('audio_input_name', '')
|
||||
|
||||
@property
|
||||
def source(self):
|
||||
"""Return currently selected input."""
|
||||
return self._lookup('input_name', "Unknown")
|
||||
|
||||
@property
|
||||
def source_list(self):
|
||||
"""Return all active, configured inputs."""
|
||||
return self._lookup('input_list', ["Unknown"])
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_select_source(self, source):
|
||||
"""Change AVR to the designated source (by name)."""
|
||||
self._update_avr('input_name', source)
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_turn_off(self):
|
||||
"""Turn AVR power off."""
|
||||
self._update_avr('power', False)
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_turn_on(self):
|
||||
"""Turn AVR power on."""
|
||||
self._update_avr('power', True)
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_set_volume_level(self, volume):
|
||||
"""Set AVR volume (0 to 1)."""
|
||||
self._update_avr('volume_as_percentage', volume)
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_mute_volume(self, mute):
|
||||
"""Engage AVR mute."""
|
||||
self._update_avr('mute', mute)
|
||||
|
||||
def _update_avr(self, propname, value):
|
||||
"""Update a property in the AVR."""
|
||||
_LOGGER.info('Sending command to AVR: set '+propname+' to '+str(value))
|
||||
setattr(self.avr.protocol, propname, value)
|
||||
|
||||
@property
|
||||
def dump_avrdata(self):
|
||||
"""Return state of avr object for debugging forensics."""
|
||||
attrs = vars(self)
|
||||
return(
|
||||
'dump_avrdata: '
|
||||
+ ', '.join('%s: %s' % item for item in attrs.items()))
|
|
@ -40,6 +40,9 @@ aiohttp_cors==0.5.0
|
|||
# homeassistant.components.sensor.amcrest
|
||||
amcrest==1.1.0
|
||||
|
||||
# homeassistant.components.media_player.anthemav
|
||||
anthemav==1.1.7
|
||||
|
||||
# homeassistant.components.apcupsd
|
||||
apcaccess==0.0.4
|
||||
|
||||
|
|
Loading…
Reference in New Issue