Skip to content

Commit

Permalink
Reduce read timeout during receiver identification / protect setup me…
Browse files Browse the repository at this point in the history
…thods with locks
  • Loading branch information
ol-iver committed Jan 23, 2022
1 parent 0a3a3d5 commit 3d33c19
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 32 deletions.
29 changes: 16 additions & 13 deletions denonavr/denonavr.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
:license: MIT, see LICENSE for more details.
"""

import asyncio
import logging
import time

Expand Down Expand Up @@ -76,6 +77,7 @@ class DenonAVR(DenonAVRFoundation):
attr.validators.instance_of(dict)),
default=attr.Factory(dict),
init=False)
_setup_lock: asyncio.Lock = attr.ib(default=attr.Factory(asyncio.Lock))
audyssey: DenonAVRAudyssey = attr.ib(
validator=attr.validators.instance_of(DenonAVRAudyssey),
default=attr.Factory(audyssey_factory, takes_self=True),
Expand Down Expand Up @@ -128,19 +130,20 @@ def create_zones(self, add_zones):

async def async_setup(self) -> None:
"""Ensure that configuration is loaded from receiver asynchronously."""
# Device setup
await self._device.async_setup()
if self._name is None:
self._name = self._device.friendly_name

# Setup other functions
self.input.setup()
await self.soundmode.async_setup()
self.tonecontrol.setup()
self.vol.setup()
self.audyssey.setup()

self._is_setup = True
async with self._setup_lock:
# Device setup
await self._device.async_setup()
if self._name is None:
self._name = self._device.friendly_name

# Setup other functions
self.input.setup()
await self.soundmode.async_setup()
self.tonecontrol.setup()
self.vol.setup()
self.audyssey.setup()

self._is_setup = True

@run_async_synchronously(async_func=async_setup)
def setup(self) -> None:
Expand Down
25 changes: 18 additions & 7 deletions denonavr/foundation.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
:license: MIT, see LICENSE for more details.
"""

import asyncio
import logging
import xml.etree.ElementTree as ET

Expand Down Expand Up @@ -67,6 +68,7 @@ class DenonAVRDeviceInfo:
_is_setup: bool = attr.ib(converter=bool, default=False, init=False)
_allow_recovery: bool = attr.ib(
converter=bool, default=False, init=False)
_setup_lock: asyncio.Lock = attr.ib(default=attr.Factory(asyncio.Lock))

def __attrs_post_init__(self) -> None:
"""Initialize special attributes."""
Expand All @@ -93,15 +95,24 @@ def get_own_zone(self):

async def async_setup(self) -> None:
"""Ensure that configuration is loaded from receiver asynchronously."""
# Own setup
await self.async_identify_receiver()
await self.async_get_device_info()
await self.async_identify_update_method()
async with self._setup_lock:
# Own setup
# Reduce read timeout during receiver identification
# deviceinfo endpoint takes very long to return 404
timeout = self.api.timeout
self.api.timeout = httpx.Timeout(self.api.timeout.connect)
try:
await self.async_identify_receiver()
await self.async_get_device_info()
finally:
self.api.timeout = timeout
await self.async_identify_update_method()

# Add tags for a potential AppCommand.xml update
self.api.add_appcommand_update_tag(AppCommands.GetAllZonePowerStatus)
# Add tags for a potential AppCommand.xml update
self.api.add_appcommand_update_tag(
AppCommands.GetAllZonePowerStatus)

self._is_setup = True
self._is_setup = True

async def async_update(
self,
Expand Down
27 changes: 15 additions & 12 deletions denonavr/soundmode.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
:license: MIT, see LICENSE for more details.
"""

import asyncio
from copy import deepcopy
import logging

Expand Down Expand Up @@ -76,6 +77,7 @@ class DenonAVRSoundMode(DenonAVRFoundation):
attr.validators.instance_of(dict)),
default=attr.Factory(sound_mode_rev_map_factory, takes_self=True),
init=False)
_setup_lock: asyncio.Lock = attr.ib(default=attr.Factory(asyncio.Lock))

# Update tags for attributes
# AppCommand.xml interface
Expand All @@ -89,18 +91,19 @@ class DenonAVRSoundMode(DenonAVRFoundation):

async def async_setup(self) -> None:
"""Ensure that the instance is initialized."""
# Add tags for a potential AppCommand.xml update
for tag in self.appcommand_attrs:
self._device.api.add_appcommand_update_tag(tag)

# Soundmode is always available for AVR-X and AVR-X-2016 receivers
# For AVR receiver it will be tested druing the first update
if self._device.receiver in [AVR_X, AVR_X_2016]:
self._support_sound_mode = True
else:
await self.async_update_sound_mode()

self._is_setup = True
async with self._setup_lock:
# Add tags for a potential AppCommand.xml update
for tag in self.appcommand_attrs:
self._device.api.add_appcommand_update_tag(tag)

# Soundmode is always available for AVR-X and AVR-X-2016 receivers
# For AVR receiver it will be tested druing the first update
if self._device.receiver in [AVR_X, AVR_X_2016]:
self._support_sound_mode = True
else:
await self.async_update_sound_mode()

self._is_setup = True

async def async_update(
self,
Expand Down

0 comments on commit 3d33c19

Please sign in to comment.