################################################################################
# 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:
#       "<short_announcement>"
#
#   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/<mqtt_feed>
#     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:
#       "<short_announcement> [<indicator>]"
#     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/<mqtt_feed>
      #
      # 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:
      #   <short_announcement> [<indicator>]
      #
      # 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:
      #   <short_announcement> [<indicator>]
      #
      # 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) }}"
