################################################################################ # PACKAGE: View Road Announcement Router ################################################################################ # 1.8: Date: 2026-07-11 # - Added MQTT View Rd Feed announcement channel. # - Added mqtt_feed script field. # - MQTT feed publishes the current local date/time to a topic built from # mqtt_view_rd_feed_topic_template by replacing TOPIC with mqtt_feed. # 1.7: # - Updated Lounge Google Home Voice to speak short_announcement only. # - This allows long_announcement to remain available for detailed channels # such as Pushover without making voice announcements too long. # 1.6: # - Updated Sony TV Lounge channel to use short_announcement only. # - Removed Sony TV Lounge payload suffix from message formatting. # 1.5: Date: 2026-06-04 # # Purpose: # Central announcement router for general Home Assistant announcements. # Other scripts and automations can call script.view_road_announcement and pass # values such as channels, title, short_announcement, long_announcement, # payload, indicator, and mqtt_feed. # # How to call this script: # # - action: script.view_road_announcement # data: # channels: "pushover_zorruno, lounge_google_home_voice, led_matrix_1" # title: "View Road" # short_announcement: "Washing complete" # long_announcement: "The washing machine has finished." # payload: "" # indicator: "wash,1,15,10,6,5,1,0" # # Available channels: # # pushover_zorruno: # Uses: # title # long_announcement # # pushover_kim: # Uses: # title # long_announcement # # sony_tv_lounge: # Uses: # title # short_announcement # Message format: # "" # # lounge_google_home_voice: # Also accepts alias: # google_home_voice # Uses: # short_announcement # Notes: # /s prefixes are stripped before being sent to voice. # Quiet time: # Blocked by default when input_boolean.quiet_time is on. # # lounge_touchscreen_voice: # Placeholder only. # Quiet time: # Blocked by default when input_boolean.quiet_time is on. # # mqtt_view_rd_feed: # Uses: # mqtt_feed # Publishes the current local date/time to MQTT. # Topic format: # viewroad-status/activityfeed/ # Payload format: # YYYY-MM-DD HH:MM:SS # # led_matrix_1: # Uses: # short_announcement # indicator # Publishes to MQTT topic: # viewroad-commands/ledmatrix1/announce # Message format: # " []" # Example: # "Washing complete [wash,1,15,10,6,5,1,0]" # # led_matrix_2: # Future channel. # Uses the same format as led_matrix_1. # Publishes to MQTT topic: # viewroad-commands/ledmatrix2/announce # # LED matrix announcement format: # # Message text [identifier,transition_mode,speed,repeat_seconds,repeat_times,static_wait,sounder_quantity,sound_id] # # Example script call: # # - action: script.view_road_announcement # data: # channels: "led_matrix_1" # short_announcement: "Washing complete" # indicator: "wash,1,15,10,6,5,1,0" # # Result sent to MQTT: # # Washing complete [wash,1,15,10,6,5,1,0] # # Indicator fields: # # identifier: # Announcement identifier used by the LED matrix ESPHome logic. # It is cleaned by ESPHome to letters, numbers, underscore, or dash, and # is limited to 8 characters. # # transition_mode: # 0 = Slide up # 1 = Slide down # 2 = Pixel fall # 3 = Static Up, Down # 4 = Static Down, Up # # speed: # Per-message scroll delay in ms. # Clamped by ESPHome from 1 to 100. # # repeat_seconds: # Delay between completed scrolls. # Clamped by ESPHome from 0 to 99. # # repeat_times: # Total number of scroll displays. # Clamped by ESPHome from 0 to 99. # 0 is treated as a one-shot announcement. # # static_wait: # Per-message static wait time in seconds. # Clamped by ESPHome from 0 to 30. # Only affects transition modes 3 and 4. # Other transition modes ignore it. # # sounder_quantity: # Number of announcement displays that should beep first. # 0 means no sounder beep. # # sound_id: # Stored by the LED matrix ESPHome logic for future sounder logic. # It is not currently used by the display logic. # # Notes: # The announcement router does not parse, clean, or clamp the indicator # fields. It passes the indicator through to the LED matrix in square # brackets. The LED matrix ESPHome YAML handles validation and behaviour. # # The router does not intentionally trim leading spaces from # short_announcement for LED matrix messages. The previous invisible leading # space guard workaround has been removed. # # Changes: # 1.5: # - Removed LED matrix invisible leading-space guard workaround. # - Updated LED matrix announcement comments for the current 8-field format. # - Updated LED matrix transition comments for modes 0-4. # - Added static_wait and sound_id documentation to router comments. # # 1.4: # - Documented LED matrix indicator format. # - Clarified that LED matrix indicator validation is handled by ESPHome. # # 1.3: # - Added led_matrix_1 announcement channel. # - Added future led_matrix_2 announcement channel. # - Added MQTT topic settings for LED matrix announcement channels. # - Added channel enable helpers for led_matrix_1 and led_matrix_2. # - Added package instructions for calling script.view_road_announcement. # - Moved more configurable values into the package settings section. # - Added lounge Google Home cast chime suppression setting. # # 1.2: # - Updated Lounge Google Home voice channel to use working Piper TTS setup. # - Added Piper voice option en_GB-alba-medium. # - Removed separate TTS language option from the voice channel. # # 1.1: # - Removed old YAML Pushover notify service setup. # - Uses existing Pushover integration notify entities. # - Added quiet time channel blocking. # - Added Lounge Google Home voice announcement channel. # # Notes: # - Use input_boolean helpers for channel enable/disable control. # - Binary sensors are read-only, so input_boolean is the correct helper type. # - Channels with no action block yet intentionally do nothing. # ################################################################################ ################################################################################ # CHANNEL ENABLE HELPERS ################################################################################ input_boolean: announcement_channel_pushover_zorruno: name: Announcement Channel - Pushover Zorruno icon: mdi:cellphone-message initial: true announcement_channel_pushover_kim: name: Announcement Channel - Pushover Kim icon: mdi:cellphone-message initial: true announcement_channel_sony_tv_lounge: name: Announcement Channel - Sony TV Lounge icon: mdi:television initial: true announcement_channel_lounge_google_home_voice: name: Announcement Channel - Lounge Google Home Voice icon: mdi:speaker initial: true announcement_channel_lounge_touchscreen_voice: name: Announcement Channel - Lounge Touchscreen Voice icon: mdi:tablet-dashboard initial: true announcement_channel_mqtt_view_rd_feed: name: Announcement Channel - MQTT View Rd Feed icon: mdi:message-processing initial: true announcement_channel_led_matrix_1: name: Announcement Channel - LED Matrix 1 icon: mdi:led-strip-variant initial: true announcement_channel_led_matrix_2: name: Announcement Channel - LED Matrix 2 icon: mdi:led-strip-variant initial: true ################################################################################ # MAIN ANNOUNCEMENT ROUTER SCRIPT ################################################################################ script: view_road_announcement: alias: View Road Announcement icon: mdi:bullhorn mode: parallel max: 20 fields: channels: name: Channels description: Comma separated list of announcement channels to use. example: "pushover_zorruno, sony_tv_lounge, led_matrix_1" selector: text: title: name: Title description: Announcement title. example: "View Road" selector: text: short_announcement: name: Short Announcement description: Short announcement text. example: "Garage door" selector: text: long_announcement: name: Long Announcement description: Longer announcement text. example: "The garage door has been opened." selector: text: multiline: true payload: name: Payload description: Payload value such as On, Off, Open, Closed, etc. example: "Open" selector: text: indicator: name: Indicator description: LED matrix indicator data in the format identifier,transition_mode,speed,repeat_seconds,repeat_times,static_wait,sounder_quantity,sound_id. example: "wash,1,15,10,6,5,1,0" selector: text: mqtt_feed: name: MQTT Feed Topic description: Activity feed topic suffix. Replaces TOPIC in mqtt_view_rd_feed_topic_template. example: "washing_machine" selector: text: sequence: ########################################################################## # PACKAGE SETTINGS / SUBSTITUTIONS ########################################################################## # # Home Assistant packages do not support ESPHome-style substitutions. # These variables act as the package-level settings for this script. # # If a value is not passed to the script, the default below is used. # If a value is passed as "", it clears the default to blank. # ########################################################################## - variables: default_channels: "pushover_zorruno, pushover_kim, sony_tv_lounge, lounge_google_home_voice, lounge_touchscreen_voice, mqtt_view_rd_feed, led_matrix_1, led_matrix_2" default_title: "View Road" default_short_announcement: "Home automation announcement" default_long_announcement: "Home automation announcement" default_payload: "" default_indicator: "" default_mqtt_feed: "" quiet_time_blocked_channels: - "google_home_voice" - "lounge_google_home_voice" - "lounge_touchscreen_voice" lounge_google_home_voice_media_player: "media_player.lounge_google_tichome_speaker" lounge_google_home_voice_tts_entity: "tts.piper" lounge_google_home_voice_piper_voice: "en_GB-jenny_dioco-medium" lounge_google_home_voice_volume_level: 0.80 lounge_google_home_voice_suppress_cast_chime: false led_matrix_1_mqtt_topic: "viewroad-commands/ledmatrix1/announce" led_matrix_2_mqtt_topic: "viewroad-commands/ledmatrix2/announce" led_matrix_mqtt_qos: 0 led_matrix_mqtt_retain: false mqtt_view_rd_feed_topic_template: "viewroad-status/activityfeed/TOPIC" mqtt_view_rd_feed_datetime_format: "%Y-%m-%d %H:%M:%S" mqtt_view_rd_feed_mqtt_qos: 0 mqtt_view_rd_feed_mqtt_retain: true ########################################################################## # RESOLVE PASSED VALUES ########################################################################## # # Lowercase field names are preferred: # channels # title # short_announcement # long_announcement # payload # indicator # mqtt_feed # # Some uppercase variants are also accepted for tolerance. # ########################################################################## - variables: ann_channels_raw: >- {% if channels is defined %} {{ channels }} {% elif channel is defined %} {{ channel }} {% elif Channel is defined %} {{ Channel }} {% else %} {{ default_channels }} {% endif %} ann_title: >- {% if title is defined %} {{ title }} {% elif Title is defined %} {{ Title }} {% else %} {{ default_title }} {% endif %} ann_short_announcement: >- {% if short_announcement is defined %} {{ short_announcement }} {% elif Short_Announcement is defined %} {{ Short_Announcement }} {% else %} {{ default_short_announcement }} {% endif %} ann_long_announcement: >- {% if long_announcement is defined %} {{ long_announcement }} {% elif Long_Announcement is defined %} {{ Long_Announcement }} {% else %} {{ default_long_announcement }} {% endif %} ann_payload: >- {% if payload is defined %} {{ payload }} {% elif Payload is defined %} {{ Payload }} {% else %} {{ default_payload }} {% endif %} ann_indicator: >- {% if indicator is defined %} {{ indicator }} {% elif Indicator is defined %} {{ Indicator }} {% else %} {{ default_indicator }} {% endif %} ann_mqtt_feed: >- {% if mqtt_feed is defined %} {{ mqtt_feed }} {% elif MQTT_Feed is defined %} {{ MQTT_Feed }} {% elif Mqtt_Feed is defined %} {{ Mqtt_Feed }} {% else %} {{ default_mqtt_feed }} {% endif %} ########################################################################## # NORMALISE CHANNEL LISTS AND MESSAGE TEXT ########################################################################## - variables: ann_channels_normalised: >- {{ ',' ~ (ann_channels_raw | string | lower | replace(' ', '') | replace('\n', '') | trim(',')) ~ ',' }} ann_quiet_time_blocked_channels_normalised: >- {{ ',' ~ (quiet_time_blocked_channels | join(',') | lower | replace(' ', '') | replace('\n', '') | trim(',')) ~ ',' }} ann_mqtt_feed_topic_suffix: >- {{ ann_mqtt_feed | string | trim }} ann_mqtt_feed_topic: >- {{ mqtt_view_rd_feed_topic_template | replace('TOPIC', ann_mqtt_feed_topic_suffix) }} ann_mqtt_feed_datetime: >- {{ now().strftime(mqtt_view_rd_feed_datetime_format) }} ann_short_announcement_raw: >- {% if short_announcement is defined %} {{ short_announcement }} {% elif Short_Announcement is defined %} {{ Short_Announcement }} {% else %} {{ default_short_announcement }} {% endif %} ann_short_announcement_clean: >- {{ ann_short_announcement_raw | string | regex_replace('^(?:/s)+', '') | trim }} ann_sony_tv_message: >- {{ ann_short_announcement_clean }} ann_voice_message: >- {{ ann_short_announcement_clean }} ann_led_matrix_1_message: >- {%- set short_text = ann_short_announcement_raw -%} {%- set indicator_text = ann_indicator | string | trim -%} {{- short_text | string -}}{{ ' [' ~ indicator_text ~ ']' if indicator_text | length > 0 else '' }} ann_led_matrix_2_message: >- {%- set short_text = ann_short_announcement_clean -%} {%- set indicator_text = ann_indicator | string | trim -%} {{- short_text | string -}}{{ ' [' ~ indicator_text ~ ']' if indicator_text | length > 0 else '' }} ########################################################################## # CHANNEL: PUSHOVER ZORRUNO ########################################################################## - if: - condition: template value_template: "{{ ',pushover_zorruno,' in ann_channels_normalised }}" - condition: state entity_id: input_boolean.announcement_channel_pushover_zorruno state: "on" then: - action: notify.pushover_zorruno data: title: "{{ ann_title }}" message: "{{ ann_long_announcement }}" ########################################################################## # CHANNEL: PUSHOVER KIM ########################################################################## - if: - condition: template value_template: "{{ ',pushover_kim,' in ann_channels_normalised }}" - condition: state entity_id: input_boolean.announcement_channel_pushover_kim state: "on" then: - action: notify.pushover_kim data: title: "{{ ann_title }}" message: "{{ ann_long_announcement }}" ########################################################################## # CHANNEL: SONY TV LOUNGE ########################################################################## - if: - condition: template value_template: "{{ ',sony_tv_lounge,' in ann_channels_normalised }}" - condition: state entity_id: input_boolean.announcement_channel_sony_tv_lounge state: "on" - condition: template value_template: "{{ ann_sony_tv_message | string | trim | length > 0 }}" then: - action: notify.sony_android_tv_lounge data: title: "{{ ann_title }}" message: "{{ ann_sony_tv_message }}" ########################################################################## # CHANNEL: LOUNGE GOOGLE HOME VOICE ########################################################################## # # Also accepts google_home_voice as an alias channel name. # # This channel is blocked during quiet time by default. # ########################################################################## - if: - condition: template value_template: >- {{ ',lounge_google_home_voice,' in ann_channels_normalised or ',google_home_voice,' in ann_channels_normalised }} - condition: state entity_id: input_boolean.announcement_channel_lounge_google_home_voice state: "on" - condition: template value_template: >- {{ not ( is_state('input_boolean.quiet_time', 'on') and ( ',lounge_google_home_voice,' in ann_quiet_time_blocked_channels_normalised or ',google_home_voice,' in ann_quiet_time_blocked_channels_normalised ) ) }} - condition: template value_template: "{{ ann_voice_message | string | trim | length > 0 }}" then: - choose: - conditions: - condition: template value_template: "{{ lounge_google_home_voice_suppress_cast_chime | bool(false) }}" - condition: template value_template: "{{ states(lounge_google_home_voice_media_player) in ['off', 'unavailable', 'unknown'] }}" sequence: - action: media_player.volume_set data: entity_id: "{{ lounge_google_home_voice_media_player }}" volume_level: 0 - action: media_player.turn_on target: entity_id: "{{ lounge_google_home_voice_media_player }}" - wait_template: >- {{ states(lounge_google_home_voice_media_player) in ['idle', 'paused', 'playing', 'on'] }} timeout: seconds: 5 continue_on_timeout: true - delay: seconds: 1 - action: media_player.volume_set data: entity_id: "{{ lounge_google_home_voice_media_player }}" volume_level: "{{ lounge_google_home_voice_volume_level | float(0.8) }}" - delay: seconds: 1 - action: tts.speak target: entity_id: "{{ lounge_google_home_voice_tts_entity }}" data: cache: false media_player_entity_id: "{{ lounge_google_home_voice_media_player }}" message: "{{ ann_voice_message }}" options: voice: "{{ lounge_google_home_voice_piper_voice }}" preferred_format: mp3 preferred_sample_rate: 44100 ########################################################################## # CHANNEL: LOUNGE TOUCHSCREEN VOICE ########################################################################## # # Placeholder only. No action yet. # # This channel is listed in quiet_time_blocked_channels for future use. # ########################################################################## ########################################################################## # CHANNEL: MQTT VIEW RD FEED ########################################################################## # # Channel name: # mqtt_view_rd_feed # # Uses: # mqtt_feed # # Publishes the current Home Assistant local time in AM/PM format. # # Topic format: # viewroad-status/activityfeed/ # # Example: # mqtt_feed: "dogfed" # # Publishes: # Topic: viewroad-status/activityfeed/dogfed # Payload: 12:32 PM # ########################################################################## - if: - condition: template value_template: "{{ ',mqtt_view_rd_feed,' in ann_channels_normalised }}" - condition: state entity_id: input_boolean.announcement_channel_mqtt_view_rd_feed state: "on" - condition: template value_template: "{{ ann_mqtt_feed_topic_suffix | string | trim | length > 0 }}" then: - action: mqtt.publish data: topic: "{{ ann_mqtt_feed_topic }}" #payload: "{{ now().strftime('%I:%M %p') | regex_replace('^0', '') }}" payload: "{{ now().strftime('%H:%M') }}" qos: "{{ mqtt_view_rd_feed_mqtt_qos | int(0) }}" retain: "{{ mqtt_view_rd_feed_mqtt_retain | bool(true) }}" ########################################################################## # CHANNEL: LOUNGE LED MATRIX DISPLAY - MATRIX 1 ########################################################################## # # Channel name: # led_matrix_1 # # Uses: # short_announcement # indicator # # Publishes: # [] # # Example payload: # Washing complete [wash,1,15,10,6,5,1,0] # # Indicator format: # identifier,transition_mode,speed,repeat_seconds,repeat_times,static_wait,sounder_quantity,sound_id # ########################################################################## - if: - condition: template value_template: "{{ ',led_matrix_1,' in ann_channels_normalised }}" - condition: state entity_id: input_boolean.announcement_channel_led_matrix_1 state: "on" - condition: template value_template: "{{ ann_led_matrix_1_message | string | trim | length > 0 }}" then: - action: mqtt.publish data: topic: "{{ led_matrix_1_mqtt_topic }}" payload: "{{ ann_led_matrix_1_message }}" qos: "{{ led_matrix_mqtt_qos | int(0) }}" retain: "{{ led_matrix_mqtt_retain | bool(false) }}" ########################################################################## # CHANNEL: LOUNGE LED MATRIX DISPLAY - MATRIX 2 ########################################################################## # # Future channel. # # Channel name: # led_matrix_2 # # Uses: # short_announcement # indicator # # Publishes: # [] # # Indicator format: # identifier,transition_mode,speed,repeat_seconds,repeat_times,static_wait,sounder_quantity,sound_id # ########################################################################## - if: - condition: template value_template: "{{ ',led_matrix_2,' in ann_channels_normalised }}" - condition: state entity_id: input_boolean.announcement_channel_led_matrix_2 state: "on" - condition: template value_template: "{{ ann_led_matrix_2_message | string | trim | length > 0 }}" then: - action: mqtt.publish data: topic: "{{ led_matrix_2_mqtt_topic }}" payload: "{{ ann_led_matrix_2_message }}" qos: "{{ led_matrix_mqtt_qos | int(0) }}" retain: "{{ led_matrix_mqtt_retain | bool(false) }}"