diff --git a/custom_components/alexa_media/alexa_entity.py b/custom_components/alexa_media/alexa_entity.py index ccd277b5..2e24f741 100644 --- a/custom_components/alexa_media/alexa_entity.py +++ b/custom_components/alexa_media/alexa_entity.py @@ -350,10 +350,11 @@ def parse_temperature_from_coordinator( coordinator: DataUpdateCoordinator, entity_id: str ) -> Optional[str]: """Get the temperature of an entity from the coordinator data.""" - value = parse_value_from_coordinator( + temperature = parse_value_from_coordinator( coordinator, entity_id, "Alexa.TemperatureSensor", "temperature" ) - return value.get("value") if value and "value" in value else None + _LOGGER.debug("parse_temperature_from_coordinator: %s", temperature) + return temperature def parse_air_quality_from_coordinator( diff --git a/custom_components/alexa_media/const.py b/custom_components/alexa_media/const.py index f9d05fa5..45b7b0fd 100644 --- a/custom_components/alexa_media/const.py +++ b/custom_components/alexa_media/const.py @@ -139,3 +139,177 @@ ALEXA_ICON_DEFAULT = "mdi:molecule" UPLOAD_PATH = "www/alexa_tts" + +# Note: Some of these are likely wrong +MODEL_IDS = { + "A10A33FOX2NUBK": "Echo Spot", + "A10L5JEZTKKCZ8": "Vobot Bunny", + "A11QM4H9HGV71H": "Echo Show 5 (Gen3)", + "A12GXV8XMS007S": "Fire TV (Gen1)", + "A12IZU8NMHSY5U": "Generic Device", + "A132LT22WVG6X5": "Samsung Soundbar Q700A", + "A13B2WB920IZ7X": "Samsung HW-Q70T Soundbar", + "A13W6HQIHKEN3Z": "Echo Auto", + "A14ZH95E6SE9Z1": "Bose Home Speaker 300", + "A15996VY63BQ2D": "Echo Show 8 (Gen2)", + "A15ERDAKK5HQQG": "Sonos", + "A15QWUTQ6FSMYX": "Echo Buds (Gen2)", + "A16MZVIFVHX6P6": "Generic Echo", + "A17LGWINFBUTZZ": "Anker Roav Viva", + "A18BI6KPKDOEI4": "Ecobee4", + "A18O6U1UQFJ0XK": "Echo Plus (Gen2)", + "A18TCD9FP10WJ9": "Orbi Voice", + "A18X8OBWBCSLD8": "Samsung Soundbar", + "A195TXHV1M5D4A": "Echo Auto", + "A1C66CX2XD756O": "Fire Tablet HD", + "A1EIANJ7PNB0Q7": "Echo Show 15 (Gen1)", + "A1ENT81UXFMNNO": "Unknown", + "A1ETW4IXK2PYBP": "Talk to Alexa", + "A1F1F76XIW4DHQ": "Unknown TV", + "A1F8D55J0FWDTN": "Fire TV (Toshiba)", + "A1H0CMF1XM0ZP4": "Bose SoundTouch 30", + "A1J16TEDOYCZTN": "Fire Tablet", + "A1JJ0KFC4ZPNJ3": "Echo Input", + "A1L4KDRIILU6N9": "Sony Speaker", + "A1LOQ8ZHF4G510": "Samsung Soundbar Q990B", + "A1M0A9L9HDBID3": "One-Link Safe and Sound", + "A1MUORL8FP149X": "Unknown", + "A1N9SW0I0LUX5Y": "Ford/Lincoln Alexa App", + "A1NL4BVLQ4L3N3": "Echo Show (Gen1)", + "A1NQ0LXWBGVQS9": "2021 Samsung QLED TV", + "A1P31Q3MOWSHOD": "Zolo Halo Speaker", + "A1P7E7V3FCZKU6": "Fire TV (Gen3)", + "A1Q69AKRWLJC0F": "TV", + "A1Q7QCGNMXAKYW": "Generic Tablet", + "A1QKZ9D0IJY332": "Samsung TV 2020-U", + "A1RABVCI4QCIKC": "Echo Dot (Gen3)", + "A1RTAM01W29CUP": "Windows App", + "A1SCI5MODUBAT1": "Pioneer DMH-W466NEX", + "A1TD5Z1R8IWBHA": "Tablet", + "A1VGB7MHSIEYFK": "Fire TV Cube Gen3", + "A1W2YILXTG9HA7": "Nextbase 522GW Dashcam", + "A1W46V57KES4B5": "Cable TV box Brazil", + "A1WZKXFLI43K86": "Fire TV Stick MAX", + "A1XWJRHALS1REP": "Echo Show 5 (Gen2)", + "A1Z88NGR2BK6A2": "Echo Show 8 (Gen1)", + "A25EC4GIHFOCSG": "Unrecognized Media Player", + "A25OJWHZA1MWNB": "2021 Samsung QLED TV", + "A265XOI9586NML": "Fire TV Stick", + "A27VEYGQBW3YR5": "Echo Link", + "A2A3XFQ1AVYLHZ": "SONY WF-1000XM5", + "A2BRQDVMSZD13S": "SURE Universal Remote", + "A2C8J6UHV0KFCV": "Alexa Gear", + "A2DS1Q2TPDJ48U": "Echo Dot Clock (Gen5)", + "A2E0SNTXJVT7WK": "Fire TV (Gen2)", + "A2E5N6DMWCW8MZ": "Brilliant Smart Switch", + "A2EZ3TS0L1S2KV": "Sonos Beam", + "A2GFL5ZMWNE0PX": "Fire TV (Gen3)", + "A2H4LV5GIZ1JFT": "Echo Dot Clock (Gen4)", + "A2HZENIFNYTXZD": "Facebook Portal", + "A2I0SCCU3561Y8": "Samsung Soundbar Q800A", + "A2IS7199CJBT71": "TV", + "A2IVLV5VM2W81": "Alexa Mobile Voice iOS", + "A2J0R2SD7G9LPA": "Lenovo SmartTab M10", + "A2JKHJ0PX4J3L3": "Fire TV Cube (Gen2)", + "A2LH725P8DQR2A": "Fabriq Riff", + "A2LWARUGJLBYEW": "Fire TV Stick (Gen2)", + "A2M35JJZWCQOMZ": "Echo Plus (Gen1)", + "A2M4YX06LWP8WI": "Fire Tablet", + "A2N49KXGVA18AR": "Fire Tablet HD 10 Plus", + "A2OSP3UA4VC85F": "Sonos", + "A2R2GLZH1DFYQO": "Zolo Halo Speaker", + "A2RU4B77X9R9NZ": "Echo Link Amp", + "A2TF17PFR55MTB": "Alexa Mobile Voice Android", + "A2TTLILJHVNI9X": "LG TV", + "A2U21SRK4QGSE1": "Echo Dot Clock (Gen4)", + "A2UONLFQW0PADH": "Echo Show 8 (Gen3)", + "A2V9UEGZ82H4KZ": "Fire Tablet HD 10", + "A2VAXZ7UNGY4ZH": "Wyze Headphones", + "A2WFDCBDEXOXR8": "Bose Soundbar 700", + "A2WN1FJ2HG09UN": "Ultimate Alexa App", + "A2X8WT9JELC577": "Ecobee5", + "A2XPGY5LRKB9BE": "Fitbit Versa 2", + "A2Y04QPFCANLPQ": "Bose QuietComfort 35 II", + "A303PJF6ISQ7IC": "Echo Auto", + "A30YDR2MK8HMRV": "Echo (Gen3)", + "A31DTMEEVDDOIV": "Fire TV Stick Lite", + "A324YMIUSWQDGE": "Samsung 8K TV", + "A32DDESGESSHZA": "Echo Dot (Gen3)", + "A32DOYMUN6DTXA": "Echo Dot (Gen3)", + "A339L426Y220I4": "Teufel Radio", + "A347G2JC8I4HC7": "Roav Car Charger Pro", + "A37CFAHI1O0CXT": "Logitech Blast", + "A37M7RU8Z6ZFB": "Garmin Speak", + "A37SHHQ3NUL7B5": "Bose Home Speaker 500", + "A38949IHXHRQ5P": "Echo Tap", + "A38BPK7OW001EX": "Raspberry Alexa", + "A38EHHIB10L47V": "Fire Tablet HD 8", + "A39BU42XNMN516": "Generic Device", + "A3B50IC5QPZPWP": "Polk Command Bar", + "A3B5K1G3EITBIF": "Facebook Portal", + "A3BRT6REMPQWA8": "Bose Home Speaker 450", + "A3BW5ZVFHRCQPO": "BMW Alexa Integration", + "A3C9PE6TNYLTCH": "Speaker Group", + "A3CY98NH016S5F": "Facebook Portal Mini", + "A3D4YURNTARP5K": "Facebook Portal TV", + "A3EVMLQTU6WL1W": "Fire TV (GenX)", + "A3F1S88NTZZXS9": "Dash Wand", + "A3FX4UWTP28V1P": "Echo (Gen3)", + "A3GFRGUNIGG1I5": "Samsung TV QN50Q60CAGXZD", + "A3HF4YRA2L7XGC": "Fire TV Cube", + "A3IYPH06PH1HRA": "Echo Frames", + "A3K69RS3EIMXPI": "Hisense Smart TV", + "A3KULB3NQN7Z1F": "Unknown TV", + "A3L0T0VL9A921N": "Fire Tablet HD 8", + "A3NPD82ABCPIDP": "Sonos Beam", + "A3QPPX1R9W5RJV": "Fabriq Chorus", + "A3QS1XP2U6UJX9": "SONY WF-1000XM4", + "A3R9S4ZZECZ6YL": "Fire Tablet HD 10", + "A3RBAYBE7VM004": "Echo Studio", + "A3RCTOK2V0A4ZG": "LG TV", + "A3RMGO6LYLH7YN": "Echo Dot (Gen4)", + "A3S5BH2HU6VAYF": "Echo Dot (Gen2)", + "A3SSG6GR8UU7SN": "Echo Sub", + "A3SSWQ04XYPXBH": "Generic Tablet", + "A3TCJ8RTT3NVI7": "Alexa Listens", + "A3VRME03NAXFUB": "Echo Flex", + "A4ZP7ZC4PI6TO": "Echo Show 5 (Gen1)", + "A4ZXE0RM7LQ7A": "Echo Dot (Gen5)", + "A52ARKF0HM2T4": "Facebook Portal+", + "A6SIQKETF3L2E": "Unknown Device", + "A7WXQPH584YP": "Echo (Gen2)", + "A81PNL0A63P93": "Home Remote", + "A8DM4FYR6D3HT": "TV", + "AA1IN44SS3X6O": "Ecobee Thermostat Premium", + "AB72C64C86AW2": "Echo (Gen1)", + "ABJ2EHL7HQT4L": "Unknown Amplifier", + "ADVBD696BHNV5": "Fire TV Stick (Gen1)", + "AE7X7Z227NFNS": "HiMirror Mini", + "AF473ZSOIRKFJ": "Onkyo VC-PX30", + "AFF50AL5E3DIU": "Fire TV (Insignia)", + "AFF5OAL5E3DIU": "Fire TV", + "AGHZIK8D6X7QR": "Fire TV", + "AHJYKVA63YCAQ": "Sonos", + "AIPK7MM90V7TB": "Echo Show 10 (Gen3)", + "AKKLQD9FZWWQS": "Jabra Elite", + "AKNO1N0KSFN8L": "Echo Dot (Gen1)", + "AKO51L5QAQKL2": "Alexa Jams", + "AKPGW064GI9HE": "Fire TV Stick 4K (Gen3)", + "ALT9P69K6LORD": "Echo Auto", + "AMCZ48H33RCDF": "Samsung HW-Q910B 9.1.2 ch Soundbar", + "AN630UQPG2CA4": "Fire TV (Toshiba)", + "AO6HHP9UE6EOF": "Unknown Media Device", + "AP1F6KUH00XPV": "Stereo/Subwoofer Pair", + "AP4RS91ZQ0OOI": "Fire TV (Toshiba)", + "APHEAY6LX7T13": "Samsung Smart Refrigerator", + "AQCGW9PSYWRF": "TV", + "AR6X0XNIME80V": "Unknown TV", + "ASQZWP4GPYUT7": "Echo Pop", + "ATNLRCEBX3W4P": "Generic Tablet", + "AUPUQSVCVHXP0": "Ecobee Switch+", + "AVD3HM0HOJAAL": "Sonos", + "AVE5HX13UR5NO": "Logitech Zero Touch", + "AVN2TMX8MU2YM": "Bose Home Speaker 500", + "AVU7CPPF2ZRAS": "Fire Tablet HD 8", + "AWZZ5CVHX2CD": "Echo Show (Gen2)", +} diff --git a/custom_components/alexa_media/media_player.py b/custom_components/alexa_media/media_player.py index ae2c79de..b5455f9b 100644 --- a/custom_components/alexa_media/media_player.py +++ b/custom_components/alexa_media/media_player.py @@ -44,6 +44,7 @@ DEPENDENT_ALEXA_COMPONENTS, MIN_TIME_BETWEEN_FORCED_SCANS, MIN_TIME_BETWEEN_SCANS, + MODEL_IDS, PLAY_SCAN_INTERVAL, UPLOAD_PATH, ) @@ -180,9 +181,14 @@ async def async_setup_entry(hass, config_entry, async_add_devices): _LOGGER.debug( "%s: Loading config entry for %s", hide_email(account), component ) - await hass.config_entries.async_forward_entry_setups( - config_entry, [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 return True raise ConfigEntryNotReady @@ -412,6 +418,15 @@ async def _refresh_if_no_audiopush(already_refreshed=False): self.hass.data[DATA_ALEXAMEDIA]["accounts"][email]["http2"] ) self.async_schedule_update_ha_state(force_refresh=force_refresh) + if self._last_called: + self.hass.bus.async_fire( + "alexa_media_last_called_event", + { + "last_called": self.device_serial_number, + "timestamp": self._last_called_timestamp, + "summary": self._last_called_summary, + }, + ) elif "bluetooth_change" in event: if event_serial == self.device_serial_number: _LOGGER.debug( @@ -1607,7 +1622,9 @@ def device_info(self): "identifiers": {(ALEXA_DOMAIN, self.unique_id)}, "name": self.name, "manufacturer": "Amazon", - "model": f"{self._device_family} {self._device_type}", + "model": MODEL_IDS.get( + self._device_type, f"{self._device_family} {self._device_type}" + ), "sw_version": self._software_version, } diff --git a/custom_components/alexa_media/sensor.py b/custom_components/alexa_media/sensor.py index 6ad68bba..e66ef2ae 100644 --- a/custom_components/alexa_media/sensor.py +++ b/custom_components/alexa_media/sensor.py @@ -182,9 +182,10 @@ async def create_temperature_sensors(account_dict, temperature_entities): coordinator = account_dict["coordinator"] for temp in temperature_entities: _LOGGER.debug( - "Creating entity %s for a temperature sensor with name %s", + "Creating entity %s for a temperature sensor with name %s (%s)", temp["id"], temp["name"], + temp, ) serial = temp["device_serial"] device_info = lookup_device_info(account_dict, serial) @@ -254,16 +255,25 @@ def __init__(self, coordinator, entity_id, name, media_player_device_id): """Initialize temperature sensor.""" super().__init__(coordinator) self.alexa_entity_id = entity_id + # Need to append "+temperature" because the Alexa entityId is for a physical device + # and a single physical device can have multiple HA entities + self._attr_unique_id = entity_id + "_temperature" self._attr_name = name + " Temperature" self._attr_device_class = SensorDeviceClass.TEMPERATURE self._attr_state_class = SensorStateClass.MEASUREMENT - self._attr_native_value: Optional[datetime.datetime] = ( + value_and_scale: Optional[datetime.datetime] = ( parse_temperature_from_coordinator(coordinator, entity_id) ) - self._attr_native_unit_of_measurement: Optional[str] = UnitOfTemperature.CELSIUS - # This includes "_temperature" because the Alexa entityId is for a physical device - # A single physical device could have multiple HA entities - self._attr_unique_id = entity_id + "_temperature" + self._attr_native_value = self._get_temperature_value(value_and_scale) + self._attr_native_unit_of_measurement = self._get_temperature_scale( + value_and_scale + ) + _LOGGER.debug( + "Coordinator init: %s: %s %s", + self._attr_name, + self._attr_native_value, + self._attr_native_unit_of_measurement, + ) self._attr_device_info = ( { "identifiers": {media_player_device_id}, @@ -276,11 +286,38 @@ def __init__(self, coordinator, entity_id, name, media_player_device_id): @callback def _handle_coordinator_update(self) -> None: """Handle updated data from the coordinator.""" - self._attr_native_value = parse_temperature_from_coordinator( + value_and_scale = parse_temperature_from_coordinator( self.coordinator, self.alexa_entity_id ) + self._attr_native_value = self._get_temperature_value(value_and_scale) + self._attr_native_unit_of_measurement = self._get_temperature_scale( + value_and_scale + ) + _LOGGER.debug( + "Coordinator update: %s: %s %s", + self._attr_name, + self._attr_native_value, + self._attr_native_unit_of_measurement, + ) super()._handle_coordinator_update() + def _get_temperature_value(self, value): + if value and "value" in value: + _LOGGER.debug("TemperatureSensor value: %s", value.get("value")) + return value.get("value") + return None + + def _get_temperature_scale(self, value): + if value and "scale" in value: + _LOGGER.debug("TemperatureSensor scale: %s", value.get("scale")) + if value.get("scale") == "CELSIUS": + return UnitOfTemperature.CELSIUS + if value.get("scale") == "FAHRENHEIT": + return UnitOfTemperature.FAHRENHEIT + if value.get("scale") == "KELVIN": + return UnitOfTemperature.KELVIN + return None + class AirQualitySensor(SensorEntity, CoordinatorEntity): """A air quality sensor reported by an Amazon indoor air quality monitor.""" diff --git a/custom_components/alexa_media/translations/ja.json b/custom_components/alexa_media/translations/ja.json new file mode 100644 index 00000000..610cb82c --- /dev/null +++ b/custom_components/alexa_media/translations/ja.json @@ -0,0 +1,106 @@ +{ + "config": { + "abort": { + "forgot_password": "The Forgot Password page was detected. This normally is the result of too may failed logins. Amazon may require action before a relogin can be attempted.", + "login_failed": "Alexa Media Player failed to login.", + "reauth_successful": "Alexa Media Player successfully reauthenticated. Please ignore the \"Aborted\" message from HA." + }, + "error": { + "2fa_key_invalid": "Invalid Built-In 2FA key", + "connection_error": "Error connecting; check network and retry", + "identifier_exists": "Email for Alexa URL already registered", + "invalid_credentials": "Invalid credentials", + "invalid_url": "URL is invalid: {message}", + "unable_to_connect_hass_url": "Unable to connect to Home Assistant url. Please check the External Url under Configuration -> General", + "unknown_error": "Unknown error: {message}" + }, + "step": { + "proxy_warning": { + "data": { + "proxy_warning": "Ignore and Continue - I understand that no support for login issues are provided for bypassing this warning." + }, + "description": "The HA server cannot connect to the URL provided: {hass_url}.\n> {error}\n\nTo fix this, please confirm your **HA server** can reach {hass_url}. This field is from the External Url under Configuration -> General but you can try your internal url.\n\nIf you are **certain** your client can reach this url, you can bypass this warning.", + "title": "Alexa Media Player - Unable to Connect to HA URL" + }, + "totp_register": { + "data": { + "registered": "OTP from the Built-in 2FA App Key confirmed successfully." + }, + "description": "**{email} - alexa.{url}** \nHave you successfully confirmed an OTP from the Built-in 2FA App Key with Amazon? \n >OTP Code {message}", + "title": "Alexa Media Player - OTP Confirmation" + }, + "user": { + "data": { + "debug": "Advanced debugging", + "email": "Email Address", + "exclude_devices": "Excluded device (comma separated)", + "extended_entity_discovery": "Include devices connected via Echo", + "hass_url": "Url to access Home Assistant", + "include_devices": "Included device (comma separated)", + "otp_secret": "Built-in 2FA App Key (automatically generate 2FA Codes). This is not six digits long.", + "password": "Password", + "queue_delay": "Seconds to wait to queue commands together", + "scan_interval": "Seconds between scans", + "securitycode": "[%key_id:55616596%]", + "url": "Amazon region domain (e.g., amazon.co.uk)" + }, + "description": "Please confirm the information below. For legacy configuration, disable `Use Login Proxy method` option.", + "title": "Alexa Media Player - Configuration" + } + } + }, + "options": { + "step": { + "init": { + "data": { + "debug": "Advanced debugging", + "exclude_devices": "Excluded device (comma separated)", + "extended_entity_discovery": "Include devices connected via Echo", + "hass_url": "Public URL to access Home Assistant (including trailing '/')", + "include_devices": "Included device (comma separated)", + "public_url": "Public URL to access Home Assistant (including trailing '/')", + "queue_delay": "Seconds to wait to queue commands together", + "scan_interval": "Seconds between scans" + }, + "description": "Required *", + "title": "Alexa Media Player - Reconfiguration" + } + } + }, + "services": { + "clear_history": { + "description": "Clear last entries from Alexa Voice history for each Alexa account.", + "fields": { + "email": { + "description": "Accounts to clear. Empty will clear all.", + "name": "Email address" + }, + "entries": { + "description": "Number of entries to clear from 1 to 50. If empty, clear 50.", + "name": "Number of Entries" + } + }, + "name": "Clear Amazon Voice History" + }, + "force_logout": { + "description": "Force account to logout. Used mainly for debugging.", + "fields": { + "email": { + "description": "Accounts to clear. Empty will clear all.", + "name": "Email address" + } + }, + "name": "Force Logout" + }, + "update_last_called": { + "description": "Forces update of last_called echo device for each Alexa account.", + "fields": { + "email": { + "description": "List of Alexa accounts to update. If empty, will update all known accounts.", + "name": "Email address" + } + }, + "name": "Update Last Called Sensor" + } + } +}