back-end ustreamer, GPS, timelapse, and photo-refresh site working

This commit is contained in:
2025-07-27 15:10:11 -07:00
parent 7496df0174
commit b740ba9991
22 changed files with 443 additions and 99 deletions

View File

@ -17,6 +17,14 @@ packages:
- wayland-utils - wayland-utils
- xscreensaver - xscreensaver
# chrome kiosk variables
kiosk_service_templates:
- chrome_resolution: "720,405"
chrome_website: "http://127.0.0.1:7123/stream"
service_name: chrome_webcam
- chrome_resolution: "720,595"
chrome_website: "http://127.0.0.1:3000"
service_name: chrome_timelapse_control
# this will be the parent folder where the photos will be stored # this will be the parent folder where the photos will be stored
@ -27,6 +35,10 @@ packages:
## /dev/sda1 939G 28K 892G 1% /opt/carputer ## /dev/sda1 939G 28K 892G 1% /opt/carputer
working_folder: "/opt/carputer/timelapse" working_folder: "/opt/carputer/timelapse"
# other folder variables
gps_service_directory: "/opt/cosmos/gps_service"
photo_refresh_folder: "/opt/cosmos/photo_refresh"
# ustreamer variables # ustreamer variables
video_device: "/dev/video0" video_device: "/dev/video0"
resolution: "1920x1080" resolution: "1920x1080"

View File

@ -0,0 +1,22 @@
<?php
header("Cache-Control: no-cache, must-revalidate");
// Path to the directory where the images are stored
$imageDirectory = 'capture/';
// Get the list of image files in the directory
$imageFiles = glob($imageDirectory . '*.{jpg,jpeg,png,gif}', GLOB_BRACE);
// If there are no image files, return a default image
if (empty($imageFiles)) {
$defaultImage = 'default.jpg'; // Provide path to your default image
header('Content-Type: image/jpeg'); // Adjust content type if default image type is different
readfile($defaultImage);
exit;
}
// Sort the image files by modification time, latest first
array_multisort(array_map('filemtime', $imageFiles), SORT_DESC, $imageFiles);
// Get the path to the latest image file
$latestImage = $imageFiles[0];
// Set header type
header('Content-Type: image/jpeg');
// Get the image
readfile($latestImage);
?>

View File

@ -0,0 +1,17 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dynamic Image Update</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
<img id="refreshedImage" src="getImage.php" width="400" alt="Refreshed Image">
<script>
// Function to update the image every second using AJAX
setInterval(function() {
$('#refreshedImage').attr('src', 'getImage.php?_=' + new Date().getTime()); // Adding timestamp to avoid caching
}, 1000);
</script>
</body>
</html>

54
files/python_gps/app.py Normal file
View File

@ -0,0 +1,54 @@
from flask import Flask, jsonify
import gps
import threading
import time
app = Flask(__name__)
# GPS data container
current_gps_data = {
"latitude": None,
"longitude": None,
"altitude": None,
"speed": None,
"timestamp": None
}
# Global gps session object
session = gps.gps(mode=gps.WATCH_ENABLE | gps.WATCH_NEWSTYLE)
def gps_monitor():
global current_gps_data
while True:
try:
# Wait for new data from the GPS device
report = session.next()
if report['class'] == 'TPV':
current_gps_data = {
"latitude": report.get('lat', None),
"longitude": report.get('lon', None),
"altitude": report.get('alt', None),
"speed": report.get('speed', None),
"timestamp": report.get('time', None)
}
except gps.GPSException as e:
print(f"GPS Error: {e}")
except KeyError as e:
print(f"Missing Key: {e}")
time.sleep(1)
@app.route('/gps', methods=['GET'])
def get_gps_data():
"""API endpoint to fetch current GPS data"""
if None in current_gps_data.values():
return jsonify({"error": "No GPS data available yet"}), 404
return jsonify(current_gps_data)
if __name__ == '__main__':
# Start GPS monitoring in a separate thread
gps_thread = threading.Thread(target=gps_monitor)
gps_thread.daemon = True # This makes the thread exit when the program does
gps_thread.start()
# Run the Flask web service
app.run(host='0.0.0.0', port=5000)

65
tasks/gps_service.yaml Normal file
View File

@ -0,0 +1,65 @@
---
# Create service Folder
- name: gps_service - create photo_refresh folder
file:
path: "{{ gps_service_directory }}"
state: directory
mode: '0755'
owner: root
group: root
# install packages
- name: gps_service - Install Packages
apt:
name:
- gpsd
- gpsd-clients
state: present
register: apt_result
ignore_errors: true
- name: gps_service - Enable and start gpsd system service
become: true
systemd:
daemon_reload: yes
state: started
enabled: yes
name: gpsd
# this should never fail if things are plugged in correctly
- name: gps_service - check for GPS device
shell: ls /dev/ttyACM0
register: check_gps_device
- name: gps_service - show device
debug:
msg: "Device Found: {{ check_gps_device.stdout_lines[0] }}"
- name: gps_service - copy service script
template:
src: gps_service.sh.j2
dest: "{{ gps_service_directory }}/gps_service.sh"
mode: 0755
# Create gps_service python service
- name: gps_service - create service file
# vars:
template:
src: gps_service.service.j2
dest: /etc/systemd/system/gps_service.service
mode: 0644
# daemon reload
- name: gps_service - daemon reload
systemd:
daemon_reload: yes
# Enable and start
- name: gps_service - enable and start api
systemd:
name: gps_service.service
state: started
enabled: yes
...

View File

@ -14,16 +14,6 @@
group: cosmos group: cosmos
mode: '0700' mode: '0700'
- name: set kiosk template vars
set_fact:
kiosk_service_templates:
- chrome_resolution: "720,405"
chrome_website: "http://127.0.0.1:7123/stream"
service_name: chrome_webcam
- chrome_resolution: "720,595"
chrome_website: "http://127.0.0.1:3000"
service_name: chrome_timelapse_control
- name: configure kiosks - name: configure kiosks
block: block:

View File

@ -11,10 +11,17 @@
### Top Cam Feed Kiosk ### Top Cam Feed Kiosk
### Bottom Service Control Panel ### Bottom Service Control Panel
- name: Carputer Early Tasks #- name: Install ustreamer
include_tasks: # include_tasks: ustreamer.yaml
- ustreamer.yaml #
- timelapse.yaml #- name: Install gps_service
- photo_refresh.yaml # include_tasks: gps_service.yaml
#
#- name: install timelapse service
# include_tasks: timelapse.yaml
#
#- name: Install photo refresh site
# include_tasks: photo_refresh.yaml
... ...

View File

@ -0,0 +1,59 @@
---
- name: photo_refresh - set folder variable
set_fact:
docker_source: "/opt/cosmos/photo_refresh"
# Create docker Folder
- name: photo_refresh - create photo_refresh folder
file:
path: "{{ docker_source }}"
state: directory
mode: '0755'
owner: timelapse
group: timelapse
- name: photo_refresh - copy files for docker container
copy:
src: "image_refresh_php/"
dest: "{{ docker_source }}/"
mode: 0755
owner: root
group: root
- name: photo_refresh - Build image
shell: "docker build -t photo_refresh {{ docker_source }}/."
# docker_image:
# name: photo_refresh # Name of the Docker image
# source: build
# build:
# path: "{{ docker_source }}" # Path to the directory containing your Dockerfile
# state: present
# tag: latest
###############################################
# Start photo_refresh
###############################################
- name: start photo_refresh
block:
- name: photo_refresh - template config
template:
src: docker-compose-node.yaml.j2
dest: "{{ docker_source }}/docker-compose.yaml"
mode: 0644
- name: photo_refresh - Start container at 0.0.0.0:3000
shell: "docker-compose -f {{ docker_source }}/docker-compose.yaml up -d"
register: local_index_output
- debug: |
msg="{{ local_index_output.stdout_lines }}"
msg="{{ local_index_output.stderr_lines }}"
...

View File

@ -1,31 +1,33 @@
--- ---
- name: photo_refresh - set folder variable
set_fact:
- docker_source: "/opt/cosmos/photo_refresh"
# Create docker Folder # Create docker Folder
- name: lldp - python - create api folder - name: photo_refresh - create photo_refresh folder
file: file:
path: "{{ docker_source }}" path: "{{ photo_refresh_folder }}"
state: directory state: directory
mode: '0755' mode: '0755'
owner: root
group: root
- name: photo_refresh - copy files for docker container - name: photo_refresh - copy files for docker container
copy: copy:
src: "image_refresh" src: "image_refresh_php/"
dest: "{{ docker_source }}" dest: "{{ photo_refresh_folder }}/html"
recursive: yes mode: 0755
owner: root
group: root
- name: photo_refresh - Build image # - name: photo_refresh - Build image
docker_image: # shell: "docker build -t photo_refresh {{ photo_refresh_folder }}/."
name: photo_refresh # Name of the Docker image
source: build # docker_image:
build: # name: photo_refresh # Name of the Docker image
path: "{{ docker_source }}" # Path to the directory containing your Dockerfile # source: build
state: present # build:
tag: latest # path: "{{ photo_refresh_folder }}" # Path to the directory containing your Dockerfile
# state: present
# tag: latest
############################################### ###############################################
@ -35,14 +37,14 @@
- name: start photo_refresh - name: start photo_refresh
block: block:
- name: photo_refresh - Copy config - name: photo_refresh - template config
template: template:
source: docker-compose-node.yaml.j2 src: docker-compose-php.yaml.j2
dest: "{{ docker_source }}docker-compose.yaml" dest: "{{ photo_refresh_folder }}/docker-compose.yaml"
mode: 0644 mode: 0644
- name: photo_refresh - Start container at 0.0.0.0:3000 - name: photo_refresh - Start container at 0.0.0.0:8080
shell: "docker-compose -f {{ docker_source }}/docker-compose.yaml up -d" shell: "docker-compose -f {{ photo_refresh_folder }}/docker-compose.yaml up -d"
register: local_index_output register: local_index_output
- debug: | - debug: |
msg="{{ local_index_output.stdout_lines }}" msg="{{ local_index_output.stdout_lines }}"

View File

@ -1,13 +1,24 @@
--- ---
# install packages
- name: photo_refresh - Install Packages
apt:
name:
- bc
- ffmpeg
- imagemagick
state: present
register: apt_result
ignore_errors: true
# Create working Folder # Create working Folder
- name: lldp - python - create api folder - name: timelapse - create working folder
file: file:
path: "{{ working_folder }}" path: "{{ working_folder }}"
state: directory state: directory
mode: '0755' mode: '0755'
- name: set template vars - name: timelapse - set template vars
set_fact: set_fact:
# timelapse related scripts # timelapse related scripts
timelapse_script_templates: timelapse_script_templates:
@ -18,23 +29,16 @@
- src: create_timelapse.sh.j2 - src: create_timelapse.sh.j2
dest: "{{ working_folder }}/create_timelapse.sh" dest: "{{ working_folder }}/create_timelapse.sh"
- name: Create user - name: timelapse - template scripts
user:
name: "timelapse"
state: present
shell: /bin/bash
groups: video
- name: template scripts
template: template:
src: "{{ item.src }}" src: "{{ item.src }}"
dest: "{{ item.dest }}" dest: "{{ item.dest }}"
loop: "{{ timelapse_script_templates }}" owner: root
owner: timelapse group: root
group: timelapse
mode: 0755 mode: 0755
loop: "{{ timelapse_script_templates }}"
- name: template service file - name: timelapse - template service file
template: template:
src: timelapse.service.j2 src: timelapse.service.j2
dest: /etc/systemd/system/timelapse.service dest: /etc/systemd/system/timelapse.service
@ -42,12 +46,11 @@
group: root group: root
mode: 0644 mode: 0644
- name: Enable and start service - name: timelapse - daemon reload
become: true become: true
systemd: systemd:
daemon_reload: yes daemon_reload: yes
state: started state: stopped
enabled: yes name: timelapse.service
name: timelapse
... ...

View File

@ -1,6 +1,6 @@
--- ---
- name: Install Packages - name: ustreamer - Install Packages
apt: apt:
name: name:
- ustreamer - ustreamer
@ -8,14 +8,14 @@
register: apt_result register: apt_result
ignore_errors: true ignore_errors: true
- name: Create user - name: ustreamer - Create user
user: user:
name: "ustreamer" name: "ustreamer"
state: present state: present
shell: /bin/bash shell: /bin/bash
groups: video groups: video
- name: create service file - name: ustreamer - create service file
template: template:
src: ustreamer.service.j2 src: ustreamer.service.j2
dest: /etc/systemd/system/ustreamer.service dest: /etc/systemd/system/ustreamer.service
@ -23,7 +23,7 @@
group: root group: root
mode: 0644 mode: 0644
- name: Enable and start service - name: ustreamer - Enable and start service
become: true become: true
systemd: systemd:
daemon_reload: yes daemon_reload: yes

View File

@ -5,6 +5,6 @@ rm $WORKING_DIR/run
sleep 5 sleep 5
# create timelapse # create timelapse
ffmpeg -r 30 -pattern_type glob -i "$WORKING_DIR/*.jpg" \ /bin/ffmpeg -r 30 -pattern_type glob -i "$WORKING_DIR/*.jpg" \
-vf "scale=1920x1080" -vcodec libx264 /$WORKING_DIR/00-timelapse.mp4 -vf "scale=1920x1080" -vcodec libx264 /$WORKING_DIR/00-timelapse.mp4

View File

@ -0,0 +1,12 @@
services:
photo_refresh:
container_name: photo_refresh
image: php:8.0-apache
ports:
- 8080:80
volumes:
- ./html:/var/www/html/
- {{ working_folder }}/small_thumbs:/var/www/html/capture
network_mode: bridge
restart: always

View File

@ -0,0 +1,16 @@
[Unit]
Description=GPS Monitoring Service
After=network.target
[Service]
Type=simple
WorkingDirectory={{ gps_service_directory }}
ExecStart={{ gps_service_directory }}/venv/bin/python3 {{ gps_service_directory }}/app.py
Restart=always
User=root
Group=root
Environment="PATH={{ gps_service_directory }}/venv/bin"
Environment="VIRTUAL_ENV={{ gps_service_directory }}/venv"
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,14 @@
[Unit]
Description=GPS Monitoring Service
After=network.target
[Service]
Type=simple
WorkingDirectory={{ gps_service_directory }}
ExecStart={{ gps_service_directory }}/gps_service.sh
Restart=always
User=root
Group=root
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,15 @@
#!/bin/bash
i=1
while [ $i ]
do
GPS_DATA=$(gpspipe -w -n 5)
LON=$(echo $GPS_DATA | jq .lon | grep -v null | tail -n 1)
LAT=$(echo $GPS_DATA | jq .lat | grep -v null | tail -n 1)
SPEED=$(echo $GPS_DATA | jq .speed | grep -v null | tail -n 1)
echo lat:$LAT > {{ gps_service_directory }}/gps_data
echo lon:$LON >> {{ gps_service_directory }}/gps_data
echo speed:$SPEED >> {{ gps_service_directory }}/gps_data
chmod 755 {{ gps_service_directory }}/gps_data
sleep 1
done

View File

@ -1,12 +1,4 @@
#!/bin/bash #!/bin/bash
# no need to do this math forever
input_width=1920
input_height=1080
output_width=1340
output_height=580
# Calculate the ratio for trimming the top part
trim_ratio=$(echo "scale=4; $output_height / $output_width * $input_width" | bc)
# this saves a snapshot of the camera every second or so depending on how long all the other crap takes # 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 # let's hope the vars make it otherwise imma need to parse a bollocksing file
@ -23,10 +15,37 @@ do
# This variable is for the timestamp # This variable is for the timestamp
TIME=$(date +%c) TIME=$(date +%c)
# Save the initial image in a temp file
convert $WORKING_DIR/$FILENAME.jpg -gravity SouthWest -pointsize 44 \
-fill yellow -annotate +30+30 "$TIME" $WORKING_DIR/$FILENAME-temp1.jpg
# Add the second annotation to the temp file
convert $WORKING_DIR/$FILENAME-temp1.jpg -gravity SouthEast -pointsize 44 \
-fill yellow -annotate +30+30 "$COORDINATES" $WORKING_DIR/$FILENAME-temp2.jpg
# Append the two images and save the final result
convert $WORKING_DIR/$FILENAME-temp2.jpg -append $WORKING_DIR/$FILENAME.jpg
# Optionally, remove intermediate files if you don't need them
rm $WORKING_DIR/$FILENAME-temp1.jpg $WORKING_DIR/$FILENAME-temp2.jpg
# this big boi should add both the timestamp and the coordinates # this big boi should add both the timestamp and the coordinates
convert $WORKING_DIR/$FILENAME.jpg \( +clone -gravity NorthWest -pointsize 22 -fill yellow -annotate +30+30 "$TIME" \) \ #convert $WORKING_DIR/$FILENAME.jpg \( +clone -gravity NorthWest -pointsize 44 -fill yellow -annotate +30+30 "$TIME" $WORKING_DIR/$FILENAME-time.jpg\) \
\( +clone -gravity NorthEast -pointsize 22 -fill yellow -annotate +30+30 "$COORDINATES" \) \ #\( +clone -gravity NorthEast -pointsize 44 -fill yellow -annotate +30+30 "$COORDINATES" $WORKING_DIR/$FILENAME-final.jpg\)
-append $WORKING_DIR/$FILENAME.jpg #-append $WORKING_DIR/$FILENAME.jpg
# This should add the current time to the image and save it
#convert $WORKING_DIR/$FILENAME.jpg -gravity SouthWest -pointsize 44 \
#-fill yellow -annotate +30+30 $TIME $WORKING_DIR/$FILENAME-temp.jpg
# Delete original
#rm $WORKING_DIR/$FILENAME.jpg
# This should add the current latlon to the image and save it
#convert $WORKING_DIR/$FILENAME-temp.jpg -gravity SouthEast -pointsize 44 \
#-fill yellow -annotate +30+30 $COORDINATES $WORKING_DIR/$FILENAME-final.jpg
# Delete temp
#rm $WORKING_DIR/$FILENAME-temp.jpg
# This is for the tiny pic # This is for the tiny pic
# I'll make one every 5 seconds # I'll make one every 5 seconds
@ -34,7 +53,7 @@ do
# Calculate the height to trim based on the aspect ratios of the input and output images # Calculate the height to trim based on the aspect ratios of the input and output images
if (( $i % 5 == 0 )); then if (( $i % 5 == 0 )); then
NOW=$(date +%Y%m%d%H%M%S) NOW=$(date +%Y%m%d%H%M%S)
convert $WORKING_DIR/$FILENAME.jpg -gravity North -crop x$trim_ratio +repage -resize 1340x580 {{ working_folder }}/small_thumbs/$NOW.jpg convert $WORKING_DIR/$FILENAME.jpg -gravity North -crop 1920x830+0+260 +repage -resize 1340x580 {{ working_folder }}/small_thumbs/$NOW.jpg
fi fi
sleep 1 sleep 1
@ -42,17 +61,3 @@ do
((i++)) ((i++))
done done
# old one-at-a-time rubbish
# # This should add the current time to the image and save it
# convert $WORKING_DIR/$FILENAME.jpg -gravity NorthWest -pointsize 22 \
# -fill yellow -annotate +30+30 $TIME $WORKING_DIR/$FILENAME-temp.jpg
# # Delete original
# rm $WORKING_DIR/$FILENAME.jpg
#
# # This should add the current latlon to the image and save it
# convert $WORKING_DIR/$FILENAME-temp.jpg -gravity NorthEast -pointsize 22 \
# -fill yellow -annotate +30+30 $COORDINATES $WORKING_DIR/$FILENAME.jpg
# # Delete temp
# rm $WORKING_DIR/$FILENAME-temp.jpg

View File

@ -5,8 +5,8 @@ After=network.target
[Service] [Service]
ExecStart=/opt/carputer/timelapse/timelapse_service.sh ExecStart=/opt/carputer/timelapse/timelapse_service.sh
Restart=always Restart=always
User=timelapse User=root
Group=timelapse Group=root
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

View File

@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
trap "source {{ working_folder }}/create_timelapse.sh &" SIGINT SIGTERM
# initialize all the variables # initialize all the variables
# basic things # basic things
@ -18,13 +19,63 @@ echo Timelapse Initiated at $BEGIN >> $WORKING_DIR/info.txt
echo Shuttlecraft Galileo located at $CITY, $STATE $ZIPCODE >> $WORKING_DIR/info.txt echo Shuttlecraft Galileo located at $CITY, $STATE $ZIPCODE >> $WORKING_DIR/info.txt
echo $DISPLAY_NAME >> $WORKING_DIR/info.txt echo $DISPLAY_NAME >> $WORKING_DIR/info.txt
# Set the run file and fork the loop # timelapse creation helpers
touch $WORKING_DIR/run echo "To create timelapse, here's the ffpmeg script" >> $WORKING_DIR/info.txt
source {{ working_folder }}/record_snapshots.sh & echo "/bin/ffmpeg -r 30 -pattern_type glob -i "$WORKING_DIR/*.jpg" \\" >> $WORKING_DIR/info.txt
echo "-vf "scale=1920x1080" -vcodec libx264 /$WORKING_DIR/00-timelapse.mp4" >> $WORKING_DIR/info.txt
# there should be a script that can do them all
echo "/bin/ffmpeg -r 30 -pattern_type glob -i "$WORKING_DIR/*.jpg" \\" >> {{ working_folder }}/generate_timelapses.sh
echo "-vf "scale=1920x1080" -vcodec libx264 /$WORKING_DIR/00-timelapse.mp4" >> {{ working_folder }}/generate_timelapses.sh
# capture time
i=1
while [ $i ]
do
FILENAME=$(printf "img-%05d" "$i")
# old API based latlon
#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)
LAT=$(cat {{ gps_service_directory }}/gps_data | grep lat | cut -d ":" -f 2 | awk '{printf("%.5f\n", $1)}')
LON=$(cat {{ gps_service_directory }}/gps_data | grep lon | cut -d ":" -f 2 | awk '{printf("%.5f\n", $1)}')
SPEED_KPH=$(cat {{ gps_service_directory }}/gps_data | grep speed | cut -d ":" -f 2)
if [ $SPEED_KPH -lt 1 ]; then
SPEED_MPH=$(echo "scale=3; $SPEEK_KPH * 0.62" | bc)
else
SPEED_MPH=0
fi
COORDINATES="$LAT, $LON, $SPEED_MPH mph"
curl http://127.0.0.1:7123/snapshot --output $WORKING_DIR/$FILENAME.jpg
# This variable is for the timestamp
TIME=$(date +%c)
# Save the initial image in a temp file
convert $WORKING_DIR/$FILENAME.jpg -gravity SouthWest -pointsize 44 \
-fill yellow -annotate +30+30 "$TIME" $WORKING_DIR/$FILENAME-temp1.jpg
# Add the second annotation to the temp file
convert $WORKING_DIR/$FILENAME-temp1.jpg -gravity SouthEast -pointsize 44 \
-fill yellow -annotate +30+30 "$COORDINATES" $WORKING_DIR/$FILENAME-temp2.jpg
# Append the two images and save the final result
convert $WORKING_DIR/$FILENAME-temp2.jpg -append $WORKING_DIR/$FILENAME.jpg
# Optionally, remove intermediate files if you don't need them
rm $WORKING_DIR/$FILENAME-temp1.jpg $WORKING_DIR/$FILENAME-temp2.jpg
# This is for the tiny pic
# I'll make one every 5 seconds
# I have 1340x580. i think i will truncate the top
# Calculate the height to trim based on the aspect ratios of the input and output images
if (( $i % 5 == 0 )); then
NOW=$(date +%Y%m%d%H%M%S)
convert $WORKING_DIR/$FILENAME.jpg -gravity North -crop 1920x830+0+260 +repage -resize 1340x580 {{ working_folder }}/small_thumbs/$NOW.jpg
fi
sleep 1
((i++))
# set the trap and wait
trap "source {{ working_folder }}/create_timelapse.sh &" SIGINT SIGTERM
while true; do
echo "Running..."
sleep 1
done done