Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to utilize the new alexa_media_last_called_event in AMP 4.13.x #2535

Closed
danielbrunt57 opened this issue Sep 17, 2024 · 12 comments
Closed

Comments

@danielbrunt57
Copy link
Collaborator

danielbrunt57 commented Sep 17, 2024

Describe the implementation

AMP 4.13.x will now fire an event when last_called is updated.

"alexa_media_last_called_event",
                    {
                        "last_called": self.device_serial_number,
                        "timestamp": self._last_called_timestamp,
                        "summary": self._last_called_summary,
                    },

This can be used in automations/scripts to wait for that event before continuing. I've implemented a timeout in the wait_for_trigger of 30 seconds but that might be excessive. You just want to avoid too short of a timeout in which the update last_called can succeed. This wait_for_trigger avoids the necessity of delaying a fixed period of time before proceeding after manually calling alexa_media.update_last_called. This method can then ensure that last_called was truly updated. It can also be utilized to avoid the case where the integration is not working and your scripts/automations fail to execute as the Alexa service/entity does not exist by checking if the wait_for_trigger timed out and then skipping the relevant alexa service calls.

wait_for_trigger:
  - platform: event
    event_type: alexa_media_last_called_event
    event_data:
      summary: good night
timeout:
  hours: 0
  minutes: 0
  seconds: 30
  milliseconds: 0
continue_on_timeout: true

The event_data: summary: good night is the last thing in the history records that was said to Alexa and is included in the event so you can include that to ensure the last_called is truly correct for this script, or you can elect to not include that check and just proceed.

Following that, I have:

variables:
  alexa_last_called_updated: "{{ wait.remaining > 0 }}"

which is then used in if-then's to conditionally call actionable notifications or notify.alexa_media_last_called or whatever.

if:
  - alias: Test if alexa_last_called_updated renders true
    condition: template
    value_template: "{{ alexa_last_called_updated }}"
  - condition: state
    entity_id: input_boolean.coffee_prepared
    state: "off"
then:
  - data:
      text: Did you prepare your coffee?
      event_id: actionable_notification_coffee_prepared
      alexa_device: "{{ states('sensor.last_alexa') }}"
    action: script.activate_alexa_actionable_notification

or

if:
  - condition: template
    value_template: "{{ alexa_last_called_updated }}"
then:
  - data:
      message: Good morning
    action: script.notify_alexa_media

Another nifty thing I figured out was to create a new Home Assistant user that is only used by the Alexa Skill to integrate HA with Alexa. The Alexa skill then authenticates with HA using that username and you can use that to watch for state_changed events that were triggered by that user to automatically update last_called when Alexa has been asked to do something in HA like turn this or that on|off or when you say something that turns on an Alexa scene (which is an HA script which then waits for the new AMP event to fire).

alias: Alexa - Triggered by Smart Home Skill user account
description: >-
  Perform action `Alexa Media Player: Update Last Called Sensor` when entity
  states are changed by Alexa Smart Home Skill user account
trigger:
  - platform: event
    event_type: state_changed
    context:
      user_id: 0df463c1efc4490f900b09b143cae9c4
condition: []
action:
  - action: alexa_media.update_last_called
    data: {}
  - delay:
      hours: 0
      minutes: 0
      seconds: 6
      milliseconds: 0
trace:
  stored_traces: 30
mode: single
max_exceeded: silent

System details

  • Home Assistant version: 2024.9.2
  • alexa_media version (from const.py or HA startup log): 4.13.1
  • alexapy version (from pip show alexapy in homeasssistant container or HA startup log): 1.29.2
  • Is Amazon 2FA/2SV enabled <!---We will not debug login issues if unanswered---> (y/n): Y
  • Amazon Domain: amazon.ca

Debug Logs (alexa_media & alexapy)

2024-09-17 04:20:13.397 DEBUG (MainThread) [custom_components.alexa_media.services] Service update_last_called for: []
2024-09-17 04:20:13.827 DEBUG (MainThread) [alexapy.alexaapi] d****l@b******a: static GET: https://www.amazon.ca/alexa-privacy/apd/rvh/customer-history-records?startTime=1726485613397&endTime=1726658413397&recordType=VOICE_HISTORY&maxRecordSize=10 returned 200:OK:application/json
2024-09-17 04:20:13.828 DEBUG (MainThread) [custom_components.alexa_media] d****l@b******a: Updated last_called: {'serialNumber': 'G************S0N', 'timestamp': 1726572010837, 'summary': 'turn on kitchen light'}
2024-09-17 04:20:13.828 DEBUG (MainThread) [custom_components.alexa_media] d****l@b******a: last_called changed: {'serialNumber': 'G************6SA', 'timestamp': 1726567732307, 'summary': ','} to {'serialNumber': 'G************S0N', 'timestamp': 1726572010837, 'summary': 'turn on kitchen light'}
2024-09-17 04:20:13.830 DEBUG (MainThread) [custom_components.alexa_media.media_player] d****l@b******a: <entity media_player.office_echo_dot_right=standby> is last_called: G************S0N
2024-09-17 04:20:13.831 DEBUG (MainThread) [custom_components.alexa_media.media_player] d****l@b******a: Refreshing notify targets
2024-09-17 04:20:13.831 DEBUG (MainThread) [custom_components.alexa_media.notify] d****l@b******a: Found last_called <entity media_player.office_echo_dot_right=standby> called at 1726572010837
2024-09-17 04:20:13.831 DEBUG (MainThread) [custom_components.alexa_media.notify] d****l@b******a: Creating last_called target last_called using <entity media_player.office_echo_dot_right=standby> called at 1726572010837
2024-09-17 04:20:13.831 DEBUG (MainThread) [custom_components.alexa_media.notify] d****l@b******a: Found last_called <entity media_player.office_echo_dot_right=standby> called at 1726572010837
2024-09-17 04:20:13.831 DEBUG (MainThread) [custom_components.alexa_media.notify] d****l@b******a: Creating last_called target last_called using <entity media_player.office_echo_dot_right=standby> called at 1726572010837
@danielbrunt57 danielbrunt57 reopened this Sep 17, 2024
Repository owner deleted a comment from github-actions bot Sep 17, 2024
@danielbrunt57 danielbrunt57 changed the title How to utilize the new alexa_media_last_called_event in AMP 4.13.1 How to utilize the new alexa_media_last_called_event in AMP 4.13.x Sep 17, 2024
@Marcelo-Y
Copy link

Marcelo-Y commented Sep 22, 2024

I have tried the code but the event never fires.
Can't see the event firing in "Developer tools/events" either.

Home assistant info:

Core 2024.9.2
Supervisor 2024.09.1
Operating System 13.1
Frontend 20240909.1

AMP info:
{
"domain": "alexa_media",
"name": "Alexa Media Player",
"codeowners": ["@alandtse", "@keatontaylor"],
"config_flow": true,
"dependencies": ["persistent_notification", "http"],
"documentation": "https://github.com/alandtse/alexa_media_player/wiki",
"iot_class": "cloud_polling",
"issue_tracker": "https://github.com/alandtse/alexa_media_player/issues",
"loggers": ["alexapy", "authcaptureproxy"],
"requirements": ["alexapy==1.29.2", "packaging>=20.3", "wrapt>=1.14.0"],
"version": "4.13.2"
}

Thanks!

@danielbrunt57
Copy link
Collaborator Author

danielbrunt57 commented Oct 7, 2024

I have tried the code but the event never fires. Can't see the event firing in "Developer tools/events" either.
Thanks!

@Marcelo-Y The code is in media_player.py lines 391-429:

        if "last_called_change" in event:
            if (
                event_serial == self.device_serial_number
                or any(
                    item["serialNumber"] == event_serial
                    for item in self._app_device_list
                )
                and self._last_called_timestamp
                != event["last_called_change"]["timestamp"]
            ):
                _LOGGER.debug(
                    "%s: %s is last_called: %s",
                    hide_email(self._login.email),
                    self,
                    hide_serial(self.device_serial_number),
                )
                self._last_called = True
                self._last_called_timestamp = event["last_called_change"]["timestamp"]
                self._last_called_summary = event["last_called_change"].get("summary")
                if self.hass and self.schedule_update_ha_state:
                    self.schedule_update_ha_state()
                await self._update_notify_targets()
            else:
                self._last_called = False
            if self.hass and self.async_schedule_update_ha_state:
                email = self._login.email
                force_refresh = not (
                    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,
                    },
                )

Check your logs for:

                _LOGGER.debug(
                    "%s: %s is last_called: %s",
                    hide_email(self._login.email),
                    self,
                    hide_serial(self.device_serial_number),
                )

@danielbrunt57
Copy link
Collaborator Author

@Marcelo-Y The event should fire after you've manually called alexa_media.update_last_called.
It should also fire when AMP's periodic polling scans the history records and updates last_called.

@Marcelo-Y
Copy link

Marcelo-Y commented Oct 10, 2024

Hello, @danielbrunt57 !
Thank you so much for helping me.

Last week I've seen the event firing during some tests. Shows that the event is OK.
My last_alexa sensor only updates few times, kind of once per day.
Maybe on periodic polling scans. When I fire it manually, the event is not fired.
Don't think the problem is the event. Maybe it is an Amazon login problem.
Tried deleting my pickle file, but no success.
Integration debug logging shows nothing.
I got only warnings in log file, but as you said in another issue, its just a warning.
Here are my logs:

AMP logs.txt

Don't know what to do, thinking about give up of my room aware automations.
It worked flawlessly for a long time, but not anymore.

Thank you!

@danielbrunt57
Copy link
Collaborator Author

danielbrunt57 commented Oct 15, 2024

The Alexa last called status used to be pushed to AMP via the HTTP2 connection that AMP opens when it starts up. The event from Amazon was "PUSH_ACTIVITY" but for the last 4 months or so there have been zero of these events from Amazon so they've ceased issuing it. AMP was using that event to update the last_called device almost instantly. HTTP2 is still actively connecting at startup but only gets these events now:

"PUSH_AUDIO_PLAYER_STATE"
"PUSH_MEDIA_CHANGE"
"PUSH_MEDIA_PROGRESS_CHANGE"
"NotifyMediaSessionsUpdated"
"NotifyNowPlayingUpdated"
"PUSH_VOLUME_CHANGE"
"PUSH_DOPPLER_CONNECTION_CHANGE"
"PUSH_EQUALIZER_STATE_CHANGE"
"PUSH_BLUETOOTH_STATE_CHANGE"
"PUSH_MEDIA_QUEUE_CHANGE"
"PUSH_NOTIFICATION_CHANGE"
"PUSH_DELETE_DOPPLER_ACTIVITIES", # delete Alexa history
"PUSH_LIST_CHANGE", # clear a shopping list #1190
"PUSH_LIST_ITEM_CHANGE", # update shopping list
"PUSH_CONTENT_FOCUS_CHANGE", # likely prime related refocus
"PUSH_DEVICE_SETUP_STATE_CHANGE", # likely device changes mid setup
"PUSH_MEDIA_PREFERENCE_CHANGE", # disliking or liking songs, #1599

Therefore, last_called is only updated when the periodic polling that updates sensor values, etc. occurs and is followed by an API call to get customer voice history records to ascertain the last_called device. The problem though is with all of the other HTTP GET requests preceding it, it often fails with the Amazon response "429: TooManyRequests". AMP delays a bit and retries the request. If another "429" occurs, it delays a bit longer and so on up to 5 times before giving up. When it gives up, last_called is not updated,

In my local modified version of AMP, I've disabled the get_history_records call in the scheduled polling sequence as I now rely on manually updating last_called myself via AMP's HA action/service alexa_media.update_last_called which spawned the idea of firing an event I could process to know when the service call finished and the notify services had refreshed. I think there is a problem though in where I placed that event. It seems to only fire when last_called has actually changed. I'm currently debugging that possible oversight...

@figorr
Copy link

figorr commented Oct 16, 2024

Hi @danielbrunt57,

Can you share which lines you edited to disable get_history_records?

I'm trying to test if disabling this part of the polling sequence can solve some of the issues for European AMP users. It seems that when the polling call for "history records" fails the integration on HA Core newer than 2024_7_4.

On version 2024_7_4 I can have polling enabled, but if I upgrade to 2024_8, 2024_9 or 2024_10... once the polling kicks in... the integration fails and there are no more notification services available. The only way to keep the integration working properly is to disable polling. At least for me. But as polling it is important for some of the automations ... is the reason why some of us we still are on 2024_7_4.

@danielbrunt57
Copy link
Collaborator Author

Can you share which lines you edited to disable get_history_records?

Sure, it's in __init__.py in async def async_update_data:

    async def async_update_data() -> Optional[AlexaEntityData]:
        # noqa pylint: disable=too-many-branches
        """Fetch data from API endpoint.

        >>> Note: The last_called device is no longer included in polling
        as that often results in http response "429: Too many requests".

        This is the place to pre-process the data to lookup tables
        so entities can quickly look up their data.

        This will poll Alexa API to identify all devices including bluetooth.
        If any guards, temperature sensors, or lights are configured, their
        current state will be acquired
        This data is returned directly so that it is available on the coordinator.

        This will add new devices and services when discovered. By default this
        runs every SCAN_INTERVAL seconds unless another method calls it. if
        push is connected, it will increase the delay 10-fold between updates.
        While throttled at MIN_TIME_BETWEEN_SCANS, care should be taken to
        reduce the number of runs to avoid flooding. Slow changing states
        should be checked here instead of in spawned components like
        media_player since this object is one per account.
        
        *** Each AlexaAPI call generally results in two (2) webpage requests. ***

        """

then about line 529 right after await process_notifications(login_obj, raw_notifications):

                if new_devices:
                    auth_info = optional_task_results.pop()
                    _LOGGER.debug(
                        "%s: Found %s devices, %s bluetooth",
                        hide_email(email),
                        len(devices) if devices is not None else "",
                        (
                            len(bluetooth.get("bluetoothStates", []))
                            if bluetooth is not None
                            else ""
                        ),
                    )

            await process_notifications(login_obj, raw_notifications)  <<< My line 529

            """Disabling automatic updates of last_called as this is 
               repeatedly causing random "429:Too many requests" errors.
               Users will have to manually run alexa_media.update_last_called
               
            # Delay to avoid "429:Too Many Requests"
            # 5s wasn't enough
            # 15s worked on first scan but not periodic polling
            await asyncio.sleep(30) 
            # Process last_called data to fire events
            await update_last_called(login_obj)                                <<<  Remove this line
            """
        except (AlexapyLoginError, JSONDecodeError):
            _LOGGER.debug(
                "%s: Alexa API disconnected; attempting to relogin : status %s",
                hide_email(email),
                login_obj.status,
            )
            if login_obj.status:
                hass.bus.async_fire(
                    "alexa_media_relogin_required",
                    event_data={"email": hide_email(email), "url": login_obj.url},
                )
            return
        except BaseException as err:
            raise UpdateFailed(f"Error communicating with API: {err}") from err

@vjbalex
Copy link

vjbalex commented Oct 22, 2024

@danielbrunt57
Core 2024.10.3
Supervisor 2024.10.2
Operating System 13.2
Frontend 20241002.3
AlexaPy Version: 1.29.3

I have set up most of these items per your instructions. However I have a couple not working and can't determine what I am doing wrong:

  1. You suggested setting up a new user to be the one firing the "Alexa - Triggered by Smart Home Skill user account" automation. I created "Last Alexa User" but when I select that user as the entity id, the script does not run. It only runs if I select myself (owner/administrator). I tried giving the new user Admin rights, External/Local only, and other combinations to no avail. What am I missing?

  2. When I use:
    wait_for_trigger:

  • platform: event
    event_type: alexa_media_last_called_event
    event_data:
    summary: good night
    timeout:
    hours: 0
    minutes: 0
    seconds: 30
    milliseconds: 0
    continue_on_timeout: true
    and
    variables:
    alexa_last_called_updated: "{{ wait.remaining > 0 }}"

Scripts/automations do not proceed until the timeout period expires (30 seconds in the example though I have tried shorter and longer periods) which negates any advantage over using hard delays.

Any suggestions?

@danielbrunt57
Copy link
Collaborator Author

@danielbrunt57 Core 2024.10.3 Supervisor 2024.10.2 Operating System 13.2 Frontend 20241002.3 AlexaPy Version: 1.29.3

I have set up most of these items per your instructions. However I have a couple not working and can't determine what I am doing wrong:

  1. You suggested setting up a new user to be the one firing the "Alexa - Triggered by Smart Home Skill user account" automation. I created "Last Alexa User" but when I select that user as the entity id, the script does not run. It only runs if I select myself (owner/administrator). I tried giving the new user Admin rights, External/Local only, and other combinations to no avail. What am I missing?
  2. When I use:
    wait_for_trigger:
  • platform: event
    event_type: alexa_media_last_called_event
    event_data:
    summary: good night
    timeout:
    hours: 0
    minutes: 0
    seconds: 30
    milliseconds: 0
    continue_on_timeout: true
    and
    variables:
    alexa_last_called_updated: "{{ wait.remaining > 0 }}"

Scripts/automations do not proceed until the timeout period expires (30 seconds in the example though I have tried shorter and longer periods) which negates any advantage over using hard delays.

Any suggestions?

  1. In your Alexa app, did you disable the Home Assistant skill then re-enable it and authenticate it to Home Assistant with the new HA user you created?
  2. The summary is what you said to Alexa and has to match. The trigger won't succeed if an event fires with a different summary. If you remove the summary then it will trigger on any event_type: alexa_media_last_called_event.

It may help if you open Dev Tools > Events and subscribe to the event alexa_media_last_called_event to see when it fires and what the data is.

@danielbrunt57
Copy link
Collaborator Author

danielbrunt57 commented Oct 22, 2024

I've created this automation that creates persistent notifications for tracking various events I'm playing with...

alias: Alexa - track alexa_media_player events
description: ""
mode: single
triggers:
  - trigger: event
    event_type: alexa_media_last_called_event
    id: alexa_media_last_called_event
  - event_type: alexa_media_last_called_updated
    id: alexa_media_last_called_updated in __init__.py
    trigger: event
  - event_type: alexa_media_media_player_event
    id: alexa_media_media_player_event in media_player.py
    trigger: event
  - event_type: update_alexa_last_called
    id: >-
      update_alexa_last_called in
      automation.alexa_trigger_update_last_called_via_event
    trigger: event
  - trigger: event
    event_type: node-red_last_alexa_updated
    id: node-red_last_alexa_updated
conditions: []
actions:
  - choose:
      - conditions:
          - condition: trigger
            id:
              - node-red_last_alexa_updated
        sequence:
          - action: persistent_notification.create
            data:
              title: Alexa Event
              message: >-
                Trigger ID: {{ trigger.id }}

                {{ now().strftime('%b %d') }} at {{ now().strftime('%-H:%M:%S
                %p') }}

                Device: {{ trigger.event.data.device.deviceName }}

                Heard: {{
                trigger.event.data.voiceHistoryRecordItems[0].transcriptText }}

                Response: {{
                trigger.event.data.voiceHistoryRecordItems[1].transcriptText }}
      - conditions:
          - condition: trigger
            id:
              - alexa_media_last_called_event
        sequence:
          - action: persistent_notification.create
            data:
              title: Alexa Event
              message: >-
                Trigger ID: {{ trigger.id }}

                {{ now().strftime('%b %d') }} at {{ now().strftime('%-H:%M:%S
                %p') }}

                Device: {{ trigger.event.data.last_called }}

                Summary: {{ trigger.event.data.summary }}

                Timestamp: {{ trigger.event.data.timestamp }}
    default:
      - action: persistent_notification.create
        data:
          title: Alexa Event
          message: >-
            Trigger ID: {{ trigger.id }}

            {{ now().strftime('%b %d') }} at {{ now().strftime('%-H:%M:%S %p')
            }}

@jamestas
Copy link

RE: AMP 4.13.x will now fire an event when last_called is updated.

This is great, awesome work actually.
Love the fact I no longer have guess 'when it is done' with a delay.

Trigger on event works beautifully.

Thanks for all you do with this.

Copy link

The issue has received no activity for 60 days and will be closed in a week.

@github-actions github-actions bot closed this as not planned Won't fix, can't repro, duplicate, stale Jan 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants