Skip to content

Commit

Permalink
Merge branch 'alandtse:dev' into config_flow
Browse files Browse the repository at this point in the history
  • Loading branch information
danielbrunt57 authored Dec 30, 2024
2 parents 67b51f3 + 7c450e0 commit 62e2b30
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 24 deletions.
6 changes: 4 additions & 2 deletions custom_components/alexa_media/alexa_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,10 @@ def is_alexa_guard(appliance: dict[str, Any]) -> bool:

def is_temperature_sensor(appliance: dict[str, Any]) -> bool:
"""Is the given appliance the temperature sensor of an Echo."""
return is_local(appliance) and has_capability(
appliance, "Alexa.TemperatureSensor", "temperature"
return (
is_local(appliance)
and has_capability(appliance, "Alexa.TemperatureSensor", "temperature")
and appliance["friendlyDescription"] != "Amazon Indoor Air Quality Monitor"
)


Expand Down
3 changes: 2 additions & 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__ = "5.1.0"
__version__ = "5.3.0"
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 Expand Up @@ -67,6 +67,7 @@
DEFAULT_SCAN_INTERVAL = 60

SERVICE_UPDATE_LAST_CALLED = "update_last_called"
SERVICE_RESTORE_VOLUME = "restore_volume"
SERVICE_FORCE_LOGOUT = "force_logout"

RECURRING_PATTERN = {
Expand Down
2 changes: 1 addition & 1 deletion custom_components/alexa_media/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
"issue_tracker": "https://github.com/alandtse/alexa_media_player/issues",
"loggers": ["alexapy", "authcaptureproxy"],
"requirements": ["alexapy==1.29.5", "packaging>=20.3", "wrapt>=1.14.0"],
"version": "5.1.0"
"version": "5.3.0"
}
19 changes: 15 additions & 4 deletions custom_components/alexa_media/media_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ def __init__(self, device, login, second_account_index=0):
self._media_is_muted = None
self._media_vol_level = None
self._previous_volume = None
self._saved_volume = None
self._source = None
self._source_list = []
self._connected_bluetooth = None
Expand Down Expand Up @@ -1129,14 +1130,23 @@ async def async_set_volume_level(self, volume):
"""Set volume level, range 0..1."""
if not self.available:
return

# Save the current volume level before we change it
_LOGGER.debug("Saving previous volume level: %s", self.volume_level)
self._previous_volume = self.volume_level

# Change the volume level on the device
if self.hass:
self.hass.async_create_task(self.alexa_api.set_volume(volume))
else:
await self.alexa_api.set_volume(volume)
self._media_vol_level = volume

# Let http2push update the new volume level
if not (
self.hass.data[DATA_ALEXAMEDIA]["accounts"][self._login.email]["http2"]
):
# Otherwise we do it ourselves
await self.async_update()

@property
Expand Down Expand Up @@ -1164,19 +1174,19 @@ async def async_mute_volume(self, mute):

self._media_is_muted = mute
if mute:
self._previous_volume = self.volume_level
self._saved_volume = self.volume_level
if self.hass:
self.hass.async_create_task(self.alexa_api.set_volume(0))
else:
await self.alexa_api.set_volume(0)
else:
if self._previous_volume is not None:
if self._saved_volume is not None:
if self.hass:
self.hass.async_create_task(
self.alexa_api.set_volume(self._previous_volume)
self.alexa_api.set_volume(self._saved_volume)
)
else:
await self.alexa_api.set_volume(self._previous_volume)
await self.alexa_api.set_volume(self._saved_volume)
else:
if self.hass:
self.hass.async_create_task(self.alexa_api.set_volume(50))
Expand Down Expand Up @@ -1620,6 +1630,7 @@ def extra_state_attributes(self):
"last_called_summary": self._last_called_summary,
"connected_bluetooth": self._connected_bluetooth,
"bluetooth_list": self._bluetooth_list,
"previous_volume": self._previous_volume,
}
return attr

Expand Down
73 changes: 64 additions & 9 deletions custom_components/alexa_media/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@

from alexapy import AlexaAPI, AlexapyLoginError, hide_email
from alexapy.errors import AlexapyConnectionError
from homeassistant.helpers import config_validation as cv
from homeassistant.const import ATTR_DEVICE_ID, ATTR_ENTITY_ID
from homeassistant.helpers import config_validation as cv, entity_registry as er
import voluptuous as vol

from .const import (
Expand All @@ -21,6 +22,7 @@
DATA_ALEXAMEDIA,
DOMAIN,
SERVICE_FORCE_LOGOUT,
SERVICE_RESTORE_VOLUME,
SERVICE_UPDATE_LAST_CALLED,
)
from .helpers import _catch_login_errors, report_relogin_required
Expand All @@ -34,6 +36,7 @@
LAST_CALL_UPDATE_SCHEMA = vol.Schema(
{vol.Optional(ATTR_EMAIL, default=[]): vol.All(cv.ensure_list, [cv.string])}
)
RESTORE_VOLUME_SCHEMA = vol.Schema({vol.Required(ATTR_ENTITY_ID): cv.entity_id})


class AlexaMediaServices:
Expand All @@ -46,31 +49,41 @@ def __init__(self, hass, functions: dict[str, Callable]):

async def register(self):
"""Register services to hass."""
self.hass.services.async_register(
DOMAIN, SERVICE_FORCE_LOGOUT, self.force_logout, schema=FORCE_LOGOUT_SCHEMA
)
self.hass.services.async_register(
DOMAIN,
SERVICE_UPDATE_LAST_CALLED,
self.last_call_handler,
schema=LAST_CALL_UPDATE_SCHEMA,
)
self.hass.services.async_register(
DOMAIN, SERVICE_FORCE_LOGOUT, self.force_logout, schema=FORCE_LOGOUT_SCHEMA
DOMAIN,
SERVICE_RESTORE_VOLUME,
self.restore_volume,
schema=RESTORE_VOLUME_SCHEMA,
)

async def unregister(self):
"""Register services to hass."""
"""Deregister services from hass."""
self.hass.services.async_remove(DOMAIN, SERVICE_FORCE_LOGOUT)
self.hass.services.async_remove(
DOMAIN,
SERVICE_UPDATE_LAST_CALLED,
)
self.hass.services.async_remove(DOMAIN, SERVICE_FORCE_LOGOUT)
self.hass.services.async_remove(
DOMAIN,
SERVICE_RESTORE_VOLUME,
)

@_catch_login_errors
async def force_logout(self, call) -> bool:
"""Handle force logout service request.
Arguments
call.ATTR_EMAIL {List[str: None]} -- Case-sensitive Alexa emails.
Default is all known emails.
call.ATTR_EMAIL {List[str: None]}: List of case-sensitive Alexa emails.
If None, all accounts are logged out.
Returns
bool -- True if force logout successful
Expand All @@ -97,12 +110,13 @@ async def force_logout(self, call) -> bool:
)
return success

@_catch_login_errors
async def last_call_handler(self, call):
"""Handle last call service request.
Args
call: List of case-sensitive Alexa email addresses. If None
all accounts are updated.
Arguments
call.ATTR_EMAIL: {List[str: None]}: List of case-sensitive Alexa emails.
If None, all accounts are updated.
"""
requested_emails = call.data.get(ATTR_EMAIL)
Expand All @@ -121,3 +135,44 @@ async def last_call_handler(self, call):
" check your network connection and try again",
hide_email(email),
)

async def restore_volume(self, call) -> bool:
"""Handle restore volume service request.
Arguments
call.ATTR_ENTITY_ID {str: None} -- Alexa media player entity.
"""
entity_id = call.data.get(ATTR_ENTITY_ID)
_LOGGER.debug("Service restore_volume called for: %s", entity_id)

# Retrieve the entity registry and entity entry
entity_registry = er.async_get(self.hass)
entity_entry = entity_registry.async_get(entity_id)

if not entity_entry:
_LOGGER.error("Entity %s not found in registry", entity_id)
return False

# Retrieve the previous volume from the entity's state attributes
state = self.hass.states.get(entity_id)
if not state or "previous_volume" not in state.attributes:
_LOGGER.error(
"Previous volume attribute not found for entity %s", entity_id
)
return False

previous_volume = state.attributes["previous_volume"]

# Call the volume_set service with the retrieved volume
await self.hass.services.async_call(
domain="media_player",
service="volume_set",
service_data={
"volume_level": previous_volume,
},
target={"entity_id": entity_id},
)

_LOGGER.debug("Volume restored to %s for entity %s", previous_volume, entity_id)
return True
24 changes: 18 additions & 6 deletions custom_components/alexa_media/services.yaml
Original file line number Diff line number Diff line change
@@ -1,24 +1,36 @@
# SPDX-License-Identifier: Apache-2.0
update_last_called:
force_logout:
# Description of the service
description: Forces update of last_called echo device for each Alexa account.
description: Force logout of Alexa Login account and deletion of .pickle. Intended for debugging use.
# Different fields that your service accepts
fields:
# Key of the field
email:
# Description of the field
description: List of Alexa accounts to update. If empty, will update all known accounts.
description: List of Alexa accounts to log out. If empty, will log out from all known accounts.
# Example value that can be passed for this field
example: "[email protected]"

force_logout:
restore_volume:
description: Restores an Alexa Media Player volume level to the previous volume level.
fields:
entity_id:
name: Entity
description: Alexa Media Player device to restore volume on.
required: true
selector:
entity:
domain: media_player
integration: alexa_media

update_last_called:
# Description of the service
description: Force logout of Alexa Login account and deletion of .pickle. Intended for debugging use.
description: Forces update of last_called echo device for each Alexa account.
# Different fields that your service accepts
fields:
# Key of the field
email:
# Description of the field
description: List of Alexa accounts to log out. If empty, will log out from all known accounts.
description: List of Alexa accounts to update. If empty, will update all known accounts.
# Example value that can be passed for this field
example: "[email protected]"
10 changes: 10 additions & 0 deletions custom_components/alexa_media/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,16 @@
}
}
},
"restore_volume": {
"description": "Restore previous volume level on Alexa media player device",
"fields": {
"entity_id": {
"description": "Entity to restore the previous volume level on",
"name": "Select media player:"
}
},
"name": "Restore Previous Volume"
},
"update_last_called": {
"name": "Update Last Called Sensor",
"description": "Forces update of last_called echo device for each Alexa account.",
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "alexa_media_player"
version = "5.1.0"
version = "5.3.0"
description = "This is a custom component to allow control of Amazon Alexa devices in [Homeassistant](https://home-assistant.io) using the unofficial Alexa API."
authors = [
"Keaton Taylor <[email protected]>",
Expand Down

0 comments on commit 62e2b30

Please sign in to comment.