浏览代码

Init Commit

Patrick Baumgartner 3 年之前
父节点
当前提交
0117943bfa

+ 1 - 1
DynLoader/main.py

@@ -7,7 +7,7 @@ from DynLoader.modInfo import modInfo
 class DynLoaderMod(object):
 
 
-    instance = dict()
+    instance = None
     @staticmethod
     def getInstance():
         return DynLoaderMod.instance

+ 1 - 6
Jessi/Contents.py

@@ -4,11 +4,6 @@
 
 class Contents(object):
     
-    text:list
-    addStats:dict
-    actions:list
-    tags:list
-
     def __init__(self, item:list = None):
         self.tags = list()
         self.addStats = dict()
@@ -31,7 +26,7 @@ class Contents(object):
         del self.text
         del self.addStats
         del self.actions
-        del self.Tags
+        del self.tags
 
 
     def hasTags(self, tagList):

+ 195 - 0
PicoVoice/Porcupine.py

@@ -0,0 +1,195 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+import argparse
+import os
+import struct
+import wave
+import configparser
+from datetime import datetime
+from threading import Thread
+import pvporcupine
+from pvrecorder import PvRecorder
+import platform
+from VoicePlay import Voice
+from DynLoader import DynLoaderMod as dyn
+
+
+class Porcupine(Thread):
+
+    def __init__(self):
+        super(Porcupine, self).__init__()
+        config = configparser.ConfigParser()
+        config.read('config/pico.ini')
+        Keywords=[]
+        Sensitivity=[]
+        isAdmin=[]
+        bHasKeywords = False
+
+        self._model_path=""
+        self._keyword_paths=""
+        self._sensitivities=0.5
+
+        self._lang = "de"
+        if "lang" in config["global"]:
+            self._lang = config["global"]["lang"]
+        self._os = platform.system().lower()
+
+        if "ApiKey" in config["PicoVoice"]:
+            self._access_key = config["PicoVoice"]["apikey"]
+        self._library_path = pvporcupine.LIBRARY_PATH
+
+
+        if "model" in config["Porcupine"]:
+            self._model_path = 'ressources/models/porcupine/' + config["Porcupine"]["model"]
+
+        for k,v in config.items():
+            if "weakword" in v and v["weakword"]!="":
+                print("  - Weakword '"+k+"': " + v["weakword"].strip())
+                Keywords.append('ressources/models/porcupine/' + v["weakword"].strip())
+                bHasKeywords = True
+                if "sensitivities" in v:
+                    Sensitivity.append(float(v["sensitivities"]))
+                else:
+                    Sensitivity.append(float(0.5))
+                if "admin" in v and (v["admin"]=="1" or v["admin"]=="true" or v["admin"]=="True" or v["admin"]=="TRUE"):
+                    isAdmin.append(True)
+                else:
+                    isAdmin.append(False)
+
+
+        if bHasKeywords == True:
+            self._keyword_paths = Keywords
+        self._sensitivities = Sensitivity
+        self.isAdmin = isAdmin
+
+        if "InputDeviceIndex" in config["Hardware"]:
+            self._input_device_index = int(config["Hardware"]["InputDeviceIndex"])
+        else:
+            self._input_device_index = None
+        self._output_path = None
+
+    instance = None
+    @staticmethod
+    def getInstance():
+        if Porcupine.instance==None:
+            Porcupine.instance = Porcupine()
+        return Porcupine.instance
+
+    def run(self):
+        print("Porcupine.run()")
+        print("  - Generate Keyword List")
+
+        _access_key = Porcupine.getInstance()._access_key
+        _library_path = Porcupine.getInstance()._library_path
+        _model_path = Porcupine.getInstance()._model_path
+        _keyword_paths = Porcupine.getInstance()._keyword_paths
+        _sensitivities = Porcupine.getInstance()._sensitivities
+        _output_path = Porcupine.getInstance()._output_path
+        __func = Porcupine.getInstance().__func
+        _isAdmin = Porcupine.getInstance().isAdmin
+        dynloader:dyn = dyn.getInstance()
+
+        keywords = list()
+        for x in self._keyword_paths:
+            keyword_phrase_part = os.path.basename(x).replace('.ppn', '').split('_')
+            if len(keyword_phrase_part) > 6:
+                keywords.append(' '.join(keyword_phrase_part[0:-6]))
+            else:
+                keywords.append(keyword_phrase_part[0])
+                
+        porcupine = None
+        recorder = None
+        wav_file = None
+        try:
+            '''porcupine = pvporcupine.create(
+                access_key=_access_key,
+                library_path=_library_path,
+                model_path=_model_path,
+                keyword_paths=_keyword_paths,
+                sensitivities=_sensitivities
+            )'''
+            porcupine = pvporcupine.create(
+                access_key=_access_key,
+                model_path=_model_path,
+                keyword_paths=_keyword_paths,
+                sensitivities=_sensitivities
+            )
+            recorder = PvRecorder(device_index=self._input_device_index, frame_length=porcupine.frame_length)
+            print("Frame Length: ",porcupine.frame_length)
+            recorder.start()
+            if _output_path is not None:
+                wav_file = wave.open(self._output_path, "w")
+                wav_file.setparams((1, 2, 16000, 512, "NONE", "NONE"))
+
+            print('Listening {')
+            for keyword, sensitivity, admin in zip(keywords, _sensitivities, _isAdmin):
+                print(keyword,str(sensitivity), admin)
+            print('}')
+
+            bRunning = True
+            while bRunning:
+                pcm = recorder.read()
+
+                if wav_file is not None:
+                    wav_file.writeframes(struct.pack("h" * len(pcm), *pcm))
+
+                result = porcupine.process(pcm)
+                if result >= 0:
+                    #print('[%s] Detected %s' % (str(datetime.now()), keywords[result]))
+                    if(self.__func):
+                        #bRunning = False
+                        recorder.stop()
+                        dynloader.execute("WeakWord",result,keywords[result],_isAdmin[result])
+                        __func(result,keywords[result],_isAdmin[result])
+                        recorder.start()
+        except pvporcupine.PorcupineInvalidArgumentError as e:
+            print("One or more arguments provided to Porcupine is invalid: {\n" +
+                  "\tAcccess Key: "+_access_key+"\n" +
+                  "\tLibrary Path: "+_library_path+"\n" +
+                  "\tModel Path: "+_model_path+"\n" +
+                  "\tKeyword Paths: ['"+', '.join(_keyword_paths)+"']\n" +
+                  "\tSensitivities: ['"+', '.join([str(int) for int in _sensitivities])+"']\n" +
+                  "}")
+            print("If all other arguments seem valid, ensure that '"+self._access_key+"' is a valid AccessKey")
+            print("Error: "+','.join(e.args))
+            Voice.getInstance().Say("Ausnahmefehler in Porcubine Argument")
+            raise e
+        except pvporcupine.PorcupineActivationError as e:
+            print("AccessKey activation error")
+            Voice.getInstance().Say("Ausnahmefehler in Porcubine Aktivierung")
+            raise e
+        except pvporcupine.PorcupineActivationLimitError as e:
+            print("AccessKey '"+self._access_key+"' has reached it's temporary device limit")
+            Voice.getInstance().Say("Ausnahmefehler in Porcubine Aktivierungs-Limit")
+            raise e
+        except pvporcupine.PorcupineActivationRefusedError as e:
+            print("AccessKey '"+self._access_key+"' refused")
+            Voice.getInstance().Say("Ausnahmefehler in Porcubine Aktivierung zurückgewiesen")
+            raise e
+        except pvporcupine.PorcupineActivationThrottledError as e:
+            print("AccessKey '"+self._access_key+"' has been throttled")
+            Voice.getInstance().Say("Ausnahmefehler in Porcubine Zugriff wurde gedrosselt")
+            raise e
+        except pvporcupine.PorcupineError as e:
+            print("Failed to initialize Porcupine")
+            Voice.getInstance().Say("Ausnahmefehler bei der Porcubine Initialisierung")
+            raise e
+        except KeyboardInterrupt:
+            print('Stopping ...')
+        finally:
+            if porcupine is not None:
+                porcupine.delete()
+            if recorder is not None:
+                recorder.delete()
+            if wav_file is not None:
+                wav_file.close()
+
+    def RegisterFunc(self, func):
+        self.__func = func
+
+    @staticmethod
+    def show_audio_devices():
+        devices = PvRecorder.get_audio_devices()
+        for i in range(len(devices)):
+            print("index: "+str(i)+", device name: '"+devices[i]+"'")
+

+ 263 - 0
PicoVoice/Rhino.py

@@ -0,0 +1,263 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+import argparse
+import struct
+import wave
+from threading import Thread
+import pvrhino
+from pvrecorder import PvRecorder
+import platform
+import configparser
+import os
+from VoicePlay import Voice
+
+class Rhino(Thread):
+
+    instance = None
+    @staticmethod
+    def getInstance():
+        if Rhino.instance==None:
+            Rhino.instance = Rhino()
+        return Rhino.instance
+
+    def __init__(self):
+        super(Rhino, self).__init__()
+
+        print("Rhino.__init__")
+
+        config = configparser.ConfigParser()
+        config.read('config/pico.ini')
+
+        self._lang = "de"
+        if "lang" in config["global"]:
+            self._lang = config["global"]["lang"]
+        self._os = platform.system().lower()
+
+        if "ApiKey" in config["PicoVoice"]:
+            print("  - Api Key: " + config["PicoVoice"]["ApiKey"])
+            self._access_key = config["PicoVoice"]["ApiKey"]
+
+        print("  - Library: " + pvrhino.LIBRARY_PATH)
+        self._library_path = pvrhino.LIBRARY_PATH
+
+        #if "Model" in config["PicoVoice"]:
+        #    print("  - Model: " + config["PicoVoice"]["Model"])
+        #    self._model_path = config["PicoVoice"]["Model"]
+        self._model_path = 'ressources/model/porcupine/' + config["Rhino"]["model"]
+
+        #self._context_path = context_path
+        self._require_endpoint = None
+
+        if "InputDeviceIndex" in config["Hardware"]:
+            print("  - Audio Device Index: " + config["Hardware"]["InputDeviceIndex"])
+            self._audio_device_index = int(config["Hardware"]["InputDeviceIndex"])
+        else:
+            self._audio_device_index = None
+
+        print("  - Output Path")
+        self._output_path = None
+
+
+    def run(self, contextFile, intentHandler, startListening = None, stopListening = None):
+        """
+        Creates an input audio stream, instantiates an instance of Rhino object, and infers the intent from spoken
+        commands.
+        """
+
+        print("Rhino.run("+contextFile+", func)")
+
+        rhino = None
+        recorder = None
+        wav_file = None
+
+
+        # Find Context File
+        bFoundContextFile = False
+        for iMajor in range(4,0,-1):
+            for iMinor in range(10,-1,-1):
+                for iRev in range(10,-1,-1):
+                    cf = "ressources/models/context/" + contextFile + "_" + self._lang + "_" + self._os + "_v" + str(iMajor) + "_" + str(iMinor) + "_" + str(iRev) + ".rhn"
+                    print("Try: "+cf)
+                    if os.path.isfile(cf):
+                        print("  FOUND")
+                        bFoundContextFile = True
+                        break
+                if bFoundContextFile == True:
+                    break
+            if bFoundContextFile == True:
+                break
+
+
+        if bFoundContextFile == True:
+            print("  - Context File Path: " + cf)
+        else:
+            print("  - Context File not found, use Version 2.0.0")
+            cf = "ressources/models/context/" + contextFile + "_" + self._lang + "_" + self._os + "_v2_0_0.rhn"
+
+        try:
+            print("  - Create Rhino")
+            rhino = pvrhino.create(
+                access_key=self._access_key,
+                library_path=self._library_path,
+                model_path=self._model_path,
+                context_path=cf,
+                require_endpoint=self._require_endpoint
+            )
+
+            print("  - Create Recorder")
+            recorder = PvRecorder(device_index=self._audio_device_index, frame_length=rhino.frame_length)
+            print("  - Start Recorder")
+            recorder.start()
+
+            if self._output_path is not None:
+                wav_file = wave.open(self._output_path, "w")
+                wav_file.setparams((1, 2, 16000, 512, "NONE", "NONE"))
+
+            print(rhino.context_info)
+            print()
+
+            print("Using device: '"+recorder.selected_device+"'")
+            print("Listening...")
+            print()
+
+            bRunning = True
+            while bRunning:
+                if startListening is not None:
+                    startListeningResult = startListening({
+                        "library": self._library_path,
+                        "model_path": self._model_path,
+                        "context_path": self._context_path,
+                        "require_endpoint": self._require_endpoint,
+                        "rhino": rhino,
+                        "recorder": recorder,
+                        "wave_file":self._output_path
+                    })
+                    if startListeningResult is not None and startListeningResult == False:
+                        bRunning=False
+                        recorder.stop()
+                        break
+                pcm = recorder.read()
+
+                if wav_file is not None:
+                    wav_file.writeframes(struct.pack("h" * len(pcm), *pcm))
+
+                is_finalized = rhino.process(pcm)
+                if is_finalized:
+                    inference = rhino.get_inference()
+                    recorder.stop()
+                    if inference.is_understood:
+                        print('{')
+                        print("  intent : '"+inference.intent+"'")
+                        print('  slots : {')
+                        for slot, value in inference.slots.items():
+                            print("    "+slot+" : '"+value+"'")
+                        print('  }')
+                        print('}\n')
+                        if intentHandler is not None:
+                            res = intentHandler(inference)
+                        if res==False and res != None:
+                            bRunning = False
+                        else:
+                            recorder.start()
+                    else:
+                        print("Didn't understand the command.\n")
+                        bRunning = False
+            print("Stop listening")
+            if stopListening is not None:
+                stopListening()
+        except pvrhino.RhinoInvalidArgumentError as e:
+            print("One or more arguments provided to Rhino is invalid: {\n" +
+                  "\t"+self._access_key+"\n" +
+                  "\t"+self._library_path+"\n" +
+                  "\t"+self._model_path+"\n" +
+                  "\t"+cf+"\n" +
+                  "\t"+str(self._require_endpoint)+"\n" +
+                  "}")
+            print("If all other arguments seem valid, ensure that '"+self._access_key+"' is a valid AccessKey")
+            Voice.getInstance().Say("Ausnahmefehler in Rhino Argument")
+            raise e
+        except pvrhino.RhinoActivationError as e:
+            print("AccessKey activation error")
+            raise e
+        except pvrhino.RhinoActivationLimitError as e:
+            print("AccessKey '"+self._access_key+"' has reached it's temporary device limit")
+            Voice.getInstance().Say("Ausnahmefehler in Rhino - Begränzung erreicht")
+            raise e
+        except pvrhino.RhinoActivationRefusedError as e:
+            print("AccessKey '"+self._access_key+"' refused")
+            raise e
+        except pvrhino.RhinoActivationThrottledError as e:
+            print("AccessKey '"+self._access_key+"' has been throttled")
+            Voice.getInstance().Say("Ausnahmefehler in Rhino - Anfragenbegränzung erreicht")
+            raise e
+        except pvrhino.RhinoError as e:
+            print("Failed to initialize Rhino")
+            Voice.getInstance().Say("Ausnahmefehler in Rhino - Initialisierung fehlgeschlagen")
+            raise e
+        except KeyboardInterrupt:
+            print('Stopping ...')
+
+        finally:
+            if recorder is not None:
+                recorder.delete()
+
+            if rhino is not None:
+                rhino.delete()
+
+            if wav_file is not None:
+                wav_file.close()
+
+    @staticmethod
+    def formatChar(c:str):
+        if c=="Ah": return "a"
+        elif c=="Be": return "b"
+        elif c=="Zeh": return "c"
+        elif c=="De": return "d"
+        elif c=="E": return "e"
+        elif c=="Ef": return "f"
+        elif c=="Geh": return "g"
+        elif c=="H": return "h"
+        elif c=="Jott": return "j"
+        elif c=="Ka": return "k"
+        elif c=="El": return "l"
+        elif c=="Em": return "m"
+        elif c=="En": return "n"
+        elif c=="O": return "o"
+        elif c=="Pe": return "p"
+        elif c=="Kuh": return "q"
+        elif c=="Er": return "r"
+        elif c=="Es": return "s"
+        elif c=="Tee": return "t"
+        elif c=="U": return "u"
+        elif c=="Fau": return "v"
+        elif c=="We": return "w"
+        elif c=="Ix": return "x"
+        elif c=="Yipsilon": return "y"
+        elif c=="Zett": return "z"
+        elif c=="Null": return "0"
+        elif c=="eins": return "1"
+        elif c=="zwei": return "2"
+        elif c=="drei": return "3"
+        elif c=="vier": return "4"
+        elif c=="fünf": return "5"
+        elif c=="sechs": return "6"
+        elif c=="sieben": return "7"
+        elif c=="acht": return "8"
+        elif c=="neun": return "9"
+        elif c=="komma": return ","
+        elif c=="punkt": return "."
+        elif c=="doppelpunkt": return ":"
+        elif c=="Dollar": return "$"
+        elif c=="Raute": return "#"
+        elif c=="Äh": return "ä"
+        elif c=="Ö": return "ö"
+        elif c=="Ü": return "ü"
+        elif c=="Ausrufezeichen": return "!"
+        elif c=="Und": return "&"
+        elif c=="Slash": return "/"
+        elif c=="Backslash": return "\\"
+        elif c=="Plus": return "+"
+        elif c=="Minus": return "-"
+        elif c=="Unterstrich": return "_"
+        elif c=="Fu": return "Foo"
+        else: return c.lower()

+ 3 - 0
PicoVoice/__init__.py

@@ -0,0 +1,3 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+from PicoVoice.main import PicoVoice

+ 52 - 0
PicoVoice/main.py

@@ -0,0 +1,52 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+import PicoVoice.Porcupine as pc
+import PicoVoice.Rhino as rh
+from pvrecorder import PvRecorder
+
+
+class PicoVoice(object):
+
+    instance = None
+    @staticmethod
+    def getInstance():
+        if PicoVoice.instance==None:
+            PicoVoice.instance = PicoVoice()
+        return PicoVoice.instance
+
+    def __init__(self):
+        self._porcupine = pc.Porcupine.getInstance()
+        self._rhino = rh.Rhino.getInstance()
+        self._porcupine.RegisterFunc(self.OnWeakWord)
+        self._weakwordFunc = []
+
+    def start(self):
+        self._porcupine.start()
+
+    @property
+    def Porcupine(self):
+        return self._porcupine
+
+    @property
+    def Rhino(self):
+        return self._rhino
+
+    def OnWeakWord(self, index:int, weakword:str, isAdmin:bool):
+        print(f"OnWeakWord({index}, '{weakword}', {isAdmin})")
+        #Schnake.Schnake.getInstance().Send("/Weakword",weakword)
+        sindex=str(index)
+        for itm in self._weakwordFunc:
+            if itm["weakword"]==sindex or itm["weakword"]==weakword or itm["weakword"]=="" or itm["weakword"]=="*" or weakword=="*":
+                itm["func"](index,weakword)
+
+    def RegisterOnWeakWord(self, func, weakword = "*"):
+        res={"func": func, "weakword": weakword}
+        self._weakwordFunc.append(res)
+
+    @staticmethod
+    def show_audio_devices():
+        devices = PvRecorder.get_audio_devices()
+        for i in range(len(devices)):
+            print("index: "+str(i)+", device name: '"+devices[i]+"'")
+
+

+ 3 - 1
README.md

@@ -15,4 +15,6 @@ librosa
 configparser  
 deepspeech  
 pyyaml  
-nanotts  
+nanotts  
+termcolor  
+vosk  

+ 2 - 1
Stats/AdminStats.py

@@ -5,7 +5,8 @@ from datetime import *
 
 class AdminStats(object):
 
-    LastAdminDate = None
+    def __init__(self):
+        self.LastAdminDate = None
 
 
     @property

+ 23 - 0
Stats/DeviceStats.py

@@ -651,6 +651,9 @@ class DeviceStats(object):
 
 
     def set(self, name:str, val:float) -> bool:
+        """
+        Setzt den Wert der entsprechenden Property
+        """
         n = name.lower()
         if n=="angry": self.angry = val
         elif n=="aroused": self.aroused = val
@@ -685,6 +688,10 @@ class DeviceStats(object):
         return True
 
     def force(self, name:str, val:float) -> bool:
+        """
+        Setzt im Gegenteil zu 'set()' den Wert der Variable.
+        Es findet keine Überprüfung auf _locked, min und max statt
+        """
         n = name.lower()
         if n=="angry": self._angry = val
         elif n=="aroused": self._aroused = val
@@ -719,6 +726,9 @@ class DeviceStats(object):
         return True
 
     def lock(self, name:str, val:float) -> bool:
+        """
+        Setzt einen Lock Wert für die entsprechende Variable
+        """
         n = name.lower()
         if n=="angry": self._locked_angry = val
         elif n=="aroused": self._locked_aroused = val
@@ -753,6 +763,9 @@ class DeviceStats(object):
         return True
 
     def get(self, name:str) -> float:
+        """
+        Gibt die entsprechende Einstellung zurück
+        """
         n = name.lower()
         if n=="angry": return self.angry
         elif n=="aroused": return self.aroused
@@ -786,6 +799,10 @@ class DeviceStats(object):
         else: return -1.0
 
     def raw(self, name:str) -> float:
+        """
+        Gibt im gegensatz zu 'get()' den Wert der Variable zurück
+        Es findet keine Überprüfung auf _locked, min und max statt
+        """
         n = name.lower()
         if n=="angry": return self._angry
         elif n=="aroused": return self._aroused
@@ -860,6 +877,9 @@ class DeviceStats(object):
         self.textconfig.Save()
 
     def Save(self, file = "stats.txt"):
+        """
+        Speichert die Daten in einer TextConfig
+        """
         if os.path.isfile(file):
             os.remove(file)
         with open(file, 'w') as f:
@@ -903,6 +923,9 @@ class DeviceStats(object):
             ])
 
     def Load(self, file = "stats.txt"):
+        """
+        Läd die Daten aus einer TextConfig
+        """
         if os.path.isfile(file) == False:
             return False
         #lines=[]

+ 104 - 12
Stats/HouseStats.py

@@ -37,24 +37,40 @@ class _temperatureProperty:
 
     @property
     def count(self) -> int:
+        '''
+        Gibt die Anzahl an Geräten zurück
+        '''
         return self.__temperature_count
 
     @property
     def coldest(self) -> float:
+        '''
+        Gibt den kältesten Wert zurück
+        '''
         return self.__temperature_coldest
 
     @property
     def hottest(self) -> float:
+        '''
+        Gibt den wärmsten Wert zurück
+        '''
         return self.__temperature_hottest
 
     @property
     def avg(self) -> float:
+        '''
+        Gibt den durchschnittswert zurück
+        '''
         if self.__temperature_count==0:
             return 20.0
         else:
             return self.__temperature_avg
 
     def set(self, id:str, value:float):
+        '''
+        Setzt den Wert eines Gerätes mit der 'id' auf den entsprechenden Wert.
+        Existiert das Gerät noch nicht in der Auflistung, wird es angelegt
+        '''
         if id in self._temperature:
             if self._temperature[id] == value:
                 # Value exists and didn't change
@@ -89,32 +105,42 @@ class _temperatureProperty:
         }
 
 class _toggleProperty(ABC):
-    _objects = dict()
-    _count = 0
-    _active = 0
-    _inactive = 0
-    _time_active = datetime.now()
-    _time_inactive = datetime.now()
-    _time_all_active = datetime.now()
-    _time_all_inactive = datetime.now()
+    
+
+    def __init__(self):
+        self._objects = dict()
+        self._count = 0
+        self._active = 0
+        self._inactive = 0
+        self._time_active = datetime.now()
+        self._time_inactive = datetime.now()
+        self._time_all_active = datetime.now()
+        self._time_all_inactive = datetime.now()
 
     @property
     def count(self) -> int:
+        '''
+        Gibt die Anzahl an Geräten zurück
+        '''
         return self._count
 
     def set(self, id:str, state:bool):
+        '''
+        Setzt den Wert eines Gerätes mit der 'id' auf den entsprechenden Wert.
+        Existiert das Gerät noch nicht in der Auflistung, wird es angelegt
+        '''
         if id in self._objects:
             if self._objects[id] == state:
                 # Value exists and didn't change
                 return
         # Key doesn't exists or value has changed
-        if self._count == self._off:
+        if self._count == self._inactive:
             self._time_all_inactive = datetime.now()
-        if self._off>0:
+        if self._inactive>0:
             self._time_inactive = datetime.now()
-        if self._count == self._open:
+        if self._count == self._active:
             self._time_all_active = datetime.now()
-        if self._on>0:
+        if self._active>0:
             self._time_active = datetime.now()
 
         self._objects[id] = state
@@ -141,22 +167,37 @@ class _toggleProperty(ABC):
 class _onOffProperty(_toggleProperty):
     @property
     def bAllOn(self) -> bool:
+        '''
+        Gibt zurück, ob alle Geräte angeschaltet sind
+        '''
         return self._count <= self._active
 
     @property
     def bAllOff(self) -> bool:
+        '''
+        Gibt zurück, ob alle Geräte ausgeschaltet sind
+        '''
         return self._count <= self._inactive
 
     @property
     def on(self) -> int:
+        '''
+        Gibt die Anzahl der angeschalteten Geräte Wieder
+        '''
         return self._active
 
     @property
     def off(self) -> int:
+        '''
+        Gibt die Anzahl der ausgeschalteten Geräte Wieder
+        '''
         return self._inactive
 
     @property
     def lastTimeOn(self) -> datetime:
+        '''
+        Gibt ein Datetime zurück das angibt, wann das letzte Gerät angeschaltet war
+        '''
         if self._active>0:
             return datetime.now()
         else:
@@ -164,6 +205,9 @@ class _onOffProperty(_toggleProperty):
 
     @property 
     def lastTimeAllOn(self) -> datetime:
+        '''
+        Gibt ein Datetime zurück das angibt, wann alle Geräte zum letzten mal angeschaltet waren
+        '''
         if self._count == self._active:
             return datetime.now()
         else:
@@ -171,6 +215,9 @@ class _onOffProperty(_toggleProperty):
 
     @property 
     def laseTimeOff(self) -> datetime:
+        '''
+        Gibt ein Datetime zurück das angibt, wann das letzte Gerät ausgeschaltet war
+        '''
         if self._inactive>0:
             return datetime.now()
         else:
@@ -178,6 +225,9 @@ class _onOffProperty(_toggleProperty):
 
     @property 
     def lastTimeAllOff(self) -> datetime:
+        '''
+        Gibt ein Datetime zurück das angibt, wann alle Geräte zum letzten mal ausgeschaltet waren
+        '''
         if self._count == self._inactive:
             return datetime.now()
         else:
@@ -199,22 +249,37 @@ class _onOffProperty(_toggleProperty):
 class _openCloseProperty(_toggleProperty):
     @property
     def bAllOpen(self) -> bool:
+        '''
+        Gibt zurück, ob alle Geräte offen sind
+        '''
         return self._count <= self._active
 
     @property
     def bAllClose(self) -> bool:
+        '''
+        Gibt zurück, ob alle Geräte geschlossen sind
+        '''
         return self._count <= self._inactive
 
     @property
     def open(self) -> int:
+        '''
+        Gibt die Anzahl der offenen Geräte Wieder
+        '''
         return self._active
 
     @property
     def close(self) -> int:
+        '''
+        Gibt die Anzahl der geschlossenen Geräte Wieder
+        '''
         return self._inactive
 
     @property
     def lastTimeOpen(self) -> datetime:
+        '''
+        Gibt ein Datetime zurück das angibt, wann das letzte Gerät geöffnet war
+        '''
         if self._active>0:
             return datetime.now()
         else:
@@ -222,6 +287,9 @@ class _openCloseProperty(_toggleProperty):
 
     @property 
     def lastTimeAllOpen(self) -> datetime:
+        '''
+        Gibt ein Datetime zurück das angibt, wann alle Geräte zum letzten mal geöffnet waren
+        '''
         if self._count == self._active:
             return datetime.now()
         else:
@@ -229,6 +297,9 @@ class _openCloseProperty(_toggleProperty):
 
     @property 
     def laseTimeClose(self) -> datetime:
+        '''
+        Gibt ein Datetime zurück das angibt, wann das letzte Gerät geschlossen war
+        '''
         if self._inactive>0:
             return datetime.now()
         else:
@@ -236,6 +307,9 @@ class _openCloseProperty(_toggleProperty):
 
     @property 
     def lastTimeAllClose(self) -> datetime:
+        '''
+        Gibt ein Datetime zurück das angibt, wann alle Geräte zum letzten mal geschlossen waren
+        '''
         if self._count == self._inactive:
             return datetime.now()
         else:
@@ -268,26 +342,44 @@ class HouseStats(object):
 
     @property
     def Lights(self) -> _onOffProperty:
+        '''
+        Gibt die Licht-Eigenschaften zurück
+        '''
         return self.__lightsProp
 
     @property
     def Switches(self) -> _onOffProperty:
+        '''
+        Gibt die Schalter-Eigenschaften zurück
+        '''
         return self.__switchesProp
 
     @property
     def Windows(self) -> _onOffProperty:
+        '''
+        Gibt die Fenster-Eigenschaften zurück
+        '''
         return self.__windowsProp
 
     @property
     def Doors(self) -> _onOffProperty:
+        '''
+        Gibt die Türen-Eigenschaften zurück
+        '''
         return self.__doorsProp
     
     @property
     def Temperature(self) -> _temperatureProperty:
+        '''
+        Gibt die Temperatur-Eigenschaften zurück
+        '''
         return self.__temperature
     
     @property
     def Air(self) -> _airStats:
+        '''
+        Gibt die Luft-Eigenschaften zurück
+        '''
         return self._air
     
     #TODO Rooms

+ 1 - 0
Stats/MicLevel.py

@@ -2,6 +2,7 @@
 # -*- coding: utf-8 -*-
 
 class MicLevel(object):
+    #TODO Umstellung auf properties
     SamplesRead = 0
     Length = 0.0
     ScaleBy = 0.0

+ 1 - 0
Stats/OutdoorStats.py

@@ -2,6 +2,7 @@
 # -*- coding: utf-8 -*-
 
 class OutdoorStats(object):
+    #TODO Umstellung auf properties
     Temperature = 20.0
     bWindy = False
     Pressure = 0.0

+ 1 - 0
Stats/OwnerStats.py

@@ -3,6 +3,7 @@
 import configparser
 
 class OwnerStats(object):
+    #TODO Umstellung auf properties
     bIsAtHome = False
     bIsSick = False
     bHasHollidays = False

+ 1 - 0
Stats/TalkingStats.py

@@ -2,6 +2,7 @@
 # -*- coding: utf-8 -*-
 
 class TalkingStats(object):
+    #TODO Umstellung auf properties
     bTalk = 0.0
     bAnswerQuestions = 0.0
     bTalkAfterOrder = 0.0

+ 12 - 6
Stats/main.py

@@ -43,17 +43,23 @@ class Stats:
     UPDATE_OUTDOOR_DELAY_MINUTE = 2.1
     UPDATE_MIC_DELAY_MINUTE = 0.5
 
-    Thread_Homematic = None
-    Thread_Emotion = None
-    Thread_DateTime = None
-    Thread_OutDoor = None
-    Thread_Mic = None
-    lastUpdate = 0.0
+    #Thread_Homematic = None
+    #Thread_Emotion = None
+    #Thread_DateTime = None
+    #Thread_OutDoor = None
+    #Thread_Mic = None
+    #lastUpdate = 0.0
 
     Mic = MicCapture.MicCapture()
 
     def __init__(self):
         print("Init Stats")
+        self.lastUpdate = 0.0
+        self.Thread_Homematic = None
+        self.Thread_Emotion = None
+        self.Thread_DateTime = None
+        self.Thread_OutDoor = None
+        self.Thread_Mic = None
         
         print("- Read Config")
         try:

+ 233 - 0
Trixy.pyproj

@@ -33,9 +33,13 @@
     <Compile Include="Jessi\main.py" />
     <Compile Include="Jessi\Replacer.py" />
     <Compile Include="Jessi\__init__.py" />
+    <Compile Include="Konsolas\main.py" />
+    <Compile Include="Konsolas\__init__.py" />
     <Compile Include="lib\MoonPhase.py" />
     <Compile Include="lib\Network.py" />
     <Compile Include="lib\RepeatTimer.py" />
+    <Compile Include="listener\main.py" />
+    <Compile Include="listener\__init__.py" />
     <Compile Include="main.py" />
     <Compile Include="MicCapture\WindowsCapture.py" />
     <Compile Include="MicCapture\__init__.py" />
@@ -53,6 +57,10 @@
     <Compile Include="OpenWeatherMap\WeatherData_Weather.py" />
     <Compile Include="OpenWeatherMap\WeatherData_Wind.py" />
     <Compile Include="OpenWeatherMap\__init__.py" />
+    <Compile Include="PicoVoice\main.py" />
+    <Compile Include="PicoVoice\Porcupine.py" />
+    <Compile Include="PicoVoice\Rhino.py" />
+    <Compile Include="PicoVoice\__init__.py" />
     <Compile Include="Stats\AdminStats.py" />
     <Compile Include="Stats\DeviceStats.py" />
     <Compile Include="Stats\HouseStats.py" />
@@ -63,6 +71,7 @@
     <Compile Include="Stats\TalkingStats.py" />
     <Compile Include="Stats\__init__.py" />
     <Compile Include="test\test_stats_deviceStats.py" />
+    <Compile Include="test\test_stats_houseStats.py" />
     <Compile Include="test\test_stats_main.py" />
     <Compile Include="test\__init__.py" />
     <Compile Include="TextConfig\__init__.py" />
@@ -73,6 +82,8 @@
     <Compile Include="VoicePlay\Cache.py" />
     <Compile Include="VoicePlay\main.py" />
     <Compile Include="VoicePlay\__init__.py" />
+    <Compile Include="Wings\main.py" />
+    <Compile Include="Wings\__init__.py" />
   </ItemGroup>
   <ItemGroup>
     <Content Include=".gitignore" />
@@ -83,11 +94,193 @@
     <Content Include="config\pico.ini" />
     <Content Include="config\README.md" />
     <Content Include="config\stats.ini" />
+    <Content Include="config\vosk.ini" />
     <Content Include="LICENSE" />
     <Content Include="mods\README.md" />
+    <Content Include="PicoVoice\export\AdminCommand.yml" />
+    <Content Include="PicoVoice\export\GeneralCommands.yml" />
+    <Content Include="PicoVoice\export\Time.yml" />
+    <Content Include="PicoVoice\export\YesNo.yml" />
+    <Content Include="PicoVoice\export\Zeiten.yml" />
+    <Content Include="PicoVoice\export\Zimmer.yml" />
+    <Content Include="PicoVoice\yml\AdminCommand.yml" />
+    <Content Include="PicoVoice\yml\GeneralCommands.yml" />
+    <Content Include="PicoVoice\yml\slots\ApplicationList.yml" />
+    <Content Include="PicoVoice\yml\slots\Cities.yml" />
+    <Content Include="PicoVoice\yml\slots\Country.yml" />
+    <Content Include="PicoVoice\yml\slots\curse.yml" />
+    <Content Include="PicoVoice\yml\slots\dayname.yml" />
+    <Content Include="PicoVoice\yml\slots\Devices.yml" />
+    <Content Include="PicoVoice\yml\slots\doorType.yml" />
+    <Content Include="PicoVoice\yml\slots\Hex.yml" />
+    <Content Include="PicoVoice\yml\slots\hours.yml" />
+    <Content Include="PicoVoice\yml\slots\jokes.yml" />
+    <Content Include="PicoVoice\yml\slots\Language.yml" />
+    <Content Include="PicoVoice\yml\slots\lichtProgramm.yml" />
+    <Content Include="PicoVoice\yml\slots\location.yml" />
+    <Content Include="PicoVoice\yml\slots\MathOperator.yml" />
+    <Content Include="PicoVoice\yml\slots\minute.yml" />
+    <Content Include="PicoVoice\yml\slots\Monat.yml" />
+    <Content Include="PicoVoice\yml\slots\monthday.yml" />
+    <Content Include="PicoVoice\yml\slots\Namen.yml" />
+    <Content Include="PicoVoice\yml\slots\nicer.yml" />
+    <Content Include="PicoVoice\yml\slots\onoff.yml" />
+    <Content Include="PicoVoice\yml\slots\Passwords.yml" />
+    <Content Include="PicoVoice\yml\slots\QHoeflichkeitsFloskel.yml" />
+    <Content Include="PicoVoice\yml\slots\QVorfloskel.yml" />
+    <Content Include="PicoVoice\yml\slots\raum.yml" />
+    <Content Include="PicoVoice\yml\slots\roomObjectType.yml" />
+    <Content Include="PicoVoice\yml\slots\StoryType.yml" />
+    <Content Include="PicoVoice\yml\slots\Surname.yml" />
+    <Content Include="PicoVoice\yml\slots\temperatur.yml" />
+    <Content Include="PicoVoice\yml\slots\timerTypes.yml" />
+    <Content Include="PicoVoice\yml\slots\times.yml" />
+    <Content Include="PicoVoice\yml\slots\Updates.yml" />
+    <Content Include="PicoVoice\yml\slots\ValueNum.yml" />
+    <Content Include="PicoVoice\yml\slots\ValueRoom.yml" />
+    <Content Include="PicoVoice\yml\slots\Weekday.yml" />
+    <Content Include="PicoVoice\yml\slots\zahlwort.yml" />
+    <Content Include="PicoVoice\yml\slots\Zeitspanne.yml" />
+    <Content Include="PicoVoice\yml\slots\zeitwort.yml" />
+    <Content Include="PicoVoice\yml\Time.yml" />
+    <Content Include="PicoVoice\yml\YesNo.yml" />
+    <Content Include="PicoVoice\yml\Zeiten.yml" />
+    <Content Include="PicoVoice\yml\Zimmer.yml" />
     <Content Include="README.md" />
     <Content Include="ressources\email\blank.html" />
     <Content Include="ressources\email\README.md" />
+    <Content Include="ressources\models\context\AdminCommand_de_windows_v2_0_0.rhn" />
+    <Content Include="ressources\models\context\AdminCommand_de_windows_v2_1_0.rhn" />
+    <Content Include="ressources\models\context\GeneralCommands_de_windows_v2_1_0.rhn" />
+    <Content Include="ressources\models\context\YesNo_de_windows_v2_1_0.rhn" />
+    <Content Include="ressources\models\context\Zeiten_de_windows_v2_0_0.rhn" />
+    <Content Include="ressources\models\porcupine\2.0\porcupine_params.pv" />
+    <Content Include="ressources\models\porcupine\2.0\porcupine_params_de.pv" />
+    <Content Include="ressources\models\porcupine\2.0\porcupine_params_es.pv" />
+    <Content Include="ressources\models\porcupine\2.0\porcupine_params_fr.pv" />
+    <Content Include="ressources\models\porcupine\2.0\system-command_de_raspberry-pi_v2_0_0.ppn" />
+    <Content Include="ressources\models\porcupine\2.0\system-command_de_windows_v2_0_0.ppn" />
+    <Content Include="ressources\models\porcupine\2.1\kiara_fr_windows_v2_1_0.ppn" />
+    <Content Include="ressources\models\porcupine\2.1\porcupine_params.pv" />
+    <Content Include="ressources\models\porcupine\2.1\porcupine_params_de.pv" />
+    <Content Include="ressources\models\porcupine\2.1\porcupine_params_es.pv" />
+    <Content Include="ressources\models\porcupine\2.1\porcupine_params_fr.pv" />
+    <Content Include="ressources\models\porcupine\2.1\system-command_de_windows_v2_1_0.ppn" />
+    <Content Include="ressources\models\porcupine\2.1\system-command_en_windows_v2_1_0.ppn" />
+    <Content Include="ressources\models\porcupine\2.1\trixi_de_windows_v2_1_0.ppn" />
+    <Content Include="ressources\models\porcupine_params.pv" />
+    <Content Include="ressources\models\porcupine_params_de.pv" />
+    <Content Include="ressources\models\porcupine_params_es.pv" />
+    <Content Include="ressources\models\porcupine_params_fr.pv" />
+    <Content Include="ressources\models\rhino\2.0\rhino_params.pv" />
+    <Content Include="ressources\models\rhino\2.0\rhino_params_de.pv" />
+    <Content Include="ressources\models\rhino\2.0\rhino_params_es.pv" />
+    <Content Include="ressources\models\rhino\2.0\rhino_params_fr.pv" />
+    <Content Include="ressources\models\rhino\2.1\rhino_params.pv" />
+    <Content Include="ressources\models\rhino\2.1\rhino_params_de.pv" />
+    <Content Include="ressources\models\rhino\2.1\rhino_params_es.pv" />
+    <Content Include="ressources\models\rhino\2.1\rhino_params_fr.pv" />
+    <Content Include="ressources\models\system-command_de_raspberry-pi_v2_0_0.ppn" />
+    <Content Include="ressources\models\system-command_de_windows_v2_0_0.ppn" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\am\final.mdl" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\am\frame_subsampling_factor" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\am\tree" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\conf\mfcc.conf" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\conf\model.conf" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\graph\disambig_tid.int" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\graph\HCLG.fst" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\graph\num_pdfs" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\graph\phones.txt" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\graph\phones\align_lexicon.int" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\graph\phones\align_lexicon.txt" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\graph\phones\disambig.int" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\graph\phones\disambig.txt" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\graph\phones\optional_silence.csl" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\graph\phones\optional_silence.int" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\graph\phones\optional_silence.txt" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\graph\phones\silence.csl" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\graph\phones\word_boundary.int" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\graph\phones\word_boundary.txt" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\graph\words.txt" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\ivector\final.dubm" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\ivector\final.ie" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\ivector\final.mat" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\ivector\global_cmvn.stats" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\ivector\online_cmvn.conf" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\ivector\splice.conf" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\README" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\rescore\G.carpa" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\rescore\G.fst" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\rnnlm\feat_embedding.final.mat" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\rnnlm\final.raw" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\rnnlm\special_symbol_opts.conf" />
+    <Content Include="ressources\models\vosk\vosk-model-de-0.21\rnnlm\word_feats.txt" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\am\final.mdl" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\am\tree" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\conf\mfcc.conf" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\conf\model.conf" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\graph\disambig_tid.int" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\graph\HCLG.fst" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\graph\num_pdfs" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\graph\phones.txt" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\graph\phones\align_lexicon.int" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\graph\phones\align_lexicon.txt" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\graph\phones\disambig.int" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\graph\phones\disambig.txt" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\graph\phones\optional_silence.csl" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\graph\phones\optional_silence.int" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\graph\phones\optional_silence.txt" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\graph\phones\silence.csl" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\graph\phones\word_boundary.int" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\graph\phones\word_boundary.txt" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\graph\words.txt" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\ivector\final.dubm" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\ivector\final.ie" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\ivector\final.mat" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\ivector\global_cmvn.stats" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\ivector\online_cmvn.conf" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\ivector\splice.conf" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\README" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\rescore\G.carpa" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\rescore\G.fst" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\rnnlm\features.txt" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\rnnlm\feat_embedding.final.mat" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\rnnlm\final.raw" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\rnnlm\oov.txt" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\rnnlm\special_symbol_opts.conf" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\rnnlm\special_symbol_opts.txt" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\rnnlm\unigram_probs.txt" />
+    <Content Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\rnnlm\word_feats.txt" />
+    <Content Include="ressources\models\vosk\vosk-model-small-de-0.15\am\final.mdl" />
+    <Content Include="ressources\models\vosk\vosk-model-small-de-0.15\conf\mfcc.conf" />
+    <Content Include="ressources\models\vosk\vosk-model-small-de-0.15\conf\model.conf" />
+    <Content Include="ressources\models\vosk\vosk-model-small-de-0.15\COPYING" />
+    <Content Include="ressources\models\vosk\vosk-model-small-de-0.15\graph\disambig_tid.int" />
+    <Content Include="ressources\models\vosk\vosk-model-small-de-0.15\graph\Gr.fst" />
+    <Content Include="ressources\models\vosk\vosk-model-small-de-0.15\graph\HCLr.fst" />
+    <Content Include="ressources\models\vosk\vosk-model-small-de-0.15\graph\phones\word_boundary.int" />
+    <Content Include="ressources\models\vosk\vosk-model-small-de-0.15\ivector\final.dubm" />
+    <Content Include="ressources\models\vosk\vosk-model-small-de-0.15\ivector\final.ie" />
+    <Content Include="ressources\models\vosk\vosk-model-small-de-0.15\ivector\final.mat" />
+    <Content Include="ressources\models\vosk\vosk-model-small-de-0.15\ivector\global_cmvn.stats" />
+    <Content Include="ressources\models\vosk\vosk-model-small-de-0.15\ivector\online_cmvn.conf" />
+    <Content Include="ressources\models\vosk\vosk-model-small-de-0.15\ivector\splice.conf" />
+    <Content Include="ressources\models\vosk\vosk-model-small-de-0.15\README" />
+    <Content Include="ressources\models\vosk\vosk-model-small-de-zamia-0.3\AUTHORS" />
+    <Content Include="ressources\models\vosk\vosk-model-small-de-zamia-0.3\disambig_tid.int" />
+    <Content Include="ressources\models\vosk\vosk-model-small-de-zamia-0.3\final.mdl" />
+    <Content Include="ressources\models\vosk\vosk-model-small-de-zamia-0.3\Gr.fst" />
+    <Content Include="ressources\models\vosk\vosk-model-small-de-zamia-0.3\HCLr.fst" />
+    <Content Include="ressources\models\vosk\vosk-model-small-de-zamia-0.3\ivector\final.dubm" />
+    <Content Include="ressources\models\vosk\vosk-model-small-de-zamia-0.3\ivector\final.ie" />
+    <Content Include="ressources\models\vosk\vosk-model-small-de-zamia-0.3\ivector\final.mat" />
+    <Content Include="ressources\models\vosk\vosk-model-small-de-zamia-0.3\ivector\global_cmvn.stats" />
+    <Content Include="ressources\models\vosk\vosk-model-small-de-zamia-0.3\ivector\online_cmvn.conf" />
+    <Content Include="ressources\models\vosk\vosk-model-small-de-zamia-0.3\ivector\splice.conf" />
+    <Content Include="ressources\models\vosk\vosk-model-small-de-zamia-0.3\LICENSE" />
+    <Content Include="ressources\models\vosk\vosk-model-small-de-zamia-0.3\mfcc.conf" />
+    <Content Include="ressources\models\vosk\vosk-model-small-de-zamia-0.3\README.md" />
+    <Content Include="ressources\models\vosk\vosk-model-small-de-zamia-0.3\word_boundary.int" />
     <Content Include="ressources\sounds\README.md" />
     <Content Include="ressources\texts\basic.json" />
     <Content Include="ressources\texts\README.md" />
@@ -105,6 +298,46 @@
     <Folder Include="mods\ModPlaySound\" />
     <Folder Include="mods\NanoTTSMod\" />
     <Folder Include="mods\WindowsSay\" />
+    <Folder Include="Konsolas\" />
+    <Folder Include="listener\" />
+    <Folder Include="PicoVoice\" />
+    <Folder Include="PicoVoice\export\" />
+    <Folder Include="PicoVoice\yml\" />
+    <Folder Include="PicoVoice\yml\slots\" />
+    <Folder Include="ressources\models\" />
+    <Folder Include="ressources\models\context\" />
+    <Folder Include="ressources\models\porcupine\" />
+    <Folder Include="ressources\models\porcupine\2.0\" />
+    <Folder Include="ressources\models\porcupine\2.1\" />
+    <Folder Include="ressources\models\rhino\" />
+    <Folder Include="ressources\models\rhino\2.0\" />
+    <Folder Include="ressources\models\rhino\2.1\" />
+    <Folder Include="ressources\models\vosk\" />
+    <Folder Include="ressources\models\vosk\vosk-model-de-0.21\" />
+    <Folder Include="ressources\models\vosk\vosk-model-de-0.21\am\" />
+    <Folder Include="ressources\models\vosk\vosk-model-de-0.21\conf\" />
+    <Folder Include="ressources\models\vosk\vosk-model-de-0.21\graph\" />
+    <Folder Include="ressources\models\vosk\vosk-model-de-0.21\graph\phones\" />
+    <Folder Include="ressources\models\vosk\vosk-model-de-0.21\ivector\" />
+    <Folder Include="ressources\models\vosk\vosk-model-de-0.21\rescore\" />
+    <Folder Include="ressources\models\vosk\vosk-model-de-0.21\rnnlm\" />
+    <Folder Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\" />
+    <Folder Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\am\" />
+    <Folder Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\conf\" />
+    <Folder Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\graph\" />
+    <Folder Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\graph\phones\" />
+    <Folder Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\ivector\" />
+    <Folder Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\rescore\" />
+    <Folder Include="ressources\models\vosk\vosk-model-de-tuda-0.6-900k\rnnlm\" />
+    <Folder Include="ressources\models\vosk\vosk-model-small-de-0.15\" />
+    <Folder Include="ressources\models\vosk\vosk-model-small-de-0.15\am\" />
+    <Folder Include="ressources\models\vosk\vosk-model-small-de-0.15\conf\" />
+    <Folder Include="ressources\models\vosk\vosk-model-small-de-0.15\graph\" />
+    <Folder Include="ressources\models\vosk\vosk-model-small-de-0.15\graph\phones\" />
+    <Folder Include="ressources\models\vosk\vosk-model-small-de-0.15\ivector\" />
+    <Folder Include="ressources\models\vosk\vosk-model-small-de-zamia-0.3\" />
+    <Folder Include="ressources\models\vosk\vosk-model-small-de-zamia-0.3\ivector\" />
+    <Folder Include="Wings\" />
     <Folder Include="ressources\texts\" />
     <Folder Include="test\" />
     <Folder Include="ressources\" />

+ 17 - 5
lib/RepeatTimer.py

@@ -4,14 +4,25 @@ from threading import Timer,Thread,Event
 class RepeatTimer():
 
     def __init__(self,t:float,hFunction:callable):
-        self.t=t
-        self.hFunction = hFunction
-        self.thread = Timer(self.t,self.handle_function)
+        self.__deamon = True
+        try:
+            self.t=t
+            self.hFunction = hFunction
+            self.thread = Timer(self.t,self.handle_function)
+            self.thread.daemon = self.__deamon
+        except (KeyboardInterrupt, SystemExit):
+            self.thread.cancel()
+            self.thread.release()
 
     def handle_function(self):
         self.hFunction()
-        self.thread = Timer(self.t,self.handle_function)
-        self.thread.start()
+        try:
+            self.thread = Timer(self.t,self.handle_function)
+            self.thread.daemon = self.__deamon
+            self.thread.start()
+        except (KeyboardInterrupt, SystemExit):
+            self.thread.cancel()
+            self.thread.release()
 
     def start(self):
         self.thread.start()
@@ -41,6 +52,7 @@ class RepeatTimer():
 
     @daemon.setter
     def daemon(self, value:bool):
+        self.__deamon = value
         self.thread.daemon = value
 
     def setDaemon(self, value:bool):

+ 34 - 9
main.py

@@ -1,28 +1,53 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
+from time import sleep
 from DynLoader import DynLoaderMod as dyn
 from VoicePlay import Voice as speach
 from Stats import Stats
 from Jessi import Text as textauswahl
+from PicoVoice import PicoVoice
+import os
+import sys
+import colorama
+from colorama import Fore, Back, Style
+from listener import Lis
+
+
+def WeakWord(index,weakword):
+    print(f"Weakword {Fore.GREEN}[{index}]{Fore.CYAN}{weakword}{Style.RESET_ALL}")
+    
 
 if __name__ == "__main__":
-    print("Start application")
+    os.system('color')
+    print(f"Start {Fore.GREEN}Application{Style.RESET_ALL}")
 
-    print("Init Mods")
+    print(f"Init {Fore.GREEN}Mods{Style.RESET_ALL}")
     mod = dyn("mods/", "on")
 
-    print("Init Stats")
+    print(f"Init {Fore.GREEN}Stats{Style.RESET_ALL}")
     stats:Stats = Stats.getInstance()
 
-    print("Init Speech")
+    print(f"Init {Fore.GREEN}Speech{Style.RESET_ALL}")
     speach.getInstance().Say(".")
 
     mod.execute("PlaySound", "welcome.wav", False)
-    mod.execute("PlaySound", "welcome.mp3", False)
     print("Sound playing...")
-
-    joke = textauswahl.getInstance().executeText("test",["joke","kurzwitz"])
-    print(joke)
-    speach.getInstance().Say(joke)
+    
+    pico:PicoVoice = PicoVoice.getInstance()
+    PicoVoice.show_audio_devices()
+    pico.RegisterOnWeakWord(WeakWord)
+    pico.start()
+
+    Lis.Start()
+    sleep(60)
+    Lis.Stop()
+
+    try:
+        inp=""
+        while inp!="qqq" and inp!="quit" and inp!="q":
+            inp=input()
+    except (KeyboardInterrupt, SystemExit):
+        pass
+    print("Exit")
 

+ 3 - 0
mods/BaseMod.py

@@ -19,3 +19,6 @@ class BaseMod:
 
     def onPlaySound(self, soundFile:str, ASync:bool=True):
         pass
+     
+    def onWeakWord(self, index:int, WeakWord:str, IsAdmin:bool):
+        pass

+ 98 - 2
test/test_stats_deviceStats.py

@@ -107,15 +107,111 @@ class test_stats_deviceStats(unittest.TestCase):
 
     def test_deviceStats_soziality_Value_Changed(self):
         self.assertTrue(self.valueChanged("soziality"))
+        
 
+class test_stats_deviceStats_Fixed(unittest.TestCase):
+
+    def valueFixedChanged(self,varName:str="") -> bool:
+        from Stats import Stats
+        s:Stats = Stats()
+        # Unlock Angry-Stat
+        s.DeviceStats.lock(varName,15.0)
+        oldValue:float=s.DeviceStats.get(varName)
+        newValue:float=oldValue+10.0
+        s.DeviceStats.set(varName,newValue)
+        return s.DeviceStats.get(varName)==15
 
     def test_deviceStats_Fixed_Value_Changed(self):
         from Stats import Stats
         s:Stats = Stats()
         # Lock Angry-Stat
-        s.DeviceStats._locked_angry = 150.0
+        s.DeviceStats._locked_angry = 15.0
         s.DeviceStats.angry=500
-        self.assertEqual(s.DeviceStats.angry, 150)
+        self.assertEqual(s.DeviceStats.angry, 15)
+
+    def test_deviceStats_aroused_Value_Changed(self):
+        self.assertTrue(self.valueFixedChanged("aroused"))
+
+    def test_deviceStats_hungry_Value_Changed(self):
+        self.assertTrue(self.valueFixedChanged("hungry"))
+
+    def test_deviceStats_happyness_Value_Changed(self):
+        self.assertTrue(self.valueFixedChanged("happyness"))
+
+    def test_deviceStats_funny_Value_Changed(self):
+        self.assertTrue(self.valueFixedChanged("funny"))
+
+    def test_deviceStats_annoyed_Value_Changed(self):
+        self.assertTrue(self.valueFixedChanged("annoyed"))
+
+    def test_deviceStats_irritated_Value_Changed(self):
+        self.assertTrue(self.valueFixedChanged("irritated"))
+
+    def test_deviceStats_bored_Value_Changed(self):
+        self.assertTrue(self.valueFixedChanged("bored"))
+
+    def test_deviceStats_intelligent_Value_Changed(self):
+        self.assertTrue(self.valueFixedChanged("intelligent"))
+
+    def test_deviceStats_sleepy_Value_Changed(self):
+        self.assertTrue(self.valueFixedChanged("sleepy"))
+
+    def test_deviceStats_energy_Value_Changed(self):
+        self.assertTrue(self.valueFixedChanged("energy"))
+
+    def test_deviceStats_exhausted_Value_Changed(self):
+        self.assertTrue(self.valueFixedChanged("exhausted"))
+
+    def test_deviceStats_depressive_Value_Changed(self):
+        self.assertTrue(self.valueFixedChanged("depressive"))
+
+    def test_deviceStats_sad_Value_Changed(self):
+        self.assertTrue(self.valueFixedChanged("sad"))
+
+    def test_deviceStats_useless_Value_Changed(self):
+        self.assertTrue(self.valueFixedChanged("useless"))
+
+    def test_deviceStats_usefull_Value_Changed(self):
+        self.assertTrue(self.valueFixedChanged("usefull"))
+
+    def test_deviceStats_compfortable_Value_Changed(self):
+        self.assertTrue(self.valueFixedChanged("compfortable"))
+
+    def test_deviceStats_temperate_Value_Changed(self):
+        self.assertTrue(self.valueFixedChanged("temperate"))
+
+    def test_deviceStats_shy_Value_Changed(self):
+        self.assertTrue(self.valueFixedChanged("shy"))
+
+    def test_deviceStats_excited_Value_Changed(self):
+        self.assertTrue(self.valueFixedChanged("excited"))
+
+    def test_deviceStats_toLoud_Value_Changed(self):
+        self.assertTrue(self.valueFixedChanged("toLoud"))
+
+    def test_deviceStats_toQuiet_Value_Changed(self):
+        self.assertTrue(self.valueFixedChanged("toQuiet"))
+
+    def test_deviceStats_playful_Value_Changed(self):
+        self.assertTrue(self.valueFixedChanged("playful"))
+
+    def test_deviceStats_naiv_Value_Changed(self):
+        self.assertTrue(self.valueFixedChanged("naiv"))
+
+    def test_deviceStats_freezy_Value_Changed(self):
+        self.assertTrue(self.valueFixedChanged("freezy"))
+
+    def test_deviceStats_hot_Value_Changed(self):
+        self.assertTrue(self.valueFixedChanged("hot"))
+
+    def test_deviceStats_humanity_Value_Changed(self):
+        self.assertTrue(self.valueFixedChanged("humanity"))
+
+    def test_deviceStats_lonely_Value_Changed(self):
+        self.assertTrue(self.valueFixedChanged("lonely"))
+
+    def test_deviceStats_soziality_Value_Changed(self):
+        self.assertTrue(self.valueFixedChanged("soziality"))
 
 
 if __name__ == '__main__':