From a473168e7fbcbda24137cf62cf96c01f98a06728 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 2 Oct 2020 12:39:25 +0200 Subject: [PATCH] Call receiver APIs in parallel / push to version 0.9.5 --- HELP.md | 137 ++++++++++++++++++++++++++-------- denonavr/__init__.py | 2 +- denonavr/denonavr.py | 171 +++++++++++++++++++++++-------------------- setup.py | 2 +- 4 files changed, 199 insertions(+), 113 deletions(-) diff --git a/HELP.md b/HELP.md index 654fbd1..725e4ef 100644 --- a/HELP.md +++ b/HELP.md @@ -84,6 +84,12 @@ CLASSES | create_zones(self, add_zones) | Create instances of additional zones for the receiver. | + | disable_tone_control(self) + | Disable tone control to change settings like bass or treble. + | + | enable_tone_control(self) + | Enable tone control to change settings like bass or treble. + | | ensure_configuration(self) | Ensure that configuration is loaded from receiver. | @@ -117,7 +123,7 @@ CLASSES | Turn off receiver via HTTP get command. | | power_on(self) - | Turn off receiver via HTTP get command. + | Turn on receiver via HTTP get command. | | previous_track(self) | Send previous track command to receiver command via HTTP post. @@ -128,6 +134,14 @@ CLASSES | send_post_command(self, command, body) | Send command via HTTP post to receiver. | + | set_bass(self, bass) + | Set receiver bass. + | + | Minimum is 0, maximum at 12 + | + | Note: + | Doesn't work, if Dynamic Equalizer is active. + | | set_input_func(self, input_func) | Set input_func of device. | @@ -145,6 +159,14 @@ CLASSES | set_sound_mode_dict(self, sound_mode_dict) | Set the matching dictionary used to match the raw sound mode. | + | set_treble(self, treble) + | Set receiver treble. + | + | Minimum is 0, maximum at 12 + | + | Note: + | Doesn't work, if Dynamic Equalizer is active. + | | set_volume(self, volume) | Set receiver volume via HTTP get command. | @@ -166,13 +188,7 @@ CLASSES | Volume up receiver via HTTP get command. | | ---------------------------------------------------------------------- - | Data descriptors defined here: - | - | __dict__ - | dictionary for instance variables (if defined) - | - | __weakref__ - | list of weak references to the object (if defined) + | Readonly properties defined here: | | album | Return album name of current playing media as string. @@ -183,6 +199,12 @@ CLASSES | band | Return band of current radio station as string. | + | bass + | Return value of bass. + | + | bass_level + | Return level of bass. + | | frequency | Return frequency of current radio station as string. | @@ -192,9 +214,6 @@ CLASSES | image_url | Return image URL of current playing media when powered on. | - | input_func - | Return the current input source as string. - | | input_func_list | Return a list of available input sources as string. | @@ -244,9 +263,6 @@ CLASSES | sm_match_dict | Return a dict to map each sound_mode_raw to matching sound_mode. | - | sound_mode - | Return the matched current sound mode as a string. - | | sound_mode_dict | Return a dict of available sound modes with their mapping values. | @@ -272,6 +288,12 @@ CLASSES | title | Return title of current playing media as string. | + | treble + | Return value of treble. + | + | treble_level + | Return level of treble. + | | volume | Return volume of Denon AVR as float. | @@ -283,6 +305,21 @@ CLASSES | | zones | Return all Zone instances of the device. + | + | ---------------------------------------------------------------------- + | Data descriptors defined here: + | + | __dict__ + | dictionary for instance variables (if defined) + | + | __weakref__ + | list of weak references to the object (if defined) + | + | input_func + | Return the current input source as string. + | + | sound_mode + | Return the matched current sound mode as a string. class DenonAVRZones(DenonAVR) | DenonAVRZones(parent_avr, zone, name) @@ -316,14 +353,11 @@ CLASSES | Return "True" on success and "False" on fail. | | ---------------------------------------------------------------------- - | Data descriptors defined here: + | Readonly properties defined here: | | sm_match_dict | Return a dict to map each sound_mode_raw to matching sound_mode. | - | sound_mode - | Return the matched current sound mode as a string. - | | sound_mode_dict | Return a dict of available sound modes with their mapping values. | @@ -334,6 +368,12 @@ CLASSES | Return the current sound mode as string as received from the AVR. | | ---------------------------------------------------------------------- + | Data descriptors defined here: + | + | sound_mode + | Return the matched current sound mode as a string. + | + | ---------------------------------------------------------------------- | Methods inherited from DenonAVR: | | construct_sm_match_dict(self) @@ -347,6 +387,12 @@ CLASSES | create_zones(self, add_zones) | Create instances of additional zones for the receiver. | + | disable_tone_control(self) + | Disable tone control to change settings like bass or treble. + | + | enable_tone_control(self) + | Enable tone control to change settings like bass or treble. + | | ensure_configuration(self) | Ensure that configuration is loaded from receiver. | @@ -380,7 +426,7 @@ CLASSES | Turn off receiver via HTTP get command. | | power_on(self) - | Turn off receiver via HTTP get command. + | Turn on receiver via HTTP get command. | | previous_track(self) | Send previous track command to receiver command via HTTP post. @@ -391,6 +437,14 @@ CLASSES | send_post_command(self, command, body) | Send command via HTTP post to receiver. | + | set_bass(self, bass) + | Set receiver bass. + | + | Minimum is 0, maximum at 12 + | + | Note: + | Doesn't work, if Dynamic Equalizer is active. + | | set_input_func(self, input_func) | Set input_func of device. | @@ -401,6 +455,14 @@ CLASSES | set_sound_mode_dict(self, sound_mode_dict) | Set the matching dictionary used to match the raw sound mode. | + | set_treble(self, treble) + | Set receiver treble. + | + | Minimum is 0, maximum at 12 + | + | Note: + | Doesn't work, if Dynamic Equalizer is active. + | | set_volume(self, volume) | Set receiver volume via HTTP get command. | @@ -422,13 +484,7 @@ CLASSES | Volume up receiver via HTTP get command. | | ---------------------------------------------------------------------- - | Data descriptors inherited from DenonAVR: - | - | __dict__ - | dictionary for instance variables (if defined) - | - | __weakref__ - | list of weak references to the object (if defined) + | Readonly properties inherited from DenonAVR: | | album | Return album name of current playing media as string. @@ -439,6 +495,12 @@ CLASSES | band | Return band of current radio station as string. | + | bass + | Return value of bass. + | + | bass_level + | Return level of bass. + | | frequency | Return frequency of current radio station as string. | @@ -448,9 +510,6 @@ CLASSES | image_url | Return image URL of current playing media when powered on. | - | input_func - | Return the current input source as string. - | | input_func_list | Return a list of available input sources as string. | @@ -513,6 +572,12 @@ CLASSES | title | Return title of current playing media as string. | + | treble + | Return value of treble. + | + | treble_level + | Return level of treble. + | | volume | Return volume of Denon AVR as float. | @@ -524,6 +589,18 @@ CLASSES | | zones | Return all Zone instances of the device. + | + | ---------------------------------------------------------------------- + | Data descriptors inherited from DenonAVR: + | + | __dict__ + | dictionary for instance variables (if defined) + | + | __weakref__ + | list of weak references to the object (if defined) + | + | input_func + | Return the current input source as string. ==================================================================================== @@ -559,6 +636,6 @@ FUNCTIONS send_ssdp_broadcast_ip(ip_addr) Send SSDP broadcast messages to a single IP. - + ssdp_request(ssdp_st, ssdp_mx=2) Return request bytes for given st and mx. \ No newline at end of file diff --git a/denonavr/__init__.py b/denonavr/__init__.py index fdc1b94..b656103 100644 --- a/denonavr/__init__.py +++ b/denonavr/__init__.py @@ -17,7 +17,7 @@ logging.getLogger(__name__).addHandler(logging.NullHandler()) __title__ = "denonavr" -__version__ = "0.9.5.dev1" +__version__ = "0.9.5" def discover(): diff --git a/denonavr/denonavr.py b/denonavr/denonavr.py index 1089ef1..368ff8c 100644 --- a/denonavr/denonavr.py +++ b/denonavr/denonavr.py @@ -645,97 +645,106 @@ def _update_avr_2016(self, compatibiliy_check=False): Returns "True" on success and "False" on fail. This method is for AVR-X devices built in 2016 and later. """ - success = True - # Collect tags for AppCommand.xml call - tags = ["GetAllZonePowerStatus", "GetAllZoneSource", - "GetAllZoneVolume", "GetAllZoneMuteStatus", - "GetSurroundModeStatus"] - # Execute call - try: - root = self.exec_appcommand_post(tags) - except requests.exceptions.ConnectTimeout: - if compatibiliy_check is True: - return None - # Assume device is off on connect timeout - self._power = POWER_OFF - self._state = STATE_OFF - return False - # Check result - if root is None: - if compatibiliy_check is False: - _LOGGER.error("Update failed.") - return False + # Use ThreadPoolExecutor to call all URLs of this method in parallel + with ThreadPoolExecutor(max_workers=2) as executor: - # Extract relevant information - zone = self._get_own_zone() + success = True + # Collect tags for AppCommand.xml call + tags = ["GetAllZonePowerStatus", "GetAllZoneSource", + "GetAllZoneVolume", "GetAllZoneMuteStatus", + "GetSurroundModeStatus"] + # Execute call + app_command = executor.submit(self.exec_appcommand_post, tags) - try: - self._power = root[0].find(zone).text - except (AttributeError, IndexError): - if compatibiliy_check is False: - _LOGGER.error("No PowerStatus found for zone %s", self.zone) - success = False + # Update tone control + executor.submit(self._update_tone_control) - try: - self._mute = root[3].find(zone).text - except (AttributeError, IndexError): - if compatibiliy_check is False: - _LOGGER.error("No MuteStatus found for zone %s", self.zone) - success = False + try: + root = app_command.result() + except requests.exceptions.ConnectTimeout: + if compatibiliy_check is True: + return None + # Assume device is off on connect timeout + self._power = POWER_OFF + self._state = STATE_OFF + return False + # Check result + if root is None: + if compatibiliy_check is False: + _LOGGER.error("Update failed.") + return False - try: - self._volume = root.find( - "./cmd/{zone}/volume".format(zone=zone)).text - except AttributeError: - if compatibiliy_check is False: - _LOGGER.error("No VolumeStatus found for zone %s", self.zone) - success = False + # Extract relevant information + zone = self._get_own_zone() - try: - inputfunc = root.find( - "./cmd/{zone}/source".format(zone=zone)).text - except AttributeError: - if compatibiliy_check is False: - _LOGGER.error("No Source found for zone %s", self.zone) - success = False - else: try: - self._input_func = self._input_func_list_rev[inputfunc] - except KeyError: - _LOGGER.info("No mapping for source %s found", inputfunc) - self._input_func = inputfunc - # Get/update sources list if current source is not known yet - if self._update_input_func_list(): - _LOGGER.info("List of input functions refreshed.") - # If input function is still not known, log error. - if (inputfunc not in self._input_func_list and - inputfunc is not None): - _LOGGER.error( - "Input function %s is not known", self._input_func) - else: - _LOGGER.error(( - "Input function list for Denon receiver at host %s " - "could not be updated."), self._host) - try: - self._sound_mode_raw = root[4][0].text.rstrip() - except (AttributeError, IndexError): - if compatibiliy_check is False: - _LOGGER.error( - "No SoundMode found for the main zone %s", self.zone - ) - success = False + self._power = root[0].find(zone).text + except (AttributeError, IndexError): + if compatibiliy_check is False: + _LOGGER.error( + "No PowerStatus found for zone %s", self.zone) + success = False - # Now playing information is not implemented for 2016+ models, because - # a HEOS API query needed. So only sync the power state for now. - if self._receiver_type == AVR_X_2016.type: - if self._power == POWER_ON: - self._state = STATE_ON + try: + self._mute = root[3].find(zone).text + except (AttributeError, IndexError): + if compatibiliy_check is False: + _LOGGER.error("No MuteStatus found for zone %s", self.zone) + success = False + + try: + self._volume = root.find( + "./cmd/{zone}/volume".format(zone=zone)).text + except AttributeError: + if compatibiliy_check is False: + _LOGGER.error( + "No VolumeStatus found for zone %s", self.zone) + success = False + + try: + inputfunc = root.find( + "./cmd/{zone}/source".format(zone=zone)).text + except AttributeError: + if compatibiliy_check is False: + _LOGGER.error("No Source found for zone %s", self.zone) + success = False else: - self._state = STATE_OFF + try: + self._input_func = self._input_func_list_rev[inputfunc] + except KeyError: + _LOGGER.info("No mapping for source %s found", inputfunc) + self._input_func = inputfunc + # Update sources list if current source is not known yet + if self._update_input_func_list(): + _LOGGER.info("List of input functions refreshed.") + # If input function is still not known, log error. + if (inputfunc not in self._input_func_list and + inputfunc is not None): + _LOGGER.error( + "Input function %s is not known", + self._input_func) + else: + _LOGGER.error(( + "Input function list for Denon receiver at host %s" + " could not be updated."), self._host) + try: + self._sound_mode_raw = root[4][0].text.rstrip() + except (AttributeError, IndexError): + if compatibiliy_check is False: + _LOGGER.error( + "No SoundMode found for the main zone %s", self.zone + ) + success = False - self._update_tone_control() + # Now playing information is not implemented for 2016+ models, as + # a HEOS API query needed. So only sync the power state for now. + if self._receiver_type == AVR_X_2016.type: + if self._power == POWER_ON: + self._state = STATE_ON + else: + self._state = STATE_OFF - return success + return success def _update_input_func_list(self): """ diff --git a/setup.py b/setup.py index 7424c41..7127a9d 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ from setuptools import find_packages, setup setup(name='denonavr', - version='0.9.5.dev1', + version='0.9.5', description='Automation Library for Denon AVR receivers', long_description='Automation Library for Denon AVR receivers', url='https://github.com/scarface-4711/denonavr',