How to access device area as a variable within ESPHome config?

The generic version of my question is how can I use the Home Assistant area as a variable within an ESPHome config, such that if I change the ESPHome device area within HA the functionality changes?

- homeassistant.service:
  service: media_player.play_media
  data:
    media_content_type: music
    media_content_id: https://github.com/esphome/firmware/raw/main/voice-assistant/sounds/timer_finished.wav
  target:
    area_id: !lambda 'return device_area;'  # <- something like this

My specific use case is as follows: I have a number of M5 Stack Atom Echos that I intend to use as voice assistants, but I want the replies to come over media players in the same room as the Echo, I would like to be able to use the same firmware for each, but instead I have to hardcode the media_player.x for each device.

substitutions:
  name: m5stack-atom-echo-b836b0
  friendly_name: M5Stack Atom Echo b836b0
packages:
  m5stack.atom-echo-voice-assistant: github://esphome/firmware/voice-assistant/m5stack-atom-echo.yaml@main
esphome:
  name: ${name}
  name_add_mac_suffix: false
  friendly_name: ${friendly_name}
api:
  encryption:
    key: +myAPIkey=

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

# Until https://github.com/esphome/firmware/pull/233 is fixed/merged
update:
  - platform: http_request
    id: !remove update_http_request


# HarvsG's customisations 
speaker:
  - platform: i2s_audio
    id: !extend echo_speaker
    i2s_dout_pin: GPIO21 # <- It is actually on 22, so this disables the speaker
    dac_type: external
    mode: mono

voice_assistant:
  on_tts_end:
     - homeassistant.service:
         service: media_player.play_media
         data:
           entity_id: media_player.study_speaker # <- this is hard-coded
           media_content_id: !lambda 'return x;'
           media_content_type: music
           announce: "true"
1 Like

In esphome, you can have a variable from a sensor value or from an attribute of sensor.
I don’t know about media_player…

I would do thing about this:

in configuration yaml

homeassistant:
  customize: !include customize.yaml

in customize.yaml

sensor.my_sensor:
  custom_area: salon

in esphome.yaml

sensor:
  - platform: homeassistant
    entity_id: sensor.my_sensor
    attribute: custom_area
    id: custom_area

Thank you for taking the time to reply.

I’m not sure this achieves what I was looking for - I was hoping to retrieve, and then use, the area of the ESP device from home assistant.

I’m not sure I see an advantage of specifying the value in a separate yaml file is?

Edit:
I now have a better idea of what you are saying -
to use this Home Assistant Text Sensor — ESPHome, however unfortunately area is not an attribute of an entity. And specifying a custom_area for each device is not much better than hard coding it in the ESP home config.

Not an attribute, but you could create a template sensor send it back to ESPHome:

I was just exploring this idea. However again, I’m looking to create a esphome.yaml config that can be shared and does not require much user-config. If I could create the JINJA template helper from within ESPHome - that would be a solution.

To be clear,

voice_assistant:
  on_tts_end:
     - homeassistant.service:
         service: media_player.play_media
         data:
           media_content_id: !lambda 'return x;'
           media_content_type: music
           announce: "true"
        target:
          area_id: !lambda "return area_of_this_esphome_device;"

So that the response plays in the room that the device is in.

Another approach would be to pass the info via mqtt and you subscibe to a topic that can have a naming convention to retrieve what you want.

Is there a way to have a text field/text input sensor? I cant find one in the esphome docs.

Because then I could do something like this, and then just have the user input the entity name of the media player they wanted the reply to come from.

text_input:
  - platform: homeassistant
    entity_id: sensor.{name}_media_player_input
    name: TTS Media Player Entity ID

sensor:
  - platform: homeassistant
    entity_id: sensor.{name}_media_player_input
    id: media_player
...
voice_assistant:
  on_tts_end:
     - homeassistant.service:
         service: media_player.play_media
         data:
           entity_id: id(media_player)
           media_content_id: !lambda 'return x;'
           media_content_type: music
           announce: "true"

text_sensor:
  - platform: homeassistant
    entity_id: sensor.moon_phases
    id: ha_moon_phases

But how can I configure it so that a user can input a field in the HA UI in the same way I get specify an input on this drop down menu? What home-assistant would call input_text.
image

Sorry, I don’t really get your question. Configure what and where in HA or ESPHome ?
FYI: you can also use service in esphome and even create your own service.

        - homeassistant.service:
             service: input_number.set_value
             data:
                entity_id: input_number.water_counter_store
                value: !lambda return id(store_count).state ;

Sorry, I don’t really get your question.

The title and the psuedo code in the first post make it clear - I think. I want to use the area of the esphome device in a service call to home assistant.

My aim is to have an esphome.yaml that causes text-to-speech to play on a media player (a google home mini) in the same room as the esphome device. I want to achieve that automatically without having to manually create helpers in home assistant, or hard-code values into esphome.yaml on a per-device basis.

That way if I move the device area in the HA UI, it magically starts replying on a different speaker.

In psuedocode, it would look like this

substitutions:
  name: m5stack-atom-echo-b836b0
  friendly_name: M5Stack Atom Echo b836b0
packages:
  m5stack.atom-echo-voice-assistant: github://esphome/firmware/voice-assistant/m5stack-atom-echo.yaml@main
esphome:
  name: ${name}
  name_add_mac_suffix: false
  friendly_name: ${friendly_name}
api:
  encryption:
    key: +myAPIkey=

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

# Until https://github.com/esphome/firmware/pull/233 is fixed/merged
update:
  - platform: http_request
    id: !remove update_http_request


# HarvsG's customisations 
speaker:
  - platform: i2s_audio
    id: !extend echo_speaker
    i2s_dout_pin: GPIO21 # <- It is actually on 22, so this disables the speaker
    dac_type: external
    mode: mono

voice_assistant:
  on_tts_end:
     - homeassistant.service:
         service: media_player.play_media
         data:
           media_content_id: !lambda 'return x;'
           media_content_type: music
           announce: "true"
         target:
           area_id: !lambda " return thisdevice.homeassistant_area;" # <-pseudo code

And are you sure to have only one media_player in a room ?
Or you want to play on all media_player of that room ? (I saw in one of my room more than one, so just asking)

My question was for the topic just upper, the main goal, I understood :wink:

And are you sure to have only one media_player in a room ?

I do allthough not all users will

My question was for the topic just upper, the main goal, I understood :wink:

:+1: Thank you, and thank you for your time

It does seem that what I want is not yet possible.

I think you should work with substitutions.
But if you want to work with text input you can apply the same logic as the last example:

text:
  - platform: template
    name: Media Player
    id: media_player
    mode: text
    optimistic: true
    restore_value: true # Save value every 60 seconds

substitutions:
  area: "bedroom"
  entity: "quarto"

switch:
  - platform: template
    optimistic: true
    name: "Light substitutions entity_id"
    turn_on_action:
      - homeassistant.service:
          service: light.turn_on
          data:
            entity_id: light.${entity}
    turn_off_action:
      - homeassistant.service:
          service: light.turn_off
          data:
            entity_id: light.${entity}
  - platform: template
    optimistic: true
    name: "Light substitutions area"
    turn_on_action:
      - homeassistant.service:
          service: light.turn_on
          data:
            area_id: ${area}
    turn_off_action:
      - homeassistant.service:
          service: light.turn_off
          data:
            area_id: ${area}
  - platform: template
    optimistic: true
    name: "Light Input Text"
    turn_on_action:
      - homeassistant.service:
          service: light.turn_on
          data:
            entity_id: !lambda |-
              return "light." + id(media_player).state;
    turn_off_action:
      - homeassistant.service:
          service: light.turn_off
          data:
            entity_id: !lambda |-
              return "light." + id(media_player).state;

Thank you for your time. But please do read the OP, I am trying to find a way of doing this without having to change the area in the YAML every time my device moves room - which may be multiple times a day - I want it to get it’s own device area from HA, which can easily be adjusted in the UI.

Have you tested the code above?
In the example above, the last “Light Input Text” switch will take the area you enter in the “Media Player” field, so every time you move the device around, you change the “Media Player” field using the UI.

image

If you don’t want to type the area manually, you can hardcode all areas in a “Template select”

Please accept my unreserved apologies for doing exactly what I accused you of. I didn’t scroll down through your code block to see your example using templates. I have now implemented this and it is working well. Thank you for your time - very helpful.

My full example

I will leave the issue open as I do think ESP Home should be able to access the device area over the native API connection.

1 Like

The home assistant API has an /api/template endpoint which renders and returns a template. So a call to the api with {{area_name('binary_sensor.m5stack_atom_echo_b836b0_assist_in_progress')}} would return the device location. I just need to work out if this has been implemented on the esphome side.

Have you ever found out if this works?