| 1 | 1. Helpers (configuration.yaml, or create them in the UI) |
| 2 | |
| 3 | input_button: |
| 4 | station_game_start: |
| 5 | name: Start station game |
| 6 | icon: mdi:bus-clock |
| 7 | |
| 8 | # One dropdown per player. Options = "-" (no pick) + station names. |
| 9 | # Station names must match the keys in the automation's `stations` map. |
| 10 | input_select: |
| 11 | station_game_alice: |
| 12 | name: Alice's station |
| 13 | icon: mdi:account |
| 14 | options: ["-", "Stratford", "Hackney Wick", "Canning Town", "Bow Road"] |
| 15 | station_game_bob: |
| 16 | name: Bob's station |
| 17 | icon: mdi:account |
| 18 | options: ["-", "Stratford", "Hackney Wick", "Canning Town", "Bow Road"] |
| 19 | station_game_carol: |
| 20 | name: Carol's station |
| 21 | icon: mdi:account |
| 22 | options: ["-", "Stratford", "Hackney Wick", "Canning Town", "Bow Road"] |
| 23 | |
| 24 | 2. Automation |
| 25 | |
| 26 | alias: Station arrival game |
| 27 | description: Everyone picks a station; soonest next arrival wins. |
| 28 | mode: single |
| 29 | triggers: |
| 30 | - trigger: state |
| 31 | entity_id: input_button.station_game_start |
| 32 | not_from: [unavailable, unknown] |
| 33 | variables: |
| 34 | pick_timeout: 60 # seconds players get to choose |
| 35 | departures_attr: departures # attribute holding the arrivals list |
| 36 | notify_action: notify.notify |
| 37 | stations: |
| 38 | Stratford: sensor.stratford_departures |
| 39 | Hackney Wick: sensor.hackney_wick_departures |
| 40 | Canning Town: sensor.canning_town_departures |
| 41 | Bow Road: sensor.bow_road_departures |
| 42 | players: |
| 43 | Alice: input_select.station_game_alice |
| 44 | Bob: input_select.station_game_bob |
| 45 | Carol: input_select.station_game_carol |
| 46 | actions: |
| 47 | # Reset everyone's pick |
| 48 | - action: input_select.select_option |
| 49 | target: |
| 50 | entity_id: "{{ players.values() | list }}" |
| 51 | data: |
| 52 | option: "-" |
| 53 | |
| 54 | - action: "{{ notify_action }}" |
| 55 | data: |
| 56 | title: "🚌 Station game!" |
| 57 | message: > |
| 58 | Pick your station within {{ pick_timeout }}s. |
| 59 | Soonest arrival wins. Options: {{ stations.keys() | join(', ') }} |
| 60 | |
| 61 | # Wait until everyone has picked, or time runs out |
| 62 | - wait_template: > |
| 63 | {{ players.values() | map('states') | select('eq', '-') | list | count == 0 }} |
| 64 | timeout: |
| 65 | seconds: "{{ pick_timeout }}" |
| 66 | continue_on_timeout: true |
| 67 | |
| 68 | # Find each player's soonest future arrival |
| 69 | - variables: |
| 70 | results: > |
| 71 | {% set ns = namespace(r=[]) %} |
| 72 | {% for player, sel in players.items() %} |
| 73 | {% set station = states(sel) %} |
| 74 | {% set sensor = stations.get(station) %} |
| 75 | {% if sensor %} |
| 76 | {% set best = namespace(t=none, d=none) %} |
| 77 | {% for d in state_attr(sensor, departures_attr) or [] %} |
| 78 | {% set t = as_datetime(d.expected) if d.expected else none %} |
| 79 | {% if t and t > now() and (best.t is none or t < best.t) %} |
| 80 | {% set best.t = t %} |
| 81 | {% set best.d = d %} |
| 82 | {% endif %} |
| 83 | {% endfor %} |
| 84 | {% if best.t is not none %} |
| 85 | {% set ns.r = ns.r + [{ |
| 86 | 'player': player, |
| 87 | 'station': station, |
| 88 | 'secs': (best.t - now()).total_seconds() | int, |
| 89 | 'line': best.d.line.designation if best.d.line is mapping else '?', |
| 90 | 'mode': best.d.line.transport_mode if best.d.line is mapping else '', |
| 91 | 'dest': best.d.destination |
| 92 | }] %} |
| 93 | {% endif %} |
| 94 | {% endif %} |
| 95 | {% endfor %} |
| 96 | {{ ns.r | sort(attribute='secs') }} |
| 97 | no_pick: > |
| 98 | {{ players.items() | selectattr('1', 'is_state', '-') | map(attribute='0') | list }} |
| 99 | winners: > |
| 100 | {% if results | count > 0 %} |
| 101 | {{ results | selectattr('secs', 'eq', results[0].secs) | map(attribute='player') | list }} |
| 102 | {% else %}[]{% endif %} |
| 103 | |
| 104 | - choose: |
| 105 | - conditions: "{{ winners | count > 0 }}" |
| 106 | sequence: |
| 107 | - action: "{{ notify_action }}" |
| 108 | data: |
| 109 | title: > |
| 110 | 🏆 {{ winners | join(' & ') }} win{{ 's' if winners | count == 1 }}! |
| 111 | message: > |
| 112 | {% for r in results -%} |
| 113 | {{ loop.index }}. {{ r.player }} – {{ r.station }}: |
| 114 | {{ r.mode | lower }} {{ r.line }} → {{ r.dest }} |
| 115 | in {{ r.secs // 60 }}m {{ '%02d' % (r.secs % 60) }}s |
| 116 | {% endfor -%} |
| 117 | {% if no_pick %}Didn't pick: {{ no_pick | join(', ') }}{% endif %} |
| 118 | default: |
| 119 | - action: "{{ notify_action }}" |
| 120 | data: |
| 121 | title: "🚌 Station game" |
| 122 | message: "No valid picks or no upcoming arrivals. Nobody wins." |
| 123 | |
| 124 | 3. Dashboard card (optional) |
| 125 | |
| 126 | type: entities |
| 127 | title: Station game |
| 128 | entities: |
| 129 | - input_button.station_game_start |
| 130 | - input_select.station_game_alice |
| 131 | - input_select.station_game_bob |
| 132 | - input_select.station_game_carol |
| 133 | |
| 134 | How it works |
| 135 | |
| 136 | - Start: press the button. Everyone's pick resets to - and a notification goes out. |
| 137 | - Picking: each player sets their own dropdown. The game ends early once all players have picked, otherwise after 60s. Anyone who didn't |
| 138 | pick is left out and named in the results. |
| 139 | - Winner: for each pick, the automation takes the earliest expected time that is still in the future (it skips past arrivals). The lowest |
| 140 | wait wins. |
| 141 | - Ties: if two players pick the same station or get identical times, they share the win. |
| 142 | - Results: the notification shows the full ranking with mode, line, destination and countdown. |