Ask Alexa for State of an Entity using Dummy bulb

I have been using the Alexa Media component as a TTS delivery mechanism

and the Alexa App routines function, to ask Alexa about the state of various entities

Edit updated program to render Grammatical sentences (unlike me :slight_smile: )

EDIT replaced this program with

Speech Parser script to extract entity Id information into readable form for Alexa or Google Assistant TTS

For example, Alexa is the patio door open.

I have been using individual input boolean to trigger these responses. This has proved cumbersome. I finally hit upon the idea of using a dummy light bulb. This allows the Alexa app to use the brightness levels and gives a 100 triggers with one entity. To create a dummy bulb in HA you need to create a blank light template

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

        turn_off:

        set_level:

This bulb can then be exposed to Alexa via Alexa Cloud or emulated Hue.

I have written the following Appdeamon script to respond to the trigger event from the dummy bulb

 #
# This program responds via ALexa TTS to trigger events generated by the Alexa App routines function.
# It responds with generated spoken description of state of the triggered entity. 
# 
# It requires a template light switch is created in HA 
#
# - platform: template
#    lights:
#      alexa_virtual:
#        friendly_name: "Alexa Dummy Light"
#        turn_on:
#
#        turn_off:
#
#        set_level:
#
# This dummy light switch brightness level is used to generate 100 unique triggers.
# Each trigger is matched to a list of entities to create the following type of Alexa conversations
#
# "Alexa is the patio door open"            (created in the Alexa app Routine)
# "Please wait while I check"               (created in the Alexa app Routine)
#  Set dummy Light bulb brightness to 1%    (created in the Alexa app Routine)
# "The patio door is closed"                (created by this app)
#
# Program requires the following parameters
# 
#
# AlexaReport:
#   module: AlexaReport
#   class: Alexareport
#   trigger: < Enity name of virtual bulb>
#   device: < Entity name of Alexa media device 
#   ent:
#     - < Entity of sensor to obtain state >
#     - < Next entity >
#     - < and so on >
# 
# 1st entiy is triggered on brightness of 1% second  on 2% etc
# Version 1.1 Added better English 
#
#
#
#
#

import appdaemon.plugins.hass.hassapi as hass
class Alexareport(hass.Hass):
    def initialize(self):
# get vars
        self.trigger=self.args["trigger"]
        self.alexa = self.args["device"]
# check if dummy bulb  triggered
        self.listen_state(self.alexareport,self.trigger,new="on")
    def alexareport (self, entity, attribute, old, new, kwargs):
        self.log(("{} triggered").format(self.trigger))
#setup list for entities 
        ent = []
# dim value of dummy light
        value = self.get_state(self.trigger, attribute = "brightness")
# Convert brightness 0 to 255 as persentage  then -1 as python list start 0 not 1
        psent = int(round(value/255*100)) -1
#get entity id from args and add to ent list
        for entity in self.args["ent"]:
            ent.append(entity)
# Test if dim value matches listnumber in  array -1 as array start at 0
        if psent > (len(ent) -1) :
# Dim value does not match entry in list error and turn off dummy light
            self.log("Message {} value out of range ignored".format(psent+1))
            self.turn_off(self.trigger)
        else:
# get entity details
            obj = ent[psent]
            fname = self.friendly_name(obj)
            state = self.get_state(obj)
            uom = self.get_state(obj, attribute = "unit_of_measurement")
            dc = self.get_state(obj, attribute = "device_class")
# if values are null set as blank to stop errors when concatenating strings 
            if uom == None :
                uom = ""
            if dc == None :
                dc = ""
# check if entity is plural or singular (is or are)
            if fname.endswith("s" ):
                sorp = " are "
            else :
                sorp = " is "
#construct alexa reply
            mess = "The " + fname + sorp + dc + " " + state + " " + uom
# Check entity type to change on/off to appropriate values. Add as required
            mess = mess.replace("opening on","open")
            mess = mess.replace("opening off","closed")
            mess = mess.replace("moisture on","wet")
            mess = mess.replace("moisture off","dry")
            mess = mess.replace("motion on","triggered")
            mess = mess.replace("motion off","off")
            mess = mess.replace("temperature","")
            mess = mess.replace("humidity", "")
            mess = mess.replace("pressure","") 
# replace symbols and SI with words. Add as required  
            mess = mess.replace("  "," ")
            mess = mess.replace("°C","Centagrade")
            mess = mess.replace("km","Kilometres")
            mess = mess.replace("GiB","Gigabytes")
            mess = mess.replace("MiB","Megabytes")
            mess = mess.replace("lx","Lux")
            mess = mess.replace("%","Percent")
            mess = mess.replace("mbar","Milibar")
            mess = mess.replace("hPa","Hectopascal")
            mess = mess.replace("lm","Lumin")
            self.log("Message {} is {}".format(psent+1,mess))
#send message to alexa and turn off dummy bulb
            self.call_service('media_player/alexa_tts', entity_id=self.alexa, message=mess) 
            self.turn_off(self.trigger)

Documentation is in the program, but the real trick is using the Alexa App Routines to trigger the dummy light bulb, you can use any scripts, node-red or even the template bulb to react to the trigger event.

So in the Alexa App, you can create a new routine

“Alexa is the patio door open” (created in the Alexa app)
“Please wait while I check” (created in the Alexa app)
Set dummy Light bulb brightness to 1% (created in the Alexa app)
“The patio door is closed” (response triggered by brightness level)

Hope this is useful

3 Likes

Very nice!

I’ve been fighting to update this app to use the -lastalexa option in

So I can respond to the Echo being spoken to. The python routine is very unreliable. Is it your roadmap to add a last mediaplayer option so I don’t need to specify it the app

Again @keatontaylor many thanks for a brilliant component

very nice approach.

1 thing:

ent = []
#get entity id from args and add to ent list
        for entity in self.args["ent"]:
            ent.append(entity)

can simply be replaced by

ent = self.args["ent"]

because you already create a list in the yaml.
but you can also use:

obj = self.args["ent"][psent]

you could also simplefy your code by setting the replacements in yaml

replacelist:
  "opening on": "open"
  "opening off": "closed"
  "km": "Kilometres"

and use this code:

 for key,value in self.args["replacelist"].items():
    mess = mess.replace(key,value)

and code to respond to the last alexa:

import subprocess
import tempfile

  def check_last_alexa(self):
    cmd = ["/path/to/alexa_remote_control.sh","-lastalexa"]
    try:
      with tempfile.TemporaryFile() as f:
        self.subprocessresult = subprocess.Popen(cmd, stdin=None, stdout=subprocess.PIPE, stderr=f)   
        out,err = self.subprocessresult.communicate()
        if out != None:
          last_alexa = out.decode("utf-8").strip()
        else:
          last_alexa = "cookie problem"
          self.log("last alexa: subprocess did return None") 
    except:
      last_alexa = "unknown error"
      self.log("last alexa couldnt be updated")
      return last_alexa

edit: and you placed this topic on the wrong place. scripts is for HA scripts. this is an AD app so it is better on its place in the appdaemon section (where people will look for AD apps)

Thank you. for your help this is my first stab at Python. Not done any serious coding since COBOL and foxpro (im that Old !) . So I lot of my of my ideas are old fashioned

My real issue is running the shell commands in Hassio its just not stable, keep getting file not found errors , and then they start working again. My system is very stable in HASS.io so I will wait for HA to catch up.

my first programming was done on a sinclair spectrum :wink: so the time that i was 25 is also long ago :wink:
and before python i didnt program very much for over 10 years, so i know where your coming from.

maybe the shell commands in hassio combined with HA are less reliable, but just try subprocess in AD, maybe that is reliable.

the advantage is that you can have better checks, logging, easier restarts, control when you are working with AD.

things evolve so fast knowadays, that people ofter forget (or just dont know) that things also can be done the way we used to. old fashioned isnt always bad :wink:

I think I’m going to steal this. I don’t have app daemon installed, but if I understand what you’re doing, I dont think I need to install that right. I should be able to do something with Alexa Routines, and Automations, right?

Alexa, is the a patio door open (alexa routine to set dimmer to 1%)
Alexa Speak Please wait while I check
Alexa Sets leve to 1%
HA Automation - Trigger when level gets set to 1% --> Speak TTS of what you want to say

Since I’m not familiar with App Daemon, I’m not sure what the benefit is.

Appdaemon lets you do in python, what you never can do in yaml :wink:
and it offers HADashboard.

but your way with automation and routines would work also.
dont forget to set the light back to off at the end from the automation :wink:

1 Like

One more dumb question. I’ve never used a platform: template as a light. Where would I put it? For example I have Platform: template Sensors?, but those are in my sensors.yaml.

In my configuration.yaml, maybe?

in the section light.

you need to see all yaml as 1 big file, thats just split up for your convenience.
in your configuration.yaml (which it the toplevel)

you have something like:

sensor: !include sensors.yaml

that makes that you can put all your sensors in a seperate file.
it could also have been:

sensor:
  some_name:
    platform:template
    some config ...

with light that is the same.
so or you create a seperate lights.yaml and connect that in your configuration.yaml
or you put a light section in your configuration.yaml and configure it there.

Ahh, makes perfect sense.

I’m thinking something like this for me without using app daemon. Does this seem like it work work

- alias: "Alexa Report"
  trigger:
  - platform: state
    entity_id: light.alexa_virtual
  condition:
  - condition: numeric_state
    entity_id: sensor.ha_runtime_in_minutes
    above: 1
  - condition: state
    entity_id: light.alexa_virtual
    state: 'on'
  action:
  - service: script.audio_notify
    data_template:
      tts_msg: >-
        {% if states('light.alexa_virtual_light_level') | float = 1  %}
          Say something here
        {% elif states('light.alexa_virtual_light_level') | float = 2 %}
          Say something different
        {% elif states('light.alexa_virtual_light_level') | float = 3 %}
          Something other than what you've already said
        {% else %}
          I'm not sure what to say here
        {% endif %}
      mplayer: "main"
  - service: light.turn_off
    entity_id: light.alexa_virtual

i cant help you with automations, but i think checking for “on” should be in the trigger and not a condition.

Either way should work, I chose not to do to: on because I figured maybe I would have an automation that set a level, then paused and set another level, but looking at this close that would not work if it’s turning it off at the end of the automation. :slight_smile:

like i said i have no clue about automations, but it looks odd. :wink:
you still could trigger 1 after the other, when you set it to on it after you set it to off.

but i think it would be more logical to set all actions in the automation that gets triggered then run a few automations after another.

I changed it. Not to Hijack this thread, but do you mean you do everything with App Daemon, and dont use automations?

Like @ReneTode I could never get my head around HA automations. When I first looked at HA as a home automation solution, I dismissed it as I struggled with yaml and the automations just never “clicked”. Only when I discovered AppDaemon and Hass.io I abandoned domoticz and moved to the “darkside”.

So to answer your question AppDaemon is a complete programming environment in python ( a programming language id never used before) that will link to everything in HA, All my programming expertise in Python has come from google “cut and paste” and help from this forum.

I have extended the script with a new idea see

My python skills are getting better, so this program is changing rapidly it just shows how powerful Python is

1 Like

Would you please let me know if you had this automation working as desired? Many thanks!

Sure does.

Here is my most current

- alias: "Alexa Report"
  trigger:
  - platform: state
    entity_id: light.alexa_virtual
    to: 'on'
  condition:
  - condition: numeric_state
    entity_id: sensor.ha_runtime_in_minutes
    above: 1
  action:
  - service: alexa_media.update_last_called
  - delay: 00:00:01
#  - service: homeassistant.update_entity
#    data:
#      entity_id: sensor.last_used_echo
  - service: notify.alexa_media
    data_template:
      target: 
        -  '{{ states.sensor.last_alexa.state }}' 
      data:
        type: tts
      message: >-
        {% if (states.light.alexa_virtual.attributes.brightness | int / 255 * 100 ) | round  == 1 and states('device_tracker.tracy_all') == 'not_home' %}
          Tracy is away and is approximately {{ states('sensor.tracys_time_to_home') }} minutes from home.
        {% elif (states.light.alexa_virtual.attributes.brightness | int / 255 * 100 ) | round  == 1 and states('device_tracker.tracy_all') != 'home' %}
          Tracy is at {{ states('device_tracker.tracy_all') }} and is approximately {{ states('sensor.tracys_time_to_home') }} minutes from home.
        {% elif (states.light.alexa_virtual.attributes.brightness | int / 255 * 100 ) | round  == 1 and states('device_tracker.tracy_all') == 'home' %}
          Tracy is at home.
        {% elif (states.light.alexa_virtual.attributes.brightness | int / 255 * 100 ) | round  == 2 and states('device_tracker.paul_all') == 'not_home' %}
          Paul is away and is approximately {{ states('sensor.pauls_time_to_home') }} minutes from home.
        {% elif (states.light.alexa_virtual.attributes.brightness | int / 255 * 100 ) | round  == 2 and states('device_tracker.paul_all') != 'home' %}
          Paul is at {{ states('device_tracker.paul_all') }} and is approximately {{ states('sensor.paul_time_to_home') }} minutes from home.
        {% elif (states.light.alexa_virtual.attributes.brightness | int / 255 * 100 ) | round  == 2 and states('device_tracker.paul_all') == 'home' %}
          Paul is at home.
        {% elif (states.light.alexa_virtual.attributes.brightness | int / 255 * 100 ) | round  == 3 %}
          Here is your local surf report.  The next tide is {{ states('sensor.noaa_tides') }}.  The waves are currently {{ states('sensor.virgina_beach_surf_report_forecast') }} feet.
        {% elif (states.light.alexa_virtual.attributes.brightness | int / 255 * 100 ) | round  == 4 %}
          At the moment, your commute to the work one will take {{ states('sensor.time_to_base') }} minutes.  Traffic is {{ states('sensor.pauls_traffic_density_to_base') }} at the moment.
        {% elif (states.light.alexa_virtual.attributes.brightness | int / 255 * 100 ) | round  == 5 %}
          At the moment, your commute to work two will take {{ states('sensor.time_to_caci') }} minutes.  Traffic is {{ states('sensor.pauls_traffic_density_to_caci') }} at the moment.
        {% elif (states.light.alexa_virtual.attributes.brightness | int / 255 * 100 ) | round  == 6 %}
          At the moment, your commute to work three will take {{ states('sensor.time_to_avis_tracy') }} minutes.  Traffic is {{ states('sensor.tracys_traffic_density_to_avis') }} at the moment.
        {% elif (states.light.alexa_virtual.attributes.brightness | int / 255 * 100 ) | round  == 7 %}
          The Garage Door is currently {{ states('cover.garage_door_opener') }}.
        {% elif (states.light.alexa_virtual.attributes.brightness | int / 255 * 100 ) | round  == 9 %}
          Based on current traffic, you have 15 minutes to leave if you want to get to work by {{ states.input_datetime.work_time_paul.attributes.hour }} {{ states.input_datetime.work_time_paul.attributes.minute }} A M .  Traffic is {{ states('sensor.pauls_traffic_density_to_work') }} today.
        {% elif (states.light.alexa_virtual.attributes.brightness | int / 255 * 100 ) | round  == 10 %}
          {{ as_timestamp(now())|timestamp_custom ('%-I %M') }} and then {{ as_timestamp(now())|timestamp_custom ('%H %M') }} and one more {{ as_timestamp(now())|timestamp_custom ('%I %M') }}  and lastly {{ as_timestamp (now()) | timestamp_custom('%-I %-M %p') }}
        {% else %}
         {{ states('light.alexa_virtual.attributes.brightness') }}
        {% endif %}
  - service: light.turn_off
    entity_id: light.alexa_virtual

Great! Thanks for replying and for sharing your latest version. I really appreciate it.