├── nhl-image ├── ansible │ ├── files │ │ ├── sb_image │ │ │ ├── 51cache │ │ │ ├── .gitconfig │ │ │ ├── sbtools │ │ │ │ ├── splash.gif │ │ │ │ ├── checkUpdate.sh │ │ │ │ ├── autoset_tz.sh │ │ │ │ ├── pi_crontab.txt │ │ │ │ ├── runtext.py │ │ │ │ ├── sb-help │ │ │ │ ├── sb-help.txt │ │ │ │ ├── issueUpload.sh │ │ │ │ ├── changelog.sh │ │ │ │ └── sb-tools │ │ │ ├── get_version │ │ │ ├── sb_splash.service │ │ │ ├── sb_autosettz.service │ │ │ ├── supervisord.service │ │ │ ├── wifi-on.service │ │ │ ├── import_readme.txt │ │ │ ├── nls_controlhub.service │ │ │ ├── nls_controlhub_cfg.toml │ │ │ ├── scoreboard-emulated.conf │ │ │ ├── install_bindings.sh │ │ │ ├── scoreboard.conf │ │ │ ├── crontab │ │ │ ├── supervisord.conf │ │ │ ├── bash.bashrc │ │ │ ├── scoreboard.bash │ │ │ ├── .bashrc │ │ │ ├── .config │ │ │ │ ├── fastfetch │ │ │ │ │ └── config.jsonc │ │ │ │ └── neofetch │ │ │ │ │ └── neofetch_config.conf │ │ │ └── aptfile │ │ ├── comitup-conf │ │ │ ├── scoreboard_ui.tar.gz │ │ │ └── comitup.conf │ │ └── zram-config │ │ │ ├── ztab │ │ │ └── install.bash │ ├── VERSION │ ├── setup-raspberry.yml │ ├── setup-raspberry-beta.yml │ └── setup-raspberry-trixie.yml ├── .vscode │ └── settings.json ├── docker-compose.yml ├── alpine.pkr.hcl ├── raspios_trixie.pkr.hcl ├── raspios.pkr.hcl ├── raspios-beta.pkr.hcl ├── BUILD.md └── raspios_trixie_3264.hcl ├── .github └── workflows │ ├── README.md │ ├── clean_tool_cache.yml │ ├── publish.yml │ ├── main.yml │ └── notify_discord.yml ├── Dockerfile ├── Dockerfile_1.10.0 ├── .gitignore └── README.md /nhl-image/ansible/files/sb_image/51cache: -------------------------------------------------------------------------------- 1 | Acquire::http { Proxy "APT_PROXY"; }; -------------------------------------------------------------------------------- /nhl-image/ansible/files/sb_image/.gitconfig: -------------------------------------------------------------------------------- 1 | [user] 2 | email = pi@scoreboard 3 | name = NHL LED Scoreboard img 4 | -------------------------------------------------------------------------------- /nhl-image/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ansible.python.interpreterPath": "/Users/falkyre/.pyenv/versions/3.11.2/bin/python" 3 | } -------------------------------------------------------------------------------- /nhl-image/ansible/files/sb_image/sbtools/splash.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/falkyre/nhl-led-scoreboard-img/HEAD/nhl-image/ansible/files/sb_image/sbtools/splash.gif -------------------------------------------------------------------------------- /nhl-image/ansible/files/comitup-conf/scoreboard_ui.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/falkyre/nhl-led-scoreboard-img/HEAD/nhl-image/ansible/files/comitup-conf/scoreboard_ui.tar.gz -------------------------------------------------------------------------------- /nhl-image/ansible/files/sb_image/get_version: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd /home/pi/nhl-led-scoreboard 4 | 5 | /home/pi/sbtools/checkUpdate.sh > /home/pi/.nhlledportal/status 6 | 7 | chown pi:pi /home/pi/.nhlledportal/status 8 | -------------------------------------------------------------------------------- /nhl-image/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.9' 2 | 3 | services: 4 | packer: 5 | #platform: linux/amd64 6 | image: packer-builder-arm-ansible:vlatest 7 | build: . 8 | privileged: true 9 | volumes: 10 | - /dev:/dev 11 | - .:/build 12 | command: 13 | - build 14 | - raspios.pkr.hcl 15 | -------------------------------------------------------------------------------- /.github/workflows/README.md: -------------------------------------------------------------------------------- 1 | To trigger a workflow: 2 | (I'm old and forget things) 3 | 4 | example: 5 | 6 | git commit -a -m "Update ansible playbook" 7 | git push 8 | git tag -a v1.6.12 -m "First release of the image based on latest raspian OS Bullseye. NOTE: The NHL API is currently being changed by the NHL so there is no guarentee that things will work properly." 9 | git push origin v1.6.12 -------------------------------------------------------------------------------- /nhl-image/ansible/files/sb_image/sb_splash.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=NHL LED Splash Startup Animation 3 | ConditionPathExists=/home/pi/sbtools/splash.sh 4 | ConditionPathExists=/home/pi/sbtools/splash.gif 5 | Before=basic.target 6 | After=local-fs.target sysinit.target 7 | DefaultDependencies=no 8 | 9 | [Service] 10 | Type=simple 11 | ExecStart=/home/pi/sbtools/splash.sh 12 | 13 | [Install] 14 | WantedBy=basic.target 15 | -------------------------------------------------------------------------------- /nhl-image/ansible/VERSION: -------------------------------------------------------------------------------- 1 | version:2025.12.2 2 | detail:This update contains the latest NHL LED Scoreboard code (2025.12.2) from my repository. Still currently running on 32 bit bookworm. This also includes V2025.12.2 of the Experimental Web UI which now allows uploading of the config.json from your PC and also downloading configuration settings that can be used to load on future versions of the image. In the Utilities section of the Web UI, there's a Download Coniguration section now. 3 | -------------------------------------------------------------------------------- /nhl-image/ansible/files/sb_image/sb_autosettz.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Setup timezone automatically based on IP address 3 | ConditionPathExists=/home/pi/.nhlledportal/setTZ 4 | After=network-online.target 5 | Wants=network-online.target 6 | 7 | [Service] 8 | WorkingDirectory=/home/pi/sbtools 9 | ExecStartPre=/bin/sh -c 'until ping -c1 google.com; do sleep 1; done;' 10 | ExecStart=/bin/sh /home/pi/sbtools/autoset_tz.sh 11 | 12 | [Install] 13 | WantedBy=multi-user.target 14 | -------------------------------------------------------------------------------- /nhl-image/ansible/files/sb_image/supervisord.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=supervisord - Supervisor process control system for UNIX 3 | Documentation=http://supervisord.org 4 | After=network-online.target 5 | 6 | [Service] 7 | Type=forking 8 | ExecStart=/usr/local/bin/supervisord -c /etc/supervisor/supervisord.conf 9 | ExecReload=/usr/local/bin/supervisorctl reload 10 | ExecStop=/usr/local/bin/supervisorctl shutdown 11 | Restart=always 12 | RestartSec=5s 13 | 14 | [Install] 15 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /nhl-image/ansible/files/sb_image/wifi-on.service: -------------------------------------------------------------------------------- 1 | # Enable wifi, even if no regulatory domain is set 2 | # 3 | # Supporting Comitup on Raspbios 4 | # 5 | # copy to e.g. /etc/systemd/system/ 6 | # "systemctl enable wifi-on.service" 7 | 8 | [Unit] 9 | Description=Turn wifi on, regardless of regulatory domain 10 | After=network.target network-online.target 11 | Wants=network-online.target 12 | 13 | [Service] 14 | Type=oneshot 15 | ExecStart=/usr/bin/nmcli radio wifi on 16 | 17 | [Install] 18 | WantedBy=NetworkManager.service 19 | -------------------------------------------------------------------------------- /nhl-image/ansible/files/sb_image/import_readme.txt: -------------------------------------------------------------------------------- 1 | If you place a zip file called configs.zip in this folder, on first boot and the first time you ssh to the scoreboard, 2 | you will be asked if you want to import the files contained in the configs.zip. 3 | 4 | The following files need to be placed in the configs.zip: 5 | 6 | /home/pi/nhl-led-scoreboard/config/config.json 7 | /home/pi/sbtools/testMatrix.sh 8 | /etc/supervisor/conf.d/scoreboard.conf 9 | 10 | You can use the NLS Control Hub Utilities page to generate the configs.zip file -------------------------------------------------------------------------------- /nhl-image/ansible/files/sb_image/nls_controlhub.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=NLS Control Hub 3 | After=network.target 4 | 5 | [Service] 6 | ExecStart=/usr/local/bin/nls_controlhub --config /etc/nls_controlhub/config.toml 7 | User=pi 8 | WorkingDirectory=/home/pi/nhl-led-scoreboard 9 | Restart=on-failure 10 | 11 | RestartSec=5 12 | 13 | # Output logging to journal (viewable via journalctl) 14 | StandardOutput=journal 15 | StandardError=journal 16 | SyslogIdentifier=nls_controlhub 17 | 18 | [Install] 19 | WantedBy=multi-user.target 20 | -------------------------------------------------------------------------------- /nhl-image/ansible/files/sb_image/sbtools/checkUpdate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd /home/pi/nhl-led-scoreboard 4 | ROOT=$(/usr/bin/dirname "$(git rev-parse --git-dir)") 5 | CURRENTLY_BUILT_VER=$(/usr/bin/cat "${ROOT}"/VERSION) # stored somewhere, e.g. spec file in my case 6 | LASTVER=$(/home/pi/nhlsb-venv/bin/lastversion falkyre/nhl-led-scoreboard -gt "${CURRENTLY_BUILT_VER}") 7 | if [[ $? -eq 0 ]]; then 8 | # LASTVER is newer, update and trigger build 9 | # .... 10 | echo "New version V${LASTVER} available!! You are running V${CURRENTLY_BUILT_VER}" 11 | 12 | else 13 | echo "You are running the latest version V${CURRENTLY_BUILT_VER}" 14 | fi 15 | -------------------------------------------------------------------------------- /nhl-image/ansible/files/sb_image/nls_controlhub_cfg.toml: -------------------------------------------------------------------------------- 1 | # Configuration for the NHL LED Scoreboard Control Hub 2 | # All values are optional. If a value is not present, the default from the script will be used. 3 | # The scoreboard_dir can be overridden by the --scoreboard_dir command-line argument, which takes the highest precedence. 4 | 5 | # SUPERVISOR_URL = "127.0.0.1" 6 | # SUPERVISOR_PORT = 9001 7 | # PORT = 8000 8 | # Set the PYTHON_EXEC to the full path of your Scoreboard venv's python executable: 9 | # eg /home/pi/nhlsb-venv/bin/python3 10 | PYTHON_EXEC = "/home/pi/nhlsb-venv/bin/python3" 11 | scoreboard_dir = "/home/pi/nhl-led-scoreboard/" 12 | -------------------------------------------------------------------------------- /nhl-image/ansible/files/sb_image/scoreboard-emulated.conf: -------------------------------------------------------------------------------- 1 | [program:scoreboard-emulated] 2 | command=/home/pi/nhlsb-venv/bin/python3 ./src/main.py --emulated --updatecheck 3 | directory=/home/pi/nhl-led-scoreboard 4 | autostart=false 5 | autorestart=true 6 | stderr_logfile=/var/log/scoreboard-emulated.stderr.log 7 | stderr_logfile_maxbytes=1MB 8 | stderr_logfile_backups=10 9 | stdout_logfile=/var/log/scoreboard-emulated.stdout.log 10 | stdout_logfile_maxbytes=1MB 11 | stdout_logfile_backups=10 12 | 13 | # Don't restart too quickly if it keeps failing 14 | startretries=3 15 | startsecs=5 16 | 17 | # Kill process gracefully (SIGTERM) before forcing (SIGKILL) 18 | stopwaitsecs=10 19 | stopsignal=TERM -------------------------------------------------------------------------------- /nhl-image/ansible/files/sb_image/install_bindings.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd /home/pi/nhl-led-scoreboard 3 | 4 | #Install rgb matrix 5 | # Pull submodule and ignore changes from script 6 | git submodule update --init --recursive 7 | git config submodule.matrix.ignore all 8 | 9 | cd submodules/matrix 10 | 11 | make build-python PYTHON=/home/pi/nhlsb-venv/bin/python3 12 | make install-python PYTHON=/home/pi/nhlsb-venv/bin/python3 13 | 14 | mv bindings/python/samples/runtext.py bindings/python/samples/runtext.py.ori 15 | mv /home/pi/sbtools/runtext.py bindings/python/samples/ 16 | 17 | cd utils 18 | make led-image-viewer 19 | 20 | cp led-image-viewer /home/pi/sbtools/led-image-viewer 21 | chmod +x /home/pi/sbtools/led-image-viewer -------------------------------------------------------------------------------- /nhl-image/ansible/files/sb_image/scoreboard.conf: -------------------------------------------------------------------------------- 1 | [program:scoreboard] 2 | command=/home/pi/nhlsb-venv/bin/python3 ./src/main.py --led-gpio-mapping=adafruit-hat-pwm --led-slowdown-gpio=2 --led-rows=32 --led-cols=64 --updatecheck 3 | directory=/home/pi/nhl-led-scoreboard 4 | autostart=true 5 | autorestart=true 6 | stderr_logfile=/var/log/scoreboard.stderr.log 7 | stderr_logfile_maxbytes=1MB 8 | stderr_logfile_backups=10 9 | stdout_logfile=/var/log/scoreboard.stdout.log 10 | stdout_logfile_maxbytes=1MB 11 | stdout_logfile_backups=10 12 | 13 | # Don't restart too quickly if it keeps failing 14 | startretries=3 15 | startsecs=5 16 | 17 | # Kill process gracefully (SIGTERM) before forcing (SIGKILL) 18 | stopwaitsecs=10 19 | stopsignal=TERM -------------------------------------------------------------------------------- /.github/workflows/clean_tool_cache.yml: -------------------------------------------------------------------------------- 1 | name: Free Disk Space (Ubuntu) 2 | on: push 3 | 4 | jobs: 5 | free-disk-space: 6 | runs-on: ubuntu-latest 7 | steps: 8 | 9 | - name: Free Disk Space (Ubuntu) 10 | uses: jlumbroso/free-disk-space@main 11 | with: 12 | # this might remove tools that are actually needed, 13 | # if set to "true" but frees about 6 GB 14 | tool-cache: false 15 | 16 | # all of these default to true, but feel free to set to 17 | # "false" if necessary for your workflow 18 | android: true 19 | dotnet: true 20 | haskell: true 21 | large-packages: true 22 | docker-images: true 23 | swap-storage: true 24 | -------------------------------------------------------------------------------- /nhl-image/ansible/files/sb_image/sbtools/autoset_tz.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | #Automatically set timezone 3 | zone=$(/usr/bin/wget -O - -q http://geoip.ubuntu.com/lookup | sed -n -e 's/.*\(.*\)<\/TimeZone>.*/\1/ p') 4 | current_tz=$(/usr/bin/timedatectl show -p Timezone --value) 5 | 6 | if [ "$zone" != "" ] && [ "$zone" != "$current_tz" ] ;then 7 | #/usr/bin/timedatectl set-timezone $zone 8 | /usr/bin/raspi-config nonint do_change_timezone $zone 9 | #Check to see if the setTZ file exists and delete it 10 | #This is for when run from the autostart service on boot 11 | if [ -f "/home/pi/.nhlledportal/setTZ" ]; then 12 | rm /home/pi/.nhlledportal/setTZ 13 | fi 14 | echo "Timezone set to $zone for your location" 15 | else 16 | echo "Timezone $current_tz already set correctly for your location found($zone)" 17 | fi 18 | 19 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax = docker/dockerfile:1.3 2 | 3 | FROM mkaczanowski/packer-builder-arm:1.0.8 4 | 5 | ENV DEBIAN_FRONTEND=noninteractive 6 | 7 | RUN --mount=type=cache,target=/var/cache/apt,id=apt \ 8 | apt update \ 9 | && apt install software-properties-common gpg-agent --no-install-recommends -y \ 10 | && add-apt-repository --yes --update ppa:ansible/ansible \ 11 | && apt install ansible --no-install-recommends -y \ 12 | && ansible-galaxy collection install ansible.posix \ 13 | # install other Ansible roles here if needed 14 | && ansible-galaxy install geerlingguy.pip \ 15 | && ansible-galaxy install geerlingguy.supervisor \ 16 | && ansible-galaxy collection install community.general\ 17 | && (rm -f /var/cache/apt/archives/*.deb \ 18 | /var/cache/apt/archives/partial/*.d:wq!eb /var/cache/apt/*.bin /var/lib/apt/lists/* || true) 19 | -------------------------------------------------------------------------------- /Dockerfile_1.10.0: -------------------------------------------------------------------------------- 1 | # syntax = docker/dockerfile:1.3 2 | 3 | FROM mkaczanowski/packer-builder-arm 4 | 5 | ENV DEBIAN_FRONTEND=noninteractive 6 | 7 | 8 | RUN --mount=type=cache,target=/var/cache/apt,id=apt \ 9 | apt update \ 10 | && apt install software-properties-common gpg-agent --no-install-recommends -y \ 11 | && add-apt-repository --yes --update ppa:ansible/ansible \ 12 | && apt install ansible --no-install-recommends -y \ 13 | && ansible-galaxy collection install ansible.posix \ 14 | # install other Ansible roles here if needed 15 | && ansible-galaxy install geerlingguy.pip \ 16 | && ansible-galaxy install geerlingguy.supervisor \ 17 | && ansible-galaxy collection install community.general\ 18 | && (rm -f /var/cache/apt/archives/*.deb \ 19 | /var/cache/apt/archives/partial/*.d:wq!eb /var/cache/apt/*.bin /var/lib/apt/lists/* || true) 20 | 21 | ENTRYPOINT ["/bin/packer"] 22 | 23 | # Install the latest version of the official plugins 24 | RUN /bin/packer plugins install "github.com/hashicorp/ansible" 25 | 26 | RUN /bin/packer init -------------------------------------------------------------------------------- /nhl-image/ansible/files/sb_image/sbtools/pi_crontab.txt: -------------------------------------------------------------------------------- 1 | # Edit this file to introduce tasks to be run by cron. 2 | # 3 | # Each task to run has to be defined through a single line 4 | # indicating with different fields when the task will be run 5 | # and what command to run for the task 6 | # 7 | # To define the time you can provide concrete values for 8 | # minute (m), hour (h), day of month (dom), month (mon), 9 | # and day of week (dow) or use '*' in these fields (for 'any'). 10 | # 11 | # Notice that tasks will be started based on the cron's system 12 | # daemon's notion of time and timezones. 13 | # 14 | # Output of the crontab jobs (including errors) is sent through 15 | # email to the user the crontab file belongs to (unless redirected). 16 | # 17 | # For example, you can run a backup of all your user accounts 18 | # at 5 a.m every week with: 19 | # 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/ 20 | # 21 | # For more information see the manual pages of crontab(5) and cron(8) 22 | # 23 | # m h dom mon dow command 24 | 0 3 * * * /home/pi/sbtools/checkUpdate.sh > /home/pi/.nhlledportal/status 25 | -------------------------------------------------------------------------------- /nhl-image/ansible/files/sb_image/crontab: -------------------------------------------------------------------------------- 1 | # /etc/crontab: system-wide crontab 2 | # Unlike any other crontab you don't have to run the `crontab' 3 | # command to install the new version when you edit this file 4 | # and files in /etc/cron.d. These files also have username fields, 5 | # that none of the other crontabs do. 6 | 7 | SHELL=/bin/sh 8 | PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin 9 | 10 | # Example of job definition: 11 | # .---------------- minute (0 - 59) 12 | # | .------------- hour (0 - 23) 13 | # | | .---------- day of month (1 - 31) 14 | # | | | .------- month (1 - 12) OR jan,feb,mar,apr ... 15 | # | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat 16 | # | | | | | 17 | # * * * * * user-name command to be executed 18 | 17 * * * * root cd / && run-parts --report /etc/cron.hourly 19 | 25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily ) 20 | 47 6 * * 7 root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly ) 21 | 52 6 1 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly ) 22 | # 23 | # RaspiWiFi Startup 24 | @reboot root run-parts /etc/cron.raspiwifi/ 25 | # scoreboard version 26 | @reboot pi run-parts /etc/cron.scoreboard 27 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: publish packer-arm-ansible 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | publish-image: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: 'Checkout GitHub Action' 11 | uses: actions/checkout@v4 12 | 13 | - name: Set up QEMU 14 | uses: docker/setup-qemu-action@v3 15 | 16 | - name: Set up Docker Buildx 17 | uses: docker/setup-buildx-action@v3 18 | 19 | - name: Login to GitHub Container Registry 20 | uses: docker/login-action@v3 21 | with: 22 | registry: ghcr.io 23 | username: ${{ github.actor }} 24 | password: ${{ secrets.GITHUB_TOKEN }} 25 | 26 | #- name: Build the packer-arm-ansible image 27 | # run: | 28 | # docker build . --tag ghcr.io/falkyre/packer-arm-ansible:latest --tag ghcr.io/falkyre/packer-arm-ansible:1.0.8 29 | # docker push ghcr.io/falkyre/packer-arm-ansible:latest 30 | 31 | - name: Build and push 32 | uses: docker/build-push-action@v5 33 | with: 34 | context: . 35 | platforms: linux/amd64,linux/arm64 36 | push: true 37 | cache-from: type=gha 38 | cache-to: type=gha,mode=max 39 | tags: falkyre/packer-arm-ansible:latest 40 | -------------------------------------------------------------------------------- /nhl-image/alpine.pkr.hcl: -------------------------------------------------------------------------------- 1 | "variables" = {} 2 | 3 | "builders" = { 4 | "type" = "arm" 5 | 6 | "file_urls" = ["http://dl-cdn.alpinelinux.org/alpine/v3.12/releases/armhf/alpine-minirootfs-3.12.0-armhf.tar.gz"] 7 | 8 | "file_checksum_url" = "http://dl-cdn.alpinelinux.org/alpine/v3.12/releases/armhf/alpine-minirootfs-3.12.0-armhf.tar.gz.sha256" 9 | 10 | "file_checksum_type" = "sha256" 11 | 12 | "file_unarchive_cmd" = ["bsdtar", "-xpf", "$ARCHIVE_PATH", "-C", "$MOUNTPOINT"] 13 | 14 | "file_target_extension" = "tar.gz" 15 | 16 | "image_build_method" = "new" 17 | 18 | "image_path" = "alpine.img" 19 | 20 | "image_size" = "4G" 21 | 22 | "image_type" = "dos" 23 | 24 | "image_partitions" = { 25 | "name" = "root" 26 | 27 | "type" = "83" 28 | 29 | "start_sector" = "4096" 30 | 31 | "filesystem" = "ext4" 32 | 33 | "size" = "0" 34 | 35 | "mountpoint" = "/" 36 | } 37 | 38 | "qemu_binary_source_path" = "/usr/bin/qemu-arm-static" 39 | 40 | "qemu_binary_destination_path" = "/usr/bin/qemu-arm-static" 41 | 42 | "image_chroot_env" = ["PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/sbin:/usr/sbin"] 43 | } 44 | 45 | "provisioners" = { 46 | "type" = "shell" 47 | 48 | "inline" = ["echo 'nameserver 8.8.8.8' > /etc/resolv.conf", "apk update"] 49 | } 50 | 51 | "post-processors" = [] -------------------------------------------------------------------------------- /nhl-image/ansible/files/sb_image/sbtools/runtext.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Display a runtext with double-buffering. 3 | from samplebase import SampleBase 4 | from rgbmatrix import graphics 5 | import time 6 | 7 | 8 | class RunText(SampleBase): 9 | def __init__(self, *args, **kwargs): 10 | super(RunText, self).__init__(*args, **kwargs) 11 | self.parser.add_argument("-t", "--text", help="The text to scroll on the RGB LED panel", default="Hello world!") 12 | 13 | def run(self): 14 | offscreen_canvas = self.matrix.CreateFrameCanvas() 15 | font = graphics.Font() 16 | font.LoadFont("../../../fonts/7x13.bdf") 17 | textColor = graphics.Color(255, 255, 0) 18 | pos = offscreen_canvas.width 19 | my_text = self.args.text 20 | 21 | i = 0 22 | while i < 350: 23 | offscreen_canvas.Clear() 24 | len = graphics.DrawText(offscreen_canvas, font, pos, 20, textColor, my_text) 25 | pos -= 1 26 | if (pos + len < 0): 27 | pos = offscreen_canvas.width 28 | 29 | time.sleep(0.05) 30 | i += 1 31 | offscreen_canvas = self.matrix.SwapOnVSync(offscreen_canvas) 32 | 33 | 34 | # Main function 35 | if __name__ == "__main__": 36 | run_text = RunText() 37 | if (not run_text.process()): 38 | run_text.print_help() 39 | -------------------------------------------------------------------------------- /nhl-image/ansible/files/sb_image/supervisord.conf: -------------------------------------------------------------------------------- 1 | ; supervisor config file 2 | 3 | [unix_http_server] 4 | file=/var/run/supervisor.sock ; (the path to the socket file) 5 | chmod=0770 ; sockef file mode (default 0700) 6 | chown=pi:pi 7 | 8 | [supervisord] 9 | 10 | logfile=/var/log/supervisord.log ; (main log file;default $CWD/supervisord.log) 11 | logfile_maxbytes = 5MB 12 | logfile_backups=2 13 | loglevel = info 14 | strip_ansi = true 15 | 16 | pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid) 17 | 18 | ; the below section must remain in the config file for RPC 19 | ; (supervisorctl/web interface) to work, additional interfaces may be 20 | ; added by defining them in separate rpcinterface: sections 21 | [rpcinterface:supervisor] 22 | supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface 23 | 24 | [supervisorctl] 25 | serverurl=unix:///var/run/supervisor.sock ; use a unix:// URL for a unix socket 26 | 27 | ; The [include] section can just contain the "files" setting. This 28 | ; setting can list multiple files (separated by whitespace or 29 | ; newlines). It can also contain wildcards. The filenames are 30 | ; interpreted as relative to this file. Included files *cannot* 31 | ; include files themselves. 32 | 33 | [inet_http_server] 34 | port=*:9001 35 | 36 | [include] 37 | files = /etc/supervisor/conf.d/*.conf -------------------------------------------------------------------------------- /nhl-image/ansible/files/sb_image/sbtools/sb-help: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Show quick help for each command line tool 3 | #echo "$(tput smul)scoreboard commands:$(tput rmul) $(tput bold)sb-tools sb-issue sb-updatecheck sb-changelog sb-stderr sb-stdout sb-livelog sb-stop sb-start sb-restart sb-status sb-sysinfo$(tput sgr0)" 4 | 5 | #Set the colors 6 | 7 | NEWT_COLORS=' 8 | root=green,black 9 | border=green,black 10 | title=green,black 11 | roottext=white,black 12 | window=green,black 13 | textbox=white,black 14 | button=black,green 15 | compactbutton=white,black 16 | listbox=white,black 17 | actlistbox=black,white 18 | actsellistbox=black,green 19 | checkbox=green,black 20 | actcheckbox=black,green 21 | ' 22 | export NEWT_COLORS 23 | 24 | calc_wt_size() { 25 | # NOTE: it's tempting to redirect stderr to /dev/null, so supress error 26 | # output from tput. However in this case, tput detects neither stdout or 27 | # stderr is a tty and so only gives default 80, 24 values 28 | WT_HEIGHT=18 29 | WT_WIDTH=$(tput cols) 30 | 31 | if [ -z "$WT_WIDTH" ] || [ "$WT_WIDTH" -lt 60 ]; then 32 | WT_WIDTH=80 33 | fi 34 | if [ "$WT_WIDTH" -gt 178 ]; then 35 | WT_WIDTH=120 36 | fi 37 | WT_MENU_HEIGHT=$(($WT_HEIGHT-7)) 38 | } 39 | 40 | calc_wt_size 41 | 42 | whiptail --backtitle "NHL LED Scoreboard Command Line Utilities Help" --title "sb-help (use arrow keys to scroll)" --scrolltext --textbox /home/pi/sbtools/sb-help.txt $WT_HEIGHT $WT_WIDTH 43 | -------------------------------------------------------------------------------- /nhl-image/ansible/files/sb_image/sbtools/sb-help.txt: -------------------------------------------------------------------------------- 1 | NHL Led Scoreboard command line utilities 2 | ========================================= 3 | General Tools 4 | ------------- 5 | sb-help - This help utility 6 | sb-tools - Configuration utility used to setup scoreboard and supervisor, 7 | do troubleshooting, check for updates 8 | sb-sysinfo - prints system information like the login banner 9 | sb-resetwifi - reboots raspberry pi back to hot spot mode to setup wifi again. 10 | 11 | Update Tools 12 | ------------ 13 | sb-updatecheck - Checks installed version against github to see if new update is available 14 | sb-changelog - shows the changelog of the latest version 15 | sb-upgrade - Upgrades the nhl-led-scoreboard install to the latest from github. 16 | 17 | Troubleshooting Tools 18 | --------------------- 19 | sb-stderr - shows the last 50kb of the supervisor scoreboard stderr logfile 20 | sb-stdout - shows the last 50kb of the supervisor scoreboard stdout logfile 21 | sb-livelog - shows the live supervisor scoreboard stdout log file. Use CTRL-C to exit 22 | sb-issue - Gather config.json (with any api keys removed), system info 23 | and supervisor log files and posts to pastebin. Returns a pastebin URL 24 | 25 | Supervisor/Process Tools 26 | ------------------------- 27 | sb-stop - stops the supervisor scoreboard process 28 | sb-start - starts the supervisor scoreboard process 29 | sb-restart - basically a combination of sb-stop and sb-start in one command 30 | sb-status - shows the supervisor status of the scoreboard process 31 | -------------------------------------------------------------------------------- /nhl-image/raspios_trixie.pkr.hcl: -------------------------------------------------------------------------------- 1 | # reuse this long string 2 | variable "raspios_url" { 3 | type = string 4 | default = "https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2025-11-24/2025-11-24-raspios-trixie-arm64-lite.img.xz" 5 | } 6 | 7 | variable "sb_img" { 8 | type = string 9 | default = "rpios-scoreboard" 10 | } 11 | 12 | source "arm" "pi" { 13 | file_checksum_type = "sha256" 14 | file_checksum_url = "${var.raspios_url}.sha256" 15 | file_target_extension = "xz" 16 | file_unarchive_cmd = ["xz", "--decompress", "$ARCHIVE_PATH"] 17 | file_urls = ["${var.raspios_url}"] 18 | image_build_method = "reuse" 19 | image_chroot_env = ["PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin"] 20 | image_mount_path = "/tmp/rpi_chroot" 21 | image_partitions { 22 | filesystem = "vfat" 23 | mountpoint = "/boot" 24 | name = "boot" 25 | size = "512M" 26 | start_sector = "16384" 27 | type = "c" 28 | } 29 | image_partitions { 30 | filesystem = "ext4" 31 | mountpoint = "/" 32 | name = "root" 33 | size = "6G" 34 | start_sector = "1064960" 35 | type = "83" 36 | } 37 | image_path = "${var.sb_img}.img" 38 | image_size = "4G" 39 | image_type = "dos" 40 | qemu_binary_destination_path = "/usr/bin/qemu-arm-static" 41 | qemu_binary_source_path = "/usr/bin/qemu-arm-static" 42 | } 43 | 44 | build { 45 | sources = ["source.arm.pi"] 46 | 47 | provisioner "ansible" { 48 | extra_arguments = [ 49 | "-vvvvv", 50 | "--connection=chroot", 51 | "-e ansible_host=/tmp/rpi_chroot" 52 | ] 53 | playbook_file = "ansible/setup-raspberry-trixie.yml" 54 | } 55 | 56 | post-processor "compress" { 57 | keep_input_artifact = true 58 | compression_level = 9 59 | output = "${var.sb_img}.img.xz" 60 | } 61 | 62 | } 63 | 64 | -------------------------------------------------------------------------------- /nhl-image/raspios.pkr.hcl: -------------------------------------------------------------------------------- 1 | # reuse this long string 2 | variable "raspios_url" { 3 | type = string 4 | default = "http://downloads.raspberrypi.com/raspios_lite_armhf/images/raspios_lite_armhf-2025-05-13/2025-05-13-raspios-bookworm-armhf-lite.img.xz" 5 | #default = "https://downloads.raspberrypi.org/raspios_lite_armhf/images/raspios_lite_armhf-2025-10-02/2025-10-01-raspios-trixie-armhf-lite.img.xz" 6 | } 7 | 8 | variable "sb_img" { 9 | type = string 10 | default = "rpios-scoreboard" 11 | } 12 | 13 | source "arm" "pi" { 14 | file_checksum_type = "sha256" 15 | file_checksum_url = "${var.raspios_url}.sha256" 16 | file_target_extension = "xz" 17 | file_unarchive_cmd = ["xz", "--decompress", "$ARCHIVE_PATH"] 18 | file_urls = ["${var.raspios_url}"] 19 | image_build_method = "resize" 20 | image_chroot_env = ["PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin"] 21 | image_mount_path = "/tmp/rpi_chroot" 22 | image_partitions { 23 | filesystem = "vfat" 24 | mountpoint = "/boot" 25 | name = "boot" 26 | size = "256M" 27 | start_sector = "8192" 28 | type = "c" 29 | } 30 | image_partitions { 31 | filesystem = "ext4" 32 | mountpoint = "/" 33 | name = "root" 34 | size = "0" 35 | start_sector = "532480" 36 | type = "83" 37 | } 38 | image_path = "${var.sb_img}.img" 39 | image_size = "4G" 40 | image_type = "dos" 41 | qemu_binary_destination_path = "/usr/bin/qemu-arm-static" 42 | qemu_binary_source_path = "/usr/bin/qemu-arm-static" 43 | } 44 | 45 | build { 46 | sources = ["source.arm.pi"] 47 | 48 | provisioner "ansible" { 49 | extra_arguments = [ 50 | "-vvvvv", 51 | "--connection=chroot", 52 | "-e ansible_host=/tmp/rpi_chroot" 53 | ] 54 | playbook_file = "ansible/setup-raspberry.yml" 55 | } 56 | 57 | post-processor "compress" { 58 | keep_input_artifact = true 59 | compression_level = 9 60 | output = "${var.sb_img}.img.xz" 61 | } 62 | 63 | } 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /nhl-image/raspios-beta.pkr.hcl: -------------------------------------------------------------------------------- 1 | # reuse this long string 2 | variable "raspios_url" { 3 | type = string 4 | default = "http://downloads.raspberrypi.com/raspios_lite_armhf/images/raspios_lite_armhf-2025-05-13/2025-05-13-raspios-bookworm-armhf-lite.img.xz" 5 | #default = "https://downloads.raspberrypi.org/raspios_lite_armhf/images/raspios_lite_armhf-2025-10-02/2025-10-01-raspios-trixie-armhf-lite.img.xz" 6 | } 7 | 8 | variable "sb_img" { 9 | type = string 10 | default = "rpios-scoreboard" 11 | } 12 | 13 | source "arm" "pi" { 14 | file_checksum_type = "sha256" 15 | file_checksum_url = "${var.raspios_url}.sha256" 16 | file_target_extension = "xz" 17 | file_unarchive_cmd = ["xz", "--decompress", "$ARCHIVE_PATH"] 18 | file_urls = ["${var.raspios_url}"] 19 | image_build_method = "resize" 20 | image_chroot_env = ["PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin"] 21 | image_mount_path = "/tmp/rpi_chroot" 22 | image_partitions { 23 | filesystem = "vfat" 24 | mountpoint = "/boot" 25 | name = "boot" 26 | size = "256M" 27 | start_sector = "8192" 28 | type = "c" 29 | } 30 | image_partitions { 31 | filesystem = "ext4" 32 | mountpoint = "/" 33 | name = "root" 34 | size = "0" 35 | start_sector = "532480" 36 | type = "83" 37 | } 38 | image_path = "${var.sb_img}.img" 39 | image_size = "4G" 40 | image_type = "dos" 41 | qemu_binary_destination_path = "/usr/bin/qemu-arm-static" 42 | qemu_binary_source_path = "/usr/bin/qemu-arm-static" 43 | } 44 | 45 | build { 46 | sources = ["source.arm.pi"] 47 | 48 | provisioner "ansible" { 49 | extra_arguments = [ 50 | "-vvvvv", 51 | "--connection=chroot", 52 | "-e ansible_host=/tmp/rpi_chroot" 53 | ] 54 | playbook_file = "ansible/setup-raspberry-beta.yml" 55 | } 56 | 57 | post-processor "compress" { 58 | keep_input_artifact = true 59 | compression_level = 9 60 | output = "${var.sb_img}.img.xz" 61 | } 62 | 63 | } 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /nhl-image/ansible/files/sb_image/bash.bashrc: -------------------------------------------------------------------------------- 1 | # System-wide .bashrc file for interactive bash(1) shells. 2 | 3 | # To enable the settings / commands in this file for login shells as well, 4 | # this file has to be sourced in /etc/profile. 5 | 6 | # If not running interactively, don't do anything 7 | [ -z "$PS1" ] && return 8 | 9 | # check the window size after each command and, if necessary, 10 | # update the values of LINES and COLUMNS. 11 | shopt -s checkwinsize 12 | 13 | # set variable identifying the chroot you work in (used in the prompt below) 14 | if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then 15 | debian_chroot=$(cat /etc/debian_chroot) 16 | fi 17 | 18 | # set a fancy prompt (non-color, overwrite the one in /etc/profile) 19 | # but only if not SUDOing and have SUDO_PS1 set; then assume smart user. 20 | if ! [ -n "${SUDO_USER}" -a -n "${SUDO_PS1}" ]; then 21 | PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ ' 22 | fi 23 | 24 | # Commented out, don't overwrite xterm -T "title" -n "icontitle" by default. 25 | # If this is an xterm set the title to user@host:dir 26 | #case "$TERM" in 27 | #xterm*|rxvt*) 28 | # PROMPT_COMMAND='echo -ne "\033]0;${USER}@${HOSTNAME}: ${PWD}\007"' 29 | # ;; 30 | #*) 31 | # ;; 32 | #esac 33 | 34 | # enable bash completion in interactive shells 35 | #if ! shopt -oq posix; then 36 | # if [ -f /usr/share/bash-completion/bash_completion ]; then 37 | # . /usr/share/bash-completion/bash_completion 38 | # elif [ -f /etc/bash_completion ]; then 39 | # . /etc/bash_completion 40 | # fi 41 | #fi 42 | 43 | # if the command-not-found package is installed, use it 44 | if [ -x /usr/lib/command-not-found -o -x /usr/share/command-not-found/command-not-found ]; then 45 | function command_not_found_handle { 46 | # check because c-n-f could've been removed in the meantime 47 | if [ -x /usr/lib/command-not-found ]; then 48 | /usr/lib/command-not-found -- "$1" 49 | return $? 50 | elif [ -x /usr/share/command-not-found/command-not-found ]; then 51 | /usr/share/command-not-found/command-not-found -- "$1" 52 | return $? 53 | else 54 | printf "%s: command not found\n" "$1" >&2 55 | return 127 56 | fi 57 | } 58 | fi 59 | for i in /etc/bashrc.d/*.sh /etc/bashrc.d/*.bash; do [ -r "$i" ] && . $i; done; unset i 60 | -------------------------------------------------------------------------------- /nhl-image/ansible/files/zram-config/ztab: -------------------------------------------------------------------------------- 1 | # Use '#' to comment out any line, add new drives with the first column 2 | # providing the drive type and then drive details separated by tab characters. 3 | # 4 | # All algorithms in '/proc/crypto' are supported but only lzo, lzo-rle, lz4, and 5 | # zstd have zramctl text strings; lz4 is the fastest with zstd having much 6 | # better text compression. 7 | # 8 | # mem_limit is the compressed memory limit and will set a hard memory limit for 9 | # the system admin. 10 | # 11 | # disk_size is the virtual uncompressed size approx. 220-450% of memory 12 | # allocated depending on the algorithm and input file. Don't make it much higher 13 | # than the compression algorithm is capable of as it will waste memory because 14 | # there is a ~0.1% memory overhead when empty 15 | # 16 | # swap_priority will set ZRAM over alternative swap devices. 17 | # 18 | # page-cluster 0 means tuning to singular pages rather than the default 3 which 19 | # caches 8 for HDD tuning, which can lower latency. 20 | # 21 | # swappiness 80 because the improved performance of ZRAM allows more usage 22 | # without any adverse affects from the default of 60. It can be raised up to 100 23 | # but that will increase process queue on intense loads such as boot time. 24 | # 25 | # target_dir is the directory you wish to hold in ZRAM, and the original will be 26 | # moved to a bind mount 'bind_dir' and is synchronized on start, stop, and write 27 | # commands. 28 | # 29 | # bind_dir is the directory where the original directory will be mounted for 30 | # sync purposes. Usually in '/opt' or '/var', name optional. 31 | # 32 | # oldlog_dir will enable log-rotation to an off device directory while retaining 33 | # only live logs in ZRAM. Usually in '/opt' or '/var', name optional. 34 | # 35 | # If you need multiple ZRAM swaps or ZRAM directories, just create another entry 36 | # in this file. 37 | # To do this safely, first stop ZRAM using 'zram-config "stop"', then edit this 38 | # file. 39 | # Once finished, restart ZRAM using 'systemctl restart zram-config.service'. 40 | 41 | # swap alg mem_limit disk_size swap_priority page-cluster swappiness 42 | swap lzo-rle 250M 750M 75 0 80 43 | 44 | # dir alg mem_limit disk_size target_dir bind_dir 45 | dir lzo-rle 350M 650M /home/pi /pi.bind 46 | 47 | # log alg mem_limit disk_size target_dir bind_dir oldlog_dir 48 | log lzo-rle 50M 150M /var/log /log.bind /opt/zram/oldlog 49 | -------------------------------------------------------------------------------- /nhl-image/BUILD.md: -------------------------------------------------------------------------------- 1 | # How to build the NHL Led Scoreboard raspberry pi OS image locally 2 | ## To update the packer arm image to include ansible 3 | 4 | See the [Dockerfile](https://github.com/falkyre/nhl-led-scoreboard-img/blob/packer/Dockerfile) 5 | 6 | ``` 7 | docker image build --build-arg BUILDKIT_INLINE_CACHE=1 --progress=plain -t packer-builder-arm-ansible:vlatest . 8 | ``` 9 | 10 | There are two things required for the build. You need to have a defined packer build configuration. We are using the Hasicorp HCL2 language for this. See the [raspios.pkr.hcl](https://github.com/falkyre/nhl-led-scoreboard-img/blob/packer/nhl-image/raspios.pkr.hcl) 11 | 12 | Also required is the ansible playbook, this is defined in the provisoner "ansible" section. The playbook is where the magic occurs for installing everything the image needs to run the scoreboard software. See the [playbook](https://github.com/falkyre/nhl-led-scoreboard-img/blob/packer/nhl-image/ansible/setup-raspberry.yml) 13 | 14 | Once you have created the above image, you can now run your build. 15 | 16 | ``` 17 | docker run -e TZ=America/Winnipeg --rm -it --privileged -v /dev:/dev -v ${PWD}:/build packer-builder-arm-ansible:vlatest build raspios.pkr.hcl 18 | ``` 19 | 20 | If you want to "test" your image without burning to an SD card and raspberry pi, you can use this docker image to mount the raspberry pi image and launch bash on it. This uses Qemu to emulate the raspberry pi arm chip. If you are running on a Mac M series chip (Apple Silicon), make sure that you are using the Rosetta Emulation in the Docker Desktop for this to run properly. At this time, it does not appear that Podman is able to run the emulator. 21 | 22 | ``` 23 | docker run --rm -it --privileged -v ${PWD}/rpios-scoreboard-V1.6.12.img:/usr/rpi/rpi.img -w /usr/rpi ryankurte/docker-rpi-emu:latest ./run.sh rpi.img /bin/bash 24 | ``` 25 | You can use this to ensure that everything has been installed in the proper locations and even test some commands (but not the actual scoreboard code) 26 | 27 | To speed image builds up, there are two proxy programs used. One, apt-cacher-ng, is used to cache the OS packages. The other is called proxpi and this caches PyPi python packages. In the ansible playbook, they are defined under the variables section at the top of the playbook. Once these were added to the build, the build time for the image dropped from about 1 hour down to about 15 minutes. 28 | 29 | [apt-cacher-ng](https://github.com/sameersbn/docker-apt-cacher-ng) 30 | [proxpi](https://github.com/EpicWink/proxpi) 31 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Create Release - Image 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | workflow_dispatch: 8 | inputs: 9 | run_type: 10 | description: 'Run type: "Test" or "Release"' 11 | required: true 12 | default: 'Test' 13 | type: choice 14 | options: 15 | - Test 16 | - Release 17 | hcl_file: 18 | description: 'HCL file to use' 19 | required: true 20 | default: 'raspios_trixie.pkr.hcl' 21 | type: choice 22 | options: 23 | - raspios.pkr.hcl 24 | - raspios-beta.pkr.hcl 25 | - raspios_trixie.pkr.hcl 26 | - raspios_trixie_3264.hcl 27 | - alpine.pkr.hcl 28 | 29 | jobs: 30 | build: 31 | 32 | runs-on: ubuntu-latest 33 | 34 | steps: 35 | - uses: actions/checkout@v4 36 | 37 | - name: Get the version (git tag) 38 | uses: maltoze/get-version-action@v1 39 | id: get_version 40 | 41 | - name: Set HCL file 42 | run: | 43 | if [ "${{ github.event_name }}" == "push" ]; then 44 | echo "HCL_FILE=raspios.pkr.hcl" >> $GITHUB_ENV 45 | else 46 | echo "HCL_FILE=${{ github.event.inputs.hcl_file }}" >> $GITHUB_ENV 47 | fi 48 | 49 | - name: Login to GitHub Container Registry 50 | uses: docker/login-action@v3 51 | with: 52 | registry: ghcr.io 53 | username: ${{ github.actor }} 54 | password: ${{ secrets.GITHUB_TOKEN }} 55 | 56 | - name: Build scoreboard image 57 | run: | 58 | docker run --rm --privileged -v /dev:/dev -v ${PWD}/nhl-image:/build ghcr.io/falkyre/packer-arm-ansible:latest build $HCL_FILE 59 | du -h nhl-image/rpios-scoreboard.img 60 | du -h --apparent-size nhl-image/rpios-scoreboard.img 61 | 62 | - name: Rename compressed image to include version 63 | run: | 64 | cp nhl-image/rpios-scoreboard.img.xz rpios-scoreboard-${{ steps.get_version.outputs.version }}.img.xz 65 | 66 | - name: Create changelog text 67 | id: changelog 68 | uses: loopwerk/tag-changelog@v1 69 | with: 70 | token: ${{ secrets.GITHUB_TOKEN }} 71 | exclude_types: other,doc,chore 72 | 73 | - name: Upload image artifact for testing 74 | if: github.event.inputs.run_type == 'Test' 75 | uses: actions/upload-artifact@v4 76 | with: 77 | name: rpios-scoreboard-${{ steps.get_version.outputs.version }}.img 78 | path: rpios-scoreboard-${{ steps.get_version.outputs.version }}.img.xz 79 | 80 | - name: GH Release 81 | if: github.event.inputs.run_type == 'Release' || github.event_name == 'push' 82 | uses: softprops/action-gh-release@v0.1.15 83 | with: 84 | prerelease: true 85 | generate_release_notes: true 86 | body: ${{ steps.changelog.outputs.changes }} 87 | files: rpios-scoreboard-${{ steps.get_version.outputs.version }}.img.xz 88 | -------------------------------------------------------------------------------- /nhl-image/ansible/files/sb_image/scoreboard.bash: -------------------------------------------------------------------------------- 1 | #Set the colors 2 | 3 | NEWT_COLORS=' 4 | root=green,black 5 | border=green,black 6 | title=green,black 7 | roottext=white,black 8 | window=green,black 9 | textbox=white,black 10 | button=black,green 11 | compactbutton=white,black 12 | listbox=white,black 13 | actlistbox=black,white 14 | actsellistbox=black,green 15 | checkbox=green,black 16 | actcheckbox=black,green 17 | ' 18 | export NEWT_COLORS 19 | 20 | 21 | if [ -t 1 ]; then 22 | RAINBOW=( 23 | "$(printf '\033[38;5;196m')" 24 | "$(printf '\033[38;5;202m')" 25 | "$(printf '\033[38;5;226m')" 26 | "$(printf '\033[38;5;082m')" 27 | "$(printf '\033[38;5;021m')" 28 | "$(printf '\033[38;5;093m')" 29 | "$(printf '\033[38;5;163m')" 30 | ) 31 | 32 | RED=$(printf '\033[31m') 33 | GREEN=$(printf '\033[32m') 34 | YELLOW=$(printf '\033[33m') 35 | BLUE=$(printf '\033[34m') 36 | BOLD=$(printf '\033[1m') 37 | DIM=$(printf '\033[2m') 38 | UNDER=$(printf '\033[4m') 39 | RESET=$(printf '\033[m') 40 | fi 41 | 42 | # Aliases 43 | # - sudo alias that allows running other aliases with "sudo": https://github.com/MichaIng/DietPi/issues/424 44 | alias sudo='sudo ' 45 | # - Scoreboard programs 46 | alias sb-help='/home/pi/sbtools/sb-help' 47 | alias sb-issue='/home/pi/sbtools/issueUpload.sh 2>1' 48 | alias sb-updatecheck='/home/pi/sbtools/checkUpdate.sh' 49 | alias sb-stderr='supervisorctl tail -50000 scoreboard stderr' 50 | alias sb-stdout='supervisorctl tail -50000 scoreboard' 51 | alias sb-livelog='supervisorctl tail -f scoreboard' 52 | alias sb-stop='supervisorctl stop scoreboard' 53 | alias sb-start='supervisorctl start scoreboard' 54 | alias sb-restart='supervisorctl restart scoreboard' 55 | alias sb-status='supervisorctl status scoreboard' 56 | alias sb-tools='/home/pi/sbtools/sb-tools' 57 | alias sb-changelog='cd /home/pi/nhl-led-scoreboard;latest=$(git tag --sort=-v:refname | head -1);previous=$(git tag --sort=-v:refname | head -2 | tail -1);echo "$(tput bold)$(tput smul)Changes since $previous$(tput sgr0)";git log --oneline --decorate $previous..$latest;cd ~' 58 | alias sb-sysinfo='neofetch --off' 59 | alias sb-upgrade='/home/pi/sbtools/sb-upgrade' 60 | alias sb-resetwifi='sudo /usr/sbin/comitup-cli d' 61 | 62 | #Output sysinfo only if not the root user 63 | 64 | if [ "$EUID" -ne 0 ]; then 65 | # Add in first run to force running the sb-tools to setup the board config 66 | if [ -f /home/pi/.nhlledportal/SETUP ]; then 67 | whiptail --msgbox "Welcome to the nhl-led-scoreboard initial setup. You will be asked to select a team and your board size for initial configuration.\n\nThis configuration will reboot after you do your setup.\n\nYou can do a more complex setup after by using the /home/pi/nhl-led-scoreboard/nhl_setup app after the reboot" 20 60 1 68 | /home/pi/sbtools/sb-tools do_firstrun 69 | fi 70 | neofetch --off 71 | #Check to see if there is an UPDATE and if there is, ask the user if they want to run it 72 | status=$(cat /home/pi/.nhlledportal/status) 73 | if [[ $status == *"New"* ]]; then 74 | while true; do 75 | read -p "$(tput bold)$(tput setaf 2)$(tput smso)$status $(tput rmso) Upgrade? (y/n)" yn 76 | case $yn in 77 | [Yy]* ) /home/pi/sbtools/sb-upgrade; break;; 78 | [Nn]* ) echo "You can update manually using the sb-upgrade tool"; break;; 79 | * ) echo "Please answer yes or no.";; 80 | esac 81 | done 82 | fi 83 | export PATH=/home/pi/nhlsb-venv/bin:$PATH 84 | fi 85 | 86 | export DIRENV_LOG_FORMAT= 87 | eval "$(direnv hook bash)" -------------------------------------------------------------------------------- /nhl-image/ansible/files/comitup-conf/comitup.conf: -------------------------------------------------------------------------------- 1 | # 2 | # Comitup configuration 3 | # 4 | 5 | 6 | # ap_name 7 | # 8 | # This defines the name used for the AP hotspot, and for the ZeroConf 9 | # host name. The "" string, if present, will be replaced with an 10 | # instance-unique, persistent number of the same length. There may be 11 | # up to four "n's" in the string. Similarly, the string "" 12 | # is replaced with the system hostname. 13 | # 14 | # For ZeroConf Service discovery to work properly, this should resolve to an 15 | # ASCII string with no spaces or special characters. 16 | # 17 | ap_name: - 18 | 19 | # ap_password 20 | # 21 | # If an ap_password is defined, then the AP hotspot is configured with 22 | # "infrastructure WPA-psk" authentication, requiring this password 23 | # to connect. The password must be between 8 and 63 characters. You 24 | # should reboot after changing this value. 25 | # 26 | # ap_password: supersecretpassword 27 | 28 | # web_service 29 | # 30 | # The name of a systemd service to be disabled when comitup is managing a 31 | # hotspot, and enabled when there is a normal wifi connection. 32 | # 33 | # Note that the service supplied here should be disabled in systemctl 34 | # (e.g. "systemctl disable apache2.service") so that it is managed solely 35 | # by comitup. 36 | # 37 | # web_service: httpd.service 38 | 39 | 40 | # service_name 41 | # 42 | # The mdns service name to advertise as. Will be merged with "._tcp" to create the 43 | # full string. (e.g. "_comitup._tcp") 44 | # 45 | service_name: scoreboard 46 | 47 | # enable_appliance_mode 48 | # 49 | # If enabled (true), and if two wifi adapters are available, comitup will 50 | # maintain the comitup- hotspot on the first, and make other AP 51 | # connections on the second. IP forwarding and NAT are enabled, so that 52 | # hosts on the comitup hotspot will be able to access external networks. 53 | # 54 | # enable_appliance_mode: true 55 | 56 | # external_callback 57 | # 58 | # An external script that is called on comitup state changes. It will 59 | # include a single argument, either 'HOTSPOT', 'CONNECTING', or 60 | # 'CONNECTED'. 61 | # 62 | # The script must be executable. It will be run with the permissions of the 63 | # owning user. 64 | # 65 | # external_callback: /usr/local/bin/comitup-callback 66 | 67 | # primary_wifi_device 68 | # 69 | # By default, the first wifi device returned by NetworkManager is used as 70 | # the primary wifi device. This allows you to override this choice. 71 | # The primary device is used to spawn the access point. 72 | # 73 | # primary_wifi_device: wlan0 74 | 75 | # verbose 76 | # 77 | # Enable verbose logging in /var/log/comitup.log. 78 | # 79 | verbose: 0 80 | 81 | # enable_nuke 82 | # 83 | # If true, Comitup will monitor, on a Raspberry Pi, for a short between pins 39 84 | # and 40. If the short is maintained for 3 seconds, the device will delete all 85 | # defined WiFi connections, flash the board LED 3 times, and restart Comitup. 86 | # 87 | # enable_nuke: 0 88 | 89 | # ipv6_link_local 90 | # 91 | # Typically, IPv4 addresses assigned by ISPs involve Network Address 92 | # Translation. This offers some protection to devices, since such addresses are 93 | # not routable from the Internet by default. This is not the case for IPv6. In 94 | # this case, extra steps often need to be taken to protect the device. This 95 | # option can force Comitup to configure IPv6 on upstream WiFi connections to be 96 | # configured only with link-local IPv6 addresses, making them inaccessible from 97 | # the Internet for that protocol. 98 | # 99 | # ipv6_link_local: 1 100 | -------------------------------------------------------------------------------- /nhl-image/raspios_trixie_3264.hcl: -------------------------------------------------------------------------------- 1 | # reuse this long string 2 | variable "raspios_url_32" { 3 | type = string 4 | default = "https://downloads.raspberrypi.org/raspios_lite_armhf/images/raspios_lite_armhf-2025-11-24/2025-11-24-raspios-trixie-armhf-lite.img.xz" 5 | } 6 | 7 | variable "raspios_url_64" { 8 | type = string 9 | default = "https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2025-11-24/2025-11-24-raspios-trixie-arm64-lite.img.xz" 10 | } 11 | 12 | variable "sb_img" { 13 | type = string 14 | default = "rpios-scoreboard" 15 | } 16 | 17 | source "arm" "pi" { 18 | file_checksum_type = "sha256" 19 | file_checksum_url = "${var.raspios_url_32}.sha256" 20 | file_target_extension = "xz" 21 | file_unarchive_cmd = ["xz", "--decompress", "$ARCHIVE_PATH"] 22 | file_urls = ["${var.raspios_url_32}"] 23 | image_build_method = "reuse" 24 | image_chroot_env = ["PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin"] 25 | image_mount_path = "/tmp/rpi_chroot" 26 | image_partitions { 27 | filesystem = "vfat" 28 | mountpoint = "/boot" 29 | name = "boot" 30 | size = "512M" 31 | start_sector = "16384" 32 | type = "c" 33 | } 34 | image_partitions { 35 | filesystem = "ext4" 36 | mountpoint = "/" 37 | name = "root" 38 | size = "0" 39 | start_sector = "1064960" 40 | type = "83" 41 | } 42 | image_path = "${var.sb_img}_32.img" 43 | image_size = "4G" 44 | image_type = "dos" 45 | qemu_binary_destination_path = "/usr/bin/qemu-arm-static" 46 | qemu_binary_source_path = "/usr/bin/qemu-arm-static" 47 | } 48 | 49 | source "arm" "pi64" { 50 | file_checksum_type = "sha256" 51 | file_checksum_url = "${var.raspios_url_64}.sha256" 52 | file_target_extension = "xz" 53 | file_unarchive_cmd = ["xz", "--decompress", "$ARCHIVE_PATH"] 54 | file_urls = ["${var.raspios_url_64}"] 55 | image_build_method = "reuse" 56 | image_chroot_env = ["PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin"] 57 | image_mount_path = "/tmp/rpi_chroot" 58 | image_partitions { 59 | filesystem = "vfat" 60 | mountpoint = "/boot" 61 | name = "boot" 62 | size = "256M" 63 | start_sector = "8192" 64 | type = "c" 65 | } 66 | image_partitions { 67 | filesystem = "ext4" 68 | mountpoint = "/" 69 | name = "root" 70 | size = "0" 71 | start_sector = "532480" 72 | type = "83" 73 | } 74 | image_path = "${var.sb_img}_64.img" 75 | image_size = "4G" 76 | image_type = "dos" 77 | qemu_binary_destination_path = "/usr/bin/qemu-aarch64-static" 78 | qemu_binary_source_path = "/usr/bin/qemu-aarch64-static" 79 | } 80 | 81 | build { 82 | sources = ["source.arm.pi", "source.arm.pi64"] 83 | 84 | provisioner "ansible" { 85 | extra_arguments = [ 86 | "-vvvvv", 87 | "--connection=chroot", 88 | "-e ansible_host=/tmp/rpi_chroot" 89 | ] 90 | playbook_file = "ansible/setup-raspberry-trixie.yml" 91 | } 92 | 93 | post-processor "compress" { 94 | only = ["source.arm.pi"] 95 | keep_input_artifact = true 96 | compression_level = 9 97 | output = "${var.sb_img}_32.img.xz" 98 | } 99 | 100 | post-processor "compress" { 101 | only = ["source.arm.pi64"] 102 | keep_input_artifact = true 103 | compression_level = 9 104 | output = "${var.sb_img}_64.img.xz" 105 | } 106 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | **/.idea/ 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | pip-wheel-metadata/ 26 | share/python-wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | MANIFEST 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .nox/ 46 | .coverage 47 | .coverage.* 48 | .cache 49 | nosetests.xml 50 | coverage.xml 51 | *.cover 52 | *.py,cover 53 | .hypothesis/ 54 | .pytest_cache/ 55 | 56 | # Translations 57 | *.mo 58 | *.pot 59 | 60 | # Django stuff: 61 | *.log 62 | local_settings.py 63 | db.sqlite3 64 | db.sqlite3-journal 65 | 66 | # Flask stuff: 67 | instance/ 68 | .webassets-cache 69 | 70 | # Scrapy stuff: 71 | .scrapy 72 | 73 | # Sphinx documentation 74 | docs/_build/ 75 | 76 | # PyBuilder 77 | target/ 78 | 79 | # Jupyter Notebook 80 | .ipynb_checkpoints 81 | 82 | # IPython 83 | profile_default/ 84 | ipython_config.py 85 | 86 | # pyenv 87 | .python-version 88 | 89 | # pipenv 90 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 91 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 92 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 93 | # install all needed dependencies. 94 | #Pipfile.lock 95 | 96 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 97 | __pypackages__/ 98 | 99 | # Celery stuff 100 | celerybeat-schedule 101 | celerybeat.pid 102 | 103 | # SageMath parsed files 104 | *.sage.py 105 | 106 | # Environments 107 | .env 108 | .venv 109 | env/ 110 | venv/ 111 | ENV/ 112 | env.bak/ 113 | venv.bak/ 114 | 115 | # Spyder project settings 116 | .spyderproject 117 | .spyproject 118 | 119 | # Rope project settings 120 | .ropeproject 121 | 122 | # mkdocs documentation 123 | /site 124 | 125 | # mypy 126 | .mypy_cache/ 127 | .dmypy.json 128 | dmypy.json 129 | 130 | # Pyre type checker 131 | .pyre/ 132 | 133 | #nhl-led-scoreboard 134 | config/config.json 135 | config/*.config.json 136 | config/config.json.backup 137 | config/location.json 138 | 139 | # RGBMatrixEmulator 140 | emulator_config.json 141 | 142 | # ignore generated logos 143 | assets/logos/* 144 | !assets/logos/_local 145 | 146 | # VScode 147 | .vscode/* 148 | !.vscode/settings.json 149 | !.vscode/tasks.json 150 | !.vscode/launch.json 151 | !.vscode/extensions.json 152 | *.code-workspace 153 | 154 | # Local History for Visual Studio Code 155 | .history/ 156 | 157 | # Testing file for the nhl api 158 | src/nhl_api_testing.py 159 | 160 | # board developement and testing folder 161 | src/renderer/boarddev 162 | 163 | # Custom_logos to overwrite the standard one 164 | config/custom_logos.json 165 | .vscode/settings.json 166 | 167 | #packer related directories 168 | .packer* 169 | 170 | # It's better to unpack these files and commit the raw source because 171 | # git has its own built in compression methods. 172 | *.7z 173 | *.jar 174 | *.rar 175 | *.zip 176 | *.gz 177 | *.gzip 178 | *.tgz 179 | *.bzip 180 | *.bzip2 181 | *.bz2 182 | *.xz 183 | *.lzma 184 | *.cab 185 | *.xar 186 | 187 | # Packing-only formats 188 | *.iso 189 | *.tar 190 | *.img 191 | 192 | # Package management formats 193 | *.dmg 194 | *.xpi 195 | *.gem 196 | *.egg 197 | *.deb 198 | *.rpm 199 | *.msi 200 | *.msm 201 | *.msp 202 | *.txz 203 | !scoreboard_ui.tar.gz 204 | .ansible/* 205 | -------------------------------------------------------------------------------- /nhl-image/ansible/files/zram-config/install.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | BASEDIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 4 | OS="$(grep -o '^ID=.*$' /etc/os-release | cut -d'=' -f2)" 5 | 6 | if [[ "$(id -u)" -ne 0 ]]; then 7 | echo "ERROR: You need to be ROOT (sudo can be used)." 8 | exit 1 9 | fi 10 | if [[ $1 == "sync" ]]; then 11 | echo "Installing zram sync service" 12 | if [[ "$OS" == "alpine" ]]; then 13 | install -m 755 "$BASEDIR"/service/OpenRC/zsync.cron /etc/periodic/daily/zsync 14 | else 15 | install -m 644 "$BASEDIR"/service/SystemD/zsync.timer /etc/systemd/system/ 16 | install -m 644 "$BASEDIR"/service/SystemD/zsync.service /etc/systemd/system/ 17 | systemctl daemon-reload 18 | systemctl enable --now zsync.timer 19 | fi 20 | echo "##### zsync service is now installed #####" 21 | exit 0 22 | fi 23 | if [[ "$(systemctl is-active zram-config.service 2> /dev/null)" == "active" ]] || rc-service zram-config status 2> /dev/null; then 24 | echo -e "ERROR: zram-config service is still running.\\nPlease run \"sudo ${BASEDIR}/update.bash\" to update zram-config instead." 25 | exit 1 26 | fi 27 | if [[ -s /usr/local/sbin/zram-config ]] || [[ -s /usr/sbin/zram-config ]]; then 28 | echo -e "ERROR: zram-config is already installed.\\nPlease run \"sudo ${BASEDIR}/update.bash\" to update zram-config instead." 29 | exit 1 30 | fi 31 | 32 | if [[ $OS == "alpine" ]] && ! [[ "$(apk info 2> /dev/null | grep -E '^(gcc|make|fts-dev|linux-headers|util-linux-misc|musl-dev)' | tr '\n' ' ')" == "fts-dev gcc make util-linux-misc musl-dev linux-headers " ]]; then 33 | echo "Installing needed packages (gcc, make, fts-dev, linux-headers, util-linux-misc, musl-dev)" 34 | apk add gcc make fts-dev linux-headers util-linux-misc musl-dev || exit 1 35 | elif ! dpkg -s 'gcc' 'make' 'libc6-dev' &> /dev/null; then 36 | echo "Installing needed packages (gcc, make, libc6-dev)" 37 | apt-get install --yes gcc make libc6-dev || exit 1 38 | fi 39 | 40 | UBUNTU_VERSION="$(grep -o '^VERSION_ID=.*$' /etc/os-release | cut -d'=' -f2 | tr -d '"')" 41 | if [[ $OS == "ubuntu" ]] && [[ $(bc -l <<< "$UBUNTU_VERSION >= 21.10") -eq 1 ]]; then 42 | echo "Installing zram module package for Ubuntu (linux-modules-extra-raspi)" 43 | if ! dpkg -s 'linux-modules-extra-raspi' &> /dev/null; then 44 | apt-get install --yes linux-modules-extra-raspi || exit 1 45 | fi 46 | fi 47 | 48 | make --always-make --directory="${BASEDIR}/overlayfs-tools" 49 | 50 | echo "Installing zram-config files" 51 | if [[ "$OS" == "alpine" ]]; then 52 | install -m 755 "$BASEDIR"/zram-config /usr/sbin/ 53 | install -m 755 "$BASEDIR"/service/OpenRC/zram-config.openrc /etc/init.d/zram-config 54 | else 55 | install -m 755 "$BASEDIR"/zram-config /usr/local/sbin/ 56 | install -m 644 "$BASEDIR"/service/SystemD/zram-config.service /etc/systemd/system/zram-config.service 57 | echo "ReadWritePaths=/usr/local/share/zram-config/log" >> /lib/systemd/system/logrotate.service 58 | fi 59 | install -m 644 "$BASEDIR"/ztab /etc/ztab 60 | mkdir -p /usr/local/share/zram-config/log 61 | ln -s /usr/local/share/zram-config/log /var/log/zram-config 62 | install -m 755 "$BASEDIR"/uninstall.bash /usr/local/share/zram-config/uninstall.bash 63 | install -m 644 "$BASEDIR"/service/zram-config.logrotate /etc/logrotate.d/zram-config 64 | mkdir -p /usr/local/lib/zram-config/ 65 | install -m 755 "$BASEDIR"/overlayfs-tools/overlay /usr/local/lib/zram-config/overlay 66 | 67 | echo "Set u zram-service to start on boot" 68 | if [[ "$OS" == "alpine" ]]; then 69 | rc-update add zram-config boot 70 | #rc-service zram-config start 71 | else 72 | #systemctl daemon-reload 73 | systemctl enable --now zram-config.service 74 | #until [[ $(systemctl show -p SubState --value zram-config) == "exited" ]]; do 75 | # sleep 5 76 | #done 77 | fi 78 | 79 | echo "##### zram-config is now installed and running #####" 80 | echo "##### edit /etc/ztab to configure options #####" 81 | -------------------------------------------------------------------------------- /.github/workflows/notify_discord.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Notify - Discord 4 | 5 | # Controls when the action will run. 6 | on: 7 | workflow_dispatch: 8 | inputs: 9 | public: 10 | type: boolean 11 | required: true 12 | description: 'Notify on Scoreboard discord?' 13 | use_version: 14 | type: boolean 15 | required: true 16 | description: 'Use VERSION file from repository for version and detail of release?' 17 | version: 18 | description: 'Version Release' 19 | required: true 20 | default: 'v1.6.0' 21 | detail: 22 | description: 'Extra Notes' 23 | required: false 24 | default: 'Excelsior!' 25 | 26 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 27 | jobs: 28 | # This workflow contains a single job called "build" 29 | Notify-Discord: 30 | # The type of runner that the job will run on 31 | runs-on: ubuntu-latest 32 | 33 | # Steps represent a sequence of tasks that will be executed as part of the job 34 | steps: 35 | - name: Get repo 36 | uses: actions/checkout@v4 37 | 38 | - name: Get version and notes from VERSION file 39 | id: release_event 40 | if: ${{ github.event.inputs.use_version == 'true' }} 41 | run: | 42 | version=$(cat nhl-image/ansible/VERSION| grep version | awk -F':' '{print $2}') 43 | detail="$(cat nhl-image/ansible/VERSION| grep detail | awk -F':' '{print $2}')" 44 | echo "version=$version" >> $GITHUB_OUTPUT 45 | echo "detail=$detail" >> $GITHUB_OUTPUT 46 | shell: bash 47 | 48 | 49 | - uses: sarisia/actions-status-discord@v1 50 | if: ${{ github.event.inputs.use_version == 'false' }} 51 | with: 52 | webhook: ${{ secrets.DISCORD_WEBHOOK }} 53 | nodetail: true 54 | title: New version of `NHL LED Scoreboard image` is ready! 55 | description: | 56 | Version `${{ github.event.inputs.version }}` 57 | Click [here](https://github.com/falkyre/nhl-led-scoreboard-img/releases/latest) to download! 58 | ---- 59 | Extra Notes: 60 | ${{ github.event.inputs.detail }} 61 | 62 | - uses: sarisia/actions-status-discord@v1 63 | if: ${{ github.event.inputs.use_version == 'true' }} 64 | with: 65 | webhook: ${{ secrets.DISCORD_WEBHOOK }} 66 | nodetail: true 67 | title: New version of `NHL LED Scoreboard image` is ready! 68 | description: | 69 | Version `${{ steps.release_event.outputs.version }}` 70 | Click [here](https://github.com/falkyre/nhl-led-scoreboard-img/releases/latest) to download! 71 | ---- 72 | Extra Notes: 73 | ${{ steps.release_event.outputs.detail }} 74 | 75 | 76 | - uses: sarisia/actions-status-discord@v1 77 | if: ${{ github.event.inputs.use_version == 'true' }} && ${{ github.event.inputs.public == 'true' }} 78 | with: 79 | webhook: ${{ secrets.NHLDISCORD_WEBHOOK }} 80 | nodetail: true 81 | title: New version of `NHL LED Scoreboard image` is ready! 82 | description: | 83 | Version `${{ steps.release_event.outputs.version }}` 84 | Click [here](https://github.com/falkyre/nhl-led-scoreboard-img/releases/latest) to download! 85 | ---- 86 | Extra Notes: 87 | ${{ steps.release_event.outputs.detail }} 88 | 89 | - uses: sarisia/actions-status-discord@v1 90 | if: ${{ github.event.inputs.use_version == 'false' }} && ${{ github.event.inputs.public == 'true' }} 91 | with: 92 | webhook: ${{ secrets.NHLDISCORD_WEBHOOK }} 93 | nodetail: true 94 | title: New version of `NHL LED Scoreboard image` is ready! 95 | description: | 96 | Version `${{ github.event.inputs.version }}` 97 | Click [here](https://github.com/falkyre/nhl-led-scoreboard-img/releases/latest) to download! 98 | ---- 99 | Extra Notes: 100 | ${{ github.event.inputs.detail }} 101 | -------------------------------------------------------------------------------- /nhl-image/ansible/files/sb_image/.bashrc: -------------------------------------------------------------------------------- 1 | # ~/.bashrc: executed by bash(1) for non-login shells. 2 | # see /usr/share/doc/bash/examples/startup-files (in the package bash-doc) 3 | # for examples 4 | 5 | # If not running interactively, don't do anything 6 | case $- in 7 | *i*) ;; 8 | *) return;; 9 | esac 10 | 11 | # don't put duplicate lines or lines starting with space in the history. 12 | # See bash(1) for more options 13 | HISTCONTROL=ignoreboth 14 | 15 | # append to the history file, don't overwrite it 16 | shopt -s histappend 17 | 18 | # for setting history length see HISTSIZE and HISTFILESIZE in bash(1) 19 | HISTSIZE=1000 20 | HISTFILESIZE=2000 21 | 22 | # check the window size after each command and, if necessary, 23 | # update the values of LINES and COLUMNS. 24 | shopt -s checkwinsize 25 | 26 | # If set, the pattern "**" used in a pathname expansion context will 27 | # match all files and zero or more directories and subdirectories. 28 | #shopt -s globstar 29 | 30 | # make less more friendly for non-text input files, see lesspipe(1) 31 | #[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)" 32 | 33 | # set variable identifying the chroot you work in (used in the prompt below) 34 | if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then 35 | debian_chroot=$(cat /etc/debian_chroot) 36 | fi 37 | 38 | # set a fancy prompt (non-color, unless we know we "want" color) 39 | case "$TERM" in 40 | xterm-color|*-256color) color_prompt=yes;; 41 | esac 42 | 43 | # uncomment for a colored prompt, if the terminal has the capability; turned 44 | # off by default to not distract the user: the focus in a terminal window 45 | # should be on the output of commands, not on the prompt 46 | force_color_prompt=yes 47 | 48 | if [ -n "$force_color_prompt" ]; then 49 | if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then 50 | # We have color support; assume it's compliant with Ecma-48 51 | # (ISO/IEC-6429). (Lack of such support is extremely rare, and such 52 | # a case would tend to support setf rather than setaf.) 53 | color_prompt=yes 54 | else 55 | color_prompt= 56 | fi 57 | fi 58 | 59 | parse_git_branch() { 60 | git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/(\1)/' 61 | } 62 | 63 | #export PS1="\u@\h \[\e[32m\]\w \[\e[91m\]\$(parse_git_branch)\[\e[00m\]$ " 64 | 65 | 66 | if [ "$color_prompt" = yes ]; then 67 | #PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w \[\e[91m\]\$(parse_git_branch)\[\e[00m\]\$\[\033[00m\] ' 68 | PS1="${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]: \[\e[32m\]\w \[\e[91m\]\$(parse_git_branch)\[\e[00m\]$ " 69 | else 70 | PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ ' 71 | fi 72 | unset color_prompt force_color_prompt 73 | 74 | # If this is an xterm set the title to user@host:dir 75 | case "$TERM" in 76 | xterm*|rxvt*) 77 | PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1" 78 | ;; 79 | *) 80 | ;; 81 | esac 82 | 83 | # enable color support of ls and also add handy aliases 84 | if [ -x /usr/bin/dircolors ]; then 85 | test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)" 86 | alias ls='ls --color=auto' 87 | #alias dir='dir --color=auto' 88 | #alias vdir='vdir --color=auto' 89 | 90 | alias grep='grep --color=auto' 91 | alias fgrep='fgrep --color=auto' 92 | alias egrep='egrep --color=auto' 93 | fi 94 | 95 | # colored GCC warnings and errors 96 | #export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01' 97 | 98 | # some more ls aliases 99 | #alias ll='ls -l' 100 | #alias la='ls -A' 101 | #alias l='ls -CF' 102 | 103 | # Alias definitions. 104 | # You may want to put all your additions into a separate file like 105 | # ~/.bash_aliases, instead of adding them here directly. 106 | # See /usr/share/doc/bash-doc/examples in the bash-doc package. 107 | 108 | if [ -f ~/.bash_aliases ]; then 109 | . ~/.bash_aliases 110 | fi 111 | 112 | # enable programmable completion features (you don't need to enable 113 | # this, if it's already enabled in /etc/bash.bashrc and /etc/profile 114 | # sources /etc/bash.bashrc). 115 | if ! shopt -oq posix; then 116 | if [ -f /usr/share/bash-completion/bash_completion ]; then 117 | . /usr/share/bash-completion/bash_completion 118 | elif [ -f /etc/bash_completion ]; then 119 | . /etc/bash_completion 120 | fi 121 | fi 122 | 123 | # Path to your nhl-led-scoreboard installation. 124 | export NHL="/home/pi/nhl-led-scoreboard" 125 | 126 | show_virtual_env() { 127 | if [[ -n "$VIRTUAL_ENV" && -n "$DIRENV_DIR" ]]; then 128 | echo "($(basename $VIRTUAL_ENV))" 129 | fi 130 | } 131 | export -f show_virtual_env 132 | PS1='$(show_virtual_env)'$PS1 133 | -------------------------------------------------------------------------------- /nhl-image/ansible/files/sb_image/.config/fastfetch/config.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json", 3 | "logo": { 4 | "source": "auto", 5 | "padding": { 6 | "top": 1 7 | } 8 | }, 9 | "display": { 10 | "separator": ": " 11 | }, 12 | "modules": [ 13 | "title", 14 | "separator", 15 | { 16 | "type": "os", 17 | "key": "OS" 18 | }, 19 | { 20 | "type": "host", 21 | "key": "Host" 22 | }, 23 | { 24 | "type": "kernel", 25 | "key": "Kernel", 26 | "format": "{1} {2}" // Matches neofetch shorthand="on" 27 | }, 28 | { 29 | "type": "uptime", 30 | "key": "Uptime" 31 | }, 32 | { 33 | "type": "packages", 34 | "key": "Packages" 35 | }, 36 | { 37 | "type": "shell", 38 | "key": "Shell" 39 | }, 40 | { 41 | "type": "display", 42 | "key": "Resolution", 43 | "compactType": "original" // Matches neofetch refresh_rate="off" 44 | }, 45 | { 46 | "type": "icons", 47 | "key": "Icons" 48 | }, 49 | { 50 | "type": "terminal", 51 | "key": "Terminal" 52 | }, 53 | { 54 | "type": "terminalfont", 55 | "key": "Terminal Font" 56 | }, 57 | { 58 | "type": "cpu", 59 | "key": "CPU", 60 | "temp": true, // cpu_temp="C" 61 | "showPeCoreCount": true 62 | }, 63 | { 64 | "type": "gpu", 65 | "key": "GPU" 66 | }, 67 | { 68 | "type": "memory", 69 | "key": "Memory" 70 | }, 71 | { 72 | "type": "cpuusage", 73 | "key": "CPU Usage" 74 | }, 75 | { 76 | "type": "disk", 77 | "key": "Disk", 78 | "folders": ["/"] 79 | }, 80 | { 81 | "type": "localip", 82 | "key": "Local IP", 83 | "showIpv6": false, 84 | "showMac": false 85 | }, 86 | { 87 | "type": "locale", 88 | "key": "Locale" 89 | }, 90 | { 91 | "type": "command", 92 | "key": "Time Zone", 93 | "text": "timedatectl show -p Timezone --value" 94 | }, 95 | "break", 96 | // Custom: NHL LED Scoreboard Image 97 | { 98 | "type": "command", 99 | "key": "NHL LED Scoreboard Image", 100 | "keyColor": "yellow", 101 | // Neofetch: bold, color 10 (bright green) 102 | "text": "echo \"\u001b[1m\u001b[92m$(cat /home/pi/.nhlledportal/imgver)\u001b[0m\"", 103 | "shell": "/bin/bash" 104 | }, 105 | // Custom: NHL LED Scoreboard Status with Logic 106 | { 107 | "type": "command", 108 | "key": "NHL LED Scoreboard", 109 | "keyColor": "yellow", 110 | // Logic: if status contains UPDATE, bold+green+reverse, else cyan 111 | "text": "status=$(cat /home/pi/.nhlledportal/status); if [[ $status == *\"UPDATE\"* ]]; then echo \"\u001b[1m\u001b[92m\u001b[7m $status \u001b[27m\u001b[0m\"; else echo \"\u001b[36m $status\u001b[0m\"; fi", 112 | "shell": "/bin/bash" 113 | }, 114 | "break", 115 | // Custom: Scoreboard Commands Header 116 | { 117 | "type": "custom", 118 | "format": "\u001b[33m\u001b[4mscoreboard commands:\u001b[0m" // Yellow + Underline 119 | }, 120 | { 121 | "type": "custom", 122 | "format": "\u001b[1msb-help sb-tools sb-issue sb-updatecheck sb-changelog sb-upgrade sb-resetwifi\u001b[0m" // Bold 123 | }, 124 | { 125 | "type": "custom", 126 | "format": "\u001b[1msb-stderr sb-stdout sb-livelog sb-stop sb-start sb-restart sb-status sb-sysinfo\u001b[0m" // Bold 127 | }, 128 | "break", 129 | // Custom: Useful Links 130 | { 131 | "type": "custom", 132 | "format": "\u001b[33m\u001b[4mUseful links:\u001b[0m" // Yellow + Underline 133 | }, 134 | { 135 | "type": "command", 136 | "key": "NLS ControlHub", 137 | // Neofetch: color 2 (green) for URL. Extracts IP from wlan0. 138 | "text": "ip=$(ip -4 addr show wlan0 | grep -oP '(?<=inet\\s)\\d+(\\.\\d+){3}'); echo \"\u001b[32mhttp://$ip:8000\u001b[0m\"", 139 | "shell": "/bin/bash" 140 | } 141 | ] 142 | } -------------------------------------------------------------------------------- /nhl-image/ansible/files/sb_image/sbtools/issueUpload.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Upload the scoreboard stderr, stdout and config.json to pastebin using pastebinit 3 | cd $HOME//nhl-led-scoreboard || exit 1 4 | ROOT=$HOME/nhl-led-scoreboard 5 | version=$(cat "$ROOT"/VERSION) 6 | currdate=$(date) 7 | SUPERVISOR_INSTALLED=false 8 | if command -v supervisorctl &> /dev/null 9 | then 10 | SUPERVISORCTL=$(command -v supervisorctl) 11 | SUPERVISOR_INSTALLED=true 12 | else 13 | SUPERVISOR_INSTALLED=false 14 | fi 15 | 16 | if [ -n "${1}" ]; then 17 | scoreboard_proc=$1 18 | else 19 | scoreboard_proc="scoreboard" 20 | fi 21 | 22 | FETCH_INSTALLED=false 23 | if command -v fastfetch &> /dev/null 24 | then 25 | FASTFETCH_CMD=$(command -v fastfetch) 26 | HOSTOS="${FASTFETCH_CMD} -l none" 27 | FETCH_INSTALLED=true 28 | elif command -v neofetch &> /dev/null 29 | then 30 | NEOFETCH_CMD=$(command -v neofetch) 31 | HOSTOS="${NEOFETCH_CMD} --off -stdout" 32 | FETCH_INSTALLED=true 33 | fi 34 | 35 | echo "nhl-led-scoreboard issue data ${currdate}" > /tmp/issue.txt 36 | echo "=============================" >>/tmp/issue.txt 37 | echo "" >> /tmp/issue.txt 38 | if [ "$FETCH_INSTALLED" = true ]; then 39 | echo "Running V${version} on a " >> /tmp/issue.txt 40 | $HOSTOS | grep Host >> /tmp/issue.txt 41 | fi 42 | 43 | echo "------------------------------------------------------" >> /tmp/issue.txt 44 | echo "Git Remotes" >> /tmp/issue.txt 45 | echo "=================================" >> /tmp/issue.txt 46 | /usr/bin/git remote -v >> /tmp/issue.txt 47 | 48 | echo "------------------------------------------------------" >> /tmp/issue.txt 49 | echo "OS Info" >> /tmp/issue.txt 50 | echo "=================================" >> /tmp/issue.txt 51 | if [ -r /etc/os-release ]; then 52 | . /etc/os-release 53 | echo $PRETTY_NAME >> /tmp/issue.txt 54 | fi 55 | 56 | echo "------------------------------------------------------" >> /tmp/issue.txt 57 | 58 | VENV_EXISTS=false 59 | if [ -d "$HOME/nhlsb-venv" ]; then 60 | echo "$HOME/nhlsb-venv ..... FOUND" >> /tmp/issue.txt 61 | VENV_EXISTS=true 62 | else 63 | echo "$HOME/nhlsb-venv ..... NOT FOUND" >> /tmp/issue.txt 64 | fi 65 | 66 | if [ -d "$HOME/nhl-led-score-board" ]; then 67 | echo "$HOME/nhl-led-score-board ..... FOUND" >> /tmp/issue.txt 68 | else 69 | echo "$HOME/nhl-led-score-board ..... NOT FOUND" >> /tmp/issue.txt 70 | fi 71 | 72 | if [ -d "$HOME/nhl-led-scoreboard/submodules/matrix/bindings/python" ]; then 73 | echo "$HOME/nhl-led-scoreboard/submodules/matrix/bindings/python ..... FOUND" >> /tmp/issue.txt 74 | else 75 | echo "$HOME/nhl-led-scoreboard/submodules/matrix/bindings/python ..... NOT FOUND" >> /tmp/issue.txt 76 | fi 77 | 78 | if [ "$VENV_EXISTS" = true ]; then 79 | echo "------------------------------------------------------" >> /tmp/issue.txt 80 | echo "pip list" >> /tmp/issue.txt 81 | echo "=================================" >> /tmp/issue.txt 82 | source "$HOME/nhlsb-venv/bin/activate" 83 | pip list >> /tmp/issue.txt 84 | deactivate 85 | fi 86 | 87 | echo "------------------------------------------------------" >> /tmp/issue.txt 88 | echo "config.json" >> /tmp/issue.txt 89 | echo "" >>/tmp/issue.txt 90 | /usr/bin/jq '.boards.weather.owm_apikey=""' "${ROOT}"/config/config.json >> /tmp/issue.txt 91 | echo "" >> /tmp/issue.txt 92 | 93 | if [ -f "$HOME/nhl-led-scoreboard/scoreboard.log" ]; then 94 | echo "------------------------------------------------------" >> /tmp/issue.txt 95 | echo "scoreboard.log" >> /tmp/issue.txt 96 | echo "=================================" >> /tmp/issue.txt 97 | cat "$HOME/nhl-led-scoreboard/scoreboard.log" >> /tmp/issue.txt 98 | SUPERVISOR_INSTALLED=true 99 | 100 | # We've read the log file, now delete it 101 | rm $HOME/nhl-led-scoreboard/scoreboard.log 102 | fi 103 | 104 | if [ "$SUPERVISOR_INSTALLED" = true ]; then 105 | echo "------------------------------------------------------" >> /tmp/issue.txt 106 | echo "supervisorctl status" >> /tmp/issue.txt 107 | echo "------------------------------------------------------" >> /tmp/issue.txt 108 | ${SUPERVISORCTL} status >>/tmp/issue.txt 109 | echo "------------------------------------------------------" >> /tmp/issue.txt 110 | echo "${scoreboard_proc} stderr log, last 50kb" >> /tmp/issue.txt 111 | echo "=================================" >> /tmp/issue.txt 112 | ${SUPERVISORCTL} tail -50000 $scoreboard_proc stderr >> /tmp/issue.txt 113 | echo "" >> /tmp/issue.txt 114 | echo "------------------------------------------------------" >> /tmp/issue.txt 115 | echo "${scoreboard_proc} stdout log, last 50kb" >> /tmp/issue.txt 116 | echo "=================================" >> /tmp/issue.txt 117 | ${SUPERVISORCTL} tail -50000 $scoreboard_proc >> /tmp/issue.txt 118 | fi 119 | 120 | if [ "$SUPERVISOR_INSTALLED" = false ]; then 121 | echo "supervisorctl not found. Please run the scoreboard with the --logtofile and the --loglevel=DEBUG option to generate a scoreboard.log. Once the issue happens again, rerun this script" 122 | else 123 | url=$(/usr/bin/pastebinit -b pastebin.com -t "nhl-led-scoreboard issue logs and config" < /tmp/issue.txt) 124 | echo "Take this url and paste it into your issue. You can create an issue @ https://github.com/falkyre/nhl-led-scoreboard/issues" 125 | echo "${url}" 126 | fi 127 | 128 | 129 | rm /tmp/issue.txt 130 | -------------------------------------------------------------------------------- /nhl-image/ansible/files/sb_image/aptfile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail 3 | [[ $TRACE ]] && set -x && export TRACE=$TRACE 4 | 5 | version() { 6 | local VERSION="dev-master" 7 | if [[ -f /var/lib/aptfile/VERSION ]]; then 8 | VERSION=$(cat /var/lib/aptfile/VERSION) 9 | fi 10 | echo "aptfile $VERSION" 11 | } 12 | 13 | usage() { 14 | version 15 | echo "Usage: aptfile " 16 | } 17 | 18 | help() { 19 | usage 20 | echo 21 | echo " is the path to a bash file with apt instructions." 22 | echo 23 | echo " -h, --help Display this help message" 24 | echo " -v, --version Display the version number" 25 | echo 26 | echo " For more information, see https://github.com/seatgeek/bash-aptfile" 27 | echo 28 | } 29 | 30 | resolve_link() { 31 | $(type -p greadlink readlink | head -1) "$1" 32 | } 33 | 34 | abs_dirname() { 35 | local cwd 36 | local path="$1" 37 | cwd="$(pwd)" 38 | 39 | while [ -n "$path" ]; do 40 | cd "${path%/*}" 41 | local name="${path##*/}" 42 | path="$(resolve_link "$name" || true)" 43 | done 44 | 45 | pwd 46 | cd "$cwd" 47 | } 48 | 49 | expand_path() { 50 | { 51 | cd "$(dirname "$1")" 2>/dev/null 52 | local dirname="$PWD" 53 | cd "$OLDPWD" 54 | echo "$dirname/$(basename "$1")" 55 | } || echo "$1" 56 | } 57 | 58 | options=() 59 | arguments=() 60 | for arg in "$@"; do 61 | if [ "${arg:0:1}" = "-" ]; then 62 | if [ "${arg:1:1}" = "-" ]; then 63 | options[${#options[*]}]="${arg:2}" 64 | else 65 | index=1 66 | while option="${arg:$index:1}"; do 67 | [ -n "$option" ] || break 68 | options[${#options[*]}]="$option" 69 | let index+=1 70 | done 71 | fi 72 | else 73 | arguments[${#arguments[*]}]="$arg" 74 | fi 75 | done 76 | 77 | for option in "${options[@]}"; do 78 | case "$option" in 79 | "h" | "help") 80 | help 81 | exit 0 82 | ;; 83 | "v" | "version") 84 | version 85 | exit 0 86 | ;; 87 | *) 88 | usage >&2 89 | exit 1 90 | ;; 91 | esac 92 | done 93 | 94 | if [ "${#arguments[@]}" -eq 0 ]; then 95 | APTFILE_PATH=$(expand_path "aptfile") 96 | if [[ ! -f $APTFILE_PATH ]]; then 97 | usage >&2 98 | exit 1 99 | fi 100 | set -- "aptfile" "$@" 101 | fi 102 | 103 | export APTFILE_COLOR_OFF="\033[0m" # unsets color to term fg color 104 | export APTFILE_RED="\033[0;31m" # red 105 | export APTFILE_GREEN="\033[0;32m" # green 106 | export APTFILE_YELLOW="\033[0;33m" # yellow 107 | export APTFILE_MAGENTA="\033[0;35m" # magenta 108 | export APTFILE_CYAN="\033[0;36m" # cyan 109 | 110 | logfile=$(basename "$0") 111 | TMP_APTFILE_LOGFILE=$(mktemp "/tmp/${logfile}.XXXXXX") || { 112 | log_fail "${APTFILE_RED}WARNING: Cannot create temp file using mktemp in /tmp dir ${APTFILE_COLOR_OFF}\n" 113 | } 114 | export TMP_APTFILE_LOGFILE="$TMP_APTFILE_LOGFILE" 115 | trap 'rm -rf "$TMP_APTFILE_LOGFILE" > /dev/null' INT TERM EXIT 116 | 117 | log_fail() { 118 | [[ $TRACE ]] && set -x 119 | echo -e "${APTFILE_RED}$*${APTFILE_COLOR_OFF}" 1>&2 120 | [[ -f "$TMP_APTFILE_LOGFILE" ]] && echo -e "verbose logs:\n" 1>&2 && sed -e 's/^/ /' "$TMP_APTFILE_LOGFILE" 121 | exit 1 122 | } 123 | 124 | log_info() { 125 | [[ $TRACE ]] && set -x 126 | echo -e "$@" 127 | } 128 | 129 | update() { 130 | [[ $TRACE ]] && set -x 131 | log_info "Running update" 132 | apt-get update >"$TMP_APTFILE_LOGFILE" 2>&1 133 | [[ $? -eq 0 ]] || log_fail "Failed to run update" 134 | } 135 | 136 | package() { 137 | [[ $TRACE ]] && set -x 138 | [[ -z $1 ]] && log_fail "Please specify a package to install" 139 | local pkg="$1" 140 | dpkg --force-confnew -s "$pkg" >"$TMP_APTFILE_LOGFILE" 2>&1 && log_info "${APTFILE_CYAN}[OK]${APTFILE_COLOR_OFF} package $pkg" && return 0 141 | apt-get -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confnew" -qq -y install "$pkg" 142 | [[ $? -eq 0 ]] || log_fail "${APTFILE_RED}[FAIL]${APTFILE_COLOR_OFF} package $pkg" 143 | log_info "${APTFILE_GREEN}[NEW]${APTFILE_COLOR_OFF} package $pkg" 144 | } 145 | 146 | package_from_url() { 147 | [[ $TRACE ]] && set -x 148 | [[ -z $2 ]] && log_fail "Please specify a name and a download url to install the package from" 149 | local name=$1 150 | local url=$2 151 | if type curl >/dev/null 2>&1; then 152 | local dl_cmd="curl" 153 | local dl_options="-so" 154 | elif type wget >/dev/null 2>&1; then 155 | local dl_cmd="wget" 156 | local dl_options="-qO" 157 | else 158 | log_fail "Neither curl nor wget found. Unable to download $url" 159 | fi 160 | dpkg --force-confnew -s "$name" >"$TMP_APTFILE_LOGFILE" 2>&1 && log_info "${APTFILE_CYAN}[OK]${APTFILE_COLOR_OFF} package $name" && return 0 161 | tempdir=$(mktemp -d) 162 | $dl_cmd $dl_options $tempdir/${name}.deb $url \ 163 | && apt-get -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confnew" -qq -y install "$tempdir/${name}.deb" 164 | if [[ $? -ne 0 ]]; then 165 | rm -r $tempdir 166 | log_fail "${APTFILE_RED}[FAIL]${APTFILE_COLOR_OFF} package $name" 167 | fi 168 | rm -r $tempdir 169 | log_info "${APTFILE_GREEN}[NEW]${APTFILE_COLOR_OFF} package $name" 170 | } 171 | 172 | packagelist() { 173 | [[ $TRACE ]] && set -x 174 | [[ -z $1 ]] && log_fail "Please specify at least one package to install" 175 | local input_packages=$@ 176 | local install_packages=() 177 | for pkg in $input_packages; do 178 | dpkg --force-confnew -s "$pkg" >"$TMP_APTFILE_LOGFILE" 2>&1 && log_info "${APTFILE_CYAN}[OK]${APTFILE_COLOR_OFF} package $pkg" && continue 179 | install_packages+=($pkg) 180 | done 181 | if [[ ${#install_packages[@]} -gt 0 ]]; then 182 | apt-get -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confnew" -qq -y install ${install_packages[@]} 183 | [[ $? -eq 0 ]] || log_fail "${APTFILE_RED}[FAIL]${APTFILE_COLOR_OFF} packages ${install_packages[@]}" 184 | log_info "${APTFILE_GREEN}[NEW]${APTFILE_COLOR_OFF} packages ${install_packages[@]}" 185 | fi 186 | } 187 | 188 | ppa() { 189 | [[ $TRACE ]] && set -x 190 | [[ -z $1 ]] && log_fail "Please specify a repository to setup" 191 | local repo="$1" 192 | if [[ -d /etc/apt/sources.list.d/ ]]; then 193 | grep ^ /etc/apt/sources.list /etc/apt/sources.list.d/* | grep -q "$repo" && log_info "${APTFILE_CYAN}[OK]${APTFILE_COLOR_OFF} ppa $repo" && return 0 194 | fi 195 | repository "ppa:$1" 196 | } 197 | 198 | repository() { 199 | [[ $TRACE ]] && set -x 200 | [[ -z $1 ]] && log_fail "Please specify a repository to setup" 201 | local repo="$1" 202 | if [[ -d /etc/apt/sources.list.d/ ]]; then 203 | grep ^ /etc/apt/sources.list /etc/apt/sources.list.d/* | grep -Fq "$repo" && log_info "${APTFILE_CYAN}[OK]${APTFILE_COLOR_OFF} repository $repo" && return 0 204 | fi 205 | add-apt-repository -y "$repo" >"$TMP_APTFILE_LOGFILE" 2>&1 206 | [[ $? -eq 0 ]] || log_fail "${APTFILE_RED}[FAIL]${APTFILE_COLOR_OFF} repository $pkg" 207 | update 208 | log_info "${APTFILE_GREEN}[NEW]${APTFILE_COLOR_OFF} repository $repo" 209 | } 210 | 211 | repository_file() { 212 | [[ $TRACE ]] && set -x 213 | [[ -z $2 ]] && log_fail "Please specify a filename and sourceline to setup" 214 | local repofile="$1" 215 | local repo="$2" 216 | # sourceline is not a complete repo configuration, needs modifying 217 | # i.e. not sourceline="deb http://domain.invalid/debian buster main extra" 218 | if [[ "$repo" != "deb "* ]]; then 219 | releasename=$(lsb_release -sc) 220 | if [[ "$repo" == *" "* ]]; then 221 | # Components given in sourceline, adding suite 222 | # i.e. sourceline="http://domain.invalid/debian main" 223 | repo="deb ${repo/ / $releasename }" 224 | else 225 | # only URL given, adding suite and component 226 | # i.e. sourceline="http://domain.invalid/debian" 227 | repo="deb ${repo} $releasename main" 228 | fi 229 | fi 230 | 231 | if [[ "$repofile" != *.list ]]; then 232 | # Adding extension to enable parsing file 233 | repofile=${repofile}.list 234 | fi 235 | # Adding path 236 | repofile="/etc/apt/sources.list.d/$repofile" 237 | 238 | [[ -d "/etc/apt/sources.list.d" ]] || mkdir -p /etc/apt/sources.list.d 239 | 240 | grep ^ /etc/apt/sources.list /etc/apt/sources.list.d/* | grep -Fq "$repo" && log_info "${APTFILE_CYAN}[OK]${APTFILE_COLOR_OFF} repository $repo" && return 0 241 | 242 | echo "Writing '$repo' to file '$repofile'" >"$TMP_APTFILE_LOGFILE" 243 | echo "$repo" >"$repofile" 2>>"$TMP_APTFILE_LOGFILE" 244 | [[ $? -eq 0 ]] || log_fail "${APTFILE_RED}[FAIL]${APTFILE_COLOR_OFF} repository $pkg" 245 | update 246 | log_info "${APTFILE_GREEN}[NEW]${APTFILE_COLOR_OFF} repository $repo" 247 | } 248 | 249 | debconf_selection() { 250 | [[ $TRACE ]] && set -x 251 | [[ -z $1 ]] && log_fail "Please specify a debconf line" 252 | echo "$1" | debconf-set-selections 253 | [[ $? -eq 0 ]] || log_fail "${APTFILE_RED}[FAIL]${APTFILE_COLOR_OFF} debconf: $1" 254 | log_info "${APTFILE_CYAN}[OK]${APTFILE_COLOR_OFF} set debconf line: $1" 255 | } 256 | 257 | if [ -z "$TMPDIR" ]; then 258 | APTFILE_TMPDIR="/tmp" 259 | else 260 | APTFILE_TMPDIR="${TMPDIR%/}" 261 | fi 262 | 263 | APTFILE_INPUT=$(expand_path "$1") 264 | APTFILE_TMPNAME="$APTFILE_TMPDIR/aptfile.$$" 265 | APTFILE_OUTPUT="${APTFILE_TMPNAME}.out" 266 | 267 | aptfile_preprocess_source() { 268 | tail -n +2 "$1" >"$APTFILE_OUTPUT" 269 | trap "aptfile_cleanup_preprocessed_source" err exit 270 | trap "aptfile_cleanup_preprocessed_source; exit 1" int 271 | } 272 | 273 | aptfile_cleanup_preprocessed_source() { 274 | rm -f "$APTFILE_TMPNAME" 275 | rm -f "$APTFILE_OUTPUT" 276 | } 277 | 278 | aptfile_preprocess_source "$APTFILE_INPUT" 279 | 280 | export -f update 281 | export -f package 282 | export -f package_from_url 283 | export -f packagelist 284 | export -f ppa 285 | export -f repository 286 | export -f repository_file 287 | export -f debconf_selection 288 | export -f log_fail 289 | export -f log_info 290 | 291 | chmod +x "$APTFILE_OUTPUT" 292 | exec "$APTFILE_OUTPUT" 293 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

6 | 7 | 8 | 9 | # NHL LED Scoreboard Raspberry Pi Image 10 | [![Create Release - Image](https://github.com/falkyre/nhl-led-scoreboard-img/actions/workflows/main.yml/badge.svg)](https://github.com/falkyre/nhl-led-scoreboard-img/actions/workflows/main.yml) 11 | [![GitHub release (latest by date)](https://badgers.space/github/release/falkyre/nhl-led-scoreboard-img?label=Version&cache=600)](https://github.com/falkyre/nhl-led-scoreboard-img/releases/latest) 12 | ![GitHub Downloads (all assets, all releases)](https://img.shields.io/github/downloads/falkyre/nhl-led-scoreboard-img/total) 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | This project provides a free [Raspbian](https://www.raspberrypi.org/downloads/raspbian/) based Raspberry Pi image with [NHL LED Scoreboard](https://github.com/falkyre/nhl-led-scoreboard) pre-installed. This is built with the Hasicorp packer with the packer-builder-arm plugin (https://github.com/mkaczanowski/packer-builder-arm) in a docker image extended with ansible. Ansible is used to do the provisioning of the image. For more information, see the [BUILD](https://github.com/falkyre/nhl-led-scoreboard-img/tree/packer/nhl-image/BUILD.md) documentation. 21 | 22 | * Works on Raspberry Pi models - Zero 2w up to Raspberry pi 4 23 | * Built on Raspbian Lite (no desktop). As of October 2025, current image build still runs on RaspiOS Bookworm lite (32bit). Trixie is being worked on. 24 | * Simple WiFi Setup (Ethernet setup not tested and should only be done by advanced users) using the [comitup](http://davesteele.github.io/comitup/) utility. 25 | 26 | This image also provides a command called `sb-tools` which helps you with various tools to run and configure the scoreboard in a text/terminal based GUI. There are also a set of command line aliases that provide similar functionaity without a GUI. See [Command Line Utilities](#NHL-Led-Scoreboard-command-line-utilities) for a list. 27 | 28 | ### Note about python 29 | Everything python related is now installed in a virtual environment that is located in the /home/pi/nhl-led-scoreboard/venv directory. The bashrc script contains code that will auto activate the venv and deactivate upson entering and leaving the /home/pi/nhl-led-scoreboard directory. The purpose behind this is to keep the OS install as clean as possible and the Python 3.11 in Bookworm will not let you install over system installed packages. In order to run the code from the command line using sudo, the python command **`must`** now reference the virtual environment installation and not the globally installed one. Everything is handled with the image but if you are asked to run the code via command line for troubleshooting issues, see below. 30 | 31 | Here's a comparison of how to run the scoreboard (assuming you are in the nhl-led-scoreboard directory) 32 | 33 | **Previous way with everything globally install as root user** 34 | 35 | > `sudo python3 ./src/main.py [command line options]` 36 | 37 | **Now with the venv** 38 | > `sudo /home/pi/nhlsb-venv/bin/python3 ./src/main.py [command line options]` 39 | 40 | Also, when you change to the nhl-led-scoreboard directory, your virtual environment will automatically be activate (and deactivated when you leave it). You can tell your virtual environment is active by your shell prompt. This is what it looks like under raspiOS Bullseye. 41 | 42 | > `(nhlsb-venv)pi@scoreboard: ~/nhl-led-scoreboard (master)$` 43 | 44 | The front part of your prompt shows the name of the virtual environment. The part after the nhl-led-scoreboard shows the git branch. 45 | 46 | 47 | ## Download 48 | 49 | Downloading the *NHL LED Scoreboard Raspberry Pi Image* is completely free (no sign up required). 50 | 51 | 52 | 53 | ### [Download Latest Version](https://github.com/falkyre/nhl-led-scoreboard-img/releases/latest) 54 | 55 | 56 | 57 | If you'd like to support me: 58 | 59 | Buy Me A Coffee 60 | 61 | 62 | ## Flash to SD Card 63 | 64 | ### NOTE: Minimum SD Card size required is: 8GB 65 | 66 | The easiest way to flash the *NHL LED Scoreboard Raspberry Pi Image* to your SD card is to use [Etcher](https://www.balena.io/etcher/). If you decide to use the official Raspberry Pi IMager, make sure you DO NOT apply any customizations to the OS when it gets transferred to the SD card. If you do, your SD card will not work as intended for this project (if at all). 67 | 68 | 69 | 70 |

71 | 72 |

73 | 74 | 1. Download and install the latest version of [Etcher](https://www.balena.io/etcher/). 75 | 2. Open Etcher and select the `rpios-scoreboard-v0.0.0.xz` file you have [downloaded](https://github.com/falkyre/nhl-led-scoreboard-img/releases/latest). There is no need to extract the `.xz` file first. 76 | 3. Choose the drive your SD card has been inserted into. 77 | 4. Click Flash. 78 | 79 | ## First Boot 80 | 81 | Now that you have flashed your SD card, you can insert it into your Raspberry Pi. Your Raspberry Pi will expand the size of your SD card to utilize it fully. This can take some time depending on the size and health of your card. Once the Raspberry Pi has expanded the filesystem, it will launch a Captive Portal that you can log into to set your WiFi. On an iPhone, this should open safari automatically. On Android 10 and 11, it shows up as manage your router in the WiFi details in the settings. Android now will open captive portal webpage automatically as well. If you don't see it, use the manage router in WiFi details. 82 | 83 | 84 | 85 | 86 | ## Default Settings 87 | 88 | | | | 89 | |-------------------------------|------------------------------------------| 90 | | **Default Hostname** | `scoreboard.local` | 91 | | **Default SSH Username** | `pi` | 92 | | **Default SSH Password** | `scoreboard` | 93 | | **Hot Spot Wifi SSID** | `scoreboard-####` | 94 | 95 | 96 | `####` will be set to the a random set of 4 digits. 97 | 98 | 99 | ### WiFi Setup 100 | 101 | Follow these steps to connect your device to WiFi: 102 | 103 | 1. Power on your device without an Ethernet cable attached. 104 | 2. Wait 1-2 minutes (if first boot, the SD card will need to be expanded so this may take longer) 105 | 3. Use your mobile phone to scan for new WiFi networks 106 | 4. Connect to the hotspot named **scoreboard-####** where `####` is the a random set of 4 digits. There is no wifi password required. 107 | 6. Wait a few moments until the captive portal opens, this portal will allow you to connect the Raspberry Pi to your local WiFi network. If the captive portal does not open, connect to your raspberry pi at `http://10.41.0.1` 108 | 109 | If you enter your WiFi credentials incorrectly the **NHL Led Scoreboard** hotspot will reappear allowing you to try again. 110 | 111 | ## First Boot / Network Setup 112 | 113 | After you set your WiFi connection, the raspberry pi will reboot and connect to your router. To find your raspberry pi, the following can be attempted: 114 | 115 | 1. Login to your router and find the "connected devices" or "dhcp clients" page to find the IP address that was assigned to the Raspberry Pi. 116 | 2. Download the [Fing](https://www.fing.com/) app for [iOS](https://itunes.apple.com/us/app/fing-network-scanner/id430921107?mt=8) or [Android](https://play.google.com/store/apps/details?id=com.overlook.android.fing&hl=en_GB) to scan your network to find the IP address of your Raspberry Pi. 117 | 3. Try to `ping scoreboard.local`. The ping will show the IP address of your pi. 118 | 119 | See the wiki [How to Find IP Address](https://github.com/falkyre/nhl-led-scoreboard-img/wiki/How-To-Find-IP-Address) 120 | 121 | ## SSH Access 122 | 123 | SSH is enabled by default. The default username is `pi` with password `scoreboard`. 124 | See the wiki [Connect with SSH](https://github.com/falkyre/nhl-led-scoreboard-img/wiki/Connect-with-SSH) for more information and links. 125 | 126 | ## First login 127 | You will need to SSH to your Raspberry Pi to finalize some settings for getting the NHL LED Scoreboard to work. On first login, you will be prompted to select a single team (to create a basic config.json), the size of your board and if you have the antiflicker mod for the adafruit boards, then it will run a test script that will display the latest version of the NHL LED Scoreboard software. If that passes, you will be asked to enable the supervisor and then the raspberry pi will reboot. 128 | 129 | On reboot, if everything is working you will see a splash screen, then a loading image and the NHL LED Scoreboard will run wth the basic config.json created on the first initial log in. If you want to change the config.json, either edit the file by hand or use the /home/pi/nhl-led-scoreboard/nhl_setup tool. 130 | 131 | ## Community 132 | 133 | The official NHL LED Scoreboard Discord server where users can discuss NHL LED Scoreboard and ask for help. 134 | 135 | 136 | 137 | [![NHL LED Scoreboard Discord](https://discordapp.com/api/guilds/648168455450656798/widget.png?style=banner2)](https://discord.gg/CWa5CzK) 138 | 139 | 140 | 141 | [NHL LED Scoreboard Forum](https://github.com/falkyre/nhl-led-scoreboard/discussions) 142 | ## NHL Led Scoreboard command line utilities 143 | 144 | This table contains important information about the command line tools you can use. 145 | 146 | | General Tools | Command | 147 | |-------------------------------|------------------------------------------| 148 | | **Help Command** | `sb-help` | 149 | | **Manage NHL LED Scoreboard** | `sb-tools` | 150 | | **system info Command** | `sb-sysinfo` | 151 | | **Reset Wifi to Hot Spot** | `sb-resetwifi` | 152 | 153 | 154 | 155 | | Troubleshooting Tools | Command | 156 | |-------------------------------|------------------------------------------| 157 | | **View Live Logs Command** | `sb-livelog` | 158 | | **View output, last 50kb** | `sb-stdout` | 159 | | **View errors, last 50kb** | `sb-stderr` | 160 | | **Collect files for github issue** | `sb-issue` | 161 | 162 | | Update Tools | Command | 163 | |-------------------------------|------------------------------------------| 164 | | **Check for Update** | `sb-updatecheck` | 165 | | **Show changelog for latest version** | `sb-changelog` | 166 | | **Upgrade to latest version** | `sb-upgrade` | 167 | 168 | | Supervisor/Process Tools | Command | 169 | |-------------------------------|------------------------------------------| 170 | | **Status Command** | `sb-status` | 171 | | **Restart Command** | `sb-restart` | 172 | | **Stop Command** | `sb-stop` | 173 | | **Start Command** | `sb-start` | 174 | 175 | 176 | 177 | ## Raspberty Pi OS Settings 178 | 179 | This raspberry pi image is built on top of the latest Raspberry Pi OS lite Bullseye (May 3, 2023 release). Bookworm supported will be coming in the future. 180 | 181 | Added to this version is the following: 182 | 183 | * Auto set timezone based on geolocation of your IP address. Initial time zone is America/Toronto because we all know that's the center of the universe. 184 | * There is a login banner that shows various system info for your raspberry pi (including timezone). It can manually be run by using the `sb-sysinfo` 185 | * /var/log, swap and /home/pi are all set to exist in zram. /var/log and /homepi will get written back to the SD card on boot or under a filesystem sync. Settings for the sizes are in /etc/ztab 186 | * The size for zram of the /home/pi directory is currently set to 350M compressed. There is room for another repository to be installed if you wish. If you don't want the /home/pi to be run from zram, then edit the /etc/ztab as root user and comment out the line with /home/pi. 187 | * raspi-config is still available with all options as a normal raspberry pi. So if your time zone is not set correctly or you don't like your hostname, you can still use raspi-config for changes 188 | * Locale is set to en_US.UTF_8 which is needed for the weather icons to work correctly. 189 | 190 | -------------------------------------------------------------------------------- /nhl-image/ansible/files/sb_image/sbtools/changelog.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zsh 2 | 3 | ############################## 4 | # CHANGELOG SCRIPT CONSTANTS # 5 | ############################## 6 | 7 | #* Holds the list of valid types recognized in a commit subject 8 | #* and the display string of such type 9 | local -A TYPES 10 | TYPES=( 11 | build "Build system" 12 | chore "Chore" 13 | ci "CI" 14 | docs "Documentation" 15 | feat "Features" 16 | fix "Bug fixes" 17 | perf "Performance" 18 | refactor "Refactor" 19 | style "Style" 20 | test "Testing" 21 | ) 22 | 23 | #* Types that will be displayed in their own section, 24 | #* in the order specified here. 25 | local -a MAIN_TYPES 26 | MAIN_TYPES=(feat fix perf docs) 27 | 28 | #* Types that will be displayed under the category of other changes 29 | local -a OTHER_TYPES 30 | OTHER_TYPES=(refactor style other) 31 | 32 | #* Commit types that don't appear in $MAIN_TYPES nor $OTHER_TYPES 33 | #* will not be displayed and will simply be ignored. 34 | 35 | 36 | ############################ 37 | # COMMIT PARSING UTILITIES # 38 | ############################ 39 | 40 | function parse-commit { 41 | 42 | # This function uses the following globals as output: commits (A), 43 | # subjects (A), scopes (A) and breaking (A). All associative arrays (A) 44 | # have $hash as the key. 45 | # - commits holds the commit type 46 | # - subjects holds the commit subject 47 | # - scopes holds the scope of a commit 48 | # - breaking holds the breaking change warning if a commit does 49 | # make a breaking change 50 | 51 | function commit:type { 52 | local type="$(sed -E 's/^([a-zA-Z_\-]+)(\(.+\))?!?: .+$/\1/' <<< "$1")" 53 | 54 | # If $type doesn't appear in $TYPES array mark it as 'other' 55 | if [[ -n "${(k)TYPES[(i)$type]}" ]]; then 56 | echo $type 57 | else 58 | echo other 59 | fi 60 | } 61 | 62 | function commit:scope { 63 | local scope 64 | 65 | # Try to find scope in "type():" format 66 | scope=$(sed -nE 's/^[a-zA-Z_\-]+\((.+)\)!?: .+$/\1/p' <<< "$1") 67 | if [[ -n "$scope" ]]; then 68 | echo "$scope" 69 | return 70 | fi 71 | 72 | # If no scope found, try to find it in ":" format 73 | # Make sure it's not a type before printing it 74 | scope=$(sed -nE 's/^([a-zA-Z_\-]+): .+$/\1/p' <<< "$1") 75 | if [[ -z "${(k)TYPES[(i)$scope]}" ]]; then 76 | echo "$scope" 77 | fi 78 | } 79 | 80 | function commit:subject { 81 | # Only display the relevant part of the commit, i.e. if it has the format 82 | # type[(scope)!]: subject, where the part between [] is optional, only 83 | # displays subject. If it doesn't match the format, returns the whole string. 84 | sed -E 's/^[a-zA-Z_\-]+(\(.+\))?!?: (.+)$/\2/' <<< "$1" 85 | } 86 | 87 | # Return subject if the body or subject match the breaking change format 88 | function commit:is-breaking { 89 | local subject="$1" body="$2" message 90 | 91 | if [[ "$body" =~ "BREAKING CHANGE: (.*)" || \ 92 | "$subject" =~ '^[^ :\)]+\)?!: (.*)$' ]]; then 93 | message="${match[1]}" 94 | # remove CR characters (might be inserted in GitHub UI commit description form) 95 | message="${message//$'\r'/}" 96 | # skip next paragraphs (separated by two newlines or more) 97 | message="${message%%$'\n\n'*}" 98 | # ... and replace newlines with spaces 99 | echo "${message//$'\n'/ }" 100 | else 101 | return 1 102 | fi 103 | } 104 | 105 | # Return truncated hash of the reverted commit 106 | function commit:is-revert { 107 | local subject="$1" body="$2" 108 | 109 | if [[ "$subject" = Revert* && \ 110 | "$body" =~ "This reverts commit ([^.]+)\." ]]; then 111 | echo "${match[1]:0:7}" 112 | else 113 | return 1 114 | fi 115 | } 116 | 117 | # Parse commit with hash $1 118 | local hash="$1" subject body warning rhash 119 | subject="$(command git show -s --format=%s $hash)" 120 | body="$(command git show -s --format=%b $hash)" 121 | 122 | # Commits following Conventional Commits (https://www.conventionalcommits.org/) 123 | # have the following format, where parts between [] are optional: 124 | # 125 | # type[(scope)][!]: subject 126 | # 127 | # commit body 128 | # [BREAKING CHANGE: warning] 129 | 130 | # commits holds the commit type 131 | commits[$hash]="$(commit:type "$subject")" 132 | # scopes holds the commit scope 133 | scopes[$hash]="$(commit:scope "$subject")" 134 | # subjects holds the commit subject 135 | subjects[$hash]="$(commit:subject "$subject")" 136 | 137 | # breaking holds whether a commit has breaking changes 138 | # and its warning message if it does 139 | if warning=$(commit:is-breaking "$subject" "$body"); then 140 | breaking[$hash]="$warning" 141 | fi 142 | 143 | # reverts holds commits reverted in the same release 144 | if rhash=$(commit:is-revert "$subject" "$body"); then 145 | reverts[$hash]=$rhash 146 | fi 147 | } 148 | 149 | ############################# 150 | # RELEASE CHANGELOG DISPLAY # 151 | ############################# 152 | 153 | function display-release { 154 | 155 | # This function uses the following globals: output, version, 156 | # commits (A), subjects (A), scopes (A), breaking (A) and reverts (A). 157 | # 158 | # - output is the output format to use when formatting (raw|text|md) 159 | # - version is the version in which the commits are made 160 | # - commits, subjects, scopes, breaking, and reverts are associative arrays 161 | # with commit hashes as keys 162 | 163 | # Remove commits that were reverted 164 | local hash rhash 165 | for hash rhash in ${(kv)reverts}; do 166 | if (( ${+commits[$rhash]} )); then 167 | # Remove revert commit 168 | unset "commits[$hash]" "subjects[$hash]" "scopes[$hash]" "breaking[$hash]" 169 | # Remove reverted commit 170 | unset "commits[$rhash]" "subjects[$rhash]" "scopes[$rhash]" "breaking[$rhash]" 171 | fi 172 | done 173 | 174 | # If no commits left skip displaying the release 175 | if (( $#commits == 0 )); then 176 | return 177 | fi 178 | 179 | ##* Formatting functions 180 | 181 | # Format the hash according to output format 182 | # If no parameter is passed, assume it comes from `$hash` 183 | function fmt:hash { 184 | #* Uses $hash from outer scope 185 | local hash="${1:-$hash}" 186 | case "$output" in 187 | raw) printf "$hash" ;; 188 | text) printf "\e[33m$hash\e[0m" ;; # red 189 | md) printf "[\`$hash\`](https://github.com/falkyre/nhl-led-scoreboard/commits/$hash)" ;; 190 | esac 191 | } 192 | 193 | # Format headers according to output format 194 | # Levels 1 to 2 are considered special, the rest are formatted 195 | # the same, except in md output format. 196 | function fmt:header { 197 | local header="$1" level="$2" 198 | case "$output" in 199 | raw) 200 | case "$level" in 201 | 1) printf "$header\n$(printf '%.0s=' {1..${#header}})\n\n" ;; 202 | 2) printf "$header\n$(printf '%.0s-' {1..${#header}})\n\n" ;; 203 | *) printf "$header:\n\n" ;; 204 | esac ;; 205 | text) 206 | case "$level" in 207 | 1|2) printf "\e[1;4m$header\e[0m\n\n" ;; # bold, underlined 208 | *) printf "\e[1m$header:\e[0m\n\n" ;; # bold 209 | esac ;; 210 | md) printf "$(printf '%.0s#' {1..${level}}) $header\n\n" ;; 211 | esac 212 | } 213 | 214 | function fmt:scope { 215 | #* Uses $scopes (A) and $hash from outer scope 216 | local scope="${1:-${scopes[$hash]}}" 217 | 218 | # Get length of longest scope for padding 219 | local max_scope=0 padding=0 220 | for hash in ${(k)scopes}; do 221 | max_scope=$(( max_scope < ${#scopes[$hash]} ? ${#scopes[$hash]} : max_scope )) 222 | done 223 | 224 | # If no scopes, exit the function 225 | if [[ $max_scope -eq 0 ]]; then 226 | return 227 | fi 228 | 229 | # Get how much padding is required for this scope 230 | padding=$(( max_scope < ${#scope} ? 0 : max_scope - ${#scope} )) 231 | padding="${(r:$padding:: :):-}" 232 | 233 | # If no scope, print padding and 3 spaces (equivalent to "[] ") 234 | if [[ -z "$scope" ]]; then 235 | printf "${padding} " 236 | return 237 | fi 238 | 239 | # Print [scope] 240 | case "$output" in 241 | raw|md) printf "[$scope]${padding} " ;; 242 | text) printf "[\e[38;5;9m$scope\e[0m]${padding} " ;; # red 9 243 | esac 244 | } 245 | 246 | # If no parameter is passed, assume it comes from `$subjects[$hash]` 247 | function fmt:subject { 248 | #* Uses $subjects (A) and $hash from outer scope 249 | local subject="${1:-${subjects[$hash]}}" 250 | 251 | # Capitalize first letter of the subject 252 | subject="${(U)subject:0:1}${subject:1}" 253 | 254 | case "$output" in 255 | raw) printf "$subject" ;; 256 | # In text mode, highlight (#) and dim text between `backticks` 257 | text) sed -E $'s|#([0-9]+)|\e[32m#\\1\e[0m|g;s|`([^`]+)`|`\e[2m\\1\e[0m`|g' <<< "$subject" ;; 258 | # In markdown mode, link to (#) issues 259 | md) sed -E 's|#([0-9]+)|[#\1](https://github.com/falkyre/nhl-led-scoreboard/issues/\1)|g' <<< "$subject" ;; 260 | esac 261 | } 262 | 263 | function fmt:type { 264 | #* Uses $type from outer scope 265 | local type="${1:-${TYPES[$type]:-${(C)type}}}" 266 | [[ -z "$type" ]] && return 0 267 | case "$output" in 268 | raw|md) printf "$type: " ;; 269 | text) printf "\e[4m$type\e[24m: " ;; # underlined 270 | esac 271 | } 272 | 273 | ##* Section functions 274 | 275 | function display:version { 276 | fmt:header "$version" 2 277 | } 278 | 279 | function display:breaking { 280 | (( $#breaking != 0 )) || return 0 281 | 282 | case "$output" in 283 | raw) fmt:header "BREAKING CHANGES" 3 ;; 284 | text|md) fmt:header "⚠ BREAKING CHANGES" 3 ;; 285 | esac 286 | 287 | local hash subject 288 | for hash message in ${(kv)breaking}; do 289 | echo " - $(fmt:hash) $(fmt:scope)$(fmt:subject "${message}")" 290 | done | sort 291 | echo 292 | } 293 | 294 | function display:type { 295 | local hash type="$1" 296 | 297 | local -a hashes 298 | hashes=(${(k)commits[(R)$type]}) 299 | 300 | # If no commits found of type $type, go to next type 301 | (( $#hashes != 0 )) || return 0 302 | 303 | fmt:header "${TYPES[$type]}" 3 304 | for hash in $hashes; do 305 | echo " - $(fmt:hash) $(fmt:scope)$(fmt:subject)" 306 | done | sort -k3 # sort by scope 307 | echo 308 | } 309 | 310 | function display:others { 311 | local hash type 312 | 313 | # Commits made under types considered other changes 314 | local -A changes 315 | changes=(${(kv)commits[(R)${(j:|:)OTHER_TYPES}]}) 316 | 317 | # If no commits found under "other" types, don't display anything 318 | (( $#changes != 0 )) || return 0 319 | 320 | fmt:header "Other changes" 3 321 | for hash type in ${(kv)changes}; do 322 | case "$type" in 323 | other) echo " - $(fmt:hash) $(fmt:scope)$(fmt:subject)" ;; 324 | *) echo " - $(fmt:hash) $(fmt:scope)$(fmt:type)$(fmt:subject)" ;; 325 | esac 326 | done | sort -k3 # sort by scope 327 | echo 328 | } 329 | 330 | ##* Release sections order 331 | 332 | # Display version header 333 | display:version 334 | 335 | # Display breaking changes first 336 | display:breaking 337 | 338 | # Display changes for commit types in the order specified 339 | for type in $MAIN_TYPES; do 340 | display:type "$type" 341 | done 342 | 343 | # Display other changes 344 | display:others 345 | } 346 | 347 | function main { 348 | # $1 = until commit, $2 = since commit 349 | local until="$1" since="$2" 350 | 351 | # $3 = output format (--text|--raw|--md) 352 | # --md: uses markdown formatting 353 | # --raw: outputs without style 354 | # --text: uses ANSI escape codes to style the output 355 | local output=${${3:-"--text"}#--*} 356 | 357 | if [[ -z "$until" ]]; then 358 | until=HEAD 359 | fi 360 | 361 | if [[ -z "$since" ]]; then 362 | # If $since is not specified: 363 | # 1) try to find the version used before updating 364 | # 2) try to find the first version tag before $until 365 | since=$(command git config --get nhl-led-scoreboard.lastVersion 2>/dev/null) || \ 366 | since=$(command git describe --abbrev=0 --tags "$until^" 2>/dev/null) || \ 367 | unset since 368 | elif [[ "$since" = --all ]]; then 369 | unset since 370 | fi 371 | 372 | # Commit classification arrays 373 | local -A commits subjects scopes breaking reverts 374 | local truncate=0 read_commits=0 375 | local hash version tag 376 | 377 | # Get the first version name: 378 | # 1) try tag-like version, or 379 | # 2) try name-rev, or 380 | # 3) try branch name, or 381 | # 4) try short hash 382 | version=$(command git describe --tags $until 2>/dev/null) \ 383 | || version=$(command git name-rev --no-undefined --name-only --exclude="remotes/*" $until 2>/dev/null) \ 384 | || version=$(command git symbolic-ref --quiet --short $until 2>/dev/null) \ 385 | || version=$(command git rev-parse --short $until 2>/dev/null) 386 | 387 | # Get commit list from $until commit until $since commit, or until root 388 | # commit if $since is unset, in short hash form. 389 | # --first-parent is used when dealing with merges: it only prints the 390 | # merge commit, not the commits of the merged branch. 391 | command git rev-list --first-parent --abbrev-commit --abbrev=7 ${since:+$since..}$until | while read hash; do 392 | # Truncate list on versions with a lot of commits 393 | if [[ -z "$since" ]] && (( ++read_commits > 35 )); then 394 | truncate=1 395 | break 396 | fi 397 | 398 | # If we find a new release (exact tag) 399 | if tag=$(command git describe --exact-match --tags $hash 2>/dev/null); then 400 | # Output previous release 401 | display-release 402 | # Reinitialize commit storage 403 | commits=() 404 | subjects=() 405 | scopes=() 406 | breaking=() 407 | reverts=() 408 | # Start work on next release 409 | version="$tag" 410 | read_commits=1 411 | fi 412 | 413 | parse-commit "$hash" 414 | done 415 | 416 | display-release 417 | 418 | if (( truncate )); then 419 | echo " ...more commits omitted" 420 | echo 421 | fi 422 | } 423 | 424 | cd "$NHL" 425 | 426 | # Use raw output if stdout is not a tty 427 | if [[ ! -t 1 && -z "$3" ]]; then 428 | main "$1" "$2" --raw 429 | else 430 | main "$@" 431 | fi 432 | -------------------------------------------------------------------------------- /nhl-image/ansible/files/sb_image/.config/neofetch/neofetch_config.conf: -------------------------------------------------------------------------------- 1 | # See this wiki page for more info: 2 | # https://github.com/dylanaraps/neofetch/wiki/Customizing-Info 3 | print_info() { 4 | info title 5 | info underline 6 | 7 | info "OS" distro 8 | info "Host" model 9 | info "Kernel" kernel 10 | info "Uptime" uptime 11 | info "Packages" packages 12 | info "Shell" shell 13 | info "Resolution" resolution 14 | #info "DE" de 15 | #info "WM" wm 16 | #info "WM Theme" wm_theme 17 | #info "Theme" theme 18 | info "Icons" icons 19 | info "Terminal" term 20 | info "Terminal Font" term_font 21 | info "CPU" cpu 22 | info "GPU" gpu 23 | info "Memory" memory 24 | 25 | # info "GPU Driver" gpu_driver # Linux/macOS only 26 | info "CPU Usage" cpu_usage 27 | info "Disk" disk 28 | # info "Battery" battery 29 | # info "Font" font 30 | # info "Song" song 31 | # [[ $player ]] && prin "Music Player" "$player" 32 | info "Local IP" local_ip 33 | #info "Public IP" public_ip 34 | # info "Users" users 35 | info "Locale" locale # This only works on glibc systems. 36 | prin "Time Zone" "$(timedatectl show -p Timezone --value)" 37 | 38 | prin 39 | prin "$(color 3)NHL LED Scoreboard Image" "$(tput bold)$(color 10)$(cat /home/pi/.nhlledportal/imgver)" 40 | prin "$(color 3)NHL LED Scoreboard" "$(status=`cat /home/pi/.nhlledportal/status`;if [[ $status == *"UPDATE"* ]]; then echo "$(tput bold)$(color 10)$(tput smso) $status $(tput rmso)";else echo "$(color 6) $status"; fi)" 41 | 42 | prin 43 | prin "$(color 3)$(tput smul)scoreboard commands:$(tput rmul)" 44 | prin "$(tput bold)sb-help sb-tools sb-issue sb-updatecheck sb-changelog sb-upgrade sb-resetwifi$(tput sgr0)" 45 | prin "$(tput bold)sb-stderr sb-stdout sb-livelog sb-stop sb-start sb-restart sb-status sb-sysinfo$(tput sgr0)" 46 | prin 47 | prin "$(color 3)$(tput smul)Useful links:$(tput rmul)" 48 | prin "$(tput bold)NLS ControlHub $(tput sgr0)" "$(color 2)$(echo "http://$(ip -4 addr show wlan0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}'):8000")" 49 | 50 | } 51 | 52 | 53 | # Kernel 54 | 55 | 56 | # Shorten the output of the kernel function. 57 | # 58 | # Default: 'on' 59 | # Values: 'on', 'off' 60 | # Flag: --kernel_shorthand 61 | # Supports: Everything except *BSDs (except PacBSD and PC-BSD) 62 | # 63 | # Example: 64 | # on: '4.8.9-1-ARCH' 65 | # off: 'Linux 4.8.9-1-ARCH' 66 | kernel_shorthand="on" 67 | 68 | 69 | # Distro 70 | 71 | 72 | # Shorten the output of the distro function 73 | # 74 | # Default: 'off' 75 | # Values: 'on', 'off', 'tiny' 76 | # Flag: --distro_shorthand 77 | # Supports: Everything except Windows and Haiku 78 | distro_shorthand="off" 79 | 80 | # Show/Hide OS Architecture. 81 | # Show 'x86_64', 'x86' and etc in 'Distro:' output. 82 | # 83 | # Default: 'on' 84 | # Values: 'on', 'off' 85 | # Flag: --os_arch 86 | # 87 | # Example: 88 | # on: 'Arch Linux x86_64' 89 | # off: 'Arch Linux' 90 | os_arch="on" 91 | 92 | 93 | # Uptime 94 | 95 | 96 | # Shorten the output of the uptime function 97 | # 98 | # Default: 'on' 99 | # Values: 'on', 'off', 'tiny' 100 | # Flag: --uptime_shorthand 101 | # 102 | # Example: 103 | # on: '2 days, 10 hours, 3 mins' 104 | # off: '2 days, 10 hours, 3 minutes' 105 | # tiny: '2d 10h 3m' 106 | uptime_shorthand="on" 107 | 108 | 109 | # Memory 110 | 111 | 112 | # Show memory pecentage in output. 113 | # 114 | # Default: 'off' 115 | # Values: 'on', 'off' 116 | # Flag: --memory_percent 117 | # 118 | # Example: 119 | # on: '1801MiB / 7881MiB (22%)' 120 | # off: '1801MiB / 7881MiB' 121 | memory_percent="off" 122 | 123 | 124 | # Packages 125 | 126 | 127 | # Show/Hide Package Manager names. 128 | # 129 | # Default: 'tiny' 130 | # Values: 'on', 'tiny' 'off' 131 | # Flag: --package_managers 132 | # 133 | # Example: 134 | # on: '998 (pacman), 8 (flatpak), 4 (snap)' 135 | # tiny: '908 (pacman, flatpak, snap)' 136 | # off: '908' 137 | package_managers="on" 138 | 139 | 140 | # Shell 141 | 142 | 143 | # Show the path to $SHELL 144 | # 145 | # Default: 'off' 146 | # Values: 'on', 'off' 147 | # Flag: --shell_path 148 | # 149 | # Example: 150 | # on: '/bin/bash' 151 | # off: 'bash' 152 | shell_path="off" 153 | 154 | # Show $SHELL version 155 | # 156 | # Default: 'on' 157 | # Values: 'on', 'off' 158 | # Flag: --shell_version 159 | # 160 | # Example: 161 | # on: 'bash 4.4.5' 162 | # off: 'bash' 163 | shell_version="on" 164 | 165 | 166 | # CPU 167 | 168 | 169 | # CPU speed type 170 | # 171 | # Default: 'bios_limit' 172 | # Values: 'scaling_cur_freq', 'scaling_min_freq', 'scaling_max_freq', 'bios_limit'. 173 | # Flag: --speed_type 174 | # Supports: Linux with 'cpufreq' 175 | # NOTE: Any file in '/sys/devices/system/cpu/cpu0/cpufreq' can be used as a value. 176 | speed_type="bios_limit" 177 | 178 | # CPU speed shorthand 179 | # 180 | # Default: 'off' 181 | # Values: 'on', 'off'. 182 | # Flag: --speed_shorthand 183 | # NOTE: This flag is not supported in systems with CPU speed less than 1 GHz 184 | # 185 | # Example: 186 | # on: 'i7-6500U (4) @ 3.1GHz' 187 | # off: 'i7-6500U (4) @ 3.100GHz' 188 | speed_shorthand="off" 189 | 190 | # Enable/Disable CPU brand in output. 191 | # 192 | # Default: 'on' 193 | # Values: 'on', 'off' 194 | # Flag: --cpu_brand 195 | # 196 | # Example: 197 | # on: 'Intel i7-6500U' 198 | # off: 'i7-6500U (4)' 199 | cpu_brand="on" 200 | 201 | # CPU Speed 202 | # Hide/Show CPU speed. 203 | # 204 | # Default: 'on' 205 | # Values: 'on', 'off' 206 | # Flag: --cpu_speed 207 | # 208 | # Example: 209 | # on: 'Intel i7-6500U (4) @ 3.1GHz' 210 | # off: 'Intel i7-6500U (4)' 211 | cpu_speed="on" 212 | 213 | # CPU Cores 214 | # Display CPU cores in output 215 | # 216 | # Default: 'logical' 217 | # Values: 'logical', 'physical', 'off' 218 | # Flag: --cpu_cores 219 | # Support: 'physical' doesn't work on BSD. 220 | # 221 | # Example: 222 | # logical: 'Intel i7-6500U (4) @ 3.1GHz' (All virtual cores) 223 | # physical: 'Intel i7-6500U (2) @ 3.1GHz' (All physical cores) 224 | # off: 'Intel i7-6500U @ 3.1GHz' 225 | cpu_cores="logical" 226 | 227 | # CPU Temperature 228 | # Hide/Show CPU temperature. 229 | # Note the temperature is added to the regular CPU function. 230 | # 231 | # Default: 'off' 232 | # Values: 'C', 'F', 'off' 233 | # Flag: --cpu_temp 234 | # Supports: Linux, BSD 235 | # NOTE: For FreeBSD and NetBSD-based systems, you'll need to enable 236 | # coretemp kernel module. This only supports newer Intel processors. 237 | # 238 | # Example: 239 | # C: 'Intel i7-6500U (4) @ 3.1GHz [27.2°C]' 240 | # F: 'Intel i7-6500U (4) @ 3.1GHz [82.0°F]' 241 | # off: 'Intel i7-6500U (4) @ 3.1GHz' 242 | cpu_temp="C" 243 | 244 | 245 | # GPU 246 | 247 | 248 | # Enable/Disable GPU Brand 249 | # 250 | # Default: 'on' 251 | # Values: 'on', 'off' 252 | # Flag: --gpu_brand 253 | # 254 | # Example: 255 | # on: 'AMD HD 7950' 256 | # off: 'HD 7950' 257 | gpu_brand="on" 258 | 259 | # Which GPU to display 260 | # 261 | # Default: 'all' 262 | # Values: 'all', 'dedicated', 'integrated' 263 | # Flag: --gpu_type 264 | # Supports: Linux 265 | # 266 | # Example: 267 | # all: 268 | # GPU1: AMD HD 7950 269 | # GPU2: Intel Integrated Graphics 270 | # 271 | # dedicated: 272 | # GPU1: AMD HD 7950 273 | # 274 | # integrated: 275 | # GPU1: Intel Integrated Graphics 276 | gpu_type="all" 277 | 278 | 279 | # Resolution 280 | 281 | 282 | # Display refresh rate next to each monitor 283 | # Default: 'off' 284 | # Values: 'on', 'off' 285 | # Flag: --refresh_rate 286 | # Supports: Doesn't work on Windows. 287 | # 288 | # Example: 289 | # on: '1920x1080 @ 60Hz' 290 | # off: '1920x1080' 291 | refresh_rate="off" 292 | 293 | 294 | # Gtk Theme / Icons / Font 295 | 296 | 297 | # Shorten output of GTK Theme / Icons / Font 298 | # 299 | # Default: 'off' 300 | # Values: 'on', 'off' 301 | # Flag: --gtk_shorthand 302 | # 303 | # Example: 304 | # on: 'Numix, Adwaita' 305 | # off: 'Numix [GTK2], Adwaita [GTK3]' 306 | gtk_shorthand="off" 307 | 308 | 309 | # Enable/Disable gtk2 Theme / Icons / Font 310 | # 311 | # Default: 'on' 312 | # Values: 'on', 'off' 313 | # Flag: --gtk2 314 | # 315 | # Example: 316 | # on: 'Numix [GTK2], Adwaita [GTK3]' 317 | # off: 'Adwaita [GTK3]' 318 | gtk2="on" 319 | 320 | # Enable/Disable gtk3 Theme / Icons / Font 321 | # 322 | # Default: 'on' 323 | # Values: 'on', 'off' 324 | # Flag: --gtk3 325 | # 326 | # Example: 327 | # on: 'Numix [GTK2], Adwaita [GTK3]' 328 | # off: 'Numix [GTK2]' 329 | gtk3="on" 330 | 331 | 332 | # IP Address 333 | 334 | 335 | # Website to ping for the public IP 336 | # 337 | # Default: 'http://ident.me' 338 | # Values: 'url' 339 | # Flag: --ip_host 340 | public_ip_host="http://ident.me" 341 | 342 | # Public IP timeout. 343 | # 344 | # Default: '2' 345 | # Values: 'int' 346 | # Flag: --ip_timeout 347 | public_ip_timeout=2 348 | 349 | 350 | # Disk 351 | 352 | 353 | # Which disks to display. 354 | # The values can be any /dev/sdXX, mount point or directory. 355 | # NOTE: By default we only show the disk info for '/'. 356 | # 357 | # Default: '/' 358 | # Values: '/', '/dev/sdXX', '/path/to/drive'. 359 | # Flag: --disk_show 360 | # 361 | # Example: 362 | # disk_show=('/' '/dev/sdb1'): 363 | # 'Disk (/): 74G / 118G (66%)' 364 | # 'Disk (/mnt/Videos): 823G / 893G (93%)' 365 | # 366 | # disk_show=('/'): 367 | # 'Disk (/): 74G / 118G (66%)' 368 | # 369 | disk_show=('/') 370 | 371 | # Disk subtitle. 372 | # What to append to the Disk subtitle. 373 | # 374 | # Default: 'mount' 375 | # Values: 'mount', 'name', 'dir' 376 | # Flag: --disk_subtitle 377 | # 378 | # Example: 379 | # name: 'Disk (/dev/sda1): 74G / 118G (66%)' 380 | # 'Disk (/dev/sdb2): 74G / 118G (66%)' 381 | # 382 | # mount: 'Disk (/): 74G / 118G (66%)' 383 | # 'Disk (/mnt/Local Disk): 74G / 118G (66%)' 384 | # 'Disk (/mnt/Videos): 74G / 118G (66%)' 385 | # 386 | # dir: 'Disk (/): 74G / 118G (66%)' 387 | # 'Disk (Local Disk): 74G / 118G (66%)' 388 | # 'Disk (Videos): 74G / 118G (66%)' 389 | disk_subtitle="mount" 390 | 391 | 392 | # Song 393 | 394 | 395 | # Manually specify a music player. 396 | # 397 | # Default: 'auto' 398 | # Values: 'auto', 'player-name' 399 | # Flag: --music_player 400 | # 401 | # Available values for 'player-name': 402 | # 403 | # amarok 404 | # audacious 405 | # banshee 406 | # bluemindo 407 | # clementine 408 | # cmus 409 | # deadbeef 410 | # deepin-music 411 | # dragon 412 | # elisa 413 | # exaile 414 | # gnome-music 415 | # gmusicbrowser 416 | # guayadeque 417 | # iTunes 418 | # juk 419 | # lollypop 420 | # mocp 421 | # mopidy 422 | # mpd 423 | # netease-cloud-music 424 | # pogo 425 | # pragha 426 | # qmmp 427 | # quodlibet 428 | # rhythmbox 429 | # sayonara 430 | # smplayer 431 | # spotify 432 | # tomahawk 433 | # vlc 434 | # xmms2d 435 | # yarock 436 | music_player="auto" 437 | 438 | # Format to display song information. 439 | # 440 | # Default: '%artist% - %album% - %title%' 441 | # Values: '%artist%', '%album%', '%title%' 442 | # Flag: --song_format 443 | # 444 | # Example: 445 | # default: 'Song: Jet - Get Born - Sgt Major' 446 | song_format="%artist% - %album% - %title%" 447 | 448 | # Print the Artist, Album and Title on separate lines 449 | # 450 | # Default: 'off' 451 | # Values: 'on', 'off' 452 | # Flag: --song_shorthand 453 | # 454 | # Example: 455 | # on: 'Artist: The Fratellis' 456 | # 'Album: Costello Music' 457 | # 'Song: Chelsea Dagger' 458 | # 459 | # off: 'Song: The Fratellis - Costello Music - Chelsea Dagger' 460 | song_shorthand="off" 461 | 462 | # 'mpc' arguments (specify a host, password etc). 463 | # 464 | # Default: '' 465 | # Example: mpc_args=(-h HOST -P PASSWORD) 466 | mpc_args=() 467 | 468 | 469 | # Text Colors 470 | 471 | 472 | # Text Colors 473 | # 474 | # Default: 'distro' 475 | # Values: 'distro', 'num' 'num' 'num' 'num' 'num' 'num' 476 | # Flag: --colors 477 | # 478 | # Each number represents a different part of the text in 479 | # this order: 'title', '@', 'underline', 'subtitle', 'colon', 'info' 480 | # 481 | # Example: 482 | # colors=(distro) - Text is colored based on Distro colors. 483 | # colors=(4 6 1 8 8 6) - Text is colored in the order above. 484 | colors=(distro) 485 | 486 | 487 | # Text Options 488 | 489 | 490 | # Toggle bold text 491 | # 492 | # Default: 'on' 493 | # Values: 'on', 'off' 494 | # Flag: --bold 495 | bold="on" 496 | 497 | # Enable/Disable Underline 498 | # 499 | # Default: 'on' 500 | # Values: 'on', 'off' 501 | # Flag: --underline 502 | underline_enabled="on" 503 | 504 | # Underline character 505 | # 506 | # Default: '-' 507 | # Values: 'string' 508 | # Flag: --underline_char 509 | underline_char="-" 510 | 511 | 512 | # Info Separator 513 | # Replace the default separator with the specified string. 514 | # 515 | # Default: ':' 516 | # Flag: --separator 517 | # 518 | # Example: 519 | # separator="->": 'Shell-> bash' 520 | # separator=" =": 'WM = dwm' 521 | separator=":" 522 | 523 | 524 | # Color Blocks 525 | 526 | 527 | # Color block range 528 | # The range of colors to print. 529 | # 530 | # Default: '0', '7' 531 | # Values: 'num' 532 | # Flag: --block_range 533 | # 534 | # Example: 535 | # 536 | # Display colors 0-7 in the blocks. (8 colors) 537 | # neofetch --block_range 0 7 538 | # 539 | # Display colors 0-15 in the blocks. (16 colors) 540 | # neofetch --block_range 0 15 541 | block_range=(0 7) 542 | 543 | # Toggle color blocks 544 | # 545 | # Default: 'on' 546 | # Values: 'on', 'off' 547 | # Flag: --color_blocks 548 | color_blocks="on" 549 | 550 | # Color block width in spaces 551 | # 552 | # Default: '3' 553 | # Values: 'num' 554 | # Flag: --block_width 555 | block_width=3 556 | 557 | # Color block height in lines 558 | # 559 | # Default: '1' 560 | # Values: 'num' 561 | # Flag: --block_height 562 | block_height=1 563 | 564 | 565 | # Progress Bars 566 | 567 | 568 | # Bar characters 569 | # 570 | # Default: '-', '=' 571 | # Values: 'string', 'string' 572 | # Flag: --bar_char 573 | # 574 | # Example: 575 | # neofetch --bar_char 'elapsed' 'total' 576 | # neofetch --bar_char '-' '=' 577 | bar_char_elapsed="-" 578 | bar_char_total="=" 579 | 580 | # Toggle Bar border 581 | # 582 | # Default: 'on' 583 | # Values: 'on', 'off' 584 | # Flag: --bar_border 585 | bar_border="on" 586 | 587 | # Progress bar length in spaces 588 | # Number of chars long to make the progress bars. 589 | # 590 | # Default: '15' 591 | # Values: 'num' 592 | # Flag: --bar_length 593 | bar_length=15 594 | 595 | # Progress bar colors 596 | # When set to distro, uses your distro's logo colors. 597 | # 598 | # Default: 'distro', 'distro' 599 | # Values: 'distro', 'num' 600 | # Flag: --bar_colors 601 | # 602 | # Example: 603 | # neofetch --bar_colors 3 4 604 | # neofetch --bar_colors distro 5 605 | bar_color_elapsed="distro" 606 | bar_color_total="distro" 607 | 608 | 609 | # Info display 610 | # Display a bar with the info. 611 | # 612 | # Default: 'off' 613 | # Values: 'bar', 'infobar', 'barinfo', 'off' 614 | # Flags: --cpu_display 615 | # --memory_display 616 | # --battery_display 617 | # --disk_display 618 | # 619 | # Example: 620 | # bar: '[---=======]' 621 | # infobar: 'info [---=======]' 622 | # barinfo: '[---=======] info' 623 | # off: 'info' 624 | cpu_display="off" 625 | memory_display="off" 626 | battery_display="off" 627 | disk_display="off" 628 | 629 | 630 | # Backend Settings 631 | 632 | 633 | # Image backend. 634 | # 635 | # Default: 'ascii' 636 | # Values: 'ascii', 'caca', 'chafa', 'jp2a', 'iterm2', 'off', 637 | # 'termpix', 'pixterm', 'tycat', 'w3m', 'kitty' 638 | # Flag: --backend 639 | image_backend="ascii" 640 | 641 | # Image Source 642 | # 643 | # Which image or ascii file to display. 644 | # 645 | # Default: 'auto' 646 | # Values: 'auto', 'ascii', 'wallpaper', '/path/to/img', '/path/to/ascii', '/path/to/dir/' 647 | # 'command output (neofetch --ascii "$(fortune | cowsay -W 30)")' 648 | # Flag: --source 649 | # 650 | # NOTE: 'auto' will pick the best image source for whatever image backend is used. 651 | # In ascii mode, distro ascii art will be used and in an image mode, your 652 | # wallpaper will be used. 653 | image_source="auto" 654 | 655 | 656 | # Ascii Options 657 | 658 | 659 | # Ascii distro 660 | # Which distro's ascii art to display. 661 | # 662 | # Default: 'auto' 663 | # Values: 'auto', 'distro_name' 664 | # Flag: --ascii_distro 665 | # 666 | # NOTE: Arch and Ubuntu have 'old' logo variants. 667 | # Change this to 'arch_old' or 'ubuntu_old' to use the old logos. 668 | # NOTE: Ubuntu has flavor variants. 669 | # Change this to 'Lubuntu', 'Xubuntu', 'Ubuntu-GNOME' or 'Ubuntu-Budgie' to use the flavors. 670 | # NOTE: Arch, Crux and Gentoo have a smaller logo variant. 671 | # Change this to 'arch_small', 'crux_small' or 'gentoo_small' to use the small logos. 672 | ascii_distro="auto" 673 | 674 | # Ascii Colors 675 | # 676 | # Default: 'distro' 677 | # Values: 'distro', 'num' 'num' 'num' 'num' 'num' 'num' 678 | # Flag: --ascii_colors 679 | # 680 | # Example: 681 | # ascii_colors=(distro) - Ascii is colored based on Distro colors. 682 | # ascii_colors=(4 6 1 8 8 6) - Ascii is colored using these colors. 683 | ascii_colors=(distro) 684 | 685 | # Bold ascii logo 686 | # Whether or not to bold the ascii logo. 687 | # 688 | # Default: 'on' 689 | # Values: 'on', 'off' 690 | # Flag: --ascii_bold 691 | ascii_bold="on" 692 | 693 | 694 | # Image Options 695 | 696 | 697 | # Image loop 698 | # Setting this to on will make neofetch redraw the image constantly until 699 | # Ctrl+C is pressed. This fixes display issues in some terminal emulators. 700 | # 701 | # Default: 'off' 702 | # Values: 'on', 'off' 703 | # Flag: --loop 704 | image_loop="off" 705 | 706 | # Thumbnail directory 707 | # 708 | # Default: '~/.cache/thumbnails/neofetch' 709 | # Values: 'dir' 710 | thumbnail_dir="${XDG_CACHE_HOME:-${HOME}/.cache}/thumbnails/neofetch" 711 | 712 | # Crop mode 713 | # 714 | # Default: 'normal' 715 | # Values: 'normal', 'fit', 'fill' 716 | # Flag: --crop_mode 717 | # 718 | # See this wiki page to learn about the fit and fill options. 719 | # https://github.com/dylanaraps/neofetch/wiki/What-is-Waifu-Crop%3F 720 | crop_mode="normal" 721 | 722 | # Crop offset 723 | # Note: Only affects 'normal' crop mode. 724 | # 725 | # Default: 'center' 726 | # Values: 'northwest', 'north', 'northeast', 'west', 'center' 727 | # 'east', 'southwest', 'south', 'southeast' 728 | # Flag: --crop_offset 729 | crop_offset="center" 730 | 731 | # Image size 732 | # The image is half the terminal width by default. 733 | # 734 | # Default: 'auto' 735 | # Values: 'auto', '00px', '00%', 'none' 736 | # Flags: --image_size 737 | # --size 738 | image_size="auto" 739 | 740 | # Gap between image and text 741 | # 742 | # Default: '3' 743 | # Values: 'num', '-num' 744 | # Flag: --gap 745 | gap=3 746 | 747 | # Image offsets 748 | # Only works with the w3m backend. 749 | # 750 | # Default: '0' 751 | # Values: 'px' 752 | # Flags: --xoffset 753 | # --yoffset 754 | yoffset=0 755 | xoffset=0 756 | 757 | # Image background color 758 | # Only works with the w3m backend. 759 | # 760 | # Default: '' 761 | # Values: 'color', 'blue' 762 | # Flag: --bg_color 763 | background_color= 764 | 765 | 766 | # Misc Options 767 | 768 | # Stdout mode 769 | # Turn off all colors and disables image backend (ASCII/Image). 770 | # Useful for piping into another command. 771 | # Default: 'off' 772 | # Values: 'on', 'off' 773 | stdout="off" 774 | -------------------------------------------------------------------------------- /nhl-image/ansible/setup-raspberry.yml: -------------------------------------------------------------------------------- 1 | - 2 | name: Setup RPi - Scoreboard setup 3 | hosts: all 4 | become: true 5 | become_user: root 6 | vars: 7 | keyboard_keymap: "us" 8 | timezone: "America/Toronto" 9 | locale: en_US.UTF-8 10 | wifi_country: CA 11 | gh_repo: "https://github.com/falkyre/nhl-led-scoreboard.git" 12 | use_beta: false 13 | gh_branch: "2025.x.x-beta" 14 | system_hostname: scoreboard 15 | apt_proxy: "" 16 | use_apt_proxy: false 17 | pypi_proxy: "" 18 | imgver: "{{ lookup('file', 'VERSION').splitlines()[0].split(':')[-1] }}" 19 | 20 | tasks: 21 | # use only become_user: root if task should be run as superuser 22 | - name: Setup hostname using raspi-config 23 | ansible.builtin.command: "/usr/bin/raspi-config nonint do_hostname {{ system_hostname }}" 24 | register: my_output # <- Registers the command output. 25 | changed_when: my_output.rc != 0 26 | 27 | - name: Setup keyboard layout using raspi-config 28 | ansible.builtin.command: "/usr/bin/raspi-config nonint do_configure_keyboard {{ keyboard_keymap }}" 29 | register: my_output # <- Registers the command output. 30 | changed_when: my_output.rc != 0 31 | 32 | - name: Setup timezone using raspi-config 33 | ansible.builtin.command: "/usr/bin/raspi-config nonint do_change_timezone {{ timezone }}" 34 | register: my_output # <- Registers the command output. 35 | changed_when: my_output.rc != 0 36 | 37 | - name: Setup locale using raspi-config 38 | ansible.builtin.command: "/usr/bin/raspi-config nonint do_change_locale {{ locale }}" 39 | register: my_output # <- Registers the command output. 40 | changed_when: my_output.rc != 0 41 | 42 | - name: Setup Wifi Regulatory Country using raspi-config 43 | ansible.builtin.command: "/usr/bin/raspi-config nonint do_wifi_country {{ wifi_country }}" 44 | register: my_output # <- Registers the command output. 45 | changed_when: my_output.rc != 0 46 | 47 | # - meta: end_play 48 | 49 | - name: Setup apt proxy 50 | ansible.builtin.copy: 51 | src: ./files/sb_image/51cache 52 | dest: /etc/apt/apt.conf.d/ 53 | mode: "0644" 54 | when: use_apt_proxy 55 | 56 | - name: Update apt proxy in 51cache file 57 | ansible.builtin.replace: 58 | path: /etc/apt/apt.conf.d/51cache 59 | regexp: 'APT_PROXY' 60 | replace: "{{ apt_proxy }}" 61 | when: use_apt_proxy 62 | 63 | - name: Install git and build essential 64 | ansible.builtin.apt: 65 | name: 66 | - git 67 | - build-essential 68 | state: present 69 | 70 | - name: Enable SSH 71 | ansible.builtin.file: 72 | path: /boot/ssh 73 | state: touch 74 | mode: "0600" 75 | 76 | - name: Disable "Please note that SSH may not work until a valid user has been set up." message 77 | ansible.builtin.file: 78 | path: /etc/ssh/sshd_config.d/rename_user.conf 79 | state: absent 80 | 81 | - name: Add temporary password for root user 82 | ansible.builtin.user: 83 | name: root 84 | password: "{{ 'letiretlebut' | password_hash('sha512') }}" 85 | 86 | # This is the new way to create a user on raspiOS headless, if you don't do it this way, the system unit for 87 | # multi-user.target never hits and can slow down boots. 88 | - name: Create pi user encrypted password 89 | ansible.builtin.shell: 90 | cmd: set -o pipefail && echo "scoreboard" | openssl passwd -6 -stdin 91 | executable: /bin/bash 92 | register: pi_passwd 93 | changed_when: pi_passwd.rc != 0 94 | 95 | - name: Add pi user name to start of line 96 | ansible.builtin.copy: 97 | dest: /boot/userconf 98 | content: 'pi:{{ pi_passwd.stdout }}' 99 | mode: preserve 100 | 101 | # This will need to change with Trixie. The cmdline.txt is in /boot/fireware now 102 | - name: Force isocpus=3 in cmdline.txt so matrix code runs smoother 103 | ansible.builtin.shell: sed -i 's/$/ isolcpus=3/' /boot/cmdline.txt # noqa: command-instead-of-module 104 | args: 105 | chdir: /boot/ 106 | register: my_output # <- Registers the command output. 107 | changed_when: my_output.rc != 0 108 | 109 | - name: Create /boot/scoreboard directory 110 | ansible.builtin.file: 111 | path: /boot/scoreboard 112 | state: directory 113 | owner: root 114 | group: root 115 | mode: '0755' 116 | 117 | - name: Copy import_readme.txt to /boot/scoreboard 118 | ansible.builtin.copy: 119 | src: ./files/sb_image/import_readme.txt 120 | dest: /boot/scoreboard/ 121 | owner: root 122 | group: root 123 | mode: 0644 124 | 125 | 126 | - name: Blacklist snd_bcm module (as per hzeller's instructions) 127 | ansible.builtin.copy: 128 | dest: /etc/modprobe.d/blacklist-rgb-matrix.conf 129 | content: | 130 | blacklist snd_bcm2835 131 | mode: preserve 132 | 133 | - name: Disable onboard audio 134 | ansible.builtin.lineinfile: 135 | path: /boot/config.txt 136 | regexp: '^dtparam=audio=on$' 137 | line: 'dtparam=audio=off' 138 | state: present 139 | 140 | - name: Copy global bash.bashrc 141 | ansible.builtin.copy: 142 | src: ./files/sb_image/bash.bashrc 143 | dest: /etc/ 144 | mode: "0644" 145 | 146 | - name: Copy scoreboard.bash to bashrc.d 147 | ansible.builtin.copy: 148 | src: ./files/sb_image/scoreboard.bash 149 | dest: /etc/bashrc.d/ 150 | mode: "0644" 151 | 152 | - name: Setup cron.scoreboard 153 | ansible.builtin.copy: 154 | src: ./files/sb_image/get_version 155 | dest: /etc/cron.scoreboard 156 | mode: "0755" 157 | 158 | - name: Setup crontab to call cron.scoreboard on reboots 159 | ansible.builtin.copy: 160 | src: ./files/sb_image/crontab 161 | dest: /etc/ 162 | backup: true 163 | mode: preserve 164 | 165 | - name: Setup pi user crontab for daily check of version 166 | ansible.builtin.cron: 167 | user: pi 168 | name: "Check scoreboard version" 169 | minute: 0 170 | hour: 3 171 | job: "/home/pi/sbtools/checkUpdate.sh > /home/pi/.nhlledportal/status" 172 | 173 | 174 | - name: Install aptfile (used for updates) 175 | ansible.builtin.copy: 176 | src: ./files/sb_image/aptfile 177 | dest: /usr/local/bin 178 | mode: "0755" 179 | 180 | - name: Install OS packages needed for scoreboard 181 | ansible.builtin.apt: 182 | update_cache: true 183 | name: 184 | - cmake 185 | - cython3 186 | - python3-pip 187 | - python3-cairosvg 188 | - python3-dev 189 | - python3-setuptools 190 | - python3-numpy 191 | - python3-pandas 192 | - python3-pil 193 | - python3-numpy 194 | - python3-pygame 195 | - python3-tk 196 | - python3-gpiozero 197 | - python3-virtualenv 198 | - python3-venv 199 | - supervisor 200 | - neofetch 201 | - pastebinit 202 | - jq 203 | - neovim 204 | - zsh 205 | - direnv 206 | - libwebp-dev 207 | - dbus 208 | - libsixel-bin 209 | - libgraphicsmagick++-dev 210 | state: present 211 | 212 | - name: Remove OS packages that will cause issues with scoreboard 213 | ansible.builtin.apt: 214 | name: 215 | - bluez 216 | - bluez-firmware 217 | - pi-bluetooth 218 | - triggerhappy 219 | clean: true 220 | state: absent 221 | 222 | - name: Enable NetworkManager service 223 | ansible.builtin.systemd: 224 | name: NetworkManager 225 | enabled: true 226 | masked: false 227 | 228 | - name: Disable Supervisor service 229 | ansible.builtin.systemd: 230 | name: supervisor 231 | enabled: false 232 | masked: false 233 | 234 | - name: Disable and mask services so we can use comitup for wifi setup 235 | ansible.builtin.systemd: 236 | name: "{{ item }}" 237 | masked: true 238 | with_items: 239 | - dhcpcd 240 | - dnsmasq 241 | - systemd-resolved 242 | - dhcpd 243 | - wpa-supplicant 244 | - NetworkManager-wait-online 245 | 246 | - name: Delete network interfaces file 247 | ansible.builtin.file: 248 | path: /etc/network/interface 249 | state: absent 250 | 251 | - name: Install comitup deb package from the internet 252 | ansible.builtin.apt: 253 | deb: https://davesteele.github.io/comitup/deb/davesteele-comitup-apt-source_1.3_all.deb 254 | dpkg_options: "force-all" 255 | 256 | - name: Install comitup and comitup-web packages 257 | ansible.builtin.apt: 258 | update_cache: true 259 | name: "{{ item }}" 260 | with_items: 261 | - comitup 262 | - comitup-watch 263 | 264 | - name: Copy scoreboard comitup.conf file 265 | ansible.builtin.copy: 266 | src: ./files/comitup-conf/comitup.conf 267 | dest: /etc/ 268 | backup: true 269 | mode: "0755" 270 | 271 | - name: Extract scoreboard template into the comitup flask template directory 272 | ansible.builtin.unarchive: 273 | src: ./files/comitup-conf/scoreboard_ui.tar.gz 274 | dest: /usr/share/comitup/web/templates 275 | 276 | - name: Update template path variable in comitupweb.py to use scoreboard template 277 | ansible.builtin.lineinfile: 278 | path: /usr/share/comitup/web/comitupweb.py 279 | regexp: '^TEMPLATE_PATH =' 280 | line: TEMPLATE_PATH = "/usr/share/comitup/web/templates/scoreboard" 281 | 282 | - name: Get nls-controlhub package 283 | ansible.builtin.get_url: 284 | url: "https://github.com/falkyre/nhl-setup/releases/download/latest-web/nls-controlhub-deb-bookworm-armv7.deb" 285 | dest: "/tmp/nls-controlhub-deb-bookworm-armv7.deb" 286 | mode: "0644" 287 | 288 | - name: Install NLS ControlHub using dpkg 289 | ansible.builtin.command: dpkg -i /tmp/nls-controlhub-deb-bookworm-armv7.deb 290 | register: dpkg_result 291 | changed_when: "'Setting up' in dpkg_result.stdout" 292 | 293 | - name: Copy nls_controlhub_cfg.toml to /etc/nls_controlhub 294 | ansible.builtin.copy: 295 | src: "./files/sb_image/nls_controlhub_cfg.toml" 296 | dest: "/etc/nls_controlhub/config.toml" 297 | mode: "0644" 298 | 299 | - name: Install scoreboard services 300 | ansible.builtin.copy: 301 | src: "./files/sb_image/{{ item }}" 302 | dest: /etc/systemd/system 303 | mode: "0644" 304 | with_items: 305 | - sb_splash.service 306 | - sb_autosettz.service 307 | - wifi-on.service 308 | - nls_controlhub.service 309 | 310 | - name: Enable scoreboard services 311 | ansible.builtin.systemd: 312 | name: "{{ item }}" 313 | enabled: true 314 | masked: false 315 | with_items: 316 | - sb_splash.service 317 | - sb_autosettz.service 318 | - wifi-on.service 319 | - nls_controlhub.service 320 | 321 | - name: Copy scoreboard supervisord.conf 322 | ansible.builtin.copy: 323 | src: ./files/sb_image/supervisord.conf 324 | dest: /etc/supervisor/ 325 | backup: true 326 | mode: preserve 327 | 328 | - name: Copy scoreboard scoreboard.conf 329 | ansible.builtin.copy: 330 | src: ./files/sb_image/scoreboard.conf 331 | dest: /etc/supervisor/conf.d/ 332 | backup: true 333 | mode: preserve 334 | 335 | - name: Copy scoreboard scoreboard-emulated.conf 336 | ansible.builtin.copy: 337 | src: ./files/sb_image/scoreboard-emulated.conf 338 | dest: /etc/supervisor/conf.d/ 339 | backup: true 340 | mode: preserve 341 | 342 | - name: Copy sbtools directory 343 | become: true 344 | become_user: pi 345 | ansible.builtin.copy: 346 | src: ./files/sb_image/sbtools 347 | dest: ~/ 348 | mode: preserve 349 | 350 | - name: Create .config/neofetch directory in pi home 351 | become: true 352 | become_user: pi 353 | ansible.builtin.file: 354 | path: ~/.config/neofetch 355 | state: directory 356 | mode: "0755" 357 | 358 | - name: Copy scoreboard neofetch config 359 | become: true 360 | become_user: pi 361 | ansible.builtin.copy: 362 | src: ./files/sb_image/.config/neofetch/neofetch_config.conf 363 | dest: ~/.config/neofetch/config.conf 364 | mode: "0755" 365 | 366 | - name: Create .nhlledportal directory 367 | become: true 368 | become_user: pi 369 | ansible.builtin.file: 370 | path: ~/.nhlledportal 371 | state: directory 372 | mode: "0755" 373 | 374 | - name: Create startup files in .nhlledportal 375 | become: true 376 | become_user: pi 377 | ansible.builtin.file: 378 | path: "~/.nhlledportal/{{ item }}" 379 | state: touch 380 | mode: "0755" 381 | with_items: 382 | - setTZ 383 | - SETUP 384 | 385 | - name: Put image version in .nhlledportal 386 | become: true 387 | become_user: pi 388 | ansible.builtin.copy: 389 | dest: ~/.nhlledportal/imgver 390 | content: 'You are running the image version {{ imgver }}' 391 | mode: preserve 392 | 393 | 394 | - name: Setup pi user bash and gitconfig 395 | become: true 396 | become_user: pi 397 | ansible.builtin.copy: 398 | src: "./files/sb_image/{{ item }}" 399 | dest: ~/ 400 | mode: "0644" 401 | with_items: 402 | - .gitconfig 403 | - .bashrc 404 | 405 | - name: Download scoreboard release repo "{{ gh_repo }}" # noqa: latest 406 | become: true 407 | become_user: pi 408 | ansible.builtin.git: 409 | repo: "{{ gh_repo }}" 410 | dest: ~/nhl-led-scoreboard 411 | recursive: true 412 | depth: 1 413 | when: not use_beta 414 | 415 | - name: Download scoreboard beta repo "{{ gh_repo }}" # noqa: latest 416 | become: true 417 | become_user: pi 418 | ansible.builtin.git: 419 | repo: "{{ gh_repo }}" 420 | dest: ~/nhl-led-scoreboard 421 | recursive: true 422 | depth: 1 423 | version: "{{ gh_branch }}" 424 | when: use_beta 425 | 426 | - name: Install required python packages for scoreboard 427 | become: true 428 | become_user: pi 429 | ansible.builtin.pip: 430 | virtualenv: /home/pi/nhlsb-venv 431 | virtualenv_python: python3 432 | virtualenv_site_packages: true 433 | extra_args: "{{ pypi_proxy }}" 434 | requirements: /home/pi/nhl-led-scoreboard/requirements.txt 435 | 436 | - name: Do pip self-update 437 | become: true 438 | become_user: pi 439 | ansible.builtin.pip: 440 | virtualenv: /home/pi/nhlsb-venv 441 | virtualenv_python: python3 442 | virtualenv_site_packages: true 443 | extra_args: "{{ pypi_proxy }}" 444 | state: forcereinstall 445 | name: 446 | - pip 447 | 448 | - name: Install extra python packages for scoreboard 449 | become: true 450 | become_user: pi 451 | ansible.builtin.pip: 452 | virtualenv: /home/pi/nhlsb-venv 453 | virtualenv_python: python3 454 | virtualenv_site_packages: true 455 | extra_args: "{{ pypi_proxy }}" 456 | name: 457 | - archey4 458 | - tzlocal 459 | - lastversion 460 | - rich-cli 461 | - cairosvg 462 | 463 | - name: Copy rgb matrix install_bindings.sh 464 | become: true 465 | become_user: pi 466 | ansible.builtin.copy: 467 | src: ./files/sb_image/install_bindings.sh 468 | dest: /home/pi/nhl-led-scoreboard 469 | mode: "0755" 470 | 471 | - name: Install rgb matrix python bindings 472 | become: true 473 | become_user: pi 474 | ansible.builtin.command: /home/pi/nhl-led-scoreboard/install_bindings.sh 475 | args: 476 | chdir: /home/pi/nhl-led-scoreboard 477 | register: my_output # <- Registers the command output. 478 | changed_when: my_output.rc != 0 479 | 480 | - name: Remove install_bindings.sh 481 | become: true 482 | become_user: pi 483 | ansible.builtin.file: 484 | path: /home/pi/nhl-led-scoreboard/install_bindings.sh 485 | state: absent 486 | 487 | - name: Create direnv setup to auto activate venv 488 | become: true 489 | become_user: pi 490 | ansible.builtin.copy: 491 | dest: /home/pi/nhl-led-scoreboard/.envrc 492 | content: | 493 | export VIRTUAL_ENV=/home/pi/nhlsb-venv 494 | source /home/pi/nhlsb-venv/bin/activate 495 | export PYVER=`python3 -V` 496 | unset PS1 497 | mode: "0755" 498 | 499 | - name: Activate direnv 500 | become: true 501 | become_user: pi 502 | ansible.builtin.command: direnv allow ~/nhl-led-scoreboard 503 | register: my_output # <- Registers the command output. 504 | changed_when: my_output.rc != 0 505 | 506 | - name: Download nhl_setup executable for 64bit Trixie systems 507 | become: true 508 | become_user: pi 509 | ansible.builtin.get_url: 510 | url: "https://github.com/falkyre/nhl-setup/releases/download/latest-cli/nhl_setup.32" 511 | dest: "/home/pi/nhl-led-scoreboard/nhl_setup" 512 | mode: '0755' 513 | when: ansible_architecture != 'aarch64' and ansible_architecture != 'arm64' and ansible_distribution_release == 'trixie' 514 | 515 | - name: Download nhl_setup executable for 64bit Trixie systems 516 | become: true 517 | become_user: pi 518 | ansible.builtin.get_url: 519 | url: "https://github.com/falkyre/nhl-setup/releases/download/latest-cli/nhl_setup.64" 520 | dest: "/home/pi/nhl-led-scoreboard/nhl_setup" 521 | mode: '0755' 522 | when: (ansible_architecture == 'aarch64' or ansible_architecture == 'arm64') and ansible_distribution_release == 'trixie' 523 | 524 | - name: Download nhl_setup executable for 32bit Bookworm systems 525 | become: true 526 | become_user: pi 527 | ansible.builtin.get_url: 528 | url: "https://github.com/falkyre/nhl-setup/releases/download/latest-cli/nhl_setup.bookworm.32" 529 | dest: "/home/pi/nhl-led-scoreboard/nhl_setup" 530 | mode: '0755' 531 | when: ansible_architecture != 'aarch64' and ansible_architecture != 'arm64' and ansible_distribution_release == 'bookworm' 532 | 533 | - name: Download nhl_setup executable for 64bit Bookworm systems 534 | become: true 535 | become_user: pi 536 | ansible.builtin.get_url: 537 | url: "https://github.com/falkyre/nhl-setup/releases/download/latest-cli/nhl_setup.bookworm.64" 538 | dest: "/home/pi/nhl-led-scoreboard/nhl_setup" 539 | mode: '0755' 540 | when: (ansible_architecture == 'aarch64' or ansible_architecture == 'arm64') and ansible_distribution_release == 'bookworm' 541 | 542 | - name: Update .nhlledportal/status file 543 | become: true 544 | become_user: pi 545 | ansible.builtin.shell: /home/pi/sbtools/checkUpdate.sh > /home/pi/.nhlledportal/status 546 | args: 547 | chdir: /home/pi/sbtools 548 | register: my_output # <- Registers the command output. 549 | changed_when: my_output.rc != 0 550 | 551 | - name: Create softlink for sb-upgrade 552 | become: true 553 | become_user: pi 554 | ansible.builtin.file: 555 | src: /home/pi/nhl-led-scoreboard/scripts/sbtools/sb-upgrade 556 | dest: /home/pi/sbtools/sb-upgrade 557 | state: link 558 | force: true 559 | 560 | - name: Remove 51cache file (delete file) 561 | 562 | ansible.builtin.file: 563 | path: /etc/apt/apt.conf.d/51cache 564 | state: absent 565 | when: use_apt_proxy 566 | 567 | - name: Clean up apt cache 568 | become: true 569 | ansible.builtin.apt: 570 | autoclean: true 571 | autoremove: true 572 | clean: true -------------------------------------------------------------------------------- /nhl-image/ansible/setup-raspberry-beta.yml: -------------------------------------------------------------------------------- 1 | - 2 | name: Setup RPi - Scoreboard setup 3 | hosts: all 4 | become: true 5 | become_user: root 6 | vars: 7 | keyboard_keymap: "us" 8 | timezone: "America/Toronto" 9 | locale: en_US.UTF-8 10 | wifi_country: CA 11 | gh_repo: "https://github.com/falkyre/nhl-led-scoreboard.git" 12 | use_beta: true 13 | gh_branch: "2025.x.x-beta" 14 | system_hostname: scoreboard 15 | apt_proxy: "" 16 | use_apt_proxy: false 17 | pypi_proxy: "" 18 | imgver: "{{ lookup('file', 'VERSION').splitlines()[0].split(':')[-1] }}" 19 | 20 | tasks: 21 | # use only become_user: root if task should be run as superuser 22 | - name: Setup hostname using raspi-config 23 | ansible.builtin.command: "/usr/bin/raspi-config nonint do_hostname {{ system_hostname }}" 24 | register: my_output # <- Registers the command output. 25 | changed_when: my_output.rc != 0 26 | 27 | - name: Setup keyboard layout using raspi-config 28 | ansible.builtin.command: "/usr/bin/raspi-config nonint do_configure_keyboard {{ keyboard_keymap }}" 29 | register: my_output # <- Registers the command output. 30 | changed_when: my_output.rc != 0 31 | 32 | - name: Setup timezone using raspi-config 33 | ansible.builtin.command: "/usr/bin/raspi-config nonint do_change_timezone {{ timezone }}" 34 | register: my_output # <- Registers the command output. 35 | changed_when: my_output.rc != 0 36 | 37 | - name: Setup locale using raspi-config 38 | ansible.builtin.command: "/usr/bin/raspi-config nonint do_change_locale {{ locale }}" 39 | register: my_output # <- Registers the command output. 40 | changed_when: my_output.rc != 0 41 | 42 | - name: Setup Wifi Regulatory Country using raspi-config 43 | ansible.builtin.command: "/usr/bin/raspi-config nonint do_wifi_country {{ wifi_country }}" 44 | register: my_output # <- Registers the command output. 45 | changed_when: my_output.rc != 0 46 | 47 | # - meta: end_play 48 | 49 | - name: Setup apt proxy 50 | ansible.builtin.copy: 51 | src: ./files/sb_image/51cache 52 | dest: /etc/apt/apt.conf.d/ 53 | mode: "0644" 54 | when: use_apt_proxy 55 | 56 | - name: Update apt proxy in 51cache file 57 | ansible.builtin.replace: 58 | path: /etc/apt/apt.conf.d/51cache 59 | regexp: 'APT_PROXY' 60 | replace: "{{ apt_proxy }}" 61 | when: use_apt_proxy 62 | 63 | - name: Install git and build essential 64 | ansible.builtin.apt: 65 | name: 66 | - git 67 | - build-essential 68 | state: present 69 | 70 | - name: Enable SSH 71 | ansible.builtin.file: 72 | path: /boot/ssh 73 | state: touch 74 | mode: "0600" 75 | 76 | - name: Disable "Please note that SSH may not work until a valid user has been set up." message 77 | ansible.builtin.file: 78 | path: /etc/ssh/sshd_config.d/rename_user.conf 79 | state: absent 80 | 81 | - name: Add temporary password for root user 82 | ansible.builtin.user: 83 | name: root 84 | password: "{{ 'letiretlebut' | password_hash('sha512') }}" 85 | 86 | # This is the new way to create a user on raspiOS headless, if you don't do it this way, the system unit for 87 | # multi-user.target never hits and can slow down boots. 88 | - name: Create pi user encrypted password 89 | ansible.builtin.shell: 90 | cmd: set -o pipefail && echo "scoreboard" | openssl passwd -6 -stdin 91 | executable: /bin/bash 92 | register: pi_passwd 93 | changed_when: pi_passwd.rc != 0 94 | 95 | - name: Add pi user name to start of line 96 | ansible.builtin.copy: 97 | dest: /boot/userconf 98 | content: 'pi:{{ pi_passwd.stdout }}' 99 | mode: preserve 100 | 101 | # This will need to change with Trixie. The cmdline.txt is in /boot/fireware now 102 | - name: Force isocpus=3 in cmdline.txt so matrix code runs smoother 103 | ansible.builtin.shell: sed -i 's/$/ isolcpus=3/' /boot/cmdline.txt # noqa: command-instead-of-module 104 | args: 105 | chdir: /boot/ 106 | register: my_output # <- Registers the command output. 107 | changed_when: my_output.rc != 0 108 | 109 | - name: Create /boot/scoreboard directory 110 | ansible.builtin.file: 111 | path: /boot/scoreboard 112 | state: directory 113 | owner: root 114 | group: root 115 | mode: '0755' 116 | 117 | - name: Copy import_readme.txt to /boot/scoreboard 118 | ansible.builtin.copy: 119 | src: ./files/sb_image/import_readme.txt 120 | dest: /boot/scoreboard/ 121 | owner: root 122 | group: root 123 | mode: 0644 124 | 125 | 126 | - name: Blacklist snd_bcm module (as per hzeller's instructions) 127 | ansible.builtin.copy: 128 | dest: /etc/modprobe.d/blacklist-rgb-matrix.conf 129 | content: | 130 | blacklist snd_bcm2835 131 | mode: preserve 132 | 133 | - name: Disable onboard audio 134 | ansible.builtin.lineinfile: 135 | path: /boot/config.txt 136 | regexp: '^dtparam=audio=on$' 137 | line: 'dtparam=audio=off' 138 | state: present 139 | 140 | - name: Copy global bash.bashrc 141 | ansible.builtin.copy: 142 | src: ./files/sb_image/bash.bashrc 143 | dest: /etc/ 144 | mode: "0644" 145 | 146 | - name: Copy scoreboard.bash to bashrc.d 147 | ansible.builtin.copy: 148 | src: ./files/sb_image/scoreboard.bash 149 | dest: /etc/bashrc.d/ 150 | mode: "0644" 151 | 152 | - name: Setup cron.scoreboard 153 | ansible.builtin.copy: 154 | src: ./files/sb_image/get_version 155 | dest: /etc/cron.scoreboard 156 | mode: "0755" 157 | 158 | - name: Setup crontab to call cron.scoreboard on reboots 159 | ansible.builtin.copy: 160 | src: ./files/sb_image/crontab 161 | dest: /etc/ 162 | backup: true 163 | mode: preserve 164 | 165 | - name: Setup pi user crontab for daily check of version 166 | ansible.builtin.cron: 167 | user: pi 168 | name: "Check scoreboard version" 169 | minute: 0 170 | hour: 3 171 | job: "/home/pi/sbtools/checkUpdate.sh > /home/pi/.nhlledportal/status" 172 | 173 | 174 | - name: Install aptfile (used for updates) 175 | ansible.builtin.copy: 176 | src: ./files/sb_image/aptfile 177 | dest: /usr/local/bin 178 | mode: "0755" 179 | 180 | - name: Install OS packages needed for scoreboard 181 | ansible.builtin.apt: 182 | update_cache: true 183 | name: 184 | - cmake 185 | - cython3 186 | - python3-pip 187 | - python3-cairosvg 188 | - python3-dev 189 | - python3-setuptools 190 | - python3-numpy 191 | - python3-pandas 192 | - python3-pil 193 | - python3-numpy 194 | - python3-pygame 195 | - python3-tk 196 | - python3-gpiozero 197 | - python3-virtualenv 198 | - python3-venv 199 | - supervisor 200 | - neofetch 201 | - pastebinit 202 | - jq 203 | - neovim 204 | - zsh 205 | - direnv 206 | - libwebp-dev 207 | - dbus 208 | - libsixel-bin 209 | - libgraphicsmagick++-dev 210 | state: present 211 | 212 | - name: Remove OS packages that will cause issues with scoreboard 213 | ansible.builtin.apt: 214 | name: 215 | - bluez 216 | - bluez-firmware 217 | - pi-bluetooth 218 | - triggerhappy 219 | clean: true 220 | state: absent 221 | 222 | - name: Enable NetworkManager service 223 | ansible.builtin.systemd: 224 | name: NetworkManager 225 | enabled: true 226 | masked: false 227 | 228 | - name: Disable Supervisor service 229 | ansible.builtin.systemd: 230 | name: supervisor 231 | enabled: false 232 | masked: false 233 | 234 | - name: Disable and mask services so we can use comitup for wifi setup 235 | ansible.builtin.systemd: 236 | name: "{{ item }}" 237 | masked: true 238 | with_items: 239 | - dhcpcd 240 | - dnsmasq 241 | - systemd-resolved 242 | - dhcpd 243 | - wpa-supplicant 244 | - NetworkManager-wait-online 245 | 246 | - name: Delete network interfaces file 247 | ansible.builtin.file: 248 | path: /etc/network/interface 249 | state: absent 250 | 251 | - name: Install comitup deb package from the internet 252 | ansible.builtin.apt: 253 | deb: https://davesteele.github.io/comitup/deb/davesteele-comitup-apt-source_1.3_all.deb 254 | dpkg_options: "force-all" 255 | 256 | - name: Install comitup and comitup-web packages 257 | ansible.builtin.apt: 258 | update_cache: true 259 | name: "{{ item }}" 260 | with_items: 261 | - comitup 262 | - comitup-watch 263 | 264 | - name: Copy scoreboard comitup.conf file 265 | ansible.builtin.copy: 266 | src: ./files/comitup-conf/comitup.conf 267 | dest: /etc/ 268 | backup: true 269 | mode: "0755" 270 | 271 | - name: Extract scoreboard template into the comitup flask template directory 272 | ansible.builtin.unarchive: 273 | src: ./files/comitup-conf/scoreboard_ui.tar.gz 274 | dest: /usr/share/comitup/web/templates 275 | 276 | - name: Update template path variable in comitupweb.py to use scoreboard template 277 | ansible.builtin.lineinfile: 278 | path: /usr/share/comitup/web/comitupweb.py 279 | regexp: '^TEMPLATE_PATH =' 280 | line: TEMPLATE_PATH = "/usr/share/comitup/web/templates/scoreboard" 281 | 282 | - name: Get nls-controlhub package 283 | ansible.builtin.get_url: 284 | url: "https://github.com/falkyre/nhl-setup/releases/download/latest-web/nls-controlhub-deb-bookworm-armv7.deb" 285 | dest: "/tmp/nls-controlhub-deb-bookworm-armv7.deb" 286 | mode: "0644" 287 | 288 | - name: Install NLS ControlHub using dpkg 289 | ansible.builtin.command: dpkg -i /tmp/nls-controlhub-deb-bookworm-armv7.deb 290 | register: dpkg_result 291 | changed_when: "'Setting up' in dpkg_result.stdout" 292 | 293 | - name: Copy nls_controlhub_cfg.toml to /etc/nls_controlhub 294 | ansible.builtin.copy: 295 | src: "./files/sb_image/nls_controlhub_cfg.toml" 296 | dest: "/etc/nls_controlhub/config.toml" 297 | mode: "0644" 298 | 299 | - name: Install scoreboard services 300 | ansible.builtin.copy: 301 | src: "./files/sb_image/{{ item }}" 302 | dest: /etc/systemd/system 303 | mode: "0644" 304 | with_items: 305 | - sb_splash.service 306 | - sb_autosettz.service 307 | - wifi-on.service 308 | - nls_controlhub.service 309 | 310 | - name: Enable scoreboard services 311 | ansible.builtin.systemd: 312 | name: "{{ item }}" 313 | enabled: true 314 | masked: false 315 | with_items: 316 | - sb_splash.service 317 | - sb_autosettz.service 318 | - wifi-on.service 319 | - nls_controlhub.service 320 | 321 | - name: Copy scoreboard supervisord.conf 322 | ansible.builtin.copy: 323 | src: ./files/sb_image/supervisord.conf 324 | dest: /etc/supervisor/ 325 | backup: true 326 | mode: preserve 327 | 328 | - name: Copy scoreboard scoreboard.conf 329 | ansible.builtin.copy: 330 | src: ./files/sb_image/scoreboard.conf 331 | dest: /etc/supervisor/conf.d/ 332 | backup: true 333 | mode: preserve 334 | 335 | - name: Copy scoreboard scoreboard-emulated.conf 336 | ansible.builtin.copy: 337 | src: ./files/sb_image/scoreboard-emulated.conf 338 | dest: /etc/supervisor/conf.d/ 339 | backup: true 340 | mode: preserve 341 | 342 | - name: Copy sbtools directory 343 | become: true 344 | become_user: pi 345 | ansible.builtin.copy: 346 | src: ./files/sb_image/sbtools 347 | dest: ~/ 348 | mode: preserve 349 | 350 | - name: Create .config/neofetch directory in pi home 351 | become: true 352 | become_user: pi 353 | ansible.builtin.file: 354 | path: ~/.config/neofetch 355 | state: directory 356 | mode: "0755" 357 | 358 | - name: Copy scoreboard neofetch config 359 | become: true 360 | become_user: pi 361 | ansible.builtin.copy: 362 | src: ./files/sb_image/.config/neofetch/neofetch_config.conf 363 | dest: ~/.config/neofetch/config.conf 364 | mode: "0755" 365 | 366 | - name: Create .nhlledportal directory 367 | become: true 368 | become_user: pi 369 | ansible.builtin.file: 370 | path: ~/.nhlledportal 371 | state: directory 372 | mode: "0755" 373 | 374 | - name: Create startup files in .nhlledportal 375 | become: true 376 | become_user: pi 377 | ansible.builtin.file: 378 | path: "~/.nhlledportal/{{ item }}" 379 | state: touch 380 | mode: "0755" 381 | with_items: 382 | - setTZ 383 | - SETUP 384 | 385 | - name: Put image version in .nhlledportal 386 | become: true 387 | become_user: pi 388 | ansible.builtin.copy: 389 | dest: ~/.nhlledportal/imgver 390 | content: 'You are running the image version {{ imgver }}' 391 | mode: preserve 392 | 393 | 394 | - name: Setup pi user bash and gitconfig 395 | become: true 396 | become_user: pi 397 | ansible.builtin.copy: 398 | src: "./files/sb_image/{{ item }}" 399 | dest: ~/ 400 | mode: "0644" 401 | with_items: 402 | - .gitconfig 403 | - .bashrc 404 | 405 | - name: Download scoreboard release repo "{{ gh_repo }}" # noqa: latest 406 | become: true 407 | become_user: pi 408 | ansible.builtin.git: 409 | repo: "{{ gh_repo }}" 410 | dest: ~/nhl-led-scoreboard 411 | recursive: true 412 | depth: 1 413 | when: not use_beta 414 | 415 | - name: Download scoreboard beta repo "{{ gh_repo }}" # noqa: latest 416 | become: true 417 | become_user: pi 418 | ansible.builtin.git: 419 | repo: "{{ gh_repo }}" 420 | dest: ~/nhl-led-scoreboard 421 | recursive: true 422 | depth: 1 423 | version: "{{ gh_branch }}" 424 | when: use_beta 425 | 426 | - name: Install required python packages for scoreboard 427 | become: true 428 | become_user: pi 429 | ansible.builtin.pip: 430 | virtualenv: /home/pi/nhlsb-venv 431 | virtualenv_python: python3 432 | virtualenv_site_packages: true 433 | extra_args: "{{ pypi_proxy }}" 434 | requirements: /home/pi/nhl-led-scoreboard/requirements.txt 435 | 436 | - name: Do pip self-update 437 | become: true 438 | become_user: pi 439 | ansible.builtin.pip: 440 | virtualenv: /home/pi/nhlsb-venv 441 | virtualenv_python: python3 442 | virtualenv_site_packages: true 443 | extra_args: "{{ pypi_proxy }}" 444 | state: forcereinstall 445 | name: 446 | - pip 447 | 448 | - name: Install extra python packages for scoreboard 449 | become: true 450 | become_user: pi 451 | ansible.builtin.pip: 452 | virtualenv: /home/pi/nhlsb-venv 453 | virtualenv_python: python3 454 | virtualenv_site_packages: true 455 | extra_args: "{{ pypi_proxy }}" 456 | name: 457 | - archey4 458 | - tzlocal 459 | - lastversion 460 | - rich-cli 461 | - cairosvg 462 | 463 | - name: Copy rgb matrix install_bindings.sh 464 | become: true 465 | become_user: pi 466 | ansible.builtin.copy: 467 | src: ./files/sb_image/install_bindings.sh 468 | dest: /home/pi/nhl-led-scoreboard 469 | mode: "0755" 470 | 471 | - name: Install rgb matrix python bindings 472 | become: true 473 | become_user: pi 474 | ansible.builtin.command: /home/pi/nhl-led-scoreboard/install_bindings.sh 475 | args: 476 | chdir: /home/pi/nhl-led-scoreboard 477 | register: my_output # <- Registers the command output. 478 | changed_when: my_output.rc != 0 479 | 480 | - name: Remove install_bindings.sh 481 | become: true 482 | become_user: pi 483 | ansible.builtin.file: 484 | path: /home/pi/nhl-led-scoreboard/install_bindings.sh 485 | state: absent 486 | 487 | - name: Create direnv setup to auto activate venv 488 | become: true 489 | become_user: pi 490 | ansible.builtin.copy: 491 | dest: /home/pi/nhl-led-scoreboard/.envrc 492 | content: | 493 | export VIRTUAL_ENV=/home/pi/nhlsb-venv 494 | source /home/pi/nhlsb-venv/bin/activate 495 | export PYVER=`python3 -V` 496 | unset PS1 497 | mode: "0755" 498 | 499 | - name: Activate direnv 500 | become: true 501 | become_user: pi 502 | ansible.builtin.command: direnv allow ~/nhl-led-scoreboard 503 | register: my_output # <- Registers the command output. 504 | changed_when: my_output.rc != 0 505 | 506 | - name: Download nhl_setup executable for 64bit Trixie systems 507 | become: true 508 | become_user: pi 509 | ansible.builtin.get_url: 510 | url: "https://github.com/falkyre/nhl-setup/releases/download/latest-cli/nhl_setup.32" 511 | dest: "/home/pi/nhl-led-scoreboard/nhl_setup" 512 | mode: '0755' 513 | when: ansible_architecture != 'aarch64' and ansible_architecture != 'arm64' and ansible_distribution_release == 'trixie' 514 | 515 | - name: Download nhl_setup executable for 64bit Trixie systems 516 | become: true 517 | become_user: pi 518 | ansible.builtin.get_url: 519 | url: "https://github.com/falkyre/nhl-setup/releases/download/latest-cli/nhl_setup.64" 520 | dest: "/home/pi/nhl-led-scoreboard/nhl_setup" 521 | mode: '0755' 522 | when: (ansible_architecture == 'aarch64' or ansible_architecture == 'arm64') and ansible_distribution_release == 'trixie' 523 | 524 | - name: Download nhl_setup executable for 32bit Bookworm systems 525 | become: true 526 | become_user: pi 527 | ansible.builtin.get_url: 528 | url: "https://github.com/falkyre/nhl-setup/releases/download/latest-cli/nhl_setup.bookworm.32" 529 | dest: "/home/pi/nhl-led-scoreboard/nhl_setup" 530 | mode: '0755' 531 | when: ansible_architecture != 'aarch64' and ansible_architecture != 'arm64' and ansible_distribution_release == 'bookworm' 532 | 533 | - name: Download nhl_setup executable for 64bit Bookworm systems 534 | become: true 535 | become_user: pi 536 | ansible.builtin.get_url: 537 | url: "https://github.com/falkyre/nhl-setup/releases/download/latest-cli/nhl_setup.bookworm.64" 538 | dest: "/home/pi/nhl-led-scoreboard/nhl_setup" 539 | mode: '0755' 540 | when: (ansible_architecture == 'aarch64' or ansible_architecture == 'arm64') and ansible_distribution_release == 'bookworm' 541 | 542 | - name: Update .nhlledportal/status file 543 | become: true 544 | become_user: pi 545 | ansible.builtin.shell: /home/pi/sbtools/checkUpdate.sh > /home/pi/.nhlledportal/status 546 | args: 547 | chdir: /home/pi/sbtools 548 | register: my_output # <- Registers the command output. 549 | changed_when: my_output.rc != 0 550 | 551 | - name: Create softlink for sb-upgrade 552 | become: true 553 | become_user: pi 554 | ansible.builtin.file: 555 | src: /home/pi/nhl-led-scoreboard/scripts/sbtools/sb-upgrade 556 | dest: /home/pi/sbtools/sb-upgrade 557 | state: link 558 | force: true 559 | 560 | - name: Remove 51cache file (delete file) 561 | 562 | ansible.builtin.file: 563 | path: /etc/apt/apt.conf.d/51cache 564 | state: absent 565 | when: use_apt_proxy 566 | 567 | - name: Clean up apt cache 568 | become: true 569 | ansible.builtin.apt: 570 | autoclean: true 571 | autoremove: true 572 | clean: true 573 | -------------------------------------------------------------------------------- /nhl-image/ansible/setup-raspberry-trixie.yml: -------------------------------------------------------------------------------- 1 | - 2 | name: Setup RPi - Scoreboard setup 64 bit Trixie 3 | hosts: all 4 | become: true 5 | become_user: root 6 | vars: 7 | keyboard_keymap: "us" 8 | timezone: "America/Toronto" 9 | locale: en_US.UTF-8 10 | wifi_country: CA 11 | gh_repo: "https://github.com/falkyre/nhl-led-scoreboard.git" 12 | use_beta: false 13 | gh_branch: "beta" 14 | system_hostname: scoreboard 15 | apt_proxy: "" 16 | use_apt_proxy: false 17 | pypi_proxy: "" 18 | imgver: "{{ lookup('file', 'VERSION').splitlines()[0].split(':')[-1] }}" 19 | 20 | tasks: 21 | # use only become_user: root if task should be run as superuser 22 | - name: Setup hostname using raspi-config 23 | ansible.builtin.command: "/usr/bin/raspi-config nonint do_hostname {{ system_hostname }}" 24 | register: my_output # <- Registers the command output. 25 | changed_when: my_output.rc != 0 26 | 27 | - name: Setup keyboard layout using raspi-config 28 | ansible.builtin.command: "/usr/bin/raspi-config nonint do_configure_keyboard {{ keyboard_keymap }}" 29 | register: my_output # <- Registers the command output. 30 | changed_when: my_output.rc != 0 31 | 32 | - name: Setup timezone using raspi-config 33 | ansible.builtin.command: "/usr/bin/raspi-config nonint do_change_timezone {{ timezone }}" 34 | register: my_output # <- Registers the command output. 35 | changed_when: my_output.rc != 0 36 | 37 | - name: Setup locale using raspi-config 38 | ansible.builtin.command: "/usr/bin/raspi-config nonint do_change_locale {{ locale }}" 39 | register: my_output # <- Registers the command output. 40 | changed_when: my_output.rc != 0 41 | 42 | - name: Setup Wifi Regulatory Country using raspi-config 43 | ansible.builtin.command: "/usr/bin/raspi-config nonint do_wifi_country {{ wifi_country }}" 44 | register: my_output # <- Registers the command output. 45 | changed_when: my_output.rc != 0 46 | 47 | # - meta: end_play 48 | 49 | - name: Setup apt proxy 50 | ansible.builtin.copy: 51 | src: ./files/sb_image/51cache 52 | dest: /etc/apt/apt.conf.d/ 53 | mode: "0644" 54 | when: use_apt_proxy 55 | 56 | - name: Update apt proxy in 51cache file 57 | ansible.builtin.replace: 58 | path: /etc/apt/apt.conf.d/51cache 59 | regexp: 'APT_PROXY' 60 | replace: "{{ apt_proxy }}" 61 | when: use_apt_proxy 62 | 63 | - name: Enable SSH 64 | ansible.builtin.file: 65 | path: /boot/ssh 66 | state: touch 67 | mode: "0600" 68 | 69 | - name: Disable "Please note that SSH may not work until a valid user has been set up." message 70 | ansible.builtin.file: 71 | path: /etc/ssh/sshd_config.d/rename_user.conf 72 | state: absent 73 | 74 | - name: Add temporary password for root user 75 | ansible.builtin.user: 76 | name: root 77 | password: "{{ 'letiretlebut' | password_hash('sha512') }}" 78 | 79 | # This is the new way to create a user on raspiOS headless, if you don't do it this way, the system unit for 80 | # multi-user.target never hits and can slow down boots. 81 | - name: Create pi user encrypted password 82 | ansible.builtin.shell: 83 | cmd: set -o pipefail && echo "scoreboard" | openssl passwd -6 -stdin 84 | executable: /bin/bash 85 | register: pi_passwd 86 | changed_when: pi_passwd.rc != 0 87 | 88 | - name: Add pi user name to start of line 89 | ansible.builtin.copy: 90 | dest: /boot/userconf 91 | content: 'pi:{{ pi_passwd.stdout }}' 92 | mode: preserve 93 | 94 | 95 | - name: Force isocpus=3 in cmdline.txt so matrix code runs smoother 96 | ansible.builtin.shell: sed -i 's/$/ isolcpus=3/' /boot/cmdline.txt # noqa: command-instead-of-module 97 | args: 98 | chdir: /boot/ 99 | register: my_output # <- Registers the command output. 100 | changed_when: my_output.rc != 0 101 | 102 | - name: Create /boot/scoreboard directory 103 | ansible.builtin.file: 104 | path: /boot/scoreboard 105 | state: directory 106 | owner: root 107 | group: root 108 | mode: '0755' 109 | 110 | - name: Copy import_readme.txt to /boot/scoreboard 111 | ansible.builtin.copy: 112 | src: ./files/sb_image/import_readme.txt 113 | dest: /boot/scoreboard/ 114 | owner: root 115 | group: root 116 | mode: 0644 117 | 118 | 119 | - name: Force filesystem resize on next boot 120 | ansible.builtin.shell: sed -i 's/$/ resize/' /boot/cmdline.txt # noqa: command-instead-of-module 121 | args: 122 | chdir: /boot/ 123 | register: my_output # <- Registers the command output. 124 | changed_when: my_output.rc != 0 125 | 126 | - name: Blacklist snd_bcm module (as per hzeller's instructions) 127 | ansible.builtin.copy: 128 | dest: /etc/modprobe.d/blacklist-rgb-matrix.conf 129 | content: | 130 | blacklist snd_bcm2835 131 | mode: preserve 132 | 133 | - name: Disable onboard audio 134 | ansible.builtin.lineinfile: 135 | path: /boot/config.txt 136 | regexp: '^dtparam=audio=on$' 137 | line: 'dtparam=audio=off' 138 | state: present 139 | 140 | - name: Copy global bash.bashrc 141 | ansible.builtin.copy: 142 | src: ./files/sb_image/bash.bashrc 143 | dest: /etc/ 144 | mode: "0644" 145 | 146 | - name: Copy scoreboard.bash to bashrc.d 147 | ansible.builtin.copy: 148 | src: ./files/sb_image/scoreboard.bash 149 | dest: /etc/bashrc.d/ 150 | mode: "0644" 151 | 152 | - name: Setup cron.scoreboard 153 | ansible.builtin.copy: 154 | src: ./files/sb_image/get_version 155 | dest: /etc/cron.scoreboard 156 | mode: "0755" 157 | 158 | - name: Setup crontab to call cron.scoreboard on reboots 159 | ansible.builtin.copy: 160 | src: ./files/sb_image/crontab 161 | dest: /etc/ 162 | backup: true 163 | mode: preserve 164 | 165 | - name: Setup pi user crontab for daily check of version 166 | ansible.builtin.cron: 167 | user: pi 168 | name: "Check scoreboard version" 169 | minute: 0 170 | hour: 3 171 | job: "/home/pi/sbtools/checkUpdate.sh > /home/pi/.nhlledportal/status" 172 | 173 | - name: Show free disk space before major package install 174 | ansible.builtin.command: "df -h /" 175 | register: df_output 176 | changed_when: false 177 | 178 | - name: Display free disk space 179 | ansible.builtin.debug: 180 | var: df_output.stdout_lines 181 | 182 | - name: Clean apt cache and remove unused packages before install 183 | ansible.builtin.apt: 184 | autoclean: true 185 | autoremove: true 186 | clean: true 187 | 188 | - name: Install OS packages needed for scoreboard 189 | ansible.builtin.apt: 190 | update_cache: true 191 | name: 192 | - git 193 | - build-essential 194 | - cmake 195 | - cython3 196 | - python3-pip 197 | - python3-cairosvg 198 | - python3-dev 199 | - python3-setuptools 200 | - python3-numpy 201 | - python3-pandas 202 | - python3-pil 203 | - python3-numpy 204 | - python3-pygame 205 | - python3-tk 206 | - python3-gpiozero 207 | - python3-virtualenv 208 | - python3-venv 209 | - supervisor 210 | - fastfetch 211 | - pastebinit 212 | - jq 213 | - neovim 214 | - zsh 215 | - direnv 216 | - libwebp-dev 217 | - dbus 218 | - libsixel-bin 219 | - libgraphicsmagick++-dev 220 | state: present 221 | 222 | - name: Remove OS packages that will cause issues with scoreboard 223 | ansible.builtin.apt: 224 | name: 225 | - bluez 226 | - bluez-firmware 227 | - pi-bluetooth 228 | - triggerhappy 229 | clean: true 230 | autoremove: true 231 | state: absent 232 | 233 | - name: Enable NetworkManager service 234 | ansible.builtin.systemd: 235 | name: NetworkManager 236 | enabled: true 237 | masked: false 238 | 239 | - name: Disable Supervisor service 240 | ansible.builtin.systemd: 241 | name: supervisor 242 | enabled: false 243 | masked: false 244 | 245 | - name: Disable and mask services so we can use comitup for wifi setup 246 | ansible.builtin.systemd: 247 | name: "{{ item }}" 248 | masked: true 249 | with_items: 250 | - dhcpcd 251 | - dnsmasq 252 | - systemd-resolved 253 | - dhcpd 254 | - wpa-supplicant 255 | - NetworkManager-wait-online 256 | 257 | - name: Delete network interfaces file 258 | ansible.builtin.file: 259 | path: /etc/network/interface 260 | state: absent 261 | 262 | - name: Install comitup deb package from the internet 263 | ansible.builtin.apt: 264 | deb: https://davesteele.github.io/comitup/deb/davesteele-comitup-apt-source_1.3_all.deb 265 | dpkg_options: "force-all" 266 | 267 | - name: Install comitup and comitup-web packages 268 | ansible.builtin.apt: 269 | update_cache: true 270 | name: "{{ item }}" 271 | with_items: 272 | - comitup 273 | - comitup-watch 274 | 275 | - name: Copy scoreboard comitup.conf file 276 | ansible.builtin.copy: 277 | src: ./files/comitup-conf/comitup.conf 278 | dest: /etc/ 279 | backup: true 280 | mode: "0755" 281 | 282 | - name: Extract scoreboard template into the comitup flask template directory 283 | ansible.builtin.unarchive: 284 | src: ./files/comitup-conf/scoreboard_ui.tar.gz 285 | dest: /usr/share/comitup/web/templates 286 | 287 | - name: Update template path variable in comitupweb.py to use scoreboard template 288 | ansible.builtin.lineinfile: 289 | path: /usr/share/comitup/web/comitupweb.py 290 | regexp: '^TEMPLATE_PATH =' 291 | line: TEMPLATE_PATH = "/usr/share/comitup/web/templates/scoreboard" 292 | 293 | - name: Get nls-controlhub package 294 | ansible.builtin.get_url: 295 | url: "https://github.com/falkyre/nhl-setup/releases/download/latest-web/nls-controlhub-deb-trixie-arm64.deb" 296 | dest: "/tmp/nls-controlhub-deb-trixie-arm64.deb" 297 | mode: "0644" 298 | 299 | - name: Install NLS ControlHub using dpkg 300 | ansible.builtin.command: dpkg -i /tmp/nls-controlhub-deb-trixie-arm64.deb 301 | register: dpkg_result 302 | changed_when: "'Setting up' in dpkg_result.stdout" 303 | 304 | - name: Copy nls_controlhub_cfg.toml to /etc/nls_controlhub 305 | ansible.builtin.copy: 306 | src: "./files/sb_image/nls_controlhub_cfg.toml" 307 | dest: "/etc/nls_controlhub/config.toml" 308 | mode: "0644" 309 | 310 | 311 | - name: Install scoreboard services 312 | ansible.builtin.copy: 313 | src: "./files/sb_image/{{ item }}" 314 | dest: /etc/systemd/system 315 | mode: "0644" 316 | with_items: 317 | - sb_splash.service 318 | - sb_autosettz.service 319 | - wifi-on.service 320 | - nls_controlhub.service 321 | 322 | - name: Enable scoreboard services 323 | ansible.builtin.systemd: 324 | name: "{{ item }}" 325 | enabled: true 326 | masked: false 327 | with_items: 328 | - sb_splash.service 329 | - sb_autosettz.service 330 | - wifi-on.service 331 | - nls_controlhub.service 332 | 333 | - name: Copy scoreboard supervisord.conf 334 | ansible.builtin.copy: 335 | src: ./files/sb_image/supervisord.conf 336 | dest: /etc/supervisor/ 337 | backup: true 338 | mode: preserve 339 | 340 | - name: Copy scoreboard scoreboard.conf 341 | ansible.builtin.copy: 342 | src: ./files/sb_image/scoreboard.conf 343 | dest: /etc/supervisor/conf.d/ 344 | backup: true 345 | mode: preserve 346 | 347 | - name: Copy scoreboard scoreboard-emulated.conf 348 | ansible.builtin.copy: 349 | src: ./files/sb_image/scoreboard-emulated.conf 350 | dest: /etc/supervisor/conf.d/ 351 | backup: true 352 | mode: preserve 353 | 354 | - name: Copy sbtools directory 355 | become: true 356 | become_user: pi 357 | ansible.builtin.copy: 358 | src: ./files/sb_image/sbtools 359 | dest: ~/ 360 | mode: preserve 361 | 362 | - name: Create .config/fastfetch directory in pi home 363 | become: true 364 | become_user: pi 365 | ansible.builtin.file: 366 | path: ~/.config/fastfetch 367 | state: directory 368 | mode: "0755" 369 | 370 | - name: Copy scoreboard fastfetch config 371 | become: true 372 | become_user: pi 373 | ansible.builtin.copy: 374 | src: ./files/sb_image/.config/fastfetch/config.jsonc 375 | dest: ~/.config/fastfetch/config.jsonc 376 | mode: "0755" 377 | 378 | - name: Create .nhlledportal directory 379 | become: true 380 | become_user: pi 381 | ansible.builtin.file: 382 | path: ~/.nhlledportal 383 | state: directory 384 | mode: "0755" 385 | 386 | - name: Create startup files in .nhlledportal 387 | become: true 388 | become_user: pi 389 | ansible.builtin.file: 390 | path: "~/.nhlledportal/{{ item }}" 391 | state: touch 392 | mode: "0755" 393 | with_items: 394 | - setTZ 395 | - SETUP 396 | 397 | - name: Put image version in .nhlledportal 398 | become: true 399 | become_user: pi 400 | ansible.builtin.copy: 401 | dest: ~/.nhlledportal/imgver 402 | content: 'You are running the image version {{ imgver }}' 403 | mode: preserve 404 | 405 | 406 | - name: Setup pi user bash and gitconfig 407 | become: true 408 | become_user: pi 409 | ansible.builtin.copy: 410 | src: "./files/sb_image/{{ item }}" 411 | dest: ~/ 412 | mode: "0644" 413 | with_items: 414 | - .gitconfig 415 | - .bashrc 416 | 417 | - name: Download scoreboard release repo "{{ gh_repo }}" # noqa: latest 418 | become: true 419 | become_user: pi 420 | ansible.builtin.git: 421 | repo: "{{ gh_repo }}" 422 | dest: ~/nhl-led-scoreboard 423 | recursive: true 424 | depth: 1 425 | when: not use_beta 426 | 427 | - name: Download scoreboard beta repo "{{ gh_repo }}" # noqa: latest 428 | become: true 429 | become_user: pi 430 | ansible.builtin.git: 431 | repo: "{{ gh_repo }}" 432 | dest: ~/nhl-led-scoreboard 433 | recursive: true 434 | depth: 1 435 | version: "{{ gh_branch }}" 436 | when: use_beta 437 | 438 | - name: Install required python packages for scoreboard 439 | become: true 440 | become_user: pi 441 | ansible.builtin.pip: 442 | virtualenv: /home/pi/nhlsb-venv 443 | virtualenv_python: python3 444 | virtualenv_site_packages: true 445 | extra_args: "{{ pypi_proxy }}" 446 | requirements: /home/pi/nhl-led-scoreboard/requirements.txt 447 | 448 | - name: Do pip self-update 449 | become: true 450 | become_user: pi 451 | ansible.builtin.pip: 452 | virtualenv: /home/pi/nhlsb-venv 453 | virtualenv_python: python3 454 | virtualenv_site_packages: true 455 | extra_args: "{{ pypi_proxy }}" 456 | state: forcereinstall 457 | name: 458 | - pip 459 | 460 | - name: Install extra python packages for scoreboard 461 | become: true 462 | become_user: pi 463 | ansible.builtin.pip: 464 | virtualenv: /home/pi/nhlsb-venv 465 | virtualenv_python: python3 466 | virtualenv_site_packages: true 467 | extra_args: "{{ pypi_proxy }}" 468 | name: 469 | - archey4 470 | - tzlocal 471 | - lastversion 472 | - rich-cli 473 | - cairosvg 474 | 475 | - name: Copy rgb matrix install_bindings.sh 476 | become: true 477 | become_user: pi 478 | ansible.builtin.copy: 479 | src: ./files/sb_image/install_bindings.sh 480 | dest: /home/pi/nhl-led-scoreboard 481 | mode: "0755" 482 | 483 | - name: Install rgb matrix python bindings 484 | become: true 485 | become_user: pi 486 | ansible.builtin.command: /home/pi/nhl-led-scoreboard/install_bindings.sh 487 | args: 488 | chdir: /home/pi/nhl-led-scoreboard 489 | register: my_output # <- Registers the command output. 490 | changed_when: my_output.rc != 0 491 | 492 | - name: Remove install_bindings.sh 493 | become: true 494 | become_user: pi 495 | ansible.builtin.file: 496 | path: /home/pi/nhl-led-scoreboard/install_bindings.sh 497 | state: absent 498 | 499 | - name: Create direnv setup to auto activate venv 500 | become: true 501 | become_user: pi 502 | ansible.builtin.copy: 503 | dest: /home/pi/nhl-led-scoreboard/.envrc 504 | content: | 505 | export VIRTUAL_ENV=/home/pi/nhlsb-venv 506 | source /home/pi/nhlsb-venv/bin/activate 507 | export PYVER=`python3 -V` 508 | unset PS1 509 | mode: "0755" 510 | 511 | - name: Activate direnv 512 | become: true 513 | become_user: pi 514 | ansible.builtin.command: direnv allow ~/nhl-led-scoreboard 515 | register: my_output # <- Registers the command output. 516 | changed_when: my_output.rc != 0 517 | 518 | - name: Download nhl_setup executable for 64bit Trixie systems 519 | become: true 520 | become_user: pi 521 | ansible.builtin.get_url: 522 | url: "https://github.com/falkyre/nhl-setup/releases/download/latest-cli/nhl_setup.32" 523 | dest: "/home/pi/nhl-led-scoreboard/nhl_setup" 524 | mode: '0755' 525 | when: ansible_architecture != 'aarch64' and ansible_architecture != 'arm64' and ansible_distribution_release == 'trixie' 526 | 527 | - name: Download nhl_setup executable for 64bit Trixie systems 528 | become: true 529 | become_user: pi 530 | ansible.builtin.get_url: 531 | url: "https://github.com/falkyre/nhl-setup/releases/download/latest-cli/nhl_setup.64" 532 | dest: "/home/pi/nhl-led-scoreboard/nhl_setup" 533 | mode: '0755' 534 | when: (ansible_architecture == 'aarch64' or ansible_architecture == 'arm64') and ansible_distribution_release == 'trixie' 535 | 536 | - name: Download nhl_setup executable for 32bit Bookworm systems 537 | become: true 538 | become_user: pi 539 | ansible.builtin.get_url: 540 | url: "https://github.com/falkyre/nhl-setup/releases/download/latest-cli/nhl_setup.bookworm.32" 541 | dest: "/home/pi/nhl-led-scoreboard/nhl_setup" 542 | mode: '0755' 543 | when: ansible_architecture != 'aarch64' and ansible_architecture != 'arm64' and ansible_distribution_release == 'bookworm' 544 | 545 | - name: Download nhl_setup executable for 64bit Bookworm systems 546 | become: true 547 | become_user: pi 548 | ansible.builtin.get_url: 549 | url: "https://github.com/falkyre/nhl-setup/releases/download/latest-cli/nhl_setup.bookworm.64" 550 | dest: "/home/pi/nhl-led-scoreboard/nhl_setup" 551 | mode: '0755' 552 | when: (ansible_architecture == 'aarch64' or ansible_architecture == 'arm64') and ansible_distribution_release == 'bookworm' 553 | 554 | - name: Update .nhlledportal/status file 555 | become: true 556 | become_user: pi 557 | ansible.builtin.shell: /home/pi/sbtools/checkUpdate.sh > /home/pi/.nhlledportal/status 558 | args: 559 | chdir: /home/pi/sbtools 560 | register: my_output # <- Registers the command output. 561 | changed_when: my_output.rc != 0 562 | 563 | - name: Create softlink for sb-upgrade 564 | become: true 565 | become_user: pi 566 | ansible.builtin.file: 567 | src: /home/pi/nhl-led-scoreboard/scripts/sbtools/sb-upgrade 568 | dest: /home/pi/sbtools/sb-upgrade 569 | state: link 570 | force: true 571 | 572 | - name: Remove 51cache file (delete file) 573 | 574 | ansible.builtin.file: 575 | path: /etc/apt/apt.conf.d/51cache 576 | state: absent 577 | when: use_apt_proxy 578 | 579 | - name: Clean up apt cache 580 | become: true 581 | ansible.builtin.apt: 582 | autoclean: true 583 | autoremove: true 584 | clean: true 585 | -------------------------------------------------------------------------------- /nhl-image/ansible/files/sb_image/sbtools/sb-tools: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | #Set the colors 4 | 5 | NEWT_COLORS=' 6 | root=green,black 7 | border=green,black 8 | title=green,black 9 | roottext=white,black 10 | window=green,black 11 | textbox=white,black 12 | button=black,green 13 | compactbutton=white,black 14 | listbox=white,black 15 | actlistbox=black,white 16 | actsellistbox=black,green 17 | checkbox=green,black 18 | actcheckbox=black,green 19 | ' 20 | export NEWT_COLORS 21 | 22 | INTERACTIVE=True 23 | ASK_TO_REBOOT=0 24 | 25 | STATUS=$(cat /home/pi/.nhlledportal/status) 26 | VERSION=$(cat /home/pi/nhl-led-scoreboard/VERSION) 27 | 28 | if [ -f /home/pi/nhl-led-scoreboard/config/config.json ]; then 29 | TEAMSET=True 30 | else 31 | TEAMSET=False 32 | fi 33 | 34 | #Check to see if the user has initially tested the matrix with sample code 35 | if [ -f /home/pi/.nhlledportal/TESTED ]; then 36 | TESTMATRIXRUN=True 37 | else 38 | TESTMATRIXRUN=False 39 | fi 40 | 41 | if [ -f /home/pi/.nhlledportal/SETUP ]; then 42 | FIRSTRUN=True 43 | else 44 | FIRSTRUN=False 45 | fi 46 | 47 | is_pi () { 48 | ARCH=$(dpkg --print-architecture) 49 | if [ "$ARCH" = "armhf" ] || [ "$ARCH" = "arm64" ] ; then 50 | return 0 51 | else 52 | return 1 53 | fi 54 | } 55 | 56 | if is_pi ; then 57 | CMDLINE=/boot/cmdline.txt 58 | else 59 | CMDLINE=/proc/cmdline 60 | fi 61 | 62 | is_pione() { 63 | if grep -q "^Revision\s*:\s*00[0-9a-fA-F][0-9a-fA-F]$" /proc/cpuinfo; then 64 | return 0 65 | elif grep -q "^Revision\s*:\s*[ 123][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]0[0-36][0-9a-fA-F]$" /proc/cpuinfo ; then 66 | return 0 67 | else 68 | return 1 69 | fi 70 | } 71 | 72 | is_pitwo() { 73 | grep -q "^Revision\s*:\s*[ 123][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]04[0-9a-fA-F]$" /proc/cpuinfo 74 | return $? 75 | } 76 | 77 | is_pizero() { 78 | grep -q "^Revision\s*:\s*[ 123][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]0[9cC][0-9a-fA-F]$" /proc/cpuinfo 79 | return $? 80 | } 81 | 82 | is_pifour() { 83 | grep -q "^Revision\s*:\s*[ 123][0-9a-fA-F][0-9a-fA-F]3[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]$" /proc/cpuinfo 84 | return $? 85 | } 86 | 87 | get_pi_type() { 88 | if is_pione; then 89 | echo 1 90 | elif is_pitwo; then 91 | echo 2 92 | else 93 | echo 0 94 | fi 95 | } 96 | 97 | is_live() { 98 | grep -q "boot=live" $CMDLINE 99 | return $? 100 | } 101 | 102 | is_ssh() { 103 | if pstree -p | grep -E --quiet --extended-regexp ".*sshd.*\($$\)"; then 104 | return 0 105 | else 106 | return 1 107 | fi 108 | } 109 | 110 | 111 | is_installed() { 112 | if [ "$(dpkg -l "$1" 2> /dev/null | tail -n 1 | cut -d ' ' -f 1)" != "ii" ]; then 113 | return 1 114 | else 115 | return 0 116 | fi 117 | } 118 | 119 | deb_ver () { 120 | ver=$(cat /etc/debian_version | cut -d . -f 1) 121 | echo $ver 122 | } 123 | 124 | calc_wt_size() { 125 | # NOTE: it's tempting to redirect stderr to /dev/null, so supress error 126 | # output from tput. However in this case, tput detects neither stdout or 127 | # stderr is a tty and so only gives default 80, 24 values 128 | WT_HEIGHT=18 129 | WT_WIDTH=$(tput cols) 130 | 131 | if [ -z "$WT_WIDTH" ] || [ "$WT_WIDTH" -lt 60 ]; then 132 | WT_WIDTH=80 133 | fi 134 | if [ "$WT_WIDTH" -gt 178 ]; then 135 | WT_WIDTH=120 136 | fi 137 | WT_MENU_HEIGHT=$(($WT_HEIGHT-7)) 138 | } 139 | 140 | do_about() { 141 | whiptail --msgbox "\ 142 | This provides a straightforward way of doing initial configuration of the NHL LED Scoreboard as well as some useful tools for troubleshooting.\ 143 | 144 | 145 | Installed: V$VERSION\ 146 | 147 | 148 | On Github: $STATUS \ 149 | " $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT 150 | } 151 | 152 | 153 | do_set_team() { 154 | if [ "$FIRSTRUN" = True ]; then 155 | whiptail --msgbox "You will now be asked to select a preferred team to create the initial config.json." $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT 156 | fi 157 | echo "Loading $(tput setaf 1)nhl_setup$(tput sgr0) to select your inital team$(tput blink).........$(tput sgr0)" 158 | cd /home/pi/nhl-led-scoreboard || exit 159 | /home/pi/nhl-led-scoreboard/nhl_setup -s && 160 | clear 161 | if [ $? -ne 0 ]; then 162 | TEAMSET=False 163 | else 164 | TEAMSET=True 165 | whiptail --msgbox "config.json successfully created in /home/pi/nhl-led-scoreboard/config" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT 166 | fi 167 | } 168 | 169 | do_check_update() { 170 | #whiptail --msgbox "Checking for update" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT 171 | output=$(/home/pi/sbtools/checkUpdate.sh) && 172 | whiptail --msgbox "$output" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT 173 | } 174 | 175 | do_upgrade() { 176 | if echo $STATUS | grep -q "UPDATE"; then 177 | if (whiptail --yesno "$STATUS Run upgrade?" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT); then 178 | /home/pi/sbtools/sb-upgrade 179 | fi 180 | else 181 | whiptail --msgbox "$STATUS No upgrade available" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT 182 | fi 183 | } 184 | 185 | 186 | do_collect() { 187 | whiptail --msgbox "Collect logs and config.json for github issue" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT 188 | output=$(/home/pi/sbtools/issueUpload.sh 2>/dev/null) && 189 | whiptail --msgbox "$output" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT 190 | } 191 | 192 | do_test_matrix() { 193 | 194 | # Ask to run test script 195 | whiptail --yesno "Run /home/pi/sbtools/testMatrix.sh?" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT 196 | if [ $? -eq 0 ]; then # yes 197 | /home/pi/sbtools/testMatrix.sh && 198 | whiptail --yesno "Did you see yellow text saying '$STATUS' on your display?" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT 199 | if [ $? -eq 0 ]; then 200 | TESTMATRIXRUN=True 201 | touch /home/pi/.nhlledportal/TESTED 202 | if [ -f /home/pi/.nhlledportal/SETUP ]; then 203 | rm /home/pi/.nhlledportal/SETUP 204 | fi 205 | 206 | if [ ! -f /home/pi/sbtools/splash.sh ]; then 207 | # Create a splash.sh that loads on boot 208 | # The t60 will run the animation for 30 seconds but it will be killed by the newest scoreboard code 209 | # So no conflicts happen with the scoreboard 210 | echo "#!/bin/bash" > /home/pi/sbtools/splash.sh 211 | echo "cd /home/pi/sbtools/" >> /home/pi/sbtools/splash.sh 212 | echo "./led-image-viewer $board_command -t60 -C splash.gif" >> /home/pi/sbtools/splash.sh 213 | chmod +x /home/pi/sbtools/splash.sh 214 | fi 215 | 216 | else 217 | TESTMATRIXRUN=False 218 | fi 219 | else 220 | TESTMATRIXRUN=False 221 | fi 222 | 223 | } 224 | 225 | do_sup_conf() { 226 | 227 | #Set --led-slowdown-gpio based on type of pi. 228 | command="command=/home/pi/nhlsb-venv/bin/python3 src/main.py " 229 | 230 | if is_pione || is_pizero; then 231 | gpio_slowdown="--led-slowdown-gpio=0" 232 | elif is_pitwo; then 233 | gpio_slowdown="--led-slowdown-gpio=2" 234 | elif is_pifour; then 235 | gpio_slowdown="--led-slowdown-gpio=4" 236 | else 237 | gpio_slowdown="--led-slowdown-gpio=2" 238 | fi 239 | 240 | board=$(whiptail --title "Create scoreboard.conf and testMatrix.sh script" --radiolist \ 241 | "Choose matrix size and gpio mapping - use space bar to select option" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT \ 242 | "1" "64x32 adafruit no flicker mod" ON \ 243 | "2" "64x32 adafruit flicker mod" OFF \ 244 | "3" "128x32 adafruit no flicker mod" OFF\ 245 | "4" "128x32 adafruit flicker mod" OFF \ 246 | "5" "128x64 adafruit no flicker mod" OFF \ 247 | "6" "128x64 adafruit flicker mod" OFF \ 248 | 3>&1 1>&2 2>&3) 249 | 250 | case "$board" in 251 | 1) board_command="--led-gpio-mapping=adafruit-hat --led-rows=32 --led-cols=64 --led-brightness=40" 252 | ;; 253 | 2) board_command="--led-gpio-mapping=adafruit-hat-pwm --led-rows=32 --led-cols=64 --led-brightness=40" 254 | ;; 255 | 3) board_command="--led-gpio-mapping=adafruit-hat --led-rows=32 --led-cols=128 --led-brightness=40" 256 | ;; 257 | 4) board_command="--led-gpio-mapping=adafruit-hat-pwm --led-rows=32 --led-cols=128 --led-brightness=40" 258 | ;; 259 | 5) board_command="--led-gpio-mapping=adafruit-hat --led-rows=64 --led-cols=128 --led-brightness=40" 260 | ;; 261 | 6) board_command="--led-gpio-mapping=adafruit-hat-pwm --led-rows=64 --led-cols=128 --led-brightness=40" 262 | ;; 263 | *) board_command="--led-gpio-mapping=adafruit-hat --led-rows=32 --led-cols=64 --led-brightness=40" 264 | ;; 265 | esac 266 | 267 | board_command="$gpio_slowdown $board_command" 268 | sup_command="$command$board_command --updatecheck" 269 | 270 | #Create a testMatrix.sh in the /home/pi/sbtools directory that contains the command line 271 | echo "#!/bin/bash" > /home/pi/sbtools/testMatrix.sh 272 | echo "echo 'Watch your display for $(tput setaf 3)$(cat /home/pi/.nhlledportal/status)$(tput sgr0) to be displayed'" >> /home/pi/sbtools/testMatrix.sh 273 | echo "echo 'This will run for about 15 seconds and then exit itself'" >> /home/pi/sbtools/testMatrix.sh 274 | echo "cd /home/pi/nhl-led-scoreboard/submodules/matrix/bindings/python/samples" >> /home/pi/sbtools/testMatrix.sh 275 | echo "sudo /home/pi/nhlsb-venv/bin/python3 runtext.py $board_command -t '$(cat /home/pi/.nhlledportal/status)' >/dev/null 2>&1" >> /home/pi/sbtools/testMatrix.sh 276 | echo "clear" >> /home/pi/sbtools/testMatrix.sh 277 | echo "exit" >> /home/pi/sbtools/testMatrix.sh 278 | chmod +x /home/pi/sbtools/testMatrix.sh 279 | 280 | #Update the installed scoreboard.conf file with the sup_command string 281 | #delete the line with command= 282 | sudo sed -i '/command=/d' /etc/supervisor/conf.d/scoreboard.conf 283 | #add the new command to scoreboard.conf 284 | sudo sed -i "/program/a $sup_command" /etc/supervisor/conf.d/scoreboard.conf 285 | 286 | whiptail --msgbox -- "Supervisor scoreboard.conf updated:\n\nnew command: $sup_command\n\nAn RGB matrix test script has been created in /home/pi/sbtools/testMatrix.sh" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT 287 | 288 | do_test_matrix 289 | 290 | if [ "$TESTMATRIXRUN" = False ]; then 291 | whiptail --msgbox "Make sure your board can run the testMatrix.sh before doing anything else. Once you've successfully run the testMatrix.sh and you get a display, you can enable the supervisor to launch the NHL LED Scoreboard" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT 292 | fi 293 | } 294 | 295 | do_enable_supervisor() { 296 | if [ "$TESTMATRIXRUN" = True ]; then 297 | whiptail --yesno "Enable supervisor systemd service. This will reboot your pi after enabling service" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT 298 | if [ $? -eq 0 ]; then # yes 299 | sudo systemctl enable supervisor 300 | sudo sync 301 | sudo reboot 302 | fi 303 | else 304 | whiptail --msgbox "testMatrix.sh has not been run successfully, please ensure your display works first" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT 305 | fi 306 | #whiptail --msgbox "$output" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT 307 | } 308 | 309 | do_sb_stdout(){ 310 | whiptail --msgbox "Show last 128kb of scoreboard stdout log - use arrow keys to move log up/down" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT 311 | stdout_log=$(supervisorctl tail -128000 scoreboard) && 312 | whiptail --msgbox --scrolltext "$stdout_log" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT 313 | } 314 | 315 | do_sb_stderr(){ 316 | whiptail --msgbox "Show last 128kb of scoreboard stderr log - use arrow keys to move log up/down" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT 317 | stderr_log=$(supervisorctl tail -128000 scoreboard stderr) && 318 | whiptail --msgbox --scrolltext "$stderr_log" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT 319 | 320 | } 321 | 322 | do_live_tail(){ 323 | whiptail --msgbox "Show live scoreboard stdout log - use CTRL-C to quit" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT 324 | supervisorctl tail -f scoreboard && 325 | whiptail --msgbox "Done" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT 326 | 327 | } 328 | 329 | 330 | do_supervisor() { 331 | if [ "$TEAMSET" = True ]; then 332 | FUN=$(whiptail --title "NHL Led Scoreboard V$VERSION Tools (Supervisor)" --menu "Supervisor" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \ 333 | "SV1 scoreboard.conf" "Create scoreboard.conf and testMatrix.sh based on board" \ 334 | "SV2 Enable supervisor" "Enable supervisor service (only do this if your board runs properly, use testMatrix.sh created in SV1)" \ 335 | 3>&1 1>&2 2>&3) 336 | RET=$? 337 | if [ $RET -eq 1 ]; then 338 | return 0 339 | elif [ $RET -eq 0 ]; then 340 | case "$FUN" in 341 | SV1\ *) do_sup_conf ;; 342 | SV2\ *) do_enable_supervisor ;; 343 | *) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;; 344 | esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1 345 | fi 346 | else 347 | whiptail --msgbox "Preferred team not selected, select your team first to create a config.json" 20 60 1 348 | fi 349 | 350 | } 351 | 352 | do_show_changelog() { 353 | 354 | #Get the last two versions committed 355 | cd /home/pi/nhl-led-scoreboard || return 356 | latest=$(git tag --sort=-v:refname | head -1) 357 | previous=$(git tag --sort=-v:refname | head -2 | tail -1) 358 | git log --oneline --decorate $previous..$latest > /tmp/changelog.txt 359 | 360 | #changes=$(/home/pi/sbtools/changelog.sh HEAD ${previous}) 361 | #whiptail --title "Changelog for V$latest - use arrow keys to scroll" --msgbox --scrolltext "$changes" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT 362 | whiptail --title "Changelog for V$latest - use arrow keys to scroll" --msgbox --scrolltext "$(cat /tmp/changelog.txt)" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT 363 | 364 | rm /tmp/changelog.txt 365 | } 366 | 367 | do_resetwifi() { 368 | 369 | whiptail --title "NHL Led Scoreboard V$VERSION Tools (Reset Wifi)" --yesno "Would you like to reset your wifi now? After reboot, look for a WiFi network starting with scoreboard- and connect to it to reset your wifi" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT 370 | if [ $? -eq 0 ]; then # yes 371 | sudo /usr/sbin/comitup-cli d 372 | sleep 5 373 | sudo sync 374 | sudo reboot 375 | fi 376 | 377 | } 378 | 379 | do_finish() { 380 | if [ $ASK_TO_REBOOT -eq 1 ]; then 381 | whiptail --yesno "Would you like to reboot now?" 20 60 2 382 | if [ $? -eq 0 ]; then # yes 383 | sudo sync 384 | sudo reboot 385 | fi 386 | fi 387 | if [ -f /home/pi/.nhlledportal/SETUP ]; then 388 | whiptail --msgbox "scoreboard not setup completely yet, rerun sb-tools again" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT 389 | fi 390 | exit 0 391 | } 392 | 393 | 394 | do_import() { 395 | calc_wt_size 396 | if [ -f /boot/firmware/scoreboard/configs.zip ]; then 397 | if (whiptail --yesno "/boot/firmware/scoreboard/configs.zip found. Import settings?" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT); then 398 | TMPDIR=$(mktemp -d) 399 | if unzip -o /boot/firmware/scoreboard/configs.zip -d $TMPDIR; then 400 | if [ -f $TMPDIR/config.json ]; then 401 | sudo cp $TMPDIR/config.json /home/pi/nhl-led-scoreboard/config/config.json 402 | sudo chown pi:pi /home/pi/nhl-led-scoreboard/config/config.json 403 | TEAMSET=True 404 | whiptail --msgbox "Imported config.json" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT 405 | fi 406 | if [ -f $TMPDIR/testMatrix.sh ]; then 407 | sudo cp $TMPDIR/testMatrix.sh /home/pi/sbtools/testMatrix.sh 408 | sudo chmod +x /home/pi/sbtools/testMatrix.sh 409 | # Update to the latest version 410 | sed -i -E "/latest version/s/V[0-9]{4}\.[0-9]{2}\.[0-9]+/$VERSION/" /home/pi/sbtools/testMatrix.sh 411 | whiptail --msgbox "Imported testMatrix.sh. Now running test." $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT 412 | do_test_matrix 413 | fi 414 | if [ -f $TMPDIR/splash.sh ]; then 415 | sudo cp $TMPDIR/splash.sh /home/pi/sbtools/splash.sh 416 | sudo chmod +x /home/pi/sbtools/splash.sh 417 | whiptail --msgbox "Imported splash.sh" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT 418 | fi 419 | if [ -f $TMPDIR/scoreboard.conf ]; then 420 | sudo cp $TMPDIR/scoreboard.conf /etc/supervisor/conf.d/scoreboard.conf 421 | sudo mkdir -p /home/pi/config_backup 422 | sudo mv /boot/firmware/scoreboard/configs.zip /home/pi/config_backup/ 423 | sudo chown -R pi:pi /home/pi/config_backup 424 | whiptail --msgbox "configs.zip moved to /home/pi/config_backup" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT 425 | do_enable_supervisor 426 | fi 427 | 428 | else 429 | whiptail --msgbox "Failed to unzip configs.zip" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT 430 | fi 431 | rm -rf $TMPDIR 432 | fi 433 | fi 434 | } 435 | 436 | #Force the user to setup scoreboard.conf for supervisor, test the matrix and then reboot once complete 437 | do_firstrun() { 438 | calc_wt_size 439 | while true; do 440 | do_import 441 | #do_scoreboard_menu 442 | if [ "$TEAMSET" = False ]; then 443 | do_set_team 444 | fi 445 | 446 | if [ "$TEAMSET" = True ]; then 447 | do_sup_conf 448 | if [ "$TESTMATRIXRUN" = True ]; then 449 | do_enable_supervisor 450 | #Should only get here if user did not enable supervisor 451 | exit 1 452 | else 453 | if (! whiptail --yesno "You need to make sure your matrix works first, try again?" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT); then 454 | exit 1 455 | else 456 | do_test_matrix 457 | fi 458 | fi 459 | else 460 | if (! whiptail --yesno "You need to select a team, try again?" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT); then 461 | exit 1 462 | fi 463 | fi 464 | done 465 | } 466 | 467 | nonint() { 468 | "$@" 469 | } 470 | 471 | 472 | 473 | do_scoreboard_menu() { 474 | if [ "$FIRSTRUN" = False ]; then 475 | FUN=$(whiptail --title "NHL Led Scoreboard V$VERSION Tools (Scoreboard Options)" --menu "Scoreboard Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \ 476 | "S1 Preferred Team" "Select preferred team for simple config.json" \ 477 | "S2 Supervisor Config" "Set up supervisor to control scoreboard"\ 478 | 3>&1 1>&2 2>&3) 479 | else 480 | FUN=$(whiptail --title "NHL Led Scoreboard V$SION Tools (Scoreboard Options)" --menu "Scoreboard Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Exit --ok-button Select\ 481 | "S1 Preferred Team" "Select preferred team for simple config.json" \ 482 | "S2 Supervisor Config" "Set up supervisor to control scoreboard"\ 483 | 3>&1 1>&2 2>&3) 484 | fi 485 | RET=$? 486 | if [ $RET -eq 1 ]; then 487 | return 0 488 | elif [ $RET -eq 0 ]; then 489 | case "$FUN" in 490 | S1\ *) do_set_team ;; 491 | S2\ *) do_supervisor ;; 492 | *) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;; 493 | esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1 494 | fi 495 | } 496 | 497 | do_trouble_menu() { 498 | if is_pi ; then 499 | FUN=$(whiptail --title "NHL Led Scoreboard V$VERSION Tools (Troubleshooting)" --menu "Troubleshooting" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \ 500 | "T1 Show live stdout log" "Watch tail of log. CTRL-C to quit" \ 501 | "T2 Show stdout log" "Show last 128kb of stdout for scoreboard" \ 502 | "T3 Show stderr log" "Show last 128kb of sterr for scoreboard" \ 503 | "T4 Collect for issue" "Post config.json and logs to pastebin for github issue" \ 504 | 3>&1 1>&2 2>&3) 505 | else 506 | FUN=$(whiptail --title "NHL Led Scoreboard V$VERSION Tools (Troubleshooting)" --menu "Troubleshooting" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \ 507 | "T1 Show live stdout log" "Watch tail of log. CTRL-C to quit" \ 508 | "T2 Show stdout log" "Show last 128kb of stdout for scoreboard" \ 509 | "T3 Show stderr log" "Show last 128kb of sterr for scoreboard" \ 510 | "T4 Collect for issue" "Post config.json and logs to pastebin for github issue" \ 511 | 3>&1 1>&2 2>&3) 512 | fi 513 | RET=$? 514 | if [ $RET -eq 1 ]; then 515 | return 0 516 | elif [ $RET -eq 0 ]; then 517 | case "$FUN" in 518 | T4\ *) do_collect ;; 519 | T2\ *) do_sb_stdout ;; 520 | T3\ *) do_sb_stderr ;; 521 | T1\ *) do_live_tail ;; 522 | *) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;; 523 | esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1 524 | fi 525 | } 526 | 527 | do_updates_menu () { 528 | if is_pi ; then 529 | FUN=$(whiptail --title "NHL Led Scoreboard V$VERSION Tools (Updates)" --menu "Updates" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \ 530 | "U1 Check for Update" "Check for newest release on github" \ 531 | "U2 Show Changelog" "Show changelog for V$VERSION" \ 532 | "U3 Upgrade" "Upgrade to latest version if one available" \ 533 | 3>&1 1>&2 2>&3) 534 | else 535 | FUN=$(whiptail --title "NHL Led Scoreboard V$VERSION Tools (Updates)" --menu "Updates" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \ 536 | "U1 Check for Update" "Check for newest release on github" \ 537 | "U2 Show Changelog" "Show changelog for V$VERSION" \ 538 | "U3 Upgrade" "Upgrade to latest version if one available" \ 539 | 3>&1 1>&2 2>&3) 540 | fi 541 | RET=$? 542 | if [ $RET -eq 1 ]; then 543 | return 0 544 | elif [ $RET -eq 0 ]; then 545 | case "$FUN" in 546 | U1\ *) do_check_update ;; 547 | U2\ *) do_show_changelog ;; 548 | U3\ *) do_upgrade ;; 549 | *) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;; 550 | esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1 551 | fi 552 | 553 | } 554 | 555 | 556 | #Check to see if called with an option on the command line 557 | case "$1" in 558 | "") ;; 559 | do_firstrun) "$@"; exit;; 560 | do_import) "$@"; exit;; 561 | *) echo "Unknown function: $1()"; exit 2;; 562 | esac 563 | 564 | 565 | # 566 | # Interactive use loop 567 | # 568 | if [ "$INTERACTIVE" = True ]; then 569 | calc_wt_size 570 | while true; do 571 | if is_pi ; then 572 | FUN=$(whiptail --title "NHL Led Scoreboard V$VERSION Tools (sb-tools)" --backtitle "$(cat /proc/device-tree/model)" --menu "Setup Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Finish --ok-button Select \ 573 | "1 Scoreboard Options" "Configure scoreboard settings" \ 574 | "2 Troubleshooting" "Troubleshooting" \ 575 | "3 Updates" "Update Info" \ 576 | "4 Reset Wifi" "Connect pi to a new wifi network" \ 577 | "5 About sb-tools" "Information about this configuration tool" \ 578 | 3>&1 1>&2 2>&3) 579 | else 580 | FUN=$(whiptail --title "NHL Led Scoreboard V$VERSION Tools (sb-tools)" --menu "Setup Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Finish --ok-button Select \ 581 | "1 Scoreboard Options" "Configure scoreboard settings" \ 582 | "2 Troubleshooting" "Troubleshooting" \ 583 | "3 Updates" "Update Info" \ 584 | "4 Reset Wifi" "Connect pi to a new wifi network" \ 585 | "5 About sb-tools" "Information about this set of tools" \ 586 | 3>&1 1>&2 2>&3) 587 | fi 588 | RET=$? 589 | if [ $RET -eq 1 ]; then 590 | do_finish 591 | elif [ $RET -eq 0 ]; then 592 | case "$FUN" in 593 | 1\ *) do_scoreboard_menu ;; 594 | 2\ *) do_trouble_menu ;; 595 | 3\ *) do_updates_menu ;; 596 | 4\ *) do_resetwifi ;; 597 | 5\ *) do_about ;; 598 | *) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;; 599 | esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1 600 | else 601 | exit 1 602 | fi 603 | done 604 | fi 605 | 606 | --------------------------------------------------------------------------------