Alexa Speak entity attributes. A Speech Engine

Tags: #<Tag:0x00007f7398b1d858>

Edit I have updated the code to add more features please use the code present in this first post not in subsequent posts Latest code will always be here on the first post , or on my GitHub page https://github.com/lonebaggie/Home_Assistant-Config in the Automation folder

@ duceduc thanks for testing :slight_smile:

After three years of using HA , I finally got round to using the inbuilt automation features. I have always used Appdeamon or python scripts to control my automations.

This script simplifies the tasks of passing entity details for Alexa to speak using the brilliant Alexa speech module. It will also enable Alexa to speak any entity or attribute value within Home assistant by just asking.

To pass details to Alexa normally requires complex Jinja2 templates to be created. Compare the following

patio door is {{states.binary_sensor.patio_door.state|replace(‘off’,‘closed’)|replace(‘on’,‘open’)}}

with

patio door is (e01s)

Which obviously outputs either the “patio door is open” or “patio door closed”

The code (e01s) will output the state of the entity with the correct device type without restoring to any code.

Home assistant automatons do not allow the use of any persistent data. However, input selects are effectively lists. So, this project uses two Input selects to hold the data.

By using a virtual bulb, we can use the Alexa App to trigger the automation.So, we can do the following

" Alexa is the patio door open"
“The Patio door is closed”

This requires a virtual bulb. The brightness levels are used to trigger the input select which in turn parses the text and output the results to Alexa.

OK we need the following items

Virtual Bulb

- platform: template
    lights:
      alexa_virtual:
        friendly_name: "Alexa Dummy Light"
        turn_on:

        turn_off:

        set_level:

Last Alexa

Replace media_player entities with your Alexa devices. This will ensure the speech is delivered to the last alexa spoken to.

sensors:
      last_alexa:
        value_template: >
          {{ states.media_player | selectattr('attributes.last_called','eq',True) | map(attribute='entity_id') | first }}

Input Select

See notes below:-

  entity_list:
    name : Entity List
    options:
      - binary_sensor.front_door
      - binary_sensor.patio_door
      - sensor.home_temperature
      - binary_sensor.hall_motion
      - light.living_room
  speak_list:
    name: Speak List
    options:
      - do not use
      - The current time is (tt) 
      - The current date is (td)
      - The current temperature of (e02f) is (e02s)
      - The battery level of (e03f) is (e03l)
      - The (e04f) is at (e04b) % brightness

The entity list contains all the entities you wish to parse (so replace the entities with the ones you wish to use). Lists start at 0. So, in the above example entity list 0 is the front door sensor and entity list 3 is the hall motion sensor.

The speak list contains all the sentence’s you wish to send to Alexa to speak. These corresponds to the brightness level of the virtual bulb so brightness 1 corresponds the “current time is”. Please ensure the first item is “do not use”. This is used by the scripts to reset the input select.

The items to parse are contained within brackets () and have the following syntax.

(tt) = Current time
(td) = Current date
(gr) = Greeting [Morning,Afternoon,Evening] based on current time
(eNNc) = Entity to parse
NN = the number in the entity list. Use 01 for 1 etc

c = will have the following syntax -

s = State with unit of measure added
$ = State only
f = Friendly name
b = Brightness (%)
B = Brightness (level)
[0-9] = Attribute number

So the following example using the input select information above will output following result

the current temperature of (e02f) is (e02s) will parse to

the current temperature of living room sensor is 21 C centigrade

If you requite a attribute not listed above use the attribute number as listed in the developer tools states attribute column . The first attribute of an entity will be 0 the second will be 1 up to 9 the tenth attribute of the entity.

The following automation scripts are used

Item select to speak.yaml

alias: Item Select to speak
description: Trigger Alexa from Item Select
trigger:
  entity_id: input_select.speak_list
  platform: state
condition:
  - condition: template
    value_template: '{{states.input_select.speak_list.state != "do not use"  }}'
action:
  - service_template: >
        {% set name = states('sensor.last_alexa').split('.') %}
        notify.alexa_media_{{name[-1]}}
    data_template:
      data:
        type: tts
      message: >
        {% set ns=namespace(speak = "") %}
        {% set ns.speak = states.input_select.speak_list.state %}
        {% for ps in range(1,10) if ns.speak.find(')') > -1 %}
          {% set dy = {'opening': ('closed','open'),'window':('closed','open'),
          'door': ('closed','open'),'garage_door':('closed','open'),
          'lock':('locked','unlocked'),'presence':('home','away'),
          'connectivity':('connected','not connected'),
          'vibration':('detected','clear'),
          'smoke':('detected','clear'),'sound':('detected','clear'),
          'motion':('detected','clear'),'battery':('on','off'),
          'occupancy':('detected','clear') } %} 
          {% set tn = ns.speak[ns.speak.find("("):ns.speak.find(")")+1] %}
          {% set rp = tn %}
          {% if tn == "(gr)" %}
            {% set gr = ['Morning','Afternoon','Evening'] %}
            {% set rp = gr[(now().hour/12)|int + (now().hour > 17)]%}
          {% elif tn == "(tt)" %}
            {% set rp = now().strftime("%-I %M %p") %}
          {% elif tn == "(td)" %}
            {% set rp = now().strftime("%A, %d. %B %Y") %}
          {% else %}
            {% set et = state_attr("input_select.entity_list","options") %}
            {% set et = et[tn[2:4]|int] %}
            {% if tn|regex_search('\(e..f\)') %}
              {% set rp = state_attr(et,"friendly_name") %}
            {% elif tn|regex_search('\(e..[s$]\)') %}
              {% if tn[4:5] == "$" %}
                {% set st = states(et) %}
              {% else %}
                {% set st = states[et].state_with_unit %}
                {% set dt = state_attr(et,"device_class")|string %}
                  {% if (dt != "None") and (st =='off' or st == 'on')  %} 
                    {% set st = dy[dt][st|int] %}
                  {% endif %}              
              {% endif %}
              {% set rp = st %}
            {% elif tn|regex_search('\(e..[bB]\)') %}
              {% set br = state_attr(et,"brightness")|int %}
              {% set rp = br|string %} 
              {% if tn[4:5] == 'b' %}
                {% set rp = ((100*br/254)|int)|string %}
              {% endif %}
            {% elif tn|regex_search('\(e..[0-9]\)') %}
              {% set al = states[et]['attributes']|list %}
              {% set rp = state_attr(et,al[tn[4:5]|int]) %}
            {% endif %}
          {% endif %}
            {% set ns.speak = ns.speak.replace(tn,rp) %}  
        {%endfor%}
        {{ns.speak}}
  - service: input_select.select_option
    data:
      entity_id: input_select.speak_list
      option: "do not use"

Dummy lights to item select.yaml

alias: Dummy light to item select
description: Trigger item select from dummy light
trigger:
  entity_id: light.alexa_virtual
  platform: state
  from: 'off'
  to: 'on'
action:
  - service: input_select.select_option
    data_template:  
      entity_id: input_select.speak_list
      option: >
        {% set slist = state_attr('input_select.speak_list','options') %}
        {% set opt = state_attr('light.alexa_virtual','brightness') %}
        {% set opt = (opt | float / 255  * 100+0.5)|int %}
        {% if opt|int > slist|length %}
          {% set opt = 0 %}
        {% endif %}
        {{slist[opt]}}
  - service: light.turn_off
    data:
      entity_id: light.alexa_virtual

These scripts are very long , so be careful when copying. The main script is triggered by the speak
list input select . You can test this by adding the input select to you Lovelace screen. You can test each line to ensure the sentence is correct.

Once you are happy with the speech. You can push the virtual bulb to Alexa with the Nabu Casa cloud or use the Emulated Hue. The Dummy light to item select script will trigger the input select once the brightness level is triggered by the Alexa app.

Enjoy.

2 Likes

Updated the code to use a last alexa sensor so the reply also comes from the the alexa you were talking to

Replace the entity_id with your alexa devices

- platform: template
    sensors:
      last_alexa:
        entity_id:
        - media_player.lr_echo
        - media_player.k_dot
        - media_player.echo_show_5
        value_template: >
          {{ states.media_player | selectattr('attributes.last_called','eq',True) | map(attribute='entity_id') | first }}

I have also updated the code to use TTS instead of announce to remove the beep before Alexa speaks

alias: Item Select to speak
description: Trigger Alexa from Item Select
trigger:
  entity_id: input_select.speak_list
  platform: state
condition:
  - condition: template
    value_template: '{{states.input_select.speak_list.state != "do not use"  }}'
    
action:
  - service_template: >
        {% set name = states('sensor.last_alexa').split('.') %}
        notify.alexa_media_{{name[-1]}}
    data_template:
      data:
        type: tts
      message: >
        {% set elist = state_attr("input_select.entity_list","options") %}
        {% set slist = states.input_select.speak_list.state %}
        {% set slist = slist.split() %}
        {% set ns=namespace(state = "") %}
        {% for obj in slist %}
          {% if obj == "(tt)" %}
            {% set obj = now().strftime("%-I %M %p") %}
            {% set mn = now().strftime("%M") %}
            {% if mn|int < 10 %}
              {% set obj = obj[:2] ~ obj[2:]|replace('0','oh ') %}
            {% endif %}
          {% endif %}
          {% if obj == "(td)" %}
            {% set obj = now().strftime("%A, %d. %B %Y") %}
          {% endif %}  
          {% if obj[:2] == "(e" %}
            {% set enum = obj[2:4]|int %}
            {% set state = states(elist[enum]) %}
            {% if state == None %}
              {% set state = "unknown" %}
            {% endif %}
            {% set fname = state_attr(elist[enum],"friendly_name") %}
            {% if fname == None %}
              {% set fname = "" %}
            {% endif %}
            {% set bri = state_attr(elist[enum],"brightness") %} 
            {% if bri == None %}
              {% set bri = "unknown" %}
            {% endif %}
            {% set blevel = state_attr(elist[enum],"battery_level") %}
            {% if blevel == None %}
              {% set blevel = "unknown" %}
            {% endif %}
            {% set uom = state_attr(elist[enum],"unit_of_measurement") %}
            {% if uom == None %}
              {% set uom = "" %}
            {% endif %}
            {% set devtype = state_attr(elist[enum],"device_class") %}
            {% if devtype == None %}
              {% set devtype = "" %}
            {% endif %}
            {% if obj[4:5] == "s" %}
              {% set obj = state ~ " " ~ uom %}
            {% endif %}
            {% if obj[4:5] == "f" %}
              {% set obj = fname %}
            {% endif %}
            {% if obj[4:5] == "b" %}
              {% set obj = bri %}
            {% endif %}
            {% if obj[4:5] == "l" %}
              {% set obj = blevel %}
            {% endif %}
            {% if devtype == "opening" or devtype == "window"
              or devtype == "door" or devtype == "garage-door"
            %}
              {% set obj = obj|replace('off','closed') %}
              {% set obj = obj|replace('on','open') %}
            {% endif %}
            {% if devtype == "motion" %}
              {% set obj = obj|replace('off','clear') %}
              {% set obj = obj|replace('on','detected') %}
            {% endif %}
            {% if devtype == "lock" %}
              {% set obj = obj|replace('off','unlocked') %}
              {% set obj = obj|replace('on','locked') %}
            {% endif %}
            {% if devtype == "battery_charging" %}
              {% set obj = obj|replace('off','not charging') %}
              {% set obj = obj|replace('on','charging') %}
            {% endif %}
            {% if devtype == "battery_charging" %}
              {% set obj = obj|replace('off','not charging') %}
              {% set obj = obj|replace('on','charging') %}
            {% endif %}
            {% if devtype == "connectivity" %}
              {% set obj = obj|replace('off','not connected') %}
              {% set obj = obj|replace('on','connected') %}
            {% endif %}
            {% if devtype == "vibration" %}
              {% set obj = obj|replace('off','clear') %}
              {% set obj = obj|replace('on','detected') %}
            {% endif %}
          {% endif %}
          {% set ns.state = ns.state + " " ~ obj  %}
        {% endfor %}
        {{ns.state}}
  - service: input_select.select_option
    data:
      entity_id: input_select.speak_list
      option: "do not use"

I also correct a bug in the Dummy light brightness levels to convert 0-255 to 1 to 100 so they match correctly

alias: Dummy light to item select
description: Trigger item select from dummy light
trigger:
  entity_id: light.alexa_virtual
  platform: state
condition: 
  - condition: state
    entity_id: light.alexa_virtual
    state: 'on'
action:
  - service: input_select.select_option
    data_template:  
      entity_id: input_select.speak_list
      option: >
        {% set slist = state_attr("input_select.speak_list","options") %}
        {% set opt = state_attr('light.alexa_virtual','brightness') %}
        {% set opt = (opt | float / 255  * 100+0.5)|int %}
        {% if opt|int > slist|length %}
          {% set opt = 0 %}
        {% endif %}
        {{slist[opt]}}
  - service: light.turn_off
    data:
      entity_id: light.alexa_virtual

I have error when I want to call the brightness of a light. With the light set to off, alexa announces the script with unknown brighness. However, if the light is set to on, I get this error.

Wow some errors messages. We need break down the problem to see where the error is

Create the item selects as follows

entity_list:
    name : Entity List
    options:
      -  blank
  speak_list:
    name: Speak List
    options:
      - do not use
      - The current time is (tt)

Use the latest version of the automation Item Select to speak then create a lovelace input select using speak list. You should be able trigger alexa via the lovelace input select screen and get the current time .

If all thats works you can play with the parser , change the input select, Add entities in the entity list . you can quickly Reload the input select via the configuration/Server controls. no HA reboots required !

Once you are happy with messages. Add the dummy Bulb and see if you can trigger the input select via the Bulb brightness . Finally expsose the bulb to Alexa via Nuba Casa or emulated Hue and test via Alexa Routines

This is my current input_select file and I can playback all except the brightness. I have tried different room light and all same. If the light is turned off, alexa will play the message with the brightness set to unknown. If the light turn on, no announcement and I see that error in log.


  entity_list:
    name : Entity List
    options:
      - switch.ac_lr_unit
      - switch.ac_mr_unit
      - switch.ac_sr_unit
      - sensor.outside_temperature
      - sensor.livingroom_temperature
      - sensor.computer_room_battery
      - light.shion_room
  speak_list:
    name: Speak List
    options:
      - do not use
      - The current time is (tt) 
      - The current date is (td)
      - The (e00f) unit is (e00s)
      - The (e01f) unit is (e01s)
      - The (e02f) unit is (e02s)
      - The current (e03f) is (e03s)
      - The current (e04f) is (e04s)
      - The (e05f) is (e05s)
      - (e06f) is at (e06b) % brightness

could it be somewhere in this code in the option?

alias: Dummy light to item select
description: Trigger item select from dummy light
trigger:
  entity_id: light.alexa_virtual
  platform: state
condition: 
  - condition: state
    entity_id: light.alexa_virtual
    state: 'on'
action:
  - service: input_select.select_option
    data_template:  
      entity_id: input_select.speak_list
      option: >
        {% set slist = state_attr("input_select.speak_list","options") %}
        {% set opt = state_attr('light.alexa_virtual','brightness') %}
        {% set opt = (opt | float / 255  * 100+0.5)|int %}
        {% if opt|int > slist|length %}
          {% set opt = 0 %}
        {% endif %}
        {{slist[opt]}}
  - service: light.turn_off
    data:
      entity_id: light.alexa_virtual

I don’t have lovelace install or configure, but I use the default tab to test the announcements.
alexa-annoucement

Can you confirm the check configuration passes all tests in configuration /server controls

So the input select on it own should trigger the voice . So if you select the current time in the input select speak list the automation should play this to the last alexa

This script requires sensor.last;alexa, input_select.speak_list and input_select.entity_list with those names

Yes, I have copied all your codes and used the input select to select individual list of phrases. All works except the brightness phrase (my cases, the last one on the list). I have not added anything to Emulated Hue file.

Can you confirm the check configuration passes all tests in configuration /server controls

I did find one thing odd. I added this part of code in automation.yaml and my code editor (notepad++), complained about the spacing as you can see with the screenshot.

with space (looks fine)

No space (looks like an error)

Either way, the code passes the configuration checks.

I am running 0.114.4 Hassio on a rpi3.

OK . To confirm all input selects when selected speak out of Alexa except the last one in the list the brightness phrase?

Try moving that phrase to a different position. Does it give an error when it tries to speak this ?
Odd that the other phrases work. If you delete the brightness phrase do all the other phrases still work ?

The above is the difficult part as it is a complex script , once working , all you need now is something to trigger the phrases from Alexa phone app.

I used a dummy bulb as it gives 100 triggers (brightness 1 to 100) by just using 1 entity.

So the Dummy light to item select automation is waiting for light.alexa_virtual to trigger . It then selects the item in input_select.speak_list that matches the brightness level. Triggers the input select and then turns the bulb off

So the next stage is trigger the virtual bulb in HA manually either with the developers tools or the Lovelace UI to see if it triggers the input select.

The last step is simply expose the virtual bulb to Alexa and trigger it with the Alexa routine in the phone app.

OK . To confirm all input selects when selected speak out of Alexa except the last one in the list the brightness phrase?

Yes, I have deleted and move the brightness phrase to different position and it’s the same result.

If I can’t get the brightness to work, it’s no biggie. Though it would be nice to figure out why it isn’t working.

The next step to add to alexa I am confuse about. So in my emulated hue file, I add this?

    light.alexa_virtual:
      name: Dummy Light
      hidden: false

Then, in the alexa app, I will do a discover and it should find and display Dummy Light
From there, I would create a routine for each of the phrases I have and adjust the brightness in 1 increments which correspond to the speak_list options?

Oh, I missed a step you wrote above. When I turn on the dummy bulb, it switches on and stays on, but I don’t hear anything. I get this error.
trigger-dummy

error

This is the Dummy light to item select code I have:

- alias: Dummy light to item select
  trigger:
    entity_id: light.alexa_virtual
    platform: state
  condition: 
  - condition: state
    entity_id: light.alexa_virtual
    state: 'on'
  action:
    service: input_select.select_option
    data_template:
      entity_id: input_select.speak_list
      option: >
        {% set slist = state_attr("input_select.speak_list","options") %}
        {% set opt = state_attr('light.alexa_virtual','brightness') %}
        {% set opt = (opt | float / 255  * 100+0.5)|int %}
        {% if opt|int > slist|length %}
          {% set opt = 0 %}
        {% endif %}
        {{slist[opt]}}
    service: light.turn_off
    data:
      entity_id: light.alexa_virtual

That was the simple script !!!

I’m running the same HA as you and version. Maybe a corruption in the script . Try this copy

alias: Dummy light to item select
description: Trigger item select from dummy light
trigger:
  entity_id: light.alexa_virtual
  platform: state
condition: 
  - condition: state
    entity_id: light.alexa_virtual
    state: 'on'
action:
  - service: input_select.select_option
    data_template:  
      entity_id: input_select.speak_list
      option: >
        {% set slist = state_attr("input_select.speak_list","options") %}
        {% set opt = state_attr('light.alexa_virtual','brightness') %}
        {% set opt = (opt | float / 255  * 100+0.5)|int %}
        {% if opt|int > slist|length %}
          {% set opt = 0 %}
        {% endif %}
        {{slist[opt]}}
  - service: light.turn_off
    data:
      entity_id: light.alexa_virtual

If still an issue cut and paste the JInja2 code ( the bit after option: > ) into the the template editor , it should return do not use

Turn the automation Dummy light to item select off and play with the brightness in the dummy bulb , the template editor should display the speak list to match the brightness

OK! Progress. It is working. Adjusting the brightness slider plays the phrase.

Good news. Glad its working . The emulated hue settings look OK for the bulb . I use Nabu Casa so car’nt be sure.

When you setup the alexa routine on the phone ,Make sure you turn on the bulb and set the brightness. I spent ages trying to find the brightness slider :slight_smile:

After you set the brightness add a second line and get Alexa to say something like “Please Wait”. This will cover the slight delay between the asking alexa, triggering the routine and getting the reply.

I will work on the emulated hue after a few more adjustments. I want alexa to reply back with the temp and the humidity as one go. This is the input I added, but she is ignoring the , or . and just respond back as one sentence. How to set the code to treat period or comma as a pause?

The current (e03f) is (e03s), humidity is (e06s)

The issue is with the way you can send messages to Alexa . You can either use Announce or TTS.

TTS is the same as saying Alexa Simon says. This is what I am currently using , it will ignore all punctuation.

Announce . will allow you to add SSML markup . See https://github.com/custom-components/alexa_media_player/wiki/Configuration%3A-Notification-Component

The big issue with announce it makes a ‘bong’ before it plays the message . This is the only reason im using TTS.

I just make the replies “more wordy”

The current (e03f) is (e03s) and the humidity is currently at (e06s)

Being English I like my Alexa to be polite :slight_smile:

If you look at the first itterration of the code you can change to Announce and see which you prefer

The above makes sense. I will do as you did and make the sentences longer.
Just curious about the brightness issue I am having. What bulbs/hub are you using?

I am using ikea blubs with hue bridge. This is one of my entity lights. I take it your brightness read out is the same?

min_mireds: 153
max_mireds: 500
brightness: 204
color_temp: 250
is_hue_group: true
friendly_name: Computer Room
supported_features: 43

Its a bug in my code. The brightness is a number not a string

change the following line

{% set bri = state_attr(elist[enum],"brightness") %}

to

{% set bri = state_attr(elist[enum],"brightness")| string %}

This will convert brightness to a string.

Im currently rewriting , so if that does not work I will update the entire code shortly

Sorry :slight_smile:

That was it. The brightness is fixed now.

Not quite currently giving brightness 0 -254 , new version with convert to 0-100 %

I have updated the code b will give the % brightness and B will give you the level.

0-9 will return any attribute as listed in the developers state attribute column.