From 1a3ea49f88888bd558dcdc1ac54c9e041b763c27 Mon Sep 17 00:00:00 2001 From: Sebastian Haas Date: Tue, 28 Dec 2021 21:16:03 +0100 Subject: [PATCH 1/3] fixes issue #10: adapt to MQTT API changes call mqtt.async_publish as coroutine --- custom_components/hisense_tv/helper.py | 2 +- custom_components/hisense_tv/manifest.json | 2 +- custom_components/hisense_tv/media_player.py | 76 +++++++++++--------- custom_components/hisense_tv/switch.py | 8 ++- 4 files changed, 50 insertions(+), 38 deletions(-) diff --git a/custom_components/hisense_tv/helper.py b/custom_components/hisense_tv/helper.py index be58374..25cfebb 100644 --- a/custom_components/hisense_tv/helper.py +++ b/custom_components/hisense_tv/helper.py @@ -21,7 +21,7 @@ async def get(): yield await asyncio.wait_for(queue.get(), timeout=10) unsubscribe = await mqtt.async_subscribe(hass=hass, topic=sub, msg_callback=put) - mqtt.async_publish(hass=hass, topic=pub, payload=payload) + await mqtt.async_publish(hass=hass, topic=pub, payload=payload) return get(), unsubscribe diff --git a/custom_components/hisense_tv/manifest.json b/custom_components/hisense_tv/manifest.json index 3b6a5bc..9efe269 100644 --- a/custom_components/hisense_tv/manifest.json +++ b/custom_components/hisense_tv/manifest.json @@ -1,7 +1,7 @@ { "domain": "hisense_tv", "name": "Hisense TV MQTT Bridge", - "version": "21.11.23", + "version": "21.12.28", "documentation": "https://github.com/sehaas/ha_hisense_tv", "dependencies": [ "mqtt" diff --git a/custom_components/hisense_tv/media_player.py b/custom_components/hisense_tv/media_player.py index 625b55a..9c4aff4 100644 --- a/custom_components/hisense_tv/media_player.py +++ b/custom_components/hisense_tv/media_player.py @@ -196,18 +196,17 @@ async def async_turn_on(self, **kwargs): """Turn the media player on.""" _LOGGER.debug("turn_on") wakeonlan.send_magic_packet(self._mac) - # self._state = STATE_OFF async def async_turn_off(self, **kwargs): """Turn off media player.""" _LOGGER.debug("turn_off") - mqtt.async_publish( + await mqtt.async_publish( hass=self._hass, - topic=self._out_topic("/remoteapp/tv/remote_service/%s/actions/sendkey"), + topic=self._out_topic( + "/remoteapp/tv/remote_service/%s/actions/sendkey"), payload="KEY_POWER", retain=False, ) - # self._state = STATE_OFF @property def is_volume_muted(self): @@ -221,11 +220,11 @@ def volume_level(self): _LOGGER.debug("volume_level %d" % self._volume) return self._volume / 100 - def set_volume_level(self, volume): + async def async_set_volume_level(self, volume): """Set volume level, range 0..1.""" _LOGGER.debug("set_volume_level %s" % volume) self._volume = int(volume * 100) - mqtt.async_publish( + await mqtt.async_publish( hass=self._hass, topic=self._out_topic( "/remoteapp/tv/platform_service/%s/actions/changevolume" @@ -233,35 +232,38 @@ def set_volume_level(self, volume): payload=self._volume, ) - def volume_up(self): + async def async_volume_up(self): """Volume up the media player.""" _LOGGER.debug("volume_up") if self._volume < 100: self._volume = self._volume + 1 - mqtt.async_publish( + await mqtt.async_publish( hass=self._hass, - topic=self._out_topic("/remoteapp/tv/remote_service/%s/actions/sendkey"), + topic=self._out_topic( + "/remoteapp/tv/remote_service/%s/actions/sendkey"), payload="KEY_VOLUMEUP", ) - def volume_down(self): + async def async_volume_down(self): """Volume down media player.""" _LOGGER.debug("volume_down") if self._volume > 0: self._volume = self._volume - 1 - mqtt.async_publish( + await mqtt.async_publish( hass=self._hass, - topic=self._out_topic("/remoteapp/tv/remote_service/%s/actions/sendkey"), + topic=self._out_topic( + "/remoteapp/tv/remote_service/%s/actions/sendkey"), payload="KEY_VOLUMEDOWN", ) - def mute_volume(self, mute): + async def async_mute_volume(self, mute): """Send mute command.""" _LOGGER.debug("mute_volume %s" % mute) self._muted = mute - mqtt.async_publish( + await mqtt.async_publish( hass=self._hass, - topic=self._out_topic("/remoteapp/tv/remote_service/%s/actions/sendkey"), + topic=self._out_topic( + "/remoteapp/tv/remote_service/%s/actions/sendkey"), payload="KEY_MUTE", ) @@ -304,7 +306,7 @@ async def async_select_source(self, source): _LOGGER.debug("async_select_source %s" % source) if source == "App": - mqtt.async_publish( + await mqtt.async_publish( hass=self._hass, topic=self._out_topic( "/remoteapp/tv/remote_service/%s/actions/sendkey" @@ -315,22 +317,24 @@ async def async_select_source(self, source): source_dic = self._source_list.get(source) payload = json.dumps({"sourceid": source_dic.get("sourceid")}) - mqtt.async_publish( + await mqtt.async_publish( hass=self._hass, - topic=self._out_topic("/remoteapp/tv/ui_service/%s/actions/changesource"), + topic=self._out_topic( + "/remoteapp/tv/ui_service/%s/actions/changesource"), payload=payload, ) - def _check_state(self): + async def _check_state(self): _LOGGER.debug("_check_state: %s", self._state) if self._state == STATE_ON: _LOGGER.debug("_check_state skip") return _LOGGER.debug("_check_state publish") - mqtt.async_publish( + await mqtt.async_publish( hass=self._hass, - topic=self._out_topic("/remoteapp/tv/ui_service/%s/actions/gettvstate"), + topic=self._out_topic( + "/remoteapp/tv/ui_service/%s/actions/gettvstate"), payload="0", ) @@ -376,16 +380,18 @@ async def _message_received_turnoff(self, msg): async def _message_received_sourcelist(self, msg): """Run when new MQTT message has been received.""" - self._check_state() + await self._check_state() payload = json.loads(msg.payload) self._source_list = {s.get("sourcename"): s for s in payload} self._source_list["App"] = {} - _LOGGER.debug("message_received_sourcelist R(%s):\n%s" % (msg.retain, payload)) + _LOGGER.debug("message_received_sourcelist R(%s):\n%s" % + (msg.retain, payload)) async def _message_received_volume(self, msg): """Run when new MQTT message has been received.""" - self._check_state() - _LOGGER.debug("message_received_volume R(%s)\n%s" % (msg.retain, msg.payload)) + await self._check_state() + _LOGGER.debug("message_received_volume R(%s)\n%s" % + (msg.retain, msg.payload)) payload = json.loads(msg.payload) if payload.get("volume_type") == 0: self._volume = payload.get("volume_value") @@ -404,16 +410,17 @@ async def _message_received_state(self, msg): _LOGGER.debug("message_received_state %s" % statetype) if self._state == STATE_OFF: - mqtt.async_publish( + await mqtt.async_publish( hass=self._hass, topic=self._out_topic( "/remoteapp/tv/platform_service/%s/actions/getvolume" ), payload="", ) - mqtt.async_publish( + await mqtt.async_publish( hass=self._hass, - topic=self._out_topic("/remoteapp/tv/ui_service/%s/actions/sourcelist"), + topic=self._out_topic( + "/remoteapp/tv/ui_service/%s/actions/sourcelist"), payload="", ) @@ -481,7 +488,8 @@ async def _build_library_node(self): try: async for msg in stream_get: payload = json.loads(msg[0].payload) - self._channel_infos = {item.get("list_para"): item for item in payload} + self._channel_infos = { + item.get("list_para"): item for item in payload} for key, item in self._channel_infos.items(): node.children.append( BrowseMedia( @@ -612,7 +620,7 @@ async def async_play_media(self, media_type, media_id, **kwargs): if media_type == MEDIA_TYPE_CHANNEL: channel = json.dumps({"channel_param": media_id}) - mqtt.async_publish( + await mqtt.async_publish( hass=self._hass, topic=self._out_topic( "/remoteapp/tv/ui_service/%s/actions/changechannel" @@ -622,10 +630,12 @@ async def async_play_media(self, media_type, media_id, **kwargs): elif media_type == MEDIA_CLASS_APP: app = self._app_list.get(media_id) payload = json.dumps( - {"appId": media_id, "name": app.get("name"), "url": app.get("url")} + {"appId": media_id, "name": app.get( + "name"), "url": app.get("url")} ) - mqtt.async_publish( + await mqtt.async_publish( hass=self._hass, - topic=self._out_topic("/remoteapp/tv/ui_service/%s/actions/launchapp"), + topic=self._out_topic( + "/remoteapp/tv/ui_service/%s/actions/launchapp"), payload=payload, ) diff --git a/custom_components/hisense_tv/switch.py b/custom_components/hisense_tv/switch.py index 83918c8..2796d3f 100644 --- a/custom_components/hisense_tv/switch.py +++ b/custom_components/hisense_tv/switch.py @@ -48,9 +48,10 @@ async def async_turn_on(self, **kwargs): async def async_turn_off(self, **kwargs): """Turn the entity off.""" - mqtt.async_publish( + await mqtt.async_publish( hass=self._hass, - topic=self._out_topic("/remoteapp/tv/remote_service/%s/actions/sendkey"), + topic=self._out_topic( + "/remoteapp/tv/remote_service/%s/actions/sendkey"), payload="KEY_POWER", retain=False, ) @@ -130,7 +131,8 @@ async def _message_received_turnoff(self, msg): async def _message_received_state(self, msg): if msg.retain == True: - _LOGGER.debug("SWITCH message_received_state - skip retained message") + _LOGGER.debug( + "SWITCH message_received_state - skip retained message") return _LOGGER.debug("SWITCH message_received_state - turn on") From 222ff43393f19c9f0ed164097fcd7c3a79d86011 Mon Sep 17 00:00:00 2001 From: Sebastian Haas Date: Thu, 30 Dec 2021 11:45:18 +0100 Subject: [PATCH 2/3] handle JSONDecodeError & fix pylint warnings - handle None payload - use lazy substitution during logging - add minimal pydoc - refactor some if checks --- custom_components/hisense_tv/__init__.py | 1 + custom_components/hisense_tv/config_flow.py | 45 +++-- custom_components/hisense_tv/helper.py | 9 +- custom_components/hisense_tv/media_player.py | 186 +++++++++++-------- custom_components/hisense_tv/switch.py | 15 +- 5 files changed, 152 insertions(+), 104 deletions(-) diff --git a/custom_components/hisense_tv/__init__.py b/custom_components/hisense_tv/__init__.py index 4610e8e..bc35c22 100644 --- a/custom_components/hisense_tv/__init__.py +++ b/custom_components/hisense_tv/__init__.py @@ -47,3 +47,4 @@ async def async_setup(hass, config): _LOGGER.debug("async_setup") hass.data.setdefault(DOMAIN, {}) return True + diff --git a/custom_components/hisense_tv/config_flow.py b/custom_components/hisense_tv/config_flow.py index 591d4c4..5db5057 100644 --- a/custom_components/hisense_tv/config_flow.py +++ b/custom_components/hisense_tv/config_flow.py @@ -1,4 +1,6 @@ +"""Hisense TV config flow.""" import json +from json.decoder import JSONDecodeError import logging import voluptuous as vol @@ -6,6 +8,7 @@ from homeassistant import config_entries from homeassistant.components import mqtt from homeassistant.const import CONF_MAC, CONF_NAME, CONF_PIN +from homeassistant.data_entry_flow import FlowResult from .const import ( CONF_MQTT_IN, @@ -15,7 +18,6 @@ DEFAULT_NAME, DOMAIN, ) -from .helper import mqtt_pub_sub _LOGGER = logging.getLogger(__name__) @@ -54,8 +56,11 @@ async def _async_pin_not_needed(self, message): async def _async_authcode_response(self, message): self._unsubscribe() - payload = json.loads(message.payload) - _LOGGER.debug("_async_authcode_respone %s" % payload) + try: + payload = json.loads(message.payload) + except JSONDecodeError: + payload = {} + _LOGGER.debug("_async_authcode_respone %s", payload) self.task_auth = payload.get("result") == 1 self.hass.async_create_task( self.hass.config_entries.flow.async_configure(flow_id=self.flow_id) @@ -69,7 +74,7 @@ def _unsubscribe(self): self._unsubscribe_sourcelist() self._unsubscribe_sourcelist = None - async def async_step_user(self, info): + async def async_step_user(self, user_input) -> FlowResult: if self.task_auth is True: return self.async_show_progress_done(next_step_id="finish") @@ -77,7 +82,7 @@ async def async_step_user(self, info): self.task_auth = None return self.async_show_progress_done(next_step_id="auth") - if info is None: + if user_input is None: _LOGGER.debug("async_step_user INFO None") return self.async_show_form( step_id="user", @@ -90,21 +95,21 @@ async def async_step_user(self, info): } ), ) - else: - _LOGGER.debug("async_step_user NOT task_mqtt") - self.task_mqtt = { - CONF_MAC: info.get(CONF_MAC), - CONF_NAME: info.get(CONF_NAME), - CONF_MQTT_IN: info.get(CONF_MQTT_IN), - CONF_MQTT_OUT: info.get(CONF_MQTT_OUT), - } - await self._check_authentication(client_id=DEFAULT_CLIENT_ID) + _LOGGER.debug("async_step_user NOT task_mqtt") + self.task_mqtt = { + CONF_MAC: user_input.get(CONF_MAC), + CONF_NAME: user_input.get(CONF_NAME), + CONF_MQTT_IN: user_input.get(CONF_MQTT_IN), + CONF_MQTT_OUT: user_input.get(CONF_MQTT_OUT), + } - return self.async_show_progress( - step_id="user", - progress_action="progress_action", - ) + await self._check_authentication(client_id=DEFAULT_CLIENT_ID) + + return self.async_show_progress( + step_id="user", + progress_action="progress_action", + ) async def _check_authentication(self, client_id): self._unsubscribe_auth = await mqtt.async_subscribe( @@ -133,10 +138,12 @@ async def _check_authentication(self, client_id): ) async def async_step_reauth(self, user_input=None): + """Reauth handler.""" self.task_auth = None return await self.async_step_auth(user_input=user_input) async def async_step_auth(self, user_input=None): + """Auth handler.""" if self.task_auth is True: _LOGGER.debug("async_step_auth finish") return self.async_show_progress_done(next_step_id="finish") @@ -178,6 +185,7 @@ async def async_step_auth(self, user_input=None): ) async def async_step_finish(self, user_input=None): + """Finish config flow.""" _LOGGER.debug("async_step_finish") return self.async_create_entry(title=self._name, data=self.task_mqtt) @@ -185,3 +193,4 @@ async def async_step_import(self, data): """Handle import from YAML.""" _LOGGER.debug("async_step_import") return self.async_create_entry(title=data[CONF_NAME], data=data) + diff --git a/custom_components/hisense_tv/helper.py b/custom_components/hisense_tv/helper.py index 25cfebb..d840269 100644 --- a/custom_components/hisense_tv/helper.py +++ b/custom_components/hisense_tv/helper.py @@ -1,3 +1,4 @@ +"""Hisene TV integration helper methods.""" import asyncio import logging @@ -10,6 +11,7 @@ async def mqtt_pub_sub(hass, pub, sub, payload=""): + """Wrapper for publishing MQTT topics and receive replies on a subscibed topic.""" loop = asyncio.get_event_loop() queue = asyncio.Queue() @@ -26,6 +28,8 @@ async def get(): class HisenseTvBase(object): + """Hisense TV base entity.""" + def __init__( self, hass, name: str, mqtt_in: str, mqtt_out: str, mac: str, uid: str ): @@ -53,7 +57,7 @@ def _out_topic(self, topic=""): out_topic = self._mqtt_out + topic % self._client except: out_topic = self._mqtt_out + topic % self._client - _LOGGER.debug("_out_topic: %s" % out_topic) + _LOGGER.debug("_out_topic: %s", out_topic) return out_topic def _in_topic(self, topic=""): @@ -61,5 +65,6 @@ def _in_topic(self, topic=""): in_topic = self._mqtt_in + topic % self._client except: in_topic = self._mqtt_in + topic - _LOGGER.debug("_in_topic: %s" % in_topic) + _LOGGER.debug("_in_topic: %s", in_topic) return in_topic + diff --git a/custom_components/hisense_tv/media_player.py b/custom_components/hisense_tv/media_player.py index 9c4aff4..bbedd11 100644 --- a/custom_components/hisense_tv/media_player.py +++ b/custom_components/hisense_tv/media_player.py @@ -1,5 +1,7 @@ +"""Hisense TV media player entity.""" import asyncio import json +from json.decoder import JSONDecodeError import logging import voluptuous as vol @@ -61,6 +63,7 @@ async def async_setup_platform(hass, config, async_add_devices, discovery_info=None): + """Set up the media player platform.""" if discovery_info: # Now handled by zeroconf in the config flow @@ -69,7 +72,7 @@ async def async_setup_platform(hass, config, async_add_devices, discovery_info=N mac = config[CONF_MAC] for entry in hass.config_entries.async_entries(DOMAIN): - _LOGGER.debug("entry: %s" % entry.data) + _LOGGER.debug("entry: %s", entry.data) if entry.data[CONF_MAC] == mac: return @@ -88,8 +91,8 @@ async def async_setup_platform(hass, config, async_add_devices, discovery_info=N async def async_setup_entry(hass, config_entry, async_add_entities): - """Set up the media player platform.""" - _LOGGER.debug("async_setup_entry config: %s" % config_entry.data) + """Set up the media player entry.""" + _LOGGER.debug("async_setup_entry config: %s", config_entry.data) name = config_entry.data[CONF_NAME] mac = config_entry.data[CONF_MAC] @@ -106,6 +109,8 @@ async def async_setup_entry(hass, config_entry, async_add_entities): class HisenseTvEntity(MediaPlayerEntity, HisenseTvBase): + """HisenseTV Media Player entity.""" + def __init__( self, hass, name: str, mqtt_in: str, mqtt_out: str, mac: str, uid: str ): @@ -189,7 +194,7 @@ def device_info(self): @property def state(self): """Return the state of the device.""" - _LOGGER.debug("state %s" % self._state) + _LOGGER.debug("state %s", self._state) return self._state async def async_turn_on(self, **kwargs): @@ -202,8 +207,7 @@ async def async_turn_off(self, **kwargs): _LOGGER.debug("turn_off") await mqtt.async_publish( hass=self._hass, - topic=self._out_topic( - "/remoteapp/tv/remote_service/%s/actions/sendkey"), + topic=self._out_topic("/remoteapp/tv/remote_service/%s/actions/sendkey"), payload="KEY_POWER", retain=False, ) @@ -211,18 +215,18 @@ async def async_turn_off(self, **kwargs): @property def is_volume_muted(self): """Boolean if volume is currently muted.""" - _LOGGER.debug("is_volume_muted %s" % self._muted) + _LOGGER.debug("is_volume_muted %s", self._muted) return self._muted @property def volume_level(self): """Volume level of the media player (0..100).""" - _LOGGER.debug("volume_level %d" % self._volume) + _LOGGER.debug("volume_level %d", self._volume) return self._volume / 100 async def async_set_volume_level(self, volume): """Set volume level, range 0..1.""" - _LOGGER.debug("set_volume_level %s" % volume) + _LOGGER.debug("set_volume_level %s", volume) self._volume = int(volume * 100) await mqtt.async_publish( hass=self._hass, @@ -239,8 +243,7 @@ async def async_volume_up(self): self._volume = self._volume + 1 await mqtt.async_publish( hass=self._hass, - topic=self._out_topic( - "/remoteapp/tv/remote_service/%s/actions/sendkey"), + topic=self._out_topic("/remoteapp/tv/remote_service/%s/actions/sendkey"), payload="KEY_VOLUMEUP", ) @@ -251,19 +254,17 @@ async def async_volume_down(self): self._volume = self._volume - 1 await mqtt.async_publish( hass=self._hass, - topic=self._out_topic( - "/remoteapp/tv/remote_service/%s/actions/sendkey"), + topic=self._out_topic("/remoteapp/tv/remote_service/%s/actions/sendkey"), payload="KEY_VOLUMEDOWN", ) async def async_mute_volume(self, mute): """Send mute command.""" - _LOGGER.debug("mute_volume %s" % mute) + _LOGGER.debug("mute_volume %s", mute) self._muted = mute await mqtt.async_publish( hass=self._hass, - topic=self._out_topic( - "/remoteapp/tv/remote_service/%s/actions/sendkey"), + topic=self._out_topic("/remoteapp/tv/remote_service/%s/actions/sendkey"), payload="KEY_MUTE", ) @@ -294,16 +295,16 @@ def media_series_title(self): if self._state == STATE_OFF: return None - if self._channel_num != None: + if self._channel_num is not None: channel = "%s (%s)" % (self._channel_name, self._channel_num) else: channel = self._channel_name - _LOGGER.debug("media_series_title %s" % channel) + _LOGGER.debug("media_series_title %s", channel) return channel async def async_select_source(self, source): """Select input source.""" - _LOGGER.debug("async_select_source %s" % source) + _LOGGER.debug("async_select_source %s", source) if source == "App": await mqtt.async_publish( @@ -319,8 +320,7 @@ async def async_select_source(self, source): payload = json.dumps({"sourceid": source_dic.get("sourceid")}) await mqtt.async_publish( hass=self._hass, - topic=self._out_topic( - "/remoteapp/tv/ui_service/%s/actions/changesource"), + topic=self._out_topic("/remoteapp/tv/ui_service/%s/actions/changesource"), payload=payload, ) @@ -333,8 +333,7 @@ async def _check_state(self): _LOGGER.debug("_check_state publish") await mqtt.async_publish( hass=self._hass, - topic=self._out_topic( - "/remoteapp/tv/ui_service/%s/actions/gettvstate"), + topic=self._out_topic("/remoteapp/tv/ui_service/%s/actions/gettvstate"), payload="0", ) @@ -381,18 +380,22 @@ async def _message_received_turnoff(self, msg): async def _message_received_sourcelist(self, msg): """Run when new MQTT message has been received.""" await self._check_state() - payload = json.loads(msg.payload) + try: + payload = json.loads(msg.payload) + except JSONDecodeError: + payload = [] self._source_list = {s.get("sourcename"): s for s in payload} self._source_list["App"] = {} - _LOGGER.debug("message_received_sourcelist R(%s):\n%s" % - (msg.retain, payload)) + _LOGGER.debug("message_received_sourcelist R(%s):\n%s", msg.retain, payload) async def _message_received_volume(self, msg): """Run when new MQTT message has been received.""" await self._check_state() - _LOGGER.debug("message_received_volume R(%s)\n%s" % - (msg.retain, msg.payload)) - payload = json.loads(msg.payload) + _LOGGER.debug("message_received_volume R(%s)\n%s", msg.retain, msg.payload) + try: + payload = json.loads(msg.payload) + except JSONDecodeError: + payload = {} if payload.get("volume_type") == 0: self._volume = payload.get("volume_value") elif payload.get("volume_type") == 2: @@ -401,13 +404,16 @@ async def _message_received_volume(self, msg): async def _message_received_state(self, msg): """Run when new MQTT message has been received.""" - if msg.retain == True: + if msg.retain is True: _LOGGER.debug("message_received_state - skip retained message") return - payload = json.loads(msg.payload) + try: + payload = json.loads(msg.payload) + except JSONDecodeError: + payload = {} statetype = payload.get("statetype") - _LOGGER.debug("message_received_state %s" % statetype) + _LOGGER.debug("message_received_state %s", statetype) if self._state == STATE_OFF: await mqtt.async_publish( @@ -419,8 +425,7 @@ async def _message_received_state(self, msg): ) await mqtt.async_publish( hass=self._hass, - topic=self._out_topic( - "/remoteapp/tv/ui_service/%s/actions/sourcelist"), + topic=self._out_topic("/remoteapp/tv/ui_service/%s/actions/sourcelist"), payload="", ) @@ -487,24 +492,35 @@ async def _build_library_node(self): try: async for msg in stream_get: - payload = json.loads(msg[0].payload) - self._channel_infos = { - item.get("list_para"): item for item in payload} - for key, item in self._channel_infos.items(): - node.children.append( - BrowseMedia( - title=item.get("list_name"), - media_class=MEDIA_CLASS_DIRECTORY, - media_content_type="channellistinfo", - media_content_id=key, - can_play=False, - can_expand=True, + try: + payload_string = msg[0].payload + if payload_string is None: + _LOGGER.debug("Skipping empty receiver list") + break + payload = json.loads(payload_string) + self._channel_infos = { + item.get("list_para"): item for item in payload + } + for key, item in self._channel_infos.items(): + node.children.append( + BrowseMedia( + title=item.get("list_name"), + media_class=MEDIA_CLASS_DIRECTORY, + media_content_type="channellistinfo", + media_content_id=key, + can_play=False, + can_expand=True, + ) ) + except JSONDecodeError as err: + _LOGGER.warning( + "Could not build Media Library from '%s': %s", msg, err.msg ) break except asyncio.TimeoutError: _LOGGER.debug("timeout error - getchannellistinfo") - pass + finally: + unsubscribe_getchannellistinfo() node.children.append( BrowseMedia( @@ -516,7 +532,6 @@ async def _build_library_node(self): can_expand=True, ) ) - unsubscribe_getchannellistinfo() return node async def _build_app_list_node(self): @@ -538,25 +553,34 @@ async def _build_app_list_node(self): try: async for msg in stream_get: - payload = json.loads(msg[0].payload) - self._app_list = {item.get("appId"): item for item in payload} - for id, item in self._app_list.items(): - node.children.append( - BrowseMedia( - title=item.get("name"), - media_class=MEDIA_CLASS_APP, - media_content_type=MEDIA_TYPE_APP, - media_content_id=id, - can_play=True, - can_expand=False, + try: + payload_string = msg[0].payload + if payload_string is None: + _LOGGER.debug("skipping empty app list") + break + payload = json.loads(payload_string) + self._app_list = {item.get("appId"): item for item in payload} + for nid, item in self._app_list.items(): + node.children.append( + BrowseMedia( + title=item.get("name"), + media_class=MEDIA_CLASS_APP, + media_content_type=MEDIA_TYPE_APP, + media_content_id=nid, + can_play=True, + can_expand=False, + ) ) + except JSONDecodeError as err: + _LOGGER.warning( + "Could not build Application list from '%s': %s", msg, err.msg ) break except asyncio.TimeoutError: _LOGGER.debug("timeout error - applist") - pass + finally: + unsubscribe_applist() - unsubscribe_applist() return node async def async_browse_media(self, media_content_type=None, media_content_id=None): @@ -564,7 +588,7 @@ async def async_browse_media(self, media_content_type=None, media_content_id=Non if media_content_id in [None, "library"]: return await self._build_library_node() - elif media_content_id == "app_list": + if media_content_id == "app_list": return await self._build_app_list_node() list_name = self._channel_infos.get(media_content_id).get("list_name") @@ -594,29 +618,37 @@ async def async_browse_media(self, media_content_type=None, media_content_id=Non try: async for msg in stream_get: - payload = json.loads(msg[0].payload) - for item in payload.get("list"): - node.children.append( - BrowseMedia( - title=item.get("channel_name"), - media_class=MEDIA_CLASS_CHANNEL, - media_content_type=MEDIA_TYPE_CHANNEL, - media_content_id=item.get("channel_param"), - can_play=True, - can_expand=False, + try: + payload_string = msg[0].payload + if payload_string is None: + _LOGGER.debug("Skipping empty channel list") + break + payload = json.loads(payload_string) + for item in payload.get("list"): + node.children.append( + BrowseMedia( + title=item.get("channel_name"), + media_class=MEDIA_CLASS_CHANNEL, + media_content_type=MEDIA_TYPE_CHANNEL, + media_content_id=item.get("channel_param"), + can_play=True, + can_expand=False, + ) ) + except JSONDecodeError as err: + _LOGGER.warning( + "Could not build channel list from '%s': %s", msg, err.msg ) break except asyncio.TimeoutError: _LOGGER.debug("timeout error - channellist") - pass unsubscribe_channellist() return node async def async_play_media(self, media_type, media_id, **kwargs): """Send the play_media command to the media player.""" - _LOGGER.debug("async_play_media %s\n%s" % (media_id, kwargs)) + _LOGGER.debug("async_play_media %s\n%s", media_id, kwargs) if media_type == MEDIA_TYPE_CHANNEL: channel = json.dumps({"channel_param": media_id}) @@ -630,12 +662,10 @@ async def async_play_media(self, media_type, media_id, **kwargs): elif media_type == MEDIA_CLASS_APP: app = self._app_list.get(media_id) payload = json.dumps( - {"appId": media_id, "name": app.get( - "name"), "url": app.get("url")} + {"appId": media_id, "name": app.get("name"), "url": app.get("url")} ) await mqtt.async_publish( hass=self._hass, - topic=self._out_topic( - "/remoteapp/tv/ui_service/%s/actions/launchapp"), + topic=self._out_topic("/remoteapp/tv/ui_service/%s/actions/launchapp"), payload=payload, ) diff --git a/custom_components/hisense_tv/switch.py b/custom_components/hisense_tv/switch.py index 2796d3f..69276b3 100644 --- a/custom_components/hisense_tv/switch.py +++ b/custom_components/hisense_tv/switch.py @@ -1,3 +1,4 @@ +"""Hisense TV switch entity""" import logging import wakeonlan @@ -13,7 +14,8 @@ async def async_setup_entry(hass, config_entry, async_add_entities): - _LOGGER.debug("async_setup_entry config: %s" % config_entry.data) + """Start HisenseTV switch setup process.""" + _LOGGER.debug("async_setup_entry config: %s", config_entry.data) name = config_entry.data[CONF_NAME] mac = config_entry.data[CONF_MAC] @@ -30,6 +32,8 @@ async def async_setup_entry(hass, config_entry, async_add_entities): class HisenseTvSwitch(SwitchEntity, HisenseTvBase): + """Hisense TV switch entity.""" + def __init__(self, hass, name, mqtt_in, mqtt_out, mac, uid): HisenseTvBase.__init__( self=self, @@ -50,8 +54,7 @@ async def async_turn_off(self, **kwargs): """Turn the entity off.""" await mqtt.async_publish( hass=self._hass, - topic=self._out_topic( - "/remoteapp/tv/remote_service/%s/actions/sendkey"), + topic=self._out_topic("/remoteapp/tv/remote_service/%s/actions/sendkey"), payload="KEY_POWER", retain=False, ) @@ -130,11 +133,11 @@ async def _message_received_turnoff(self, msg): self.async_write_ha_state() async def _message_received_state(self, msg): - if msg.retain == True: - _LOGGER.debug( - "SWITCH message_received_state - skip retained message") + if msg.retain is True: + _LOGGER.debug("SWITCH message_received_state - skip retained message") return _LOGGER.debug("SWITCH message_received_state - turn on") self._is_on = True self.async_write_ha_state() + From b658e856eb799f6ac0fa5d0413d29f636dd3c994 Mon Sep 17 00:00:00 2001 From: Sebastian Haas Date: Thu, 30 Dec 2021 12:11:59 +0100 Subject: [PATCH 3/3] add minimal HA version info --- README.md | 1 + info.md | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e5d135b..b2619dd 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Hisense TV Integration for Home Assistant Integration an Hisense TV as media player into Home Assistant. The communication is handled via the integrated MQTT broker and wake-on-LAN. +Requires Home Assistant >= `2021.12.x`. ## Current features: * Turn on / off diff --git a/info.md b/info.md index 99eba83..98b7aa2 100644 --- a/info.md +++ b/info.md @@ -4,4 +4,6 @@ A Hisense TV media player integration for Home Assistant using the embedded MQTT * [Current Features](https://github.com/sehaas/ha_hisense_tv#current-features) * [Configuration](https://github.com/sehaas/ha_hisense_tv#configuration) -* [Setup in Home Assistant](https://github.com/sehaas/ha_hisense_tv#setup-in-home-assistant) \ No newline at end of file +* [Setup in Home Assistant](https://github.com/sehaas/ha_hisense_tv#setup-in-home-assistant) + +Requires HA >= `2021.12.x` \ No newline at end of file