Skip to content

Commit

Permalink
chore: update dependencies via HACS
Browse files Browse the repository at this point in the history
  • Loading branch information
aronnebrivio committed Nov 2, 2024
1 parent f786179 commit 7a0437c
Show file tree
Hide file tree
Showing 123 changed files with 35,338 additions and 23,284 deletions.
24 changes: 12 additions & 12 deletions custom_components/alexa_media/.translations/de.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"config": {
"abort": {
"forgot_password": "Die \"Passwort vergessen Funktion\" wurde erkannt. Amazon verlangt möglicherweise Maßnahmen bevor ein erneuter Anmeldeversuch unternommen werden kann.",
"forgot_password": "Die \"Passwort vergessen Seite\" wurde erkannt. Amazon verlangt möglicherweise Maßnahmen bevor ein erneuter Anmeldeversuch unternommen werden kann.",
"login_failed": "Alexa Media Player konnte nicht angemeldet werden.",
"reauth_successful": "Alexa Media Player erfolgreich authentifiziert"
},
"error": {
"2fa_key_invalid": "Ungültiger 2-Faktor Schlüssel",
"connection_error": "Verbindungsfehler; Netzwerk prüfen und erneut versuchen",
"identifier_exists": "Diese E-Mail-Adresse ist bereits registriert",
"invalid_credentials": "Falsche Zugangsdaten",
"invalid_credentials": "Ungültige Zugangsdaten",
"invalid_url": "URL ist ungültig: {message}",
"unable_to_connect_hass_url": "Es kann keine Verbindung zur Home Assistant-URL hergestellt werden. Bitte überprüfen Sie die externe URL unter Konfiguration -> Allgemein",
"unknown_error": "Unbekannter Fehler: {message}"
Expand Down Expand Up @@ -39,7 +39,7 @@
"include_devices": "Eingebundene Geräte (Komma getrennt)",
"otp_secret": "Integrierter 2FA-App-Schlüssel (automatisch generierte 2FA-Code)",
"password": "Passwort",
"queue_delay": "Sekunden zu warten, um Befehle in die Warteschlange zu stellen",
"queue_delay": "Zu wartende Sekunden, um Befehle in die Warteschlange zu stellen",
"scan_interval": "Sekunden zwischen den Scans",
"securitycode": "2FA-Code (empfohlen, um Anmeldeprobleme zu vermeiden)",
"url": "Amazon Region (z.B. amazon.de)"
Expand All @@ -56,10 +56,10 @@
"debug": "Erweitertes Debugging",
"exclude_devices": "Ausgeschlossene Geräte (Komma getrennt)",
"extended_entity_discovery": "Schließen Sie Geräte ein, die über Echo verbunden sind",
"hass_url": "Öffentliche URL zum Zugriff auf Home Assistant (einschließlich '/' am Ende)",
"hass_url": "Öffentliche URL für den Zugriff auf Home Assistant (einschließlich '/' am Ende)",
"include_devices": "Eingebundene Geräte (Komma getrennt)",
"public_url": "Öffentliche URL zum Zugriff auf Home Assistant (einschließlich '/' am Ende)",
"queue_delay": "Sekunden zu warten, um Befehle in die Warteschlange zu stellen",
"public_url": "Öffentliche URL für den Zugriff auf Home Assistant (einschließlich '/' am Ende)",
"queue_delay": "Zu wartende Sekunden, um Befehle in die Warteschlange zu stellen",
"scan_interval": "Sekunden zwischen den Scans"
},
"description": "Erforderlich *",
Expand All @@ -73,7 +73,7 @@
"fields": {
"email": {
"description": "Zu löschende Accounts. Falls leer, werden alle gelöscht.",
"name": "E-Mail Adresse"
"name": "E-Mail-Adresse"
},
"entries": {
"description": "Zu löschende Einträge von 1 bis 50. Falls leer, lösche 50.",
Expand All @@ -83,21 +83,21 @@
"name": "Lösche Alexa Stimmbefehl-Historie."
},
"force_logout": {
"description": "Logout erzwingen. Primär für Debugging notwendig.",
"description": "Logout erzwingen. Primär für Debugging genutzt.",
"fields": {
"email": {
"description": "Zu löschende Accounts. Falls leer werden alle gelöscht.",
"name": "E-Mail Adresse"
"description": "Zu löschende Accounts. Falls leer werden alle gelöscht.",
"name": "E-Mail-Adresse"
}
},
"name": "Logout erzwingen"
},
"update_last_called": {
"description": "Erzwinge Updates der zuletzt aufgerufenen Echo Geräte für jeden Alexa Account ",
"description": "Erzwinge Updates der zuletzt aufgerufenen Echo Geräte für jeden Alexa Account.",
"fields": {
"email": {
"description": "Liste der zu aktualisierenden Alexa-Konten. Wenn leer, werden alle bekannten Konten aktualisiert.",
"name": "E-Mail Adresse"
"name": "E-Mail-Adresse"
}
},
"name": "Aktualisiere den zuletzt aufgerufenen Sensor"
Expand Down
6 changes: 3 additions & 3 deletions custom_components/alexa_media/.translations/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"debug": "Débogage avancé",
"email": "Adresse Email",
"exclude_devices": "Appareil exclu (séparé par des virgules)",
"extended_entity_discovery": "Include devices connected via Echo",
"extended_entity_discovery": "Inclure les appareils connectés via Echo",
"hass_url": "URL pour accéder à Home Assistant",
"include_devices": "Appareil inclus (séparé par des virgules)",
"otp_secret": "Clé d'application 2FA (2 facteurs) intégrée (génère automatiquement des codes 2FA). Il ne s'agit pas d'un code à six chiffres.",
Expand Down Expand Up @@ -62,8 +62,8 @@
"queue_delay": "Secondes à attendre pour mettre les commandes en file d'attente ensemble",
"scan_interval": "Secondes entre les analyses"
},
"description": "Required *",
"title": "Alexa Media Player - Reconfiguration"
"description": "Requis *",
"title": "Alexa Media Player Reconfiguration"
}
}
},
Expand Down
106 changes: 76 additions & 30 deletions custom_components/alexa_media/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,11 @@ async def setup_alexa(hass, config_entry, login_obj: AlexaLogin):
# pylint: disable=too-many-statements,too-many-locals
"""Set up a alexa api based on host parameter."""

# Initialize throttling state and lock
last_dnd_update_times: dict[str, datetime] = {}
pending_dnd_updates: dict[str, bool] = {}
dnd_update_lock = asyncio.Lock()

async def async_update_data() -> Optional[AlexaEntityData]:
# noqa pylint: disable=too-many-branches
"""Fetch data from API endpoint.
Expand Down Expand Up @@ -644,31 +649,17 @@ async def async_update_data() -> Optional[AlexaEntityData]:
cleaned_config = config.copy()
cleaned_config.pop(CONF_PASSWORD, None)
# CONF_PASSWORD contains sensitive info which is no longer needed
for component in ALEXA_COMPONENTS:
entry_setup = len(
hass.data[DATA_ALEXAMEDIA]["accounts"][email]["entities"][component]
# Load multiple platforms in parallel using async_forward_entry_setups
_LOGGER.debug("Loading platforms: %s", ", ".join(ALEXA_COMPONENTS))
try:
await hass.config_entries.async_forward_entry_setups(
config_entry, ALEXA_COMPONENTS
)
if not entry_setup:
_LOGGER.debug("Loading config entry for %s", component)
try:
await hass.config_entries.async_forward_entry_setups(
config_entry, [component]
)
except (asyncio.TimeoutError, TimeoutException) as ex:
raise ConfigEntryNotReady(
f"Timeout while loading config entry for {component}"
) from ex
else:
_LOGGER.debug("Loading %s", component)
hass.async_create_task(
async_load_platform(
hass,
component,
DOMAIN,
{CONF_NAME: DOMAIN, "config": cleaned_config},
cleaned_config,
)
)
except (asyncio.TimeoutError, TimeoutException) as ex:
_LOGGER.error(f"Error while loading platforms: {ex}")
raise ConfigEntryNotReady(
f"Timeout while loading platforms: {ex}"
) from ex

hass.data[DATA_ALEXAMEDIA]["accounts"][email]["new_devices"] = False
# prune stale devices
Expand Down Expand Up @@ -843,21 +834,76 @@ async def update_bluetooth_state(login_obj, device_serial):
)
return None

@util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
async def schedule_update_dnd_state(email: str):
"""Schedule an update_dnd_state call after MIN_TIME_BETWEEN_FORCED_SCANS."""
await asyncio.sleep(MIN_TIME_BETWEEN_FORCED_SCANS)
async with dnd_update_lock:
if pending_dnd_updates.get(email, False):
pending_dnd_updates[email] = False
_LOGGER.debug(
"Executing scheduled forced DND update for %s", hide_email(email)
)
# Assume login_obj can be retrieved or passed appropriately
login_obj = hass.data[DATA_ALEXAMEDIA]["accounts"][email]["login_obj"]
await update_dnd_state(login_obj)

@_catch_login_errors
async def update_dnd_state(login_obj) -> None:
"""Update the dnd state on ws dnd combo event."""
dnd = await AlexaAPI.get_dnd_state(login_obj)
"""Update the DND state on websocket DND combo event."""
email = login_obj.email
now = datetime.utcnow()

async with dnd_update_lock:
last_run = last_dnd_update_times.get(email)
cooldown = timedelta(seconds=MIN_TIME_BETWEEN_SCANS)

if last_run and (now - last_run) < cooldown:
# If within cooldown, mark a pending update if not already marked
if not pending_dnd_updates.get(email, False):
pending_dnd_updates[email] = True
_LOGGER.debug(
"Throttling active for %s, scheduling a forced DND update.",
hide_email(email),
)
asyncio.create_task(schedule_update_dnd_state(email))
else:
_LOGGER.debug(
"Throttling active for %s, forced DND update already scheduled.",
hide_email(email),
)
return

# Update the last run time
last_dnd_update_times[email] = now

_LOGGER.debug("Updating DND state for %s", hide_email(email))

try:
# Fetch the DND state using the Alexa API
dnd = await AlexaAPI.get_dnd_state(login_obj)
except asyncio.TimeoutError:
_LOGGER.error(
"Timeout occurred while fetching DND state for %s", hide_email(email)
)
return
except Exception as e:
_LOGGER.error(
"Unexpected error while fetching DND state for %s: %s",
hide_email(email),
e,
)
return

# Check if DND data is valid and dispatch an update event
if dnd is not None and "doNotDisturbDeviceStatusList" in dnd:
async_dispatcher_send(
hass,
f"{DOMAIN}_{hide_email(email)}"[0:32],
{"dnd_update": dnd["doNotDisturbDeviceStatusList"]},
)
return
_LOGGER.debug("%s: get_dnd_state failed: dnd:%s", hide_email(email), dnd)
return
else:
_LOGGER.debug("%s: get_dnd_state failed: dnd:%s", hide_email(email), dnd)

async def http2_connect() -> HTTP2EchoClient:
"""Open HTTP2 Push connection.
Expand Down Expand Up @@ -1363,7 +1409,7 @@ async def async_remove_entry(hass, entry) -> bool:
login_obj = AlexaLogin(
url="",
email=email,
password="",
password="", # nosec
outputpath=hass.config.path,
)
# Delete cookiefile
Expand Down
23 changes: 8 additions & 15 deletions custom_components/alexa_media/alarm_control_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,8 @@
from typing import List, Optional

from alexapy import hide_email, hide_serial
from homeassistant.const import (
CONF_EMAIL,
STATE_ALARM_ARMED_AWAY,
STATE_ALARM_DISARMED,
STATE_UNAVAILABLE,
)
from homeassistant.components.alarm_control_panel import AlarmControlPanelEntity
from homeassistant.const import CONF_EMAIL, STATE_UNAVAILABLE
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.update_coordinator import CoordinatorEntity

Expand All @@ -34,13 +30,12 @@
from .helpers import _catch_login_errors, add_devices

try:
from homeassistant.components.alarm_control_panel import (
AlarmControlPanelEntity as AlarmControlPanel,
)
except ImportError:
from homeassistant.components.alarm_control_panel import AlarmControlPanel

from homeassistant.components.alarm_control_panel import AlarmControlPanelState

STATE_ALARM_ARMED_AWAY = AlarmControlPanelState.ARMED_AWAY
STATE_ALARM_DISARMED = AlarmControlPanelState.DISARMED
except ImportError:
from homeassistant.const import STATE_ALARM_ARMED_AWAY, STATE_ALARM_DISARMED
_LOGGER = logging.getLogger(__name__)

DEPENDENCIES = [ALEXA_DOMAIN]
Expand Down Expand Up @@ -135,7 +130,7 @@ async def async_unload_entry(hass, entry) -> bool:
return True


class AlexaAlarmControlPanel(AlarmControlPanel, AlexaMedia, CoordinatorEntity):
class AlexaAlarmControlPanel(AlarmControlPanelEntity, AlexaMedia, CoordinatorEntity):
"""Implementation of Alexa Media Player alarm control panel."""

def __init__(self, login, coordinator, guard_entity, media_players=None) -> None:
Expand Down Expand Up @@ -224,8 +219,6 @@ def state(self):
)
if _state == "ARMED_AWAY":
return STATE_ALARM_ARMED_AWAY
if _state == "ARMED_STAY":
return STATE_ALARM_DISARMED
return STATE_ALARM_DISARMED

@property
Expand Down
10 changes: 9 additions & 1 deletion custom_components/alexa_media/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,11 @@ async def async_step_user(self, user_input=None):

""" External URL for cloud connected services """
try:
DEFAULT_PUBLIC_URL: str = get_url(self.hass, allow_internal=False)
url: str = get_url(self.hass, allow_internal=False)
except NoURLAvailableError:
DEFAULT_PUBLIC_URL = ""
else:
DEFAULT_PUBLIC_URL = url if url.endswith("/") else url + "/"

self.proxy_schema = OrderedDict(
[
Expand Down Expand Up @@ -768,6 +770,8 @@ def _save_user_input_to_config(self, user_input=None) -> None:
if CONF_URL in user_input:
self.config[CONF_URL] = user_input[CONF_URL]
if CONF_PUBLIC_URL in user_input:
if not user_input[CONF_PUBLIC_URL].endswith("/"):
user_input[CONF_PUBLIC_URL] = user_input[CONF_PUBLIC_URL] + "/"
self.config[CONF_PUBLIC_URL] = user_input[CONF_PUBLIC_URL]
if CONF_SCAN_INTERVAL in user_input:
self.config[CONF_SCAN_INTERVAL] = (
Expand Down Expand Up @@ -951,6 +955,10 @@ async def async_step_init(
user_input[CONF_OTPSECRET] = self.config_entry.data[CONF_OTPSECRET]
if CONF_OAUTH in self.config_entry.data:
user_input[CONF_OAUTH] = self.config_entry.data[CONF_OAUTH]
"""Ensure public_url ends with trailing slash"""
if CONF_PUBLIC_URL in self.config_entry.data:
if not user_input[CONF_PUBLIC_URL].endswith("/"):
user_input[CONF_PUBLIC_URL] = user_input[CONF_PUBLIC_URL] + "/"

self.hass.config_entries.async_update_entry(
self.config_entry, data=user_input, options=self.config_entry.options
Expand Down
2 changes: 1 addition & 1 deletion custom_components/alexa_media/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
PERCENTAGE,
)

__version__ = "4.13.2"
__version__ = "4.13.7"
PROJECT_URL = "https://github.com/alandtse/alexa_media_player/"
ISSUE_URL = f"{PROJECT_URL}issues"
NOTIFY_URL = f"{PROJECT_URL}wiki/Configuration%3A-Notification-Component#use-the-notifyalexa_media-service"
Expand Down
Loading

0 comments on commit 7a0437c

Please sign in to comment.