From 6e2442e4df1b3f6037f4d09aebe37385447bb75c Mon Sep 17 00:00:00 2001 From: caiosweet <24454580+caiosweet@users.noreply.github.com> Date: Tue, 1 Nov 2022 20:50:52 +0100 Subject: [PATCH 01/13] New binary sensor Alexa Speak --- apps/notifier/alexa_manager.py | 116 +++++++++++++++++++------------ apps/notifier/alexa_manager.yaml | 6 +- 2 files changed, 75 insertions(+), 47 deletions(-) diff --git a/apps/notifier/alexa_manager.py b/apps/notifier/alexa_manager.py index 3559f73..457270f 100755 --- a/apps/notifier/alexa_manager.py +++ b/apps/notifier/alexa_manager.py @@ -25,13 +25,10 @@ DEFAULT_VOL = "default_vol" # Parameters -# MODE = "mode" TITLE = "title" MESSAGE = "message" - AUDIO = "audio" EVENT_ID = "event_id" -TYPE = "type" LANGUAGE = "language" MEDIA_CONTENT_ID = "media_content_id" MEDIA_CONTENT_TYPE = "media_content_type" @@ -43,10 +40,12 @@ RATE = "rate" SSML = "ssml" SSML_VOL = "ssml_volume" +TYPE = "type" VOICE = "voice" VOLUME = "volume" WAIT_TIME = "wait_time" WHISPER = "whisper" +# MODE = "mode" MOBILE_PUSH_TYPE = (PUSH, "dropin", "dropin_notification") SUB_VOICE = [ @@ -291,16 +290,16 @@ class Alexa_Manager(hass.Hass): def initialize(self) -> None: self.debug_sensor = self.args.get("debug_sensor") - self.component_installed = self.is_component_installed(ALEXA_SERVICE) self.notify_services = self.list_notify_services(ALEXA_SERVICE) self.service2player = self.alexa_services_to_players(self.notify_services) self.volumes_saved = {} # dict media players volume saved - - # Entity + # Entities + self.binary_speak = self.args.get("binary_speak") self.sensor_player = self.args.get("sensor_player") self.sensor_volume = self.args.get("sensor_day_volume") self.select_language = self.args.get("select_language") + self.select_alexa_language = self.args.get("select_alexa_language") self.select_player = self.args.get("select_player") self.select_type = self.args.get("select_type") self.select_method = self.args.get("select_method") @@ -310,7 +309,7 @@ def initialize(self) -> None: self.prosody = self.args.get("prosody") self.number_wait_time = self.args.get("number_wait_time") self.text_actionable_notification = self.args.get("actionable_notification") - # + self.queue = Queue(maxsize=0) self._when_tts_done_callback_queue = Queue() t = Thread(target=self.worker) @@ -321,7 +320,7 @@ def initialize(self) -> None: def speak(self, alexa: dict, skill_id: str) -> None: """Speak the provided text through the media player.""" if not self.service2player: - self.set_debug_sensor("I can't find the Alexa Media services", CUSTOM_COMPONENT_URL) + self.set_debug_sensor("Alexa Services not found", CUSTOM_COMPONENT_URL) return self.lg(f"------ ALEXA START DISPATCH ------") self.lg(f"FROM DISPATCH: {type(alexa)} value {alexa}") @@ -335,6 +334,19 @@ def speak(self, alexa: dict, skill_id: str) -> None: default_vol = float(self.get_state(self.sensor_volume, default=10)) / 100 volume = float(alexa.get(VOLUME, default_vol)) + alexa_lang = self.get_state(self.select_alexa_language, default="Master") + master_lang = self.get_state(self.select_language, default="it-IT") + final_language = alexa_lang if alexa_lang != "Master" else master_lang + language = str(alexa.get(LANGUAGE, final_language)) + + state_method = self.get_state(self.select_method, default="all") + state_voice = self.get_state(self.select_voice, default="Alexa") + state_wait_time = self.get_state(self.number_wait_time, default=3.0) + state_rate = self.get_state(self.prosody[RATE], default=100.0) + state_pitch = self.get_state(self.prosody[PITCH], default=0.0) + state_ssml_volume = self.get_state(self.prosody[VOLUME], default=0.0) + state_ssml = self.get_state(self.bool_ssml, default="off") + # Actionable notification if event_id := alexa.get(EVENT_ID): self.set_textvalue( @@ -343,7 +355,7 @@ def speak(self, alexa: dict, skill_id: str) -> None: ) # Push notification - Only one device is needed - push = self.check_bool(alexa.get(PUSH)) + push = self.check_bool(alexa.get(PUSH, False)) if (push or data_type in MOBILE_PUSH_TYPE) and message: message_push = self.remove_tags(self.replace_regular(message, SUB_TEXT)) type_ = {TYPE: PUSH} if push else {TYPE: data_type} @@ -357,7 +369,7 @@ def speak(self, alexa: dict, skill_id: str) -> None: # Media Content # TODO Restore volume?? if media_content_id := alexa.get(MEDIA_CONTENT_ID): - self.volume_get_and_save(media_player, volume, default_vol) + self.volume_get_save(media_player, volume, default_vol) self.volume_set(media_player, volume) self.call_service( "media_player/play_media", @@ -377,19 +389,19 @@ def speak(self, alexa: dict, skill_id: str) -> None: TYPE: data_type, DEFAULT_VOL: default_vol, VOLUME: volume, + LANGUAGE: language, EVENT_ID: event_id, SKILL_ID: skill_id, AUDIO: alexa.get(AUDIO, None), NOTIFIER: str(alexa.get(NOTIFIER, ALEXA_SERVICE)), - LANGUAGE: str(alexa.get(LANGUAGE, self.get_state(self.select_language, default="it-IT"))), - METHOD: str(alexa.get(METHOD, self.get_state(self.select_method, default="all")).lower()), - VOICE: str(alexa.get(VOICE, self.get_state(self.select_voice, default="Alexa"))).capitalize(), - WAIT_TIME: float(alexa.get(WAIT_TIME, self.get_state(self.number_wait_time))), - RATE: float(alexa.get(RATE, self.get_state(self.prosody[RATE], default=100.0))), - PITCH: float(alexa.get(PITCH, self.get_state(self.prosody[PITCH], default=0.0))), - SSML_VOL: float(alexa.get(SSML_VOL, self.get_state(self.prosody[VOLUME], default=0.0))), + METHOD: str(alexa.get(METHOD, state_method).lower()), + VOICE: str(alexa.get(VOICE, state_voice)).capitalize(), + WAIT_TIME: float(alexa.get(WAIT_TIME, state_wait_time)), + RATE: float(alexa.get(RATE, state_rate)), + PITCH: float(alexa.get(PITCH, state_pitch)), + SSML_VOL: float(alexa.get(SSML_VOL, state_ssml_volume)), WHISPER: self.check_bool(alexa.get(WHISPER, False)), - SSML: self.check_bool(alexa.get(SSML, self.get_state(self.bool_ssml, default="off"))), + SSML: self.check_bool(alexa.get(SSML, state_ssml)), } ) @@ -427,7 +439,7 @@ def remove_tags(self, text: str) -> str: def speak_tags(self, value: str) -> str: """This will add a tag when using tts method""" - return f"{value}" if not "" in value or "{value}" def effect_tags(self, value: str) -> str: """This will add a tag and applies a whispering effect.""" @@ -512,15 +524,15 @@ def alexa_services_to_players(self, alexa_notify_services) -> list: return service2player def check_media_player(self, media_player: list) -> list: - mp_list = [] + mplist = [] if not isinstance(media_player, list): media_player = self.str2list(str(media_player.lower())) self.lg(f"USER PLAYER: {media_player} - TYPE: {type(media_player)}") - player_options = list(self.get_state(self.select_player, attribute="options", default="")) - name2entity = self.entity_from_name(player_options + media_player) + media_name = self.get_state(self.select_player, attribute="options", default="") + name2entity = self.entity_from_name(list(media_name) + media_player) for mp in media_player: if mp == "test": - mp_list = self.service2player + mplist = self.service2player self.lg(f"TEST: {mp}") break if not self.entity_exists(mp): @@ -528,18 +540,18 @@ def check_media_player(self, media_player: list) -> list: mp = name2entity.get(mp) if mp: if "group." in mp: - mp_list.extend(self.get_state(mp, attribute="entity_id", default="")) + mplist.extend(self.get_state(mp, attribute="entity_id", default="")) elif "sensor." in mp: - mp_list.append(self.get_state(mp)) + mplist.append(self.get_state(mp)) elif "media_player." in mp: - mp_list.append(mp) + mplist.append(mp) else: self.log(f"Invalid entity ({mp})", level="WARNING") - if not mp_list: - mp_list = self.service2player - self.log(f"Not {media_player} found. Default {mp_list}", level="WARNING") - self.lg(f"GET PLAYER: {mp_list}") - return mp_list + if not mplist: + mplist = self.service2player + self.log(f"Not {media_player} found. Default {mplist}", level="WARNING") + self.lg(f"GET PLAYER: {mplist}") + return mplist def entity_from_name(self, name_list: list) -> dict: """Given a list of names, it takes the entity_id from the friendly_name. @@ -557,13 +569,13 @@ def entity_from_name(self, name_list: list) -> dict: self.lg(f"NAME-ENTITY_ID DICT: {name2entity}") return name2entity - def volume_get_and_save(self, media_player: list, vol_set: float, def_vol: float) -> None: - """Get and save the volume of each media player.""" + def volume_get_save(self, media_player: list, volume: float, defvol: float) -> None: + """Get and save the volume of each media player only if different.""" self.volumes_saved = {} for i in media_player: - vol_get = self.get_state(i, attribute="volume_level", default=def_vol) - if vol_get != vol_set: - self.volumes_saved[i] = vol_get + volume_get = self.get_state(i, attribute="volume_level", default=defvol) + if volume_get != volume: + self.volumes_saved[i] = volume_get self.lg(f"GET VOLUME: {self.volumes_saved}") def volume_restore(self) -> None: @@ -573,19 +585,25 @@ def volume_restore(self) -> None: for i, j in self.volumes_saved.items(): self.call_service("media_player/volume_set", entity_id=i, volume_level=j) time.sleep(1) - # Force attribute volume_level in Home assistant and update last called device + # Force attribute volume_level in Home assistant self.set_state(i, attributes={"volume_level": j}) self.call_service("alexa_media/update_last_called", return_result=True) - self.lg(f"RESTORE VOL: {i} {j} [State: {self.get_state(i, attribute='volume_level')}]") + self.lg( + f"RESTORE VOL: {i} {j} [State:" + f" {self.get_state(i, attribute='volume_level')}]" + ) def volume_set(self, media_player: list, volume: float) -> None: """Set the volume of each media player.""" - smart_volume = self.check_bool(self.get_state(self.bool_smart_volume_set, default="off")) + state_smart_volume = self.get_state(self.bool_smart_volume_set, default="off") + smart_volume = self.check_bool(state_smart_volume) if not self.volumes_saved and smart_volume: return media_player = list(self.volumes_saved.keys()) if smart_volume else media_player - self.call_service("media_player/volume_set", entity_id=media_player, volume_level=volume) - # Not strictly necessary (Home assistant does not update the status, but it works) + self.call_service( + "media_player/volume_set", entity_id=media_player, volume_level=volume + ) + # Not strictly necessary for player in media_player: self.set_state(player, attributes={"volume_level": volume}) self.lg(f"SET VOLUMES: {player} {volume}") @@ -604,10 +622,11 @@ def worker(self): while True: try: data = self.queue.get() + self.set_state(self.binary_speak, state="on") self.lg(f"------ ALEXA WORKER QUEUE ------") self.lg(f"WORKER: {type(data)} value {data}") media_player = data[MEDIA_PLAYER] - self.volume_get_and_save(media_player, data[VOLUME], data[DEFAULT_VOL]) + self.volume_get_save(media_player, data[VOLUME], data[DEFAULT_VOL]) self.volume_set(media_player, data[VOLUME]) # Replace and clean message @@ -644,7 +663,9 @@ def worker(self): if voice != "Alexa": msg = self.voice_tags(msg, voice) msg = self.audio_tags(data[AUDIO]) + msg - msg = self.prosody_tags(msg, data[RATE], data[PITCH], data[SSML_VOL]) + msg = self.prosody_tags( + msg, data[RATE], data[PITCH], data[SSML_VOL] + ) if self.inbetween(20, data[RATE], 200) != 100: if data[RATE] < 100: duration += (100 - data[RATE]) * (duration / 100) @@ -652,13 +673,16 @@ def worker(self): duration /= 2 if data[WHISPER]: msg = self.effect_tags(msg) - if data[TYPE] == "tts": + if data[TYPE] == "tts" and ">> self.call_service( @@ -698,6 +722,8 @@ def worker(self): self.log("Alexa Manager - CallBack Error", level="ERROR") self.set_debug_sensor("Alexa Manager - CallBack Error ", ex) pass # Nothing in queue + + self.set_state(self.binary_speak, state="off") self.lg("------ ALEXA END ------\n") # def terminate(self): diff --git a/apps/notifier/alexa_manager.yaml b/apps/notifier/alexa_manager.yaml index 757c9c6..3f3a641 100755 --- a/apps/notifier/alexa_manager.yaml +++ b/apps/notifier/alexa_manager.yaml @@ -2,15 +2,17 @@ Alexa_Manager: module: alexa_manager class: Alexa_Manager - # log_level: DEBUG + log_level: DEBUG # Alexa hub + binary_speak: binary_sensor.notifyhub_alexa_speak bool_smart_volume_set: input_boolean.alexa_smart_volume_set - select_player: input_select.notification_media_player_alexa sensor_player: sensor.media_player_alexa + select_player: input_select.notification_media_player_alexa select_type: input_select.default_alexa_type select_method: input_select.default_alexa_method select_voice: input_select.alexa_voice + select_alexa_language: input_select.alexa_language bool_ssml: input_boolean.alexa_ssml prosody: rate: input_number.alexa_prosody_rate From 7addc710a574d4806c4deaf305280df1e3be644e Mon Sep 17 00:00:00 2001 From: caiosweet <24454580+caiosweet@users.noreply.github.com> Date: Tue, 1 Nov 2022 20:53:42 +0100 Subject: [PATCH 02/13] Update alexa_manager.yaml --- apps/notifier/alexa_manager.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/notifier/alexa_manager.yaml b/apps/notifier/alexa_manager.yaml index 3f3a641..8f03ece 100755 --- a/apps/notifier/alexa_manager.yaml +++ b/apps/notifier/alexa_manager.yaml @@ -2,7 +2,7 @@ Alexa_Manager: module: alexa_manager class: Alexa_Manager - log_level: DEBUG + # log_level: DEBUG # Alexa hub binary_speak: binary_sensor.notifyhub_alexa_speak From d67c8d605f1bcc767f22fba288fc21593b57b54b Mon Sep 17 00:00:00 2001 From: caiosweet <24454580+caiosweet@users.noreply.github.com> Date: Mon, 7 Nov 2022 19:36:42 +0100 Subject: [PATCH 03/13] Beta 07-11-2022 variable names changed --- .gitignore | 2 ++ apps/notifier/alexa_manager.yaml | 32 +++++++++++------------ apps/notifier/gh_manager.py | 0 apps/notifier/gh_manager.yaml | 12 ++++----- apps/notifier/helpermodule.py | 0 apps/notifier/notification_manager.py | 0 apps/notifier/notification_manager.yaml | 6 ++--- apps/notifier/notifier_dispatch.py | 14 +++++----- apps/notifier/notifier_dispatch.yaml | 34 ++++++++++++------------- apps/notifier/phone_manager.py | 0 apps/notifier/phone_manager.yaml | 0 11 files changed, 52 insertions(+), 48 deletions(-) create mode 100644 .gitignore mode change 100644 => 100755 apps/notifier/gh_manager.py mode change 100644 => 100755 apps/notifier/gh_manager.yaml mode change 100644 => 100755 apps/notifier/helpermodule.py mode change 100644 => 100755 apps/notifier/notification_manager.py mode change 100644 => 100755 apps/notifier/notification_manager.yaml mode change 100644 => 100755 apps/notifier/notifier_dispatch.py mode change 100644 => 100755 apps/notifier/notifier_dispatch.yaml mode change 100644 => 100755 apps/notifier/phone_manager.py mode change 100644 => 100755 apps/notifier/phone_manager.yaml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9bea433 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ + +.DS_Store diff --git a/apps/notifier/alexa_manager.yaml b/apps/notifier/alexa_manager.yaml index 8f03ece..7076c87 100755 --- a/apps/notifier/alexa_manager.yaml +++ b/apps/notifier/alexa_manager.yaml @@ -5,27 +5,27 @@ Alexa_Manager: # log_level: DEBUG # Alexa hub - binary_speak: binary_sensor.notifyhub_alexa_speak - bool_smart_volume_set: input_boolean.alexa_smart_volume_set - sensor_player: sensor.media_player_alexa - select_player: input_select.notification_media_player_alexa - select_type: input_select.default_alexa_type - select_method: input_select.default_alexa_method - select_voice: input_select.alexa_voice - select_alexa_language: input_select.alexa_language - bool_ssml: input_boolean.alexa_ssml + binary_speak: binary_sensor.notifier_alexa_speak + bool_smart_volume_set: input_boolean.notifier_alexa_smart_volume + sensor_player: sensor.notifier_player_alexa + select_player: select.notifier_player_alexa + select_type: input_select.notifier_alexa_type + select_method: input_select.notifier_alexa_method + select_voice: input_select.notifier_alexa_voice + select_alexa_language: input_select.notifier_alexa_language + bool_ssml: input_boolean.notifier_alexa_ssml prosody: - rate: input_number.alexa_prosody_rate - pitch: input_number.alexa_prosody_pitch - volume: input_number.alexa_prosody_volume + rate: input_number.notifier_alexa_prosody_rate + pitch: input_number.notifier_alexa_prosody_pitch + volume: input_number.notifier_alexa_prosody_volume # Main hub - number_wait_time: input_number.tts_wait_time - select_language: input_select.language - sensor_day_volume: sensor.period_of_day_volume + number_wait_time: input_number.notifier_tts_wait_time + select_language: input_select.notifier_language + sensor_day_volume: sensor.notifier_day_period_volume # Optional https://github.com/keatontaylor/alexa-actions actionable_notification: input_text.alexa_actionable_notification # Debug Sensor Generated by appdaemon - debug_sensor: sensor.centro_notifiche + debug_sensor: sensor.notifier_debug_error diff --git a/apps/notifier/gh_manager.py b/apps/notifier/gh_manager.py old mode 100644 new mode 100755 diff --git a/apps/notifier/gh_manager.yaml b/apps/notifier/gh_manager.yaml old mode 100644 new mode 100755 index 6372614..11aa094 --- a/apps/notifier/gh_manager.yaml +++ b/apps/notifier/gh_manager.yaml @@ -6,11 +6,11 @@ GH_Manager: ytube_player: media_player.ytube_music_player # Main hub - gh_wait_time: input_number.tts_wait_time - gh_select_media_player: input_select.notification_media_player_google - gh_sensor_media_player: sensor.media_player_google - tts_period_of_day_volume: sensor.period_of_day_volume - tts_language: input_select.language + gh_wait_time: input_number.notifier_tts_wait_time + gh_select_media_player: select.notifier_player_google + gh_sensor_media_player: sensor.notifier_player_google + tts_period_of_day_volume: sensor.notifier_day_period_volume + tts_language: input_select.notifier_language # Debug Sensor - debug_sensor: sensor.centro_notifiche + debug_sensor: sensor.notifier_debug_error diff --git a/apps/notifier/helpermodule.py b/apps/notifier/helpermodule.py old mode 100644 new mode 100755 diff --git a/apps/notifier/notification_manager.py b/apps/notifier/notification_manager.py old mode 100644 new mode 100755 diff --git a/apps/notifier/notification_manager.yaml b/apps/notifier/notification_manager.yaml old mode 100644 new mode 100755 index 176495e..4de5015 --- a/apps/notifier/notification_manager.yaml +++ b/apps/notifier/notification_manager.yaml @@ -2,6 +2,6 @@ Notification_Manager: module: notification_manager class: Notification_Manager - text_last_message: input_text.last_message - boolean_wrap_text: input_boolean.wrap_text - boolean_tts_clock: input_boolean.tts_clock + text_last_message: input_text.notifier_last_message + boolean_wrap_text: input_boolean.notifier_wrap_text + boolean_tts_clock: input_boolean.notifier_tts_clock diff --git a/apps/notifier/notifier_dispatch.py b/apps/notifier/notifier_dispatch.py old mode 100644 new mode 100755 index ccd8d2a..b8c6492 --- a/apps/notifier/notifier_dispatch.py +++ b/apps/notifier/notifier_dispatch.py @@ -1,7 +1,9 @@ -import hassapi as hass import sys -import yaml + +import hassapi as hass import helpermodule as h +import yaml + # # Centralizes messaging. # @@ -58,7 +60,7 @@ def initialize(self): self.alexa_manager = self.get_app("Alexa_Manager") self.phone_manager = self.get_app("Phone_Manager") ### LISTEN EVENT ### - self.listen_event(self.notify_hub, "hub") + self.listen_event(self.notifier, "notifier") ##################################################################### def set_debug_sensor(self, state, error): @@ -89,7 +91,7 @@ def createTTSdict(self,data) -> list: flag = True return [flag,dizionario] - def notify_hub(self, event_name, data, kwargs): + def notifier(self, event_name, data, kwargs): self.log("#### START NOTIFIER_DISPATCH ####") location_status = self.get_state(self.location_tracker) ### FLAG @@ -101,7 +103,7 @@ def notify_hub(self, event_name, data, kwargs): google_flag = self.createTTSdict(data["google"])[0] if len(str(data["google"])) != 0 else False google = self.createTTSdict(data["google"])[1] if len(str(data["google"])) != 0 else False google_priority_flag = False - if google_flag : + if google_flag: if "priority" in google: if str(google.get("priority")).lower() in ["true","on","yes","1"]: google_priority_flag = True @@ -109,7 +111,7 @@ def notify_hub(self, event_name, data, kwargs): alexa_flag = self.createTTSdict(data["alexa"])[0] if len(str(data["alexa"])) != 0 else False alexa = self.createTTSdict(data["alexa"])[1] if len(str(data["alexa"])) != 0 else False alexa_priority_flag = False - if alexa_flag : + if alexa_flag: if "priority" in alexa: if str(alexa.get("priority")).lower() in ["true","on","yes","1"]: alexa_priority_flag = True diff --git a/apps/notifier/notifier_dispatch.yaml b/apps/notifier/notifier_dispatch.yaml old mode 100644 new mode 100755 index d8364f0..488fcb9 --- a/apps/notifier/notifier_dispatch.yaml +++ b/apps/notifier/notifier_dispatch.yaml @@ -2,31 +2,31 @@ Notifier_Dispatch: module: notifier_dispatch class: Notifier_Dispatch - gh_tts_google_mode: input_select.tts_notify - gh_switch: input_boolean.google_switch + gh_tts_google_mode: input_select.notifier_tts_notify + gh_switch: input_boolean.notifier_google_switch - alexa_switch: input_boolean.alexa_switch + alexa_switch: input_boolean.notifier_alexa_switch - text_notifications: input_boolean.text_notifications - screen_notifications: input_boolean.screen_notifications - speech_notifications: input_boolean.speech_notifications - phone_notifications: input_boolean.phone_notifications + text_notifications: input_boolean.notifier_text_notifications + screen_notifications: input_boolean.notifier_screen_notifications + speech_notifications: input_boolean.notifier_speech_notifications + phone_notifications: input_boolean.notifier_phone_notifications - html_mode: input_boolean.html_mode + html_mode: input_boolean.notifier_html_mode persistent_notification_info: persistent_notification.info_messages - location_tracker: group.location_tracker - text_notify: input_select.text_notify - phone_notify: input_select.phone_notify - priority_message: input_boolean.priority_message - guest_mode: input_boolean.guest_mode - dnd: binary_sensor.dnd + location_tracker: group.notifier_location_tracker + text_notify: input_select.notifier_text_notify + phone_notify: input_select.notifier_phone_notify + priority_message: input_boolean.notifier_priority_message + guest_mode: input_boolean.notifier_guest_mode + dnd: binary_sensor.notifier_dnd - personal_assistant_name: input_text.personal_assistant_name - phone_called_number: input_text.phone_called_number + personal_assistant_name: input_text.notifier_personal_assistant + phone_called_number: input_text.notifier_called_number - debug_sensor: sensor.centro_notifiche + debug_sensor: sensor.notifier_debug_error dependencies: - Notification_Manager diff --git a/apps/notifier/phone_manager.py b/apps/notifier/phone_manager.py old mode 100644 new mode 100755 diff --git a/apps/notifier/phone_manager.yaml b/apps/notifier/phone_manager.yaml old mode 100644 new mode 100755 From 4a31473727847d8647eb1d802084b6ac0239f05d Mon Sep 17 00:00:00 2001 From: caiosweet <24454580+caiosweet@users.noreply.github.com> Date: Mon, 7 Nov 2022 22:27:51 +0100 Subject: [PATCH 04/13] alexa secret name --- apps/notifier/notifier_dispatch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/notifier/notifier_dispatch.py b/apps/notifier/notifier_dispatch.py index b8c6492..f393c08 100755 --- a/apps/notifier/notifier_dispatch.py +++ b/apps/notifier/notifier_dispatch.py @@ -52,7 +52,7 @@ def initialize(self): self.phone_sip_server = cfg.get("sip_server_name", "fritz.box:5060") self.gh_tts_cloud = cfg.get("tts_google_cloud", "google_cloud") self.reverso_tts = cfg.get("reverso_tts", "reversotts_say") - self.alexa_skill_id = cfg.get("notifyhub_alexa_actionable_skill_id", "") + self.alexa_skill_id = cfg.get("notifier_alexa_actionable_skill_id", "") ### APP MANAGER ### self.notification_manager = self.get_app("Notification_Manager") From 4af8e0ee01c07799352f44a4133535ee5a1da3ae Mon Sep 17 00:00:00 2001 From: caiosweet <24454580+caiosweet@users.noreply.github.com> Date: Tue, 8 Nov 2022 09:14:02 +0100 Subject: [PATCH 05/13] Alexa Backwards compatible message_tts --- apps/notifier/alexa_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/notifier/alexa_manager.py b/apps/notifier/alexa_manager.py index 457270f..4b574c8 100755 --- a/apps/notifier/alexa_manager.py +++ b/apps/notifier/alexa_manager.py @@ -326,7 +326,7 @@ def speak(self, alexa: dict, skill_id: str) -> None: self.lg(f"FROM DISPATCH: {type(alexa)} value {alexa}") # Backwards compatible message_tts - message = str(alexa.get(MESSAGE, alexa.get("message_tts", ""))) + message = str(alexa.get("message_tts", alexa.get(MESSAGE, ""))) get_players = alexa.get(MEDIA_PLAYER, self.get_state(self.sensor_player)) media_player = self.check_media_player(get_players) get_type = alexa.get(TYPE, self.get_state(self.select_type, default="tts")) From a19e62bd9180f86d8ca45ef72a9d5768fe23734b Mon Sep 17 00:00:00 2001 From: caiosweet <24454580+caiosweet@users.noreply.github.com> Date: Thu, 10 Nov 2022 16:19:50 +0100 Subject: [PATCH 06/13] Fixed volume muted --- apps/notifier/alexa_manager.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/apps/notifier/alexa_manager.py b/apps/notifier/alexa_manager.py index 4b574c8..79c656e 100755 --- a/apps/notifier/alexa_manager.py +++ b/apps/notifier/alexa_manager.py @@ -46,6 +46,7 @@ WAIT_TIME = "wait_time" WHISPER = "whisper" # MODE = "mode" +# PRIORITY = "priority" MOBILE_PUSH_TYPE = (PUSH, "dropin", "dropin_notification") SUB_VOICE = [ @@ -319,11 +320,17 @@ def initialize(self) -> None: def speak(self, alexa: dict, skill_id: str) -> None: """Speak the provided text through the media player.""" + self.lg(f"------ ALEXA START DISPATCH ------") + self.lg(f"FROM DISPATCH: {type(alexa)} value {alexa}") + if not self.service2player: self.set_debug_sensor("Alexa Services not found", CUSTOM_COMPONENT_URL) return - self.lg(f"------ ALEXA START DISPATCH ------") - self.lg(f"FROM DISPATCH: {type(alexa)} value {alexa}") + default_vol = float(self.get_state(self.sensor_volume, default=10)) / 100 + volume = float(alexa.get(VOLUME, default_vol)) + if volume == 0.0: + self.log("ALEXA VOLUME MUTED.", level="WARNING") + return # Backwards compatible message_tts message = str(alexa.get("message_tts", alexa.get(MESSAGE, ""))) @@ -331,8 +338,6 @@ def speak(self, alexa: dict, skill_id: str) -> None: media_player = self.check_media_player(get_players) get_type = alexa.get(TYPE, self.get_state(self.select_type, default="tts")) data_type = str(get_type).lower().replace("dropin", "dropin_notification") - default_vol = float(self.get_state(self.sensor_volume, default=10)) / 100 - volume = float(alexa.get(VOLUME, default_vol)) alexa_lang = self.get_state(self.select_alexa_language, default="Master") master_lang = self.get_state(self.select_language, default="it-IT") From ac269c25e80374563940ddcf6109359ab2993e0d Mon Sep 17 00:00:00 2001 From: caiosweet <24454580+caiosweet@users.noreply.github.com> Date: Sun, 13 Nov 2022 09:25:53 +0100 Subject: [PATCH 07/13] If whisper is true, it will apply effects, even though SSML is not active. --- apps/notifier/alexa_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/notifier/alexa_manager.py b/apps/notifier/alexa_manager.py index 79c656e..fa50858 100755 --- a/apps/notifier/alexa_manager.py +++ b/apps/notifier/alexa_manager.py @@ -660,7 +660,7 @@ def worker(self): alexa_data = {TYPE: "tts"} # TAGS SSML - if data[SSML] and not "" in msg: + if (data[SSML] or data[WHISPER]) and not "" in msg: voice = "Alexa" if data[VOICE] not in VOICE_NAMES else data[VOICE] if voice == "Alexa" and not data[WHISPER]: msg = self.find_speechcon(msg) From dc0e20dcbc0dd82bd9f632215ebd298fc8e05128 Mon Sep 17 00:00:00 2001 From: caiosweet <24454580+caiosweet@users.noreply.github.com> Date: Mon, 14 Nov 2022 20:44:55 +0100 Subject: [PATCH 08/13] Moved secret file from package to main. (/config/secret.yaml) --- apps/notifier/notifier_dispatch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/notifier/notifier_dispatch.py b/apps/notifier/notifier_dispatch.py index f393c08..f980fe1 100755 --- a/apps/notifier/notifier_dispatch.py +++ b/apps/notifier/notifier_dispatch.py @@ -44,7 +44,7 @@ def initialize(self): config = self.get_plugin_config() config_dir = config["config_dir"] self.log(f"configuration dir: {config_dir}") - secretsFile = config_dir + "/packages/secrets.yaml" + secretsFile = config_dir + "/secrets.yaml" with open(secretsFile, "r") as ymlfile: cfg = yaml.load(ymlfile, Loader=yaml.FullLoader) # yaml.safe_load self.gh_tts = cfg.get("tts_google", "google_translate_say") From a6419a8728e7d61c0edc50584eb5b2a11e943270 Mon Sep 17 00:00:00 2001 From: caiosweet <24454580+caiosweet@users.noreply.github.com> Date: Tue, 15 Nov 2022 20:56:59 +0100 Subject: [PATCH 09/13] fixed language in phone manager --- apps/notifier/notifier_dispatch.py | 3 +-- apps/notifier/phone_manager.py | 11 +++++++---- apps/notifier/phone_manager.yaml | 2 ++ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/apps/notifier/notifier_dispatch.py b/apps/notifier/notifier_dispatch.py index f980fe1..aa795c6 100755 --- a/apps/notifier/notifier_dispatch.py +++ b/apps/notifier/notifier_dispatch.py @@ -202,8 +202,7 @@ def notifier(self, event_name, data, kwargs): self.alexa_manager.speak(alexa, self.alexa_skill_id) if usePhone: try: - language = self.get_state(self.tts_language) - self.phone_manager.send_voice_call(data, phone_notify_name, self.phone_sip_server, language) + self.phone_manager.send_voice_call(data, phone_notify_name, self.phone_sip_server) except Exception as ex: self.log("An error occurred in phone notification: {}".format(ex),level="ERROR") self.set_debug_sensor("Error in Phone Notification: ", ex) diff --git a/apps/notifier/phone_manager.py b/apps/notifier/phone_manager.py index 3dc2090..11464cb 100755 --- a/apps/notifier/phone_manager.py +++ b/apps/notifier/phone_manager.py @@ -1,5 +1,7 @@ -import hassapi as hass import datetime + +import hassapi as hass + # import helpermodule as h @@ -12,6 +14,7 @@ class Phone_Manager(hass.Hass): def initialize(self): + self.tts_language = self.args.get("tts_language") self.dict_lingua = { "it-IT": "it-IT-Standard-A", "en-GB": "en-GB-Standard-A", @@ -21,11 +24,11 @@ def initialize(self): "es-ES": "es-ES-Standard-A", } - def send_voice_call(self, data, phone_name: str, sip_server_name: str, language: str): - message = self.replace_regular(data["message"], SUB_TTS) + def send_voice_call(self, data, phone_name: str, sip_server_name: str): + message = h.replace_regular(data["message"], SUB_TTS) message_tts = message.replace(" ", "%20") called_number = data["called_number"] - lang = self.dict_lingua.get(language) + lang = self.dict_lingua.get(self.tts_language) phone_name = phone_name.lower().replace(" ", "_") if phone_name.find("voip_call") != -1: if called_number != "": diff --git a/apps/notifier/phone_manager.yaml b/apps/notifier/phone_manager.yaml index dbf5ba0..21173c2 100755 --- a/apps/notifier/phone_manager.yaml +++ b/apps/notifier/phone_manager.yaml @@ -1,3 +1,5 @@ Phone_Manager: module: phone_manager class: Phone_Manager + + tts_language: input_select.notifier_language From db0391f07928988318b9ab50d6671e3b05f46506 Mon Sep 17 00:00:00 2001 From: caiosweet <24454580+caiosweet@users.noreply.github.com> Date: Thu, 17 Nov 2022 16:57:28 +0100 Subject: [PATCH 10/13] Sync with dev15/11 --- apps/notifier/alexa_manager.py | 2 +- apps/notifier/notification_manager.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/notifier/alexa_manager.py b/apps/notifier/alexa_manager.py index fa50858..79c656e 100755 --- a/apps/notifier/alexa_manager.py +++ b/apps/notifier/alexa_manager.py @@ -660,7 +660,7 @@ def worker(self): alexa_data = {TYPE: "tts"} # TAGS SSML - if (data[SSML] or data[WHISPER]) and not "" in msg: + if data[SSML] and not "" in msg: voice = "Alexa" if data[VOICE] not in VOICE_NAMES else data[VOICE] if voice == "Alexa" and not data[WHISPER]: msg = self.find_speechcon(msg) diff --git a/apps/notifier/notification_manager.py b/apps/notifier/notification_manager.py index d200ec3..8d6dd9d 100755 --- a/apps/notifier/notification_manager.py +++ b/apps/notifier/notification_manager.py @@ -64,7 +64,7 @@ def send_notify(self, data, notify_name, assistant_name: str): pushover = data["pushover"] if "pushover" in data else "" mobile = data["mobile"] if "mobile" in data else "" discord = data["discord"] if "discord" in data else "" - whatsapp_addon = data["whatsapp"] if "whatsapp" in data else "" + whatsapp_addon = data["whatsapp"] if "whatsapp" in data else "" notify_vector = self.check_notifier(h.return_array(h.replace_regular(data["notify"], SUB_REMOVE_SPACE)),self.split_device_list(str(notify_name))) #self.log("[NOTIFY_VECTOR]: {}".format(notify_vector), ascii_encode = False) ## target ## @@ -137,7 +137,7 @@ def send_notify(self, data, notify_name, assistant_name: str): {"text": messaggio } }) #self.log("[EXTRA-DATA_ELSE]: {}".format(extra_data), ascii_encode = False) - self.call_service("whatsapp/send_message", **extra_data) + self.call_service("whatsapp/send_message", **extra_data) #### WHATSAPP ####################### elif item.find("whatsapp") != -1: messaggio, titolo = self.prepare_text(html, message, title, timestamp, assistant_name) From 20d05427bdaf0ed9610854ffc85af0ff0200d5f1 Mon Sep 17 00:00:00 2001 From: caiosweet <24454580+caiosweet@users.noreply.github.com> Date: Thu, 17 Nov 2022 18:21:32 +0100 Subject: [PATCH 11/13] Fixed language in phone manager app --- apps/notifier/phone_manager.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/notifier/phone_manager.py b/apps/notifier/phone_manager.py index 11464cb..8af563c 100755 --- a/apps/notifier/phone_manager.py +++ b/apps/notifier/phone_manager.py @@ -15,6 +15,7 @@ class Phone_Manager(hass.Hass): def initialize(self): self.tts_language = self.args.get("tts_language") + self.log(f"lang: {self.tts_language}") self.dict_lingua = { "it-IT": "it-IT-Standard-A", "en-GB": "en-GB-Standard-A", @@ -28,7 +29,7 @@ def send_voice_call(self, data, phone_name: str, sip_server_name: str): message = h.replace_regular(data["message"], SUB_TTS) message_tts = message.replace(" ", "%20") called_number = data["called_number"] - lang = self.dict_lingua.get(self.tts_language) + lang = self.dict_lingua.get(self.get_state(self.tts_language)) phone_name = phone_name.lower().replace(" ", "_") if phone_name.find("voip_call") != -1: if called_number != "": @@ -44,3 +45,4 @@ def send_voice_call(self, data, phone_name: str, sip_server_name: str): called_number, message_tts, lang ) self.call_service("shell_command/telegram_call", url=url_tts) + self.log(f"telegtam call: {url_tts}") \ No newline at end of file From f2cc5a38a9310dc1a82a9ffc69e894807b24bff5 Mon Sep 17 00:00:00 2001 From: caiosweet <24454580+caiosweet@users.noreply.github.com> Date: Thu, 17 Nov 2022 21:19:45 +0100 Subject: [PATCH 12/13] Deleted log phone manager --- apps/notifier/phone_manager.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/notifier/phone_manager.py b/apps/notifier/phone_manager.py index 8af563c..8bec01b 100755 --- a/apps/notifier/phone_manager.py +++ b/apps/notifier/phone_manager.py @@ -15,7 +15,6 @@ class Phone_Manager(hass.Hass): def initialize(self): self.tts_language = self.args.get("tts_language") - self.log(f"lang: {self.tts_language}") self.dict_lingua = { "it-IT": "it-IT-Standard-A", "en-GB": "en-GB-Standard-A", @@ -45,4 +44,4 @@ def send_voice_call(self, data, phone_name: str, sip_server_name: str): called_number, message_tts, lang ) self.call_service("shell_command/telegram_call", url=url_tts) - self.log(f"telegtam call: {url_tts}") \ No newline at end of file + From a7dd8a0720fe60fc4887ae83f7297c53a17dcd37 Mon Sep 17 00:00:00 2001 From: caiosweet <24454580+caiosweet@users.noreply.github.com> Date: Wed, 15 Feb 2023 21:00:41 +0100 Subject: [PATCH 13/13] volume auto silent --- apps/notifier/alexa_manager.py | 66 +++++++++++++++++++++++--------- apps/notifier/alexa_manager.yaml | 2 +- apps/notifier/gh_manager.py | 1 - 3 files changed, 49 insertions(+), 20 deletions(-) diff --git a/apps/notifier/alexa_manager.py b/apps/notifier/alexa_manager.py index 79c656e..60ca681 100755 --- a/apps/notifier/alexa_manager.py +++ b/apps/notifier/alexa_manager.py @@ -9,7 +9,7 @@ """ Class Alexa Manager handles sending text to speech messages to Alexa media players Following features are implemented: - - Speak text to choosen media_player + - Speak text to choosen media player - Full queue support to manage async multiple TTS commands - Full wait to tts to finish to be able to supply a callback method - Mobile PUSH message @@ -28,6 +28,7 @@ TITLE = "title" MESSAGE = "message" AUDIO = "audio" +AUTO_VOLUMES = "auto_volumes" EVENT_ID = "event_id" LANGUAGE = "language" MEDIA_CONTENT_ID = "media_content_id" @@ -50,15 +51,15 @@ MOBILE_PUSH_TYPE = (PUSH, "dropin", "dropin_notification") SUB_VOICE = [ - # ("[.]{2,}", "."), - ("[\?\.\!,]+(?=[\?\.\!,])", ""), # Exclude duplicate - ("(\s+\.|\s+\.\s+|[\.])(?! )(?![^{]*})(?![^\d.]*\d)", ". "), + ("[\U00010000-\U0010ffff]", ""), # strip emoji + ("[\?\.\!,]+(?=[\?\.\!,])", ""), # strip duplicate ., + ("(\s+\.|\s+\.\s+|[\.])(?! )(?![^{<]*[}>])(?![^\d.]*\d)", ". "), ("&", " and "), # escape - # ("(? None: if not self.service2player: self.set_debug_sensor("Alexa Services not found", CUSTOM_COMPONENT_URL) return + default_vol = float(self.get_state(self.sensor_volume, default=10)) / 100 volume = float(alexa.get(VOLUME, default_vol)) - if volume == 0.0: + auto_volumes = self.check_bool(alexa.get(AUTO_VOLUMES, False)) + if volume == 0.0 and not auto_volumes: self.log("ALEXA VOLUME MUTED.", level="WARNING") return @@ -353,7 +356,7 @@ def speak(self, alexa: dict, skill_id: str) -> None: state_ssml = self.get_state(self.bool_ssml, default="off") # Actionable notification - if event_id := alexa.get(EVENT_ID): + if event_id := alexa.get(EVENT_ID, ""): self.set_textvalue( self.text_actionable_notification, json.dumps({"text": "", "event": event_id}), @@ -389,14 +392,15 @@ def speak(self, alexa: dict, skill_id: str) -> None: elif data_type not in MOBILE_PUSH_TYPE and message: self.queue.put( { + SKILL_ID: skill_id, + DEFAULT_VOL: default_vol, + VOLUME: volume, + AUTO_VOLUMES: auto_volumes, MESSAGE: message, MEDIA_PLAYER: media_player, TYPE: data_type, - DEFAULT_VOL: default_vol, - VOLUME: volume, LANGUAGE: language, EVENT_ID: event_id, - SKILL_ID: skill_id, AUDIO: alexa.get(AUDIO, None), NOTIFIER: str(alexa.get(NOTIFIER, ALEXA_SERVICE)), METHOD: str(alexa.get(METHOD, state_method).lower()), @@ -574,13 +578,33 @@ def entity_from_name(self, name_list: list) -> dict: self.lg(f"NAME-ENTITY_ID DICT: {name2entity}") return name2entity + def volume_auto_silent(self, media_player: list, defvol: float) -> None: + """Based on the time of day it automatically sets the volumes, silently.""" + m = 'volume' + for i in media_player: + status = self.get_state(i, attribute="all", default={}) + volume_get = status.get("attributes", {}).get("volume_level", -1) + if volume_get == defvol: + continue + self.lg(f"DIFFERENT VOLUMES: {volume_get} - DEFAULT: {defvol}") + if status.get("state", "") != "playing": + self.call_service( + NOTIFY + ALEXA_SERVICE, data={TYPE: "tts"}, target=i, message=m + ) + time.sleep(2) + self.call_service( + "media_player/volume_set", entity_id=i, volume_level=defvol + ) + self.set_state(i, attributes={"volume_level": defvol}) + self.call_service("alexa_media/update_last_called", return_result=True) + def volume_get_save(self, media_player: list, volume: float, defvol: float) -> None: """Get and save the volume of each media player only if different.""" self.volumes_saved = {} for i in media_player: - volume_get = self.get_state(i, attribute="volume_level", default=defvol) + volume_get = self.get_state(i, attribute="volume_level", default=-1) if volume_get != volume: - self.volumes_saved[i] = volume_get + self.volumes_saved[i] = defvol if volume_get == -1 else volume_get self.lg(f"GET VOLUME: {self.volumes_saved}") def volume_restore(self) -> None: @@ -590,7 +614,7 @@ def volume_restore(self) -> None: for i, j in self.volumes_saved.items(): self.call_service("media_player/volume_set", entity_id=i, volume_level=j) time.sleep(1) - # Force attribute volume_level in Home assistant + # Force attribute volume level in Home assistant self.set_state(i, attributes={"volume_level": j}) self.call_service("alexa_media/update_last_called", return_result=True) self.lg( @@ -614,6 +638,7 @@ def volume_set(self, media_player: list, volume: float) -> None: self.lg(f"SET VOLUMES: {player} {volume}") def set_debug_sensor(self, state: str, error: str) -> None: + """Set, based on the error, the debug sensor of the app notifier.""" attributes = {} attributes["icon"] = "si:amazonalexa" attributes["alexa_error"] = error @@ -627,10 +652,13 @@ def worker(self): while True: try: data = self.queue.get() - self.set_state(self.binary_speak, state="on") self.lg(f"------ ALEXA WORKER QUEUE ------") self.lg(f"WORKER: {type(data)} value {data}") + self.set_state(self.binary_speak, state="on", attributes = data) media_player = data[MEDIA_PLAYER] + if data[AUTO_VOLUMES]: + self.volume_auto_silent(media_player, data[DEFAULT_VOL]) + raise UnboundLocalError(data[AUTO_VOLUMES]) self.volume_get_save(media_player, data[VOLUME], data[DEFAULT_VOL]) self.volume_set(media_player, data[VOLUME]) @@ -640,7 +668,7 @@ def worker(self): self.lg(f"MESSAGE CLEAN: {msg}") # Speech time calculator - words = len(self.remove_tags(msg).split()) + words = len(self.remove_tags(msg).split()) or 1 # division by zero chars = self.remove_tags(msg).count("") duration = (words * 0.007) * 60 @@ -688,6 +716,7 @@ def worker(self): f"DURATION-WAIT: {round(duration, 2)}" f" - words: {words} - Chars: {chars}" ) + self.lg(f"WAIT_TIME: {data[WAIT_TIME]}") # Speak >>> self.call_service( @@ -711,6 +740,10 @@ def worker(self): time.sleep(duration) self.volume_restore() + except UnboundLocalError as ex: + self.lg(f"VOLUMES AUTO SILENT: {ex}") + pass + except Exception as ex: self.log("Error Alexa Manager (worker): {}".format(ex), level="ERROR") self.log(f"DATA: {data}", level="ERROR") @@ -730,6 +763,3 @@ def worker(self): self.set_state(self.binary_speak, state="off") self.lg("------ ALEXA END ------\n") - - # def terminate(self): - # self.log("Terminating!") diff --git a/apps/notifier/alexa_manager.yaml b/apps/notifier/alexa_manager.yaml index 7076c87..8ff49a2 100755 --- a/apps/notifier/alexa_manager.yaml +++ b/apps/notifier/alexa_manager.yaml @@ -2,7 +2,7 @@ Alexa_Manager: module: alexa_manager class: Alexa_Manager - # log_level: DEBUG + log_level: DEBUG # Alexa hub binary_speak: binary_sensor.notifier_alexa_speak diff --git a/apps/notifier/gh_manager.py b/apps/notifier/gh_manager.py index a859a66..3bbc3db 100755 --- a/apps/notifier/gh_manager.py +++ b/apps/notifier/gh_manager.py @@ -1,6 +1,5 @@ import hassapi as hass import time -import datetime import sys from queue import Queue from threading import Thread