initial commit, carputer ansible role

This commit is contained in:
2025-07-26 15:09:47 -07:00
commit b81872440f
17 changed files with 526 additions and 0 deletions

11
README.md Normal file
View File

@ -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

33
defaults/main.yaml Normal file
View File

@ -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"
...

78
files/app.py Normal file
View File

@ -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)

12
files/create_timelapse.sh Normal file
View File

@ -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

75
files/notes.txt Normal file
View File

@ -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

13
files/timelapse.service Normal file
View File

@ -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

View File

@ -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
...

61
tasks/kiosk.yaml Normal file
View File

@ -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
...

23
tasks/main.yaml Normal file
View File

@ -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
...

0
tasks/php_site.yaml Normal file
View File

15
tasks/timelapse.yaml Normal file
View File

@ -0,0 +1,15 @@
---
- name: Create user
user:
name: "timelapse"
state: present
shell: /bin/bash
groups: video
...

34
tasks/ustreamer.yaml Normal file
View File

@ -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
...

15
templates/chrome-app.service.j2 Executable file
View File

@ -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

View File

@ -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

3
templates/sddm.conf.j2 Normal file
View File

@ -0,0 +1,3 @@
[Autologin]
User=cosmos
Session=plasma.desktop

View File

@ -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

View File

@ -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