Let Appdaemon speak without tts and mediaplayer in hass

spoken logfiles for your apps?
thats easy possible now.
let appdaemon tell you why the lights are turning on or if motion is detected. with just 2 lines of code.
want to create a playlist of music in between the messages? also possible with 2 lines.

here is the app:

###########################################################################################
#                                                                                         
#  Rene Tode ( [email protected] )                                                            
#                                                                                         
#  2016/12/21 Germany                                                                     
#
###########################################################################################                                                                                         
#  you need to install gtts and mpg321
#  that can be done by:
#  
#  sudo apt-get install gtts
#  sudo apt-get install mpg321
#
#  save this app in your app dir with name: sound.py
#
###########################################################################################                                                                                         
#  in the appdaemon configfile you need to set:                                           
#                                                                                         
#  [soundfunctions]                                                                       
#  module = sound                                                                         
#  class = sound                                                                          
#  soundfilesdir = /an/empty!!/temp/file/dir/                                             
#                                                                                         
###########################################################################################                                                                                         
#  then you can use it like this in any app                                               
#                                                                                         
#  sound = self.get_app("soundfunctions")                                                 
#  sound.say("Any text you like","your_language","your_priority")    
#                     
#  supported languages: * 'af' : 'Afrikaans'
#                       * 'sq' : 'Albanian'
#                       * 'ar' : 'Arabic'
#                       * 'hy' : 'Armenian'
#                       * 'bn' : 'Bengali'
#                       * 'ca' : 'Catalan'
#                       * 'zh' : 'Chinese'
#                       * 'zh-cn' : 'Chinese (Mandarin/China)'
#                       * 'zh-tw' : 'Chinese (Mandarin/Taiwan)'
#                       * 'zh-yue' : 'Chinese (Cantonese)'
#                       * 'hr' : 'Croatian'
#                       * 'cs' : 'Czech'
#                       * 'da' : 'Danish'
#                       * 'nl' : 'Dutch'
#                       * 'en' : 'English'
#                       * 'en-au' : 'English (Australia)'
#                       * 'en-uk' : 'English (United Kingdom)'
#                       * 'en-us' : 'English (United States)'
#                       * 'eo' : 'Esperanto'
#                       * 'fi' : 'Finnish'
#                       * 'fr' : 'French'
#                       * 'de' : 'German'
#                       * 'el' : 'Greek'
#                       * 'hi' : 'Hindi'
#                       * 'hu' : 'Hungarian'
#                       * 'is' : 'Icelandic'
#                       * 'id' : 'Indonesian'
#                       * 'it' : 'Italian'
#                       * 'ja' : 'Japanese'
#                       * 'ko' : 'Korean'
#                       * 'la' : 'Latin'
#                       * 'lv' : 'Latvian'
#                       * 'mk' : 'Macedonian'
#                       * 'no' : 'Norwegian'
#                       * 'pl' : 'Polish'
#                       * 'pt' : 'Portuguese'
#                       * 'pt-br' : 'Portuguese (Brazil)'
#                       * 'ro' : 'Romanian'
#                       * 'ru' : 'Russian'
#                       * 'sr' : 'Serbian'
#                       * 'sk' : 'Slovak'
#                       * 'es' : 'Spanish'
#                       * 'es-es' : 'Spanish (Spain)'
#                       * 'es-us' : 'Spanish (United States)'
#                       * 'sw' : 'Swahili'
#                       * 'sv' : 'Swedish'
#                       * 'ta' : 'Tamil'
#                       * 'th' : 'Thai'
#                       * 'tr' : 'Turkish'
#                       * 'vi' : 'Vietnamese'
#                       * 'cy' : 'Welsh'
#                                                                                       
#  for priority give "1","2","3","4" or "5"
#
###########################################################################################                                                                                         
#  you can also use:
#                                                                                        
#  sound = self.get_app("soundfunctions")                                                 
#  sound.playsound("any valid mp3 file","your_priority")
#
#  to put music in your soundlist (or sounds)
#
###########################################################################################

import appdaemon.appapi as appapi
import datetime
import tempfile
import subprocess
import os
from gtts import gTTS

class sound(appapi.AppDaemon):

  def initialize(self):
    self.run_in(self.check_soundlist,30)
    return

  def check_soundlist(self, kwargs):
    filelist = os.listdir(self.args["soundfilesdir"])
    if filelist:
      filelist = sorted(filelist)
      for file in filelist:
        priority = file[0]
        tempfile = file[2]
        fnamefile = open(self.args["soundfilesdir"] + file, 'r')
        for line in fnamefile:
          fname = line      
        self.play(fname)
        if tempfile == "1":
          os.remove(fname)
        fnamefile.close()
        os.remove(self.args["soundfilesdir"] + file)
        self.run_in(self.check_soundlist,2)               
        return
    self.run_in(self.check_soundlist,2)
    return
    
  def say(self,text,lang,priority):
    with tempfile.NamedTemporaryFile(suffix='.mp3', delete=False) as f:
        fname = f.name
    tts = gTTS(text=text, lang=lang)
    tts.save(fname)
    self. write_in_prioritylist(priority,fname,"1")
  
  def playsound(self,file,priority):
    self. write_in_prioritylist(priority,file,"2")

  def play(self,filename):
    cmd = ['mpg321',filename]
    with tempfile.TemporaryFile() as f:
      subprocess.call(cmd, stdout=f, stderr=f)
      f.seek(0)
      output = f.read()
    
  def write_in_prioritylist(self,priority,file,tempfile):
    try:
      log = open(self.args["soundfilesdir"] + priority + "_" + tempfile + "_" + file[-10:], 'w')
      log.write(file)
      log.close()
    except:
      self.log("SOUNDFILEDIR PROBLEM!!")
4 Likes

@aimc please look at this code why it could go wrong, about once in every 1,5 hours (or 0,5 or 2,5) it just stops functioning.
i now made an inputboolean to restart. and then it runs again, just for some time.
off course i can make an extra option that just restarts it every hour.

but thats just fixing with a bandage.

absolutely no error anywhere, except that this:

2016-12-23 00:43:38.034360 WARNING ------------------------------------------------------------
2016-12-23 00:59:43.096208 WARNING ------------------------------------------------------------
2016-12-23 00:59:43.104012 WARNING Unexpected error:
2016-12-23 00:59:43.105962 WARNING ------------------------------------------------------------
2016-12-23 00:59:43.173115 WARNING Traceback (most recent call last):
  File "/usr/local/lib/python3.4/dist-packages/appdaemon/appdaemon.py", line 409, in worker
    function(ha.sanitize_timer_kwargs(args["kwargs"]))
  File "/home/pi/.homeassistant/apps/sound.py", line 130, in check_soundlist
    self.run_in(self.check_soundlist,2)
  File "/usr/local/lib/python3.4/dist-packages/appdaemon/appapi.py", line 326, in run_in
    handle = ha.insert_schedule(name, exec_time, callback, False, None, **kwargs)
  File "/usr/local/lib/python3.4/dist-packages/appdaemon/homeassistant.py", line 187, in insert_schedule
    conf.schedule[name][handle] = {"name": name, "id": conf.objects[name]["id"], "callback": callback, "timestamp": ts, "interval": interval, "basetime": utc, "repeat": repeat, "offset": c_offset, "type": type, "kwargs": kwargs}
KeyError: 'soundfunctions'

2016-12-23 00:59:43.180108 WARNING ------------------------------------------------------------

comes up a lot off times (but not at the time the function stops)
where soundfunctions is the “object” (so the headsection in the cfg)

Hi Rene - I’ll take a look later today.

1 Like

Are you running the latest version of AppDaemon?

i have no idea.
i think i have 1.3.2

Ok, it looks like the error you are seeing was fixed couple of releases ago when I added data structure locking to prevent race conditions. I don know for sure if that is the issue here, but if you upgrade to the latest that should fix the log messages and maybe the issue itself.

oke, ill try that later on.

Hi again Rene -

I like what you did here and I just had a need for something similar. I am using the built in TTS but there is still the problem of more than one request hitting at the same time. I think you may eventually run into issues with your file based approach above because I don’t believe it is thread safe. Imagine what would happen if someone is writing to the file at the exact moment you are trying to read it - bad things might happen :frowning: It may work fine but there is always the possibility of a problem. I decided to work around the concurrency issue using threading and a Queue - I think it should not suffer form the same problem. If you do have issues with your approach I think you could easily rework it to use Queues based on my code, so for reference, here is what I came up with:

import appdaemon.appapi as appapi
from queue import Queue
import threading
import time
#
# App to manage announcements via TTS
#
# Provides methods to enqueue TTS requests and make sure that only one is executed at a time
# Volume of the media player is set to a defined level and then restored afterwards
#
# Args:
#
# player - media player to use for announcements
# volume - volume to use for announcements
#
# To use from another APP:
#
# announce = self.get_app("Announce")
# announce.announce("{} arrived home".format(person))# Release Notes
#
# Version 1.0:
#   Initial Version

class Announce(appapi.AppDaemon):

  def initialize(self):
    
    # Create Queue
    self.queue = Queue(maxsize=0)

    # Create worker thread
    t = threading.Thread(target=self.worker)
    t.daemon = True
    t.start()
    
  def worker(self):
    while True:
      try:
        # Get text to say
        text = self.queue.get()
        # Save current volume
        volume = self.get_state(self.args["player"], attribute="volume_level")
        # Set to the desired volume
        self.call_service("media_player/volume_set", entity_id = self.args["player"], volume_level = 0.5)
        # Call TTS service
        self.call_service("tts/google_say", entity_id = self.args["player"], message = text)
        # Sleep to allow message to complete before restoring volume
        time.sleep(int(self.args["length"]))
        # Restore volume
        self.call_service("media_player/volume_set", entity_id = self.args["player"], volume_level = volume)
        # Set state locally as well to avoid race condition
        self.set_state(self.args["player"], attributes = {"volume_level": volume})
        # Rinse and repeat
        self.queue.task_done()
      except:
        self.log("Error")
    
  def announce(self, text):
    self.queue.put(text)
    

hi andrew,

i think you have given me the problem.
the code i have runs most of the times correctly.
but once in a while it just stops. (even with the latest version)
i have made a second check, that checks every 15 mins if the loop is still running.
so a info timer and if it exist then just do nothing and if not restart the loop.

that isnt working fine also. it gives moments that the loop gets doubled.

you use a sleeptime in between, but what if the text is unusual long?

another problem with your code is:
if you call that def out of 3 different apps at the same time, they run at the same time.
thats why i made a seperate loop for creating the files and for reading the files.

but i think thats where the problem lies. if the file is still created but is already there, the other loop tries to read it and stops running.

i made my code like this and got confused from the fact that i got the “PROBLEM IN CHECK_SOUNDLIST” several times after another. and i edited out the try part to check what was wrong. but probably nothing was wrong at all at that time :wink:

so i change it back now and see how it goes

###########################################################################################
#                                                                                         
#  Rene Tode ( [email protected] )                                                            
#                                                                                         
#  2016/12/21 Germany                                                                     
#
###########################################################################################                                                                                         
#  you need to install gtts and mpg321
#  that can be done by:
#  
#  sudo apt-get install gtts
#  sudo apt-get install mpg321
#
#  save this app in your app dir with name: sound.py
#
###########################################################################################                                                                                         
#  in the appdaemon configfile you need to set:                                           
#                                                                                         
#  [soundfunctions]                                                                       
#  module = sound                                                                         
#  class = sound                                                                          
#  soundfilesdir = /an/empty!!/temp/file/dir/                                             
#                                                                                         
###########################################################################################                                                                                         
#  then you can use it like this in any app                                               
#                                                                                         
#  sound = self.get_app("soundfunctions")                                                 
#  sound.say("Any text you like","your_language","your_priority")    
#                     
#  supported languages: * 'af' : 'Afrikaans'
#                       * 'sq' : 'Albanian'
#                       * 'ar' : 'Arabic'
#                       * 'hy' : 'Armenian'
#                       * 'bn' : 'Bengali'
#                       * 'ca' : 'Catalan'
#                       * 'zh' : 'Chinese'
#                       * 'zh-cn' : 'Chinese (Mandarin/China)'
#                       * 'zh-tw' : 'Chinese (Mandarin/Taiwan)'
#                       * 'zh-yue' : 'Chinese (Cantonese)'
#                       * 'hr' : 'Croatian'
#                       * 'cs' : 'Czech'
#                       * 'da' : 'Danish'
#                       * 'nl' : 'Dutch'
#                       * 'en' : 'English'
#                       * 'en-au' : 'English (Australia)'
#                       * 'en-uk' : 'English (United Kingdom)'
#                       * 'en-us' : 'English (United States)'
#                       * 'eo' : 'Esperanto'
#                       * 'fi' : 'Finnish'
#                       * 'fr' : 'French'
#                       * 'de' : 'German'
#                       * 'el' : 'Greek'
#                       * 'hi' : 'Hindi'
#                       * 'hu' : 'Hungarian'
#                       * 'is' : 'Icelandic'
#                       * 'id' : 'Indonesian'
#                       * 'it' : 'Italian'
#                       * 'ja' : 'Japanese'
#                       * 'ko' : 'Korean'
#                       * 'la' : 'Latin'
#                       * 'lv' : 'Latvian'
#                       * 'mk' : 'Macedonian'
#                       * 'no' : 'Norwegian'
#                       * 'pl' : 'Polish'
#                       * 'pt' : 'Portuguese'
#                       * 'pt-br' : 'Portuguese (Brazil)'
#                       * 'ro' : 'Romanian'
#                       * 'ru' : 'Russian'
#                       * 'sr' : 'Serbian'
#                       * 'sk' : 'Slovak'
#                       * 'es' : 'Spanish'
#                       * 'es-es' : 'Spanish (Spain)'
#                       * 'es-us' : 'Spanish (United States)'
#                       * 'sw' : 'Swahili'
#                       * 'sv' : 'Swedish'
#                       * 'ta' : 'Tamil'
#                       * 'th' : 'Thai'
#                       * 'tr' : 'Turkish'
#                       * 'vi' : 'Vietnamese'
#                       * 'cy' : 'Welsh'
#                                                                                       
#  for priority give "1","2","3","4" or "5"
#
###########################################################################################                                                                                         
#  you can also use:
#                                                                                        
#  sound = self.get_app("soundfunctions")                                                 
#  sound.playsound("any valid mp3 file","your_priority")
#
#  to put music in your soundlist (or sounds)
#
###########################################################################################

import appdaemon.appapi as appapi
import datetime
import tempfile
import subprocess
import os
from gtts import gTTS


class sound(appapi.AppDaemon):

  def initialize(self):
    self.sound_handle = self.run_in(self.check_soundlist,30,normal_loop="running")
    self.listen_state(self.extrastart,"input_boolean.geluidforceren")
    runtime = datetime.datetime.now() + datetime.timedelta(minutes=15)
    self.run_every(self.autoherstart,runtime,15*60)


  def autoherstart(self, kwargs):   
    try:
      time, interval, kwargs = self.info_timer(self.sound_handle)
      handletest = kwargs["normal_loop"]
    except:
      handletest=""
    if handletest != "running":
      self.cancel_timer(self.sound_handle)
      self.sound_handle2 = self.run_in(self.check_soundlist,2,normal_loop="restarted")
      self.log("geluidsmodule herstart automatisch geforceerd")
    else:
      self.log("geluidsmodule loopt nog correct")

  def extrastart(self, entity, attribute, old, new, kwargs):
    self.cancel_timer(self.sound_handle)
    self.sound_handle2 = self.run_in(self.check_soundlist,2,normal_loop="restarted")
    self.turn_off("input_boolean.geluidforceren")
    self.log("geluidsmodule herstart geforceerd")


  def check_soundlist(self, kwargs):
    #try:
      priority = self.read_prioritylist()
      fname = priority["fname"]
      tempfile = priority["tempfile"]
      tempfname = priority["tempfilename"]
      if fname != "":
        self.play(fname)
        if tempfile == "1":
          os.remove(fname)
        os.remove(tempfname)
    #except:
      #self.log("PROBLEM IN CHECK_SOUNDLIST")
      self.sound_handle = self.run_in(self.check_soundlist,2,normal_loop="running")
    

  def say(self,text,lang,priority):
    with tempfile.NamedTemporaryFile(suffix='.mp3', delete=False) as f:
        fname = f.name
    tts = gTTS(text=text, lang=lang)
    tts.save(fname)
    self. write_in_prioritylist(priority,fname,"1")
  

  def playsound(self,file,priority):
    self. write_in_prioritylist(priority,file,"2")


  def play(self,filename):
    opnieuwaan = False
    if self.get_state("input_boolean.swr3")=="on":
      self.turn_off("input_boolean.swr3")
      opnieuwaan = True
    cmd = ['mpg321',filename]
    with tempfile.TemporaryFile() as f:
      subprocess.call(cmd, stdout=f, stderr=f)
      f.seek(0)
      output = f.read()
    if opnieuwaan:
      self.turn_on("input_boolean.swr3")
    

  def write_in_prioritylist(self,priority,file,tempfile):
    try:
      runtime = datetime.datetime.now().strftime("%d-%m-%y %H:%M:%S.%f")
      log = open(self.args["soundfilesdir"] + priority + "_" + tempfile + "_" + file[-10:], 'w')
      log.write(runtime + ";" + file)
      log.close()
    except:
      self.log("SOUNDFILEDIR PROBLEM!!")


  def read_prioritylist(self):
    toppriority = 0
    activefile = ""
    firsttime = datetime.datetime.strptime("31-12-2050","%d-%m-%Y")
    activetemp = ""
    activefnamefile = ""
    filelist = os.listdir(self.args["soundfilesdir"])
    if filelist:
      for file in filelist:
        priority = int(file[0])
        tempfile = file[2]
        tempfilename = self.args["soundfilesdir"] + file
        fnamefile = open(tempfilename, 'r')
        for line in fnamefile:
          splitline = line.split(";")
          fname = splitline[1]
          #self.log(splitline[0])
          fdate = datetime.datetime.strptime(splitline[0],"%d-%m-%y %H:%M:%S.%f")
        fnamefile.close()
        if priority >= toppriority:
          if firsttime > fdate:
            #self.log(firsttime.strftime("%d-%m-%y %H:%M:%S.%f") + " > " + fdate.strftime("%d-%m-%y %H:%M:%S.%f"))
            firsttime = fdate
            toppriority = priority
            activefile = fname
            activetemp = tempfile
            activefnamefile = tempfilename
    return {"fname":activefile,"tempfilename":activefnamefile,"tempfile":activetemp}    

oke i have a problem that i dont know how to correct.

this piece of code:

  def autoherstart(self, kwargs):   
    try:
      time, interval, kwargs = self.info_timer(self.sound_handle)
      handletest = kwargs["normal_loop"]
    except:
      handletest=""
    if handletest != "running":
      self.cancel_timer(self.sound_handle)
      self.sound_handle2 = self.run_in(self.check_soundlist,2,normal_loop="restarted")
      self.log("geluidsmodule herstart automatisch geforceerd")
    else:
      self.log("geluidsmodule loopt nog correct")

isnt working like it should.
sometimes it start the loop, even when its running.
so self.info_timer isnt working consistent.

i found the problem.
the handle gets flushed as soon as the code starts. and because i restart a run_in at the end of the code there is a gap where there is no handle.
if the controlfunction runs exactly when the loopfunction is running it thinks that the loop isnt running and starts a new loop.
making things double.

so to go around that problem i made a boolean that is set at true at the start of the loopcode and false at the end of the code.

@aimc is it possible to flush the handle when the code has ended, in stead of when it starts?
it would be more logical in my eyes.

edit: spook to soon. setting up a flag isnt working. somehow the flag isnt right all the time.
matbe i should use a sleep from 1 second and a retry in the check function instead. and run that for 30 times or so to make sure.

That should not be the case, did you actually try this and see an issue? If so I will need to fix it!

As it is designed, my queue takes the place of your files, and there are separate threads to read and write the queue so it is conceptually similar. The announce procedure puts an item on the queue. The queue itself is threadsafe and even if 100 programs put the item on simultaneously,they will all take their turn, I have tried this multiple times form a single program, and it works well.

The next step is that a single additional thread only reads from the Queue so that they are all processed in order.

Rene, I think that most of your problems hinge around the multi-threaded concurrency of AppDaemon. No matter how many flags you set you will run into issues as setting and reading a global flag is not threadsafe. At the very least you should be using proper thread safe locks - using the Queue as I did makes this a lot more convenient.

Regarding changing the behavior of the handles - that would be complex to do, and is really fixing a corner case where you rely on functionality I haven’t documented. It is also undesirable because having a valid handle implies you can cancel a timer or state callback, which is not possible once the callback has started.

The real way to fix this is to make it thread safe with locking and/or Queue use. I am very happy to help you work through it :slight_smile:

That is a hard one - the TTS call is asynchronous so you don’t know when it has finished. The only solution is to make a delay long enough for the text to complete and I also made it configurable. Note that it is OK to put a sleep in the TTS worker thread since it is not an AppDaemon worker thread. Doing that in an AppDaemon worker thread would be bad :slight_smile:

In the case of TTS with the Sonos, if a new TTS is triggered before the old one ends, the first phrase is stopped and the second one takes precedence. It might work differently on other platforms. Setting a delay also ensures a minimum time between announcements which is also a good thing I think.

thats why i started the queue like i did.
calling from 3 apps like this:

    sound = self.get_app("soundfunctions")
    sound.say(speaktext,"nl","5")

makes 3 threads if the function isnt locking somehow.
and i didnt know about locking possibilities until now :wink:

i did reread your loop now, and that is helping a lot to understand multithreading.

it seems like i got things under control for now. and because other things will get priority i think i leave it by that for now, but i will look into your option and rewrite it afterwards.

this code:

  def initialize(self):
    self.lasttime = datetime.datetime.now()
    self.totaltime = datetime.timedelta(seconds=1)
    self.minutesrunning = 0
    self.herstarts = 0
    self.sound_handle = self.run_in(self.check_soundlist,30,normal_loop="running")
    self.listen_state(self.extrastart,"input_boolean.geluidforceren")
    runtime = datetime.datetime.now() + datetime.timedelta(minutes=2)
    self.run_every(self.autoherstart,runtime,2*60)


  def autoherstart(self, kwargs):   
    self.log("start autoherstart")
    counter = 0
    counter2 = 1
    handletest = "leeg"
    while handletest == "leeg" and counter < 30:
      try:
        time, interval, kwargs = self.info_timer(self.sound_handle)
        handletest = kwargs["normal_loop"]
        counter = 60
      except:
        handletest="leeg"
        counter = counter + 1
        counter2 = counter
        timelib.sleep(1)
    self.log("handle kwarg: " + handletest + " aantal keer getest: " + str(counter2))
    if handletest != "running":
      self.cancel_timer(self.sound_handle)
      self.lasttime = datetime.datetime.now()
      self.totaltime = datetime.timedelta(seconds=1)
      self.minutesrunning = 0
      self.herstarts = self.herstarts + 1
      self.sound_handle2 = self.run_in(self.check_soundlist,2,normal_loop="restarted")
      self.log("geluidsmodule herstart automatisch geforceerd")
      
    else:
      self.log("geluidsmodule loopt nog correct")

  def extrastart(self, entity, attribute, old, new, kwargs):
    self.cancel_timer(self.sound_handle)
    self.sound_handle2 = self.run_in(self.check_soundlist,2,normal_loop="restarted")
    self.turn_off("input_boolean.geluidforceren")
    self.log("geluidsmodule herstart geforceerd")


  def check_soundlist(self, kwargs):
    try:
      priority = self.read_prioritylist()
      fname = priority["fname"]
      tempfile = priority["tempfile"]
      tempfname = priority["tempfilename"]
      if fname != "":
        self.play(fname)
        if tempfile == "1":
          os.remove(fname)
        os.remove(tempfname)
    except:
      self.log("PROBLEM IN CHECK_SOUNDLIST")
    self.sound_handle = self.run_in(self.check_soundlist,2,normal_loop="running")
    actualtime = datetime.datetime.now()
    timedifference = actualtime - self.lasttime
    self.lasttime = actualtime
    if timedifference < datetime.timedelta(seconds=1):
       self.log("LOOP RUNNING MORE THEN ONCE")
    else:
       self.totaltime = self.totaltime + timedifference
    minutesrunning = self.totaltime.seconds
    minutesrunning = minutesrunning//60
    if minutesrunning > self.minutesrunning:
      self.minutesrunning = minutesrunning
      self.log("SOUNDFILE RUNS CORRECTLY SINCE: " + str(self.totaltime) + " RESTARTED: " + str(self.herstarts) + " TIMES")

seems to run as it should, but obviously this is way to complex :wink:
but i am proud that i somehow still can find an answer to complex problems, with the little knowledge/experience i have.
:wink:

Excellent - if it works then it works!

1 Like

I must not speak to soon, but it is running for over 90 minutes now without double loops and it has spoken several times in between.
untill now i got a double loop before that, so it looks promising.

it helps that i now know where the problems were coming from.

your code is very good too, but i would still need a failsafe, and thats already implemented in mine.
Ill keep you posted about problems or changes.

1 Like

Rene, as someone who has learned python on his own as well, concurrency and threading is a very daunting subject to tackle. It is however very rewarding, as it allows applications like AppDaemon and HomeAssistant to run smoothly, and more importantly, scale up to a potentially very large program. I think you will find many useful paradigms by following aimc’s advice above!

All in all, I like that you bring up the idea of announcing from the logs. While I wouldn’t go this route, I could see how it would be useful to have AppDaemon speak any errors that happen, extracting key information from the stacktrace. In my vision of home automation, I prefer practicality over excessive. This is a great topic though!

1 Like

it is my plan to have speakers all over the house again in the near future.
it is only logical in my eyes to have appdeamon speak warnings at that moment.

not just a sound when someones is at the door, but the house telling me that someone is on the driveway towards my door, or the smoke detector in a specific room has low battery, or some log cant be accessed, or there is a power problem somewhere, or just something nice to make you smile.

because of the fact that all my automations are run by appdaemon, its only sensible to have appdaemon do the tts in my eyes.

Heh - I have the same vision Rene, but my wife hates the house talking to her! So far I only have TTS when she leaves or comes home so she will never hear it … I will probably add some alarm conditions though when I can think of what I need to alarm on. Maybe things like leaving doors unlocked or unexpected motion.

I am waiting to see how the Amazon/Sonos integration turns out as well as hoping for Amazon to add push to their dots, then I will probably add some play 1s in the house, I already have Dots everywhere but I can;t yet use them for TTS.

1 Like

i showed my wife a video off a conversation with google home, and she loved it.
i let, at random times, homeassistant say nice things to her, like:

  • did you know thay your husband loves you,
  • Olinde is a beutiful woman,
  • dont forget to relax, please breath deeply,
  • isnt it a nice day today?,
  • a smile does everyone good, did you smile already today?
  • when Olinde smiles, the sun shines.
  • etc.

because the timing is random it Always comes when she doesnt expect it. and she loves it.
i intent to make that list longer and longer.

in between those things i let hass tell out loud when the thermostat is set to a different temperature, or when a connection with an arduino cannot be established.

edit: by the way, it is now running for 24 hours without any problem.
the failsafe check wasnt 1 time used in that time!
so i think i finally got it running like it should.

4 Likes