From b81872440f5c2584157675a99318a212ad239b85 Mon Sep 17 00:00:00 2001 From: Matt Date: Sat, 26 Jul 2025 15:09:47 -0700 Subject: [PATCH] initial commit, carputer ansible role --- README.md | 11 +++++ defaults/main.yaml | 33 +++++++++++++ files/app.py | 78 +++++++++++++++++++++++++++++++ files/create_timelapse.sh | 12 +++++ files/notes.txt | 75 +++++++++++++++++++++++++++++ files/timelapse.service | 13 ++++++ tasks/cosmos_autologin.yaml | 76 ++++++++++++++++++++++++++++++ tasks/kiosk.yaml | 61 ++++++++++++++++++++++++ tasks/main.yaml | 23 +++++++++ tasks/php_site.yaml | 0 tasks/timelapse.yaml | 15 ++++++ tasks/ustreamer.yaml | 34 ++++++++++++++ templates/chrome-app.service.j2 | 15 ++++++ templates/record_snapshots.sh | 33 +++++++++++++ templates/sddm.conf.j2 | 3 ++ templates/timelapse_service.sh.j2 | 30 ++++++++++++ templates/ustreamer.service.j2 | 14 ++++++ 17 files changed, 526 insertions(+) create mode 100644 README.md create mode 100644 defaults/main.yaml create mode 100644 files/app.py create mode 100644 files/create_timelapse.sh create mode 100644 files/notes.txt create mode 100644 files/timelapse.service create mode 100644 tasks/cosmos_autologin.yaml create mode 100644 tasks/kiosk.yaml create mode 100644 tasks/main.yaml create mode 100644 tasks/php_site.yaml create mode 100644 tasks/timelapse.yaml create mode 100644 tasks/ustreamer.yaml create mode 100755 templates/chrome-app.service.j2 create mode 100644 templates/record_snapshots.sh create mode 100644 templates/sddm.conf.j2 create mode 100644 templates/timelapse_service.sh.j2 create mode 100644 templates/ustreamer.service.j2 diff --git a/README.md b/README.md new file mode 100644 index 0000000..ae42143 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +This playbook will prep a computer to be a road trip car computer + +It will capture snapshots from an attached camera, and it will display the feed on the screen + +It will also host a site for the remote end that searches a working directory for the newest file and displays it and it does so every second + +It will also have a site to press a button to start and stop the timelapse + +A lot of this doesn't exist yet or needs to be tested + +I want to write most of it first and then push it out to test \ No newline at end of file diff --git a/defaults/main.yaml b/defaults/main.yaml new file mode 100644 index 0000000..fadbb3d --- /dev/null +++ b/defaults/main.yaml @@ -0,0 +1,33 @@ +--- + +packages: + - ethtool + - python3 + - python3-venv + - pip + - python-is-python3 + - dbus-user-session + - apache2 + - php + - kde-plasma-desktop + - clinfo + - mesa-utils + - vulkan-tools + - wayland-utils + - xscreensaver + +# this will be the parent folder where the photos will be stored +# it should be big, I'm using a 1TB SD card for the 2025 road trip +## ===Disk Info======================================= +## Filesystem Size Used Avail Use% Mounted on +## /dev/mmcblk0p2 112G 3.0G 103G 3% / +## /dev/sda1 939G 28K 892G 1% /opt/carputer +working_folder: "/opt/carputer" + +# ustreamer variables +video_device: "/dev/video0" +resolution: "1920x1080" +ustreamer_port: "7123" + + +... \ No newline at end of file diff --git a/files/app.py b/files/app.py new file mode 100644 index 0000000..906f61a --- /dev/null +++ b/files/app.py @@ -0,0 +1,78 @@ +# app.py +from flask import Flask, request, jsonify +import subprocess +import requests +import psycopg2 +import os +import re +from datetime import datetime, timedelta +import io +import json + +app = Flask(__name__) + +# API endpoint for charge +@app.route('/test', methods=['GET']) +def test(): + return jsonify({"message": "Hello there"}), 200 + +# API endpoint for service status +@app.route('/get-info', methods=['GET']) +def get_info(): + result = service_status() + print(result) + return jsonify(result) + +# # Stop Service +# @app.route('/stop', methods=['GET']) +# def stop_service(): +# result = stop_timelapse() +# print(result) +# return jsonify(result) +# +# # Start Service +# @app.route('/start', methods=['GET']) +# def start_service(): +# result = start_timelapse() +# print(result) +# return jsonify(result) + +# get service status + +def service_status(): + service_name = "timelapse.service" + try: + # Check the status of the service using systemctl + result = subprocess.run(['systemctl', 'is-active', service_name], check=True, capture_output=True, text=True) + + if result.returncode == 0: + active_status = "running" if result.stdout.strip() == b'active\n' else "not running" + + # Get the last message from the system log for this service + logs_result = subprocess.run(['journalctl', '-u', service_name, '-xe'], check=True, capture_output=True, text=True) + last_message = logs_result.stdout + + response = { + "service_status": active_status, + "service_message": last_message.strip() if last_message else "No message available" + } + + return json.dumps(response) + else: + raise Exception("Service is not running") + except subprocess.CalledProcessError as e: + response = { + "service_status": "error", + "service_message": str(e) + } + return json.dumps(response) + except Exception as e: + response = { + "service_status": "error", + "service_message": str(e) + } + return json.dumps(response) + + +if __name__ == '__main__': + app.run(debug=True, host='0.0.0.0', port=5000) diff --git a/files/create_timelapse.sh b/files/create_timelapse.sh new file mode 100644 index 0000000..09b8cfc --- /dev/null +++ b/files/create_timelapse.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# create timelapse + +# Check if the timestamp is provided +#if [ -z "$1" ]; then +# echo "Usage: ./test.sh --timestamp=\"2025-07-25 12:00:00\"" +# exit 1 +#fi + +# Extract the timestamp from the argument +#TIMESTAMP=$(echo $1 | awk -F'=' '{print $2}')4 + diff --git a/files/notes.txt b/files/notes.txt new file mode 100644 index 0000000..080b98e --- /dev/null +++ b/files/notes.txt @@ -0,0 +1,75 @@ +Notes on system itself +The service contol will have the following components: + PHP site that will make the API calls + I don't have time to learn something new + It will be similar to the photo refresh site + Flow will be get status API, draw box depending on result, if pressed execute appropriate API, return + The API calls are to a another service running a Python script + I think I will have the following text in Green (Red): + - THE SERVICE IS (NOT) RUNNING + The button will be Red (Green): + - Stop (Start) + I'll rip off gali for the style + +Notes on the python service script: + There will be the cheap API, with a start, stop, and status + Start will initialize another service + There will be a variable to store the time of the last start/stop API call and limit calls for 5 seconds after this time + No such limit will exist on the status API + +Some Timelapse and snapshot Notes +I think I will have two services, and the snapshot recording service will be a subservice of the timelapse service +The timelapse service will start the snapshot + +=== Old Timelapse Code === + +## Script to create timelapse + +# Set TODAY variable +TODAY=$(date +%Y-%m-%d) + +# Create folder for today's photos +mkdir /var/opt/mattstream/$TODAY + +# How long between photos +PERIOD=1 + +# How long to record for +LENGTH=900 + +# Calculate number of photos to take +ITERATIONS=$((LENGTH / PERIOD)) + +# Begin capture loop +i=1 +while [ $i -le $ITERATIONS ] +do + + # This variable is for the filename + NOW=$(date +%Y%m%d-%H%M%S) + # This variable is for the timestamp + TIME=$(date +%Y%m%d-%H%M) + # Download current capture + curl http://10.55.10.1:6288/snapshot --output /var/opt/mattstream/$TODAY/$NOW.jpg + # This should add the current time to the image and save it + convert /var/opt/mattstream/$TODAY/$NOW.jpg -gravity NorthWest -pointsize 22 \ + -fill yellow -annotate +30+30 $TIME /var/opt/mattstream/$TODAY/$NOW-ts.jpg + # Delete original + rm /var/opt/mattstream/$TODAY/$NOW.jpg + # And now we wait + sleep $PERIOD + # Increment the counter and start over duh + ((i++)) +# For more obvious commentary, the loop is now over +done + +# Generate the timelapse now +ffmpeg -r 24 -pattern_type glob -i "/var/opt/mattstream/$TODAY/*.jpg" \ +-vf "scale=1280x720" -vcodec libx264 /var/opt/mattstream/$TODAY/$TODAY.mp4 + +# If we want to start deleting original images, uncomment this +#rm /var/opt/mattstream/$TODAY/*.jpg + +# Slap a copy of this somewhere convenient for me to grab it +cp /var/opt/mattstream/$TODAY/$TODAY.mp4 /media/pleiades/matt-lapse/$TODAY.mp4 + diff --git a/files/timelapse.service b/files/timelapse.service new file mode 100644 index 0000000..46db2cb --- /dev/null +++ b/files/timelapse.service @@ -0,0 +1,13 @@ +[Unit] +Description=Timelapse service +After=network.target + +[Service] +ExecStart=/opt/carputer/timelapse/timelapse_service.sh + +Restart=always +User=timelapse +Group=timelapse + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/tasks/cosmos_autologin.yaml b/tasks/cosmos_autologin.yaml new file mode 100644 index 0000000..b4e2509 --- /dev/null +++ b/tasks/cosmos_autologin.yaml @@ -0,0 +1,76 @@ +--- + +############################################### +# Install KDE and other additional packages + +# Install Packages +- name: prereqs - install packages including KDE, will be slow + apt: + name: "{{ item }}" + state: present + loop: "{{ packages }}" + +# Set sddm as def +- name: prereqs - enable sddm in xdm + debconf: + name: sddm + question: shared/default-x-display-manager + value: sddm + vtype: select + +# enable sddm service +- name: prereqs - enable sddm + systemd: + name: sddm + enabled: yes + state: started + +# Configure Autologin +- name: prereqs - configure autologin et. al. + template: + src: sddm.conf.j2 + dest: /etc/sddm.conf + mode: 0644 + +############################################### +# Install Chrome +- name: prereqs - Chrome - Check if installed + command: dpkg -l google-chrome-stable + register: chrome_installed + ignore_errors: true + +- name: prereqs - Chrome - Set chrome_present variable + set_fact: + chrome_present: "{{ chrome_installed.rc == 0 }}" + +- name: prereqs - Install Chrome + include_tasks: /var/jenkins_home/ansible/roles/install_apps/tasks/chrome.yaml + when: not chrome_present | bool + +############################################### +# Apply Profile +# Create Cosmos user folder +- name: prereqs - create cosmos user profile folder + file: + path: /home/cosmos + owner: cosmos + group: cosmos + state: directory + mode: '0700' + +# Extract profile +- name: prereqs - extract cosmos user profile + unarchive: + src: /var/jenkins_home/ansible-files/lldp_scan/cosmos.tar.gz + dest: /home + owner: cosmos + group: cosmos + mode: 0700 + +# Restart sddm to initialize user profile +- name: prereqs - restart sddm + systemd: + name: sddm + state: restarted + +... \ No newline at end of file diff --git a/tasks/kiosk.yaml b/tasks/kiosk.yaml new file mode 100644 index 0000000..ef385ff --- /dev/null +++ b/tasks/kiosk.yaml @@ -0,0 +1,61 @@ +--- +############################################### +# Chrome Kiosk Config +# This creates the service and configures the user profile +############################################### + +- name: lldp - apache - clear www + command: rm -R /var/www/html +# Copy Web Server Files + +- name: lldp - apache - web - copy files + copy: + src: "{{ item }}" + dest: /var/www/html/ + owner: www-data + mode: 0755 + with_fileglob: + - "/var/jenkins_home/ansible/roles/lldp_scan/files/www/*" + +- name: Chrome Kiosk - Create User Service Folder + file: + path: /home/cosmos/.config/systemd/user + state: directory + owner: cosmos + group: cosmos + mode: '0700' + +- name: Chrome Kiosk - user stop service + command: systemctl --user -M cosmos@ stop chrome.service + register: user_start_chrome + ignore_errors: true + +- name: Chrome Kiosk - Kill chrome if running otherwise + command: pkill chrome + ignore_errors: true + +- name: Chrome Kiosk - Copy chrome service file + template: + src: chrome-app.service.j2 + dest: /home/cosmos/.config/systemd/user/chrome.service + owner: cosmos + group: cosmos + mode: 0600 + +- name: Chrome Kiosk - user daemon reload + command: systemctl --user -M cosmos@ daemon-reload + register: user_daemon_reload + ignore_errors: true + +- name: Chrome Kiosk - user enable service + command: systemctl --user -M cosmos@ enable chrome.service + register: user_enable_chrome + ignore_errors: true + +- name: Chrome Kiosk - user start service + command: systemctl --user -M cosmos@ start chrome.service + register: user_start_chrome + ignore_errors: true + + +... \ No newline at end of file diff --git a/tasks/main.yaml b/tasks/main.yaml new file mode 100644 index 0000000..27b48b3 --- /dev/null +++ b/tasks/main.yaml @@ -0,0 +1,23 @@ +--- + +########################################################### +# Playbook for configuring GOLE as Road Trip Carputer +########################################################### +# Steps: +### uStreamer +### Timelapse Service +### Sharing Page +### Cosmos Autologin +### Top Cam Feed Kiosk +### Bottom Service Control Panel + +# select display_name, city, state, postcode from addresses order by id desc limit 1; +# curl -s http://10.18.1.1:8184/wheres_gali?api_key=srJraFFprHadzQTDZf5hrmXh + +- name: Carputer Early Tasks + include_tasks: + - ustreamer.yaml + - timelapse.yaml + - php_site.yaml + +... \ No newline at end of file diff --git a/tasks/php_site.yaml b/tasks/php_site.yaml new file mode 100644 index 0000000..e69de29 diff --git a/tasks/timelapse.yaml b/tasks/timelapse.yaml new file mode 100644 index 0000000..57890d4 --- /dev/null +++ b/tasks/timelapse.yaml @@ -0,0 +1,15 @@ +--- + + + + +- name: Create user + user: + name: "timelapse" + state: present + shell: /bin/bash + groups: video + + + +... \ No newline at end of file diff --git a/tasks/ustreamer.yaml b/tasks/ustreamer.yaml new file mode 100644 index 0000000..a394c6b --- /dev/null +++ b/tasks/ustreamer.yaml @@ -0,0 +1,34 @@ +--- + +- name: Install Packages + apt: + name: + - ustreamer + state: present + register: apt_result + ignore_errors: true + +- name: Create user + user: + name: "ustreamer" + state: present + shell: /bin/bash + groups: video + +- name: create service file + template: + src: ustreamer.service.j2 + dest: /etc/systemd/system/ustreamer.service + owner: root + group: root + mode: 0644 + +- name: Enable and start service + become: true + systemd: + daemon_reload: yes + state: started + enabled: yes + name: ustreamer + +... \ No newline at end of file diff --git a/templates/chrome-app.service.j2 b/templates/chrome-app.service.j2 new file mode 100755 index 0000000..1d5f249 --- /dev/null +++ b/templates/chrome-app.service.j2 @@ -0,0 +1,15 @@ +[Unit] +Description=Chrome +After=lldp_api.service +After=sddm.service + + +[Service] +Restart=always +RestartSec=3s +#ExecStartPre=/bin/sleep 5 +ExecStart=/usr/bin/google-chrome \ + --app="{{ chrome_website }}" + +[Install] +WantedBy=graphical-session.target \ No newline at end of file diff --git a/templates/record_snapshots.sh b/templates/record_snapshots.sh new file mode 100644 index 0000000..56c69f2 --- /dev/null +++ b/templates/record_snapshots.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# this saves a snapshot of the camera every second or so depending on how long all the other crap takes +# let's hope the vars make it otherwise imma need to parse a bollocksing file + +i=1 +while [ -f "$WORKING_DIR/run" ] +do + FILENAME=$(printf "img-%05d" "$i") + LATLON=$(curl -s http://10.18.1.1:8184/where_is?api_key={{ tesla_api_key }} ) + LON=$(echo $LATLON | jq .lon) + LAT=$(echo $LATLON | jq .lat) + COORDINATES="$LAT, $LON" + curl http://127.0.0.1:7123/snapshot --output $WORKING_DIR/$BEGIN/$FILENAME.jpg + # This variable is for the timestamp + TIME=$(date +%Y%m%d-%H%M)xqa + # This should add the current time to the image and save it + convert $WORKING_DIR/$BEGIN/$FILENAME.jpg -gravity NorthWest -pointsize 22 \ + -fill yellow -annotate +30+30 $TIME $WORKING_DIR/$BEGIN/$FILENAME-temp.jpg + # Delete original + rm $WORKING_DIR/$BEGIN/$FILENAME.jpg + + # This should add the current latlon to the image and save it + convert $WORKING_DIR/$BEGIN/$FILENAME-temp.jpg -gravity NorthEast -pointsize 22 \ + -fill yellow -annotate +30+30 $COORDINATES $WORKING_DIR/$BEGIN/$FILENAME.jpg + # Delete temp + rm $WORKING_DIR/$BEGIN/$FILENAME-temp.jpg + + sleep 1 + + ((i++)) + +done \ No newline at end of file diff --git a/templates/sddm.conf.j2 b/templates/sddm.conf.j2 new file mode 100644 index 0000000..cee5b03 --- /dev/null +++ b/templates/sddm.conf.j2 @@ -0,0 +1,3 @@ +[Autologin] +User=cosmos +Session=plasma.desktop diff --git a/templates/timelapse_service.sh.j2 b/templates/timelapse_service.sh.j2 new file mode 100644 index 0000000..8e9348b --- /dev/null +++ b/templates/timelapse_service.sh.j2 @@ -0,0 +1,30 @@ +#!/bin/bash + +# initialize all the variables +# basic things +BEGIN=$(date +%Y%m%d-%H%M%S) +WORKING_DIR="/opt/carputer/timelapse/storage/$BEGIN" +# be greedy about API calls +WHERES_GALI=$(curl -s http://10.18.1.1:8184/wheres_gali?api_key={{ tesla_api_key }}) +# parse the one API call +CITY=$(echo $WHERES_GALI | jq .city) +STATE=$(echo $WHERES_GALI | jq .state) +ZIPCODE=$(echo $WHERES_GALI | jq .postcode) +DISPLAY_NAME=$(echo $WHERES_GALI | jq .display_name) + +# Generate Status Report +mkdir -p $WORKING_DIR +echo Timelapse Initiated at $BEGIN >> $WORKING_DIR/info.txt +echo Shuttlecraft Galileo located at $CITY, $STATE $ZIPCODE >> $WORKING_DIR/info.txt +echo $DISPLAY_NAME >> $WORKING_DIR/info.txt + +# Set the run file and fork the loop +touch $WORKING_DIR/run +source ./record_snapshots.sh & + +# set the trap and wait +trap "source ./record_timelapse" SIGINT SIGTERM +while true; do + echo "Running..." + sleep 1 +done \ No newline at end of file diff --git a/templates/ustreamer.service.j2 b/templates/ustreamer.service.j2 new file mode 100644 index 0000000..5532abc --- /dev/null +++ b/templates/ustreamer.service.j2 @@ -0,0 +1,14 @@ +[Unit] +Description=uStreamer - Lightweight HTTP Live Streaming server +After=network.target + +[Service] +ExecStart=/usr/bin/ustreamer --format=jpeg --resolution={{ resolution }} \ + --port={{ ustreamer_port}} --host=0.0.0.0 --device={{ video_device }} + +Restart=always +User=ustreamer +Group=ustreamer + +[Install] +WantedBy=multi-user.target \ No newline at end of file