From f19318c28dfb5061ad78a75e490a5ac3f4df9eb2 Mon Sep 17 00:00:00 2001 From: George Zhao Date: Sun, 11 Jul 2021 14:06:53 +0800 Subject: [PATCH] add configuration manually --- custom_components/apple_airplayer/__init__.py | 18 +++++++--- .../apple_airplayer/config_flow.py | 28 +++++++++++++-- .../apple_airplayer/device_manager.py | 35 +++++++++++-------- .../apple_airplayer/manifest.json | 2 +- .../apple_airplayer/media_player.py | 16 ++++++--- .../apple_airplayer/translations/en.json | 11 +++++- .../apple_airplayer/translations/zh-Hans.json | 11 +++++- 7 files changed, 92 insertions(+), 29 deletions(-) diff --git a/custom_components/apple_airplayer/__init__.py b/custom_components/apple_airplayer/__init__.py index c9d4a92..42472be 100644 --- a/custom_components/apple_airplayer/__init__.py +++ b/custom_components/apple_airplayer/__init__.py @@ -1,6 +1,6 @@ from .const import DOMAIN, DEVICES from homeassistant.core import HomeAssistant -from homeassistant.const import CONF_DEVICE +from homeassistant.const import CONF_DEVICE, CONF_ADDRESS async def async_setup(hass: HomeAssistant, hass_config: dict): hass.data.setdefault(DOMAIN, {}) @@ -8,19 +8,27 @@ async def async_setup(hass: HomeAssistant, hass_config: dict): async def async_setup_entry(hass: HomeAssistant, config_entry): - identifier = config_entry.data[CONF_DEVICE] + identifier = config_entry.data.get(CONF_DEVICE) + address = config_entry.data.get(CONF_ADDRESS) if DOMAIN not in hass.data: hass.data[DOMAIN] = {} if DEVICES not in hass.data[DOMAIN]: hass.data[DOMAIN][DEVICES] = [] - hass.data[DOMAIN][DEVICES].append(identifier) + if identifier is not None: + hass.data[DOMAIN][DEVICES].append(identifier) + elif address is not None: + hass.data[DOMAIN][DEVICES].append(address) hass.async_create_task(hass.config_entries.async_forward_entry_setup( config_entry, "media_player")) return True async def async_unload_entry(hass: HomeAssistant, config_entry): - identifier = config_entry.data[CONF_DEVICE] - hass.data[DOMAIN][DEVICES].remove(identifier) + identifier = config_entry.data(CONF_DEVICE) + address = config_entry.data.get(CONF_ADDRESS) + if identifier is not None: + hass.data[DOMAIN][DEVICES].remove(identifier) + elif address is not None: + hass.data[DOMAIN][DEVICES].remove(address) await hass.config_entries.async_forward_entry_unload(config_entry, "media_player") return True diff --git a/custom_components/apple_airplayer/config_flow.py b/custom_components/apple_airplayer/config_flow.py index 429a340..e54b4c0 100644 --- a/custom_components/apple_airplayer/config_flow.py +++ b/custom_components/apple_airplayer/config_flow.py @@ -1,7 +1,7 @@ import logging from .const import DOMAIN, DEVICES, CONF_CACHE_DIR from homeassistant import config_entries -from homeassistant.const import CONF_DEVICE +from homeassistant.const import CONF_DEVICE, CONF_ADDRESS from .device_manager import DeviceManager import voluptuous as vol @@ -26,7 +26,7 @@ async def async_step_user(self, user_input=None, error=None): if len(self.available_device) > 0: return await self.async_step_devinfo() else: - return await self.async_step_user(error="no_devices") + return await self.async_step_manually() _LOGGER.debug(user_input, error) return self.async_show_form( step_id="user", @@ -47,3 +47,27 @@ async def async_step_devinfo(self, user_input=None, error=None): }), errors={"base": error} if error else None ) + + async def async_step_manually(self, user_input=None, error=None): + if user_input is not None: + man = DeviceManager(self.hass.loop) + device = await man.async_get_device_by_address(user_input[CONF_ADDRESS]) + if device is not None: + identifier = device.identifier + if identifier in self.hass.data[DOMAIN][DEVICES] or \ + user_input[CONF_ADDRESS] in self.hass.data[DOMAIN][DEVICES]: + return await self.async_step_manually(error="device_exist") + return self.async_create_entry( + title=f"{device.name}", + data=user_input) + else: + return await self.async_step_manually(error="no_devices") + + return self.async_show_form( + step_id="manually", + data_schema=vol.Schema({ + vol.Required(CONF_ADDRESS): str, + vol.Required(CONF_CACHE_DIR, default="/tmp/tts"): str + }), + errors={"base": error} if error else None + ) \ No newline at end of file diff --git a/custom_components/apple_airplayer/device_manager.py b/custom_components/apple_airplayer/device_manager.py index 4429ef7..bdb8095 100644 --- a/custom_components/apple_airplayer/device_manager.py +++ b/custom_components/apple_airplayer/device_manager.py @@ -1,7 +1,7 @@ import pyatv import logging from homeassistant.const import STATE_ON, STATE_OFF -from pyatv.const import DeviceModel, FeatureName, FeatureState, DeviceState +from pyatv.const import DeviceModel, FeatureName, FeatureState _LOGGER = logging.getLogger(__name__) @@ -69,13 +69,6 @@ def volume_level(self): else: return 0.5 - async def async_set_volume_level(self, volume): - if self._atv_interface is not None and 0 <= volume <= 1: - try: - await self._atv_interface.audio.set_volume(volume * 100) - except Exception as e: - _LOGGER.debug(f"Exception raised in async_set_volume_level, {e}") - async def async_open(self): if self._atv_interface is None: try: @@ -87,7 +80,7 @@ async def async_open(self): elif k == FeatureName.StreamFile and f.state == FeatureState.Available: self._support_stream_file = True except Exception as e: - _LOGGER.debug(f"Exception raised in async_open, {e}") + _LOGGER.error(f"Exception raised in async_open, {e}") async def async_close(self): if self._atv_interface is not None: @@ -95,25 +88,28 @@ async def async_close(self): self._atv_interface.close() self._atv_interface = None except Exception as e: - _LOGGER.debug(f"Exception raised in async_close, {e}") + _LOGGER.error(f"Exception raised in async_close, {e}") async def async_play_url(self, url): if self._atv_interface is not None: try: await self._atv_interface.stream.play_url(url) except Exception as e: - _LOGGER.debug(f"Exception raised in async_play_url, {e}") + _LOGGER.error(f"Exception raised in async_play_url, {e}") async def async_stream_file(self, filename): if self._atv_interface is not None: try: await self._atv_interface.stream.stream_file(filename) except Exception as e: - _LOGGER.debug(f"Exception raised in async_stream_file, {e}") + _LOGGER.error(f"Exception raised in async_stream_file, {e}") async def async_set_volume(self, volume): if self._atv_interface is not None: - await self._atv_interface.audio.set_volume(volume * 100) + try: + await self._atv_interface.audio.set_volume(volume * 100) + except Exception as e: + _LOGGER.error(f"Exception raised in async_set_volume, {e}") class DeviceManager: @@ -131,9 +127,20 @@ async def async_get_all_devices(self): await device.async_close() return devices - async def async_get_device(self, identifier): + async def async_get_device_by_identifier(self, identifier): atvs = await pyatv.scan(self._event_loop) for atv in atvs: if atv.identifier.replace(":", "").lower() == identifier.lower(): return AirPlayDevice(atv, self._event_loop) return None + + async def async_get_device_by_address(self, address): + atvs = await pyatv.scan(self._event_loop, hosts=[address]) + if len(atvs) > 0: + atv = atvs[0] + device = AirPlayDevice(atv, self._event_loop) + await device.async_open() + await device.async_close() + if device.support_play_url or device.support_stream_file: + return device + return None diff --git a/custom_components/apple_airplayer/manifest.json b/custom_components/apple_airplayer/manifest.json index f0e507f..be989f9 100644 --- a/custom_components/apple_airplayer/manifest.json +++ b/custom_components/apple_airplayer/manifest.json @@ -1,7 +1,7 @@ { "domain": "apple_airplayer", "name": "Apple AirPlayer", - "version": "v0.0.3", + "version": "v0.0.4", "config_flow": true, "documentation": "https://github.com/georgezhao2010/apple_airplayer", "issue_tracker": "https://github.com/georgezhao2010/apple_airplayer/issue", diff --git a/custom_components/apple_airplayer/media_player.py b/custom_components/apple_airplayer/media_player.py index bf4372f..7b74631 100644 --- a/custom_components/apple_airplayer/media_player.py +++ b/custom_components/apple_airplayer/media_player.py @@ -8,18 +8,24 @@ ) from .device_manager import DeviceManager, AirPlayDevice from .const import DOMAIN, CONF_CACHE_DIR -from homeassistant.const import CONF_DEVICE +from homeassistant.const import CONF_DEVICE, CONF_ADDRESS _LOGGER = logging.getLogger(__name__) async def async_setup_entry(hass, config_entry, async_add_entities): - identifier = config_entry.data[CONF_DEVICE] + identifier = config_entry.data.get(CONF_DEVICE) + address = config_entry.data.get(CONF_ADDRESS) cache_dir = config_entry.data[CONF_CACHE_DIR] man = DeviceManager(hass.loop) - device = await man.async_get_device(identifier) - await device.async_open() - async_add_entities([AirPlayer(device, cache_dir)]) + device = None + if identifier is not None: + device = await man.async_get_device_by_identifier(identifier) + elif address is not None: + device = await man.async_get_device_by_address(address) + if device is not None: + await device.async_open() + async_add_entities([AirPlayer(device, cache_dir)]) class AirPlayer(MediaPlayerEntity): diff --git a/custom_components/apple_airplayer/translations/en.json b/custom_components/apple_airplayer/translations/en.json index 7137475..ee889e5 100644 --- a/custom_components/apple_airplayer/translations/en.json +++ b/custom_components/apple_airplayer/translations/en.json @@ -1,7 +1,8 @@ { "config": { "error": { - "no_devices": "No available devices found on the network" + "no_devices": "No available devices found on the network", + "device_exist": "Device already configured, choice another one" }, "step": { "user": { @@ -15,6 +16,14 @@ }, "description": "Choice a device to add", "title": "New device found" + }, + "manually": { + "data": { + "Address": "address", + "cache_dir": "Cache dir" + }, + "description": "Enter the IP address of the AirPlay device you want to add", + "title": "No device found" } } } diff --git a/custom_components/apple_airplayer/translations/zh-Hans.json b/custom_components/apple_airplayer/translations/zh-Hans.json index 0db1ff8..52bd7ad 100644 --- a/custom_components/apple_airplayer/translations/zh-Hans.json +++ b/custom_components/apple_airplayer/translations/zh-Hans.json @@ -1,7 +1,8 @@ { "config": { "error": { - "no_devices": "未在网络上发现可用的AirPlay设备" + "no_devices": "未在网络上发现可用的AirPlay设备", + "device_exist": "设备已经存在,请添加其它设备" }, "step": { "user": { @@ -15,6 +16,14 @@ }, "description": "选择一个设备并添加", "title": "发现新设备" + }, + "manually": { + "data": { + "Address": "地址", + "cache_dir": "缓存目录" + }, + "description": "输入你想要添加的设备的IP地址", + "title": "没有自动发现设备" } } }