diff --git a/archive/docker-compose-owncast.yaml.j2 b/archive/docker-compose-owncast.yaml.j2 new file mode 100644 index 0000000..3b64b34 --- /dev/null +++ b/archive/docker-compose-owncast.yaml.j2 @@ -0,0 +1,12 @@ +services: + + owncast: + container_name: owncast + image: owncast/owncast:latest + volumes: + - {{ owncast_working_folder}}:/app/data + ports: + - {{ owncast_web_port }}:8080 + - 1935:1935 + restart: always + network_mode: bridge \ No newline at end of file diff --git a/files/notes.txt b/archive/notes.txt similarity index 68% rename from files/notes.txt rename to archive/notes.txt index 080b98e..30987c4 100644 --- a/files/notes.txt +++ b/archive/notes.txt @@ -73,3 +73,14 @@ ffmpeg -r 24 -pattern_type glob -i "/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 +| | | | | +| :---------------------- | :---------------------- | :---------------------- | :---------------------- | +| **Tuesday:** | **Wednesday:** | **Thursday:** | **Friday:** | +| • Soda Springs, CA | • Battle Mountain, NV | • Sidney, NE | • Manitowoc, WI | +| • Fernley, NV | • West Wendover, NV | • North Platte, NE | • KEVIN!!!!!! | +| | • Park City, UT | • Kearney, NE | | +| | • Rock Springs, WY | • Lincoln, NE | | +| | • Rawlins, WY | • Des Moins, IA | | +| | • Laramie, WY | • Iowa City, IA | | +| | | • Madison, WI | | + diff --git a/archive/owncast.yaml b/archive/owncast.yaml new file mode 100644 index 0000000..85a9d8a --- /dev/null +++ b/archive/owncast.yaml @@ -0,0 +1,27 @@ +############################################### +# Start photo_refresh +############################################### +- name: start owncast + block: + + # Create service Folder + - name: carputer - owncast - create owncast_working_folder folder + file: + path: "{{ owncast_working_folder }}" + state: directory + mode: '0755' + owner: root + group: root + + - name: carputer - owncast - template config + template: + src: docker-compose-owncast.yaml.j2 + dest: "{{ owncast_working_folder }}/docker-compose.yaml" + mode: 0644 + + - name: "carputer - owncast - Start container at 0.0.0.0:{{ owncast_web_port }}" + shell: "docker-compose -f {{ owncast_working_folder }}/docker-compose.yaml up -d" + register: docker_output + - debug: | + msg="{{ docker_output.stdout_lines }}" + msg="{{ docker_output.stderr_lines }}" \ No newline at end of file diff --git a/defaults/main.yaml b/defaults/main.yaml index f143357..d3cfed7 100644 --- a/defaults/main.yaml +++ b/defaults/main.yaml @@ -31,7 +31,7 @@ autologin_packages: # chrome kiosk variables kiosk_service_templates: - chrome_resolution: "720,405" - chrome_website: "http://127.0.0.1:7123/stream" + chrome_website: "http://172.30.5.20:7123/stream" service_name: chrome_webcam extra_service_configs: | After=ustreamer.service @@ -40,7 +40,7 @@ kiosk_service_templates: extra_chrome_configs: | --user-data-dir=/opt/chrome/one \ - chrome_resolution: "720,550" - chrome_website: "http://127.0.0.1:8081" + chrome_website: "http://172.30.5.20:8081" service_name: chrome_timelapse_control extra_service_configs: "" user_data_dir: "/opt/chrome/two" @@ -76,8 +76,13 @@ video_device: "/dev/video0" resolution: "1920x1080" ustreamer_port: "7123" +# owncast vars +owncast_working_folder: "/opt/cosmos/owncast" +owncast_web_port: "8082" + # other rando vars cpu_architecture: "amd64" extra_volumes: "" + ... \ No newline at end of file diff --git a/files/service_control_api/website/styles.css b/files/service_control_api/website/styles.css index b85b52a..066c1b6 100644 --- a/files/service_control_api/website/styles.css +++ b/files/service_control_api/website/styles.css @@ -74,6 +74,11 @@ button:hover { background: radial-gradient(circle, #003699 0%, #337aff 100%); color: #bdc3c7; /* Dimmer text color */ } +.deactivating { + background-color: #0d2b2c; + background: radial-gradient(circle, #0d2b2c 0%, #123738 100%); + color: #bdc3c7; /* Dimmer text color */ +} .unknown { background-color: #ec701e; background: radial-gradient(circle, #c9580d 0%, #ec701e 100%); diff --git a/tasks/gps_service.yaml b/tasks/gps_service.yaml index 3cdf972..751df26 100644 --- a/tasks/gps_service.yaml +++ b/tasks/gps_service.yaml @@ -1,7 +1,7 @@ --- # Create service Folder -- name: carputer - gps_service - create photo_refresh folder +- name: carputer - gps_service - create gps_service_directory folder file: path: "{{ gps_service_directory }}" state: directory diff --git a/tasks/main.yaml b/tasks/main.yaml index df5373d..3e342bb 100644 --- a/tasks/main.yaml +++ b/tasks/main.yaml @@ -14,15 +14,15 @@ ### Bottom Service Control Panel -#- name: Install Packages -# apt: -# name: "{{ item }}" -# state: present -# loop: "{{ main_packages }}" -# -#- name: Install ustreamer -# include_tasks: ustreamer.yaml -# +- name: Install Packages + apt: + name: "{{ item }}" + state: present + loop: "{{ main_packages }}" + +- name: Install ustreamer + include_tasks: ustreamer.yaml + #- name: Install gps_service # include_tasks: gps_service.yaml # @@ -31,14 +31,14 @@ # #- name: Install photo refresh site # include_tasks: photo_refresh.yaml -# -#- name: Install timelapse service control -# include_tasks: service_control.yaml -# + +- name: Install timelapse service control + include_tasks: service_control.yaml + #- name: Install cosmos autologin user # include_tasks: cosmos_autologin.yaml # -- name: Set up Chrome Kiosk Services - include_tasks: chrome.yaml +#- name: Set up Chrome Kiosk Services +# include_tasks: chrome.yaml ... \ No newline at end of file diff --git a/tasks/photo_refresh.yaml b/tasks/photo_refresh.yaml index b8d2c18..8a637b3 100644 --- a/tasks/photo_refresh.yaml +++ b/tasks/photo_refresh.yaml @@ -51,10 +51,41 @@ - name: carputer - photo_refresh - Start container at 0.0.0.0:8080 shell: "docker-compose -f {{ photo_refresh_folder }}/docker-compose.yaml up -d" - register: local_index_output + register: docker_output - debug: | - msg="{{ local_index_output.stdout_lines }}" - msg="{{ local_index_output.stderr_lines }}" + msg="{{ docker_output.stdout_lines }}" + msg="{{ docker_output.stderr_lines }}" +############################################### +# set up thumbnail_purge +############################################### + +- name: timelapse - purge thumbnail cron job + block: + + - name: timelapse - create purge script + copy: + dest: "{{ photo_refresh_folder }}/purge_thumbnails.sh" + content: | + #!/bin/bash + TARGET_DIR="{{ working_folder }}/small_thumbs" + cd "$TARGET_DIR" || exit + # Find all files, sort them by modification time (oldest first), and delete all but the 50 most recent + ls -t | tail -n +1000 | while read -r file; do + if [ -f "$file" ]; then + # echo "Deleting file: $file" + rm "$file" + fi + done + mode: 0755 + + - name: timelapse - add purge to cron + copy: + dest: "/etc/cron.d/purge_thumbnails" + content: | + # This is for deleting the small thumbnails generated by the timelapse + # It should run once an hour + 00 * * * * root {{ photo_refresh_folder }}/purge_thumbnails.sh + mode: 0755 ... \ No newline at end of file diff --git a/tasks/service_control.yaml b/tasks/service_control.yaml index ea57b0c..3518c86 100644 --- a/tasks/service_control.yaml +++ b/tasks/service_control.yaml @@ -29,8 +29,8 @@ # Copy API Code - name: carputer - service_control api - copy api code - copy: - src: service_control_api/app.py + template: + src: app-service.py.j2 dest: "{{ service_control_folder }}/app.py" mode: 0644 @@ -111,12 +111,12 @@ mode: 0644 ############################################### - # Start photo_refresh + # Start service_control_website ############################################### - name: start service_control_website block: - - name: set container_name + - name: set container variables set_fact: container_name: "service_control_website" container_http_port: "8081" @@ -129,10 +129,10 @@ - name: "service_control_website - Start container at 0.0.0.0:{{ container_http_port }}" shell: "docker-compose -f {{ service_control_web_folder }}/docker-compose.yaml up -d" - register: local_index_output + register: docker_output - debug: | - msg="{{ local_index_output.stdout_lines }}" - msg="{{ local_index_output.stderr_lines }}" + msg="{{ docker_output.stdout_lines }}" + msg="{{ docker_output.stderr_lines }}" diff --git a/tasks/timelapse.yaml b/tasks/timelapse.yaml index f754b2a..39601d3 100644 --- a/tasks/timelapse.yaml +++ b/tasks/timelapse.yaml @@ -31,4 +31,20 @@ state: stopped name: timelapse.service +# create smb for this + +- name: timelapse - Copy smb.conf + template: + src: smb.conf.j2 + dest: /etc/samba/smb.conf.d/timelapse.conf + owner: root + group: root + mode: 0644 + +- name: timelapse - Restart smb + service: + name: smbd + state: restarted + enabled: yes + ... \ No newline at end of file diff --git a/files/service_control_api/app.py b/templates/app-service.py.j2 similarity index 68% rename from files/service_control_api/app.py rename to templates/app-service.py.j2 index 68d4d4b..4738f3e 100644 --- a/files/service_control_api/app.py +++ b/templates/app-service.py.j2 @@ -3,6 +3,7 @@ import requests from lxml import html from flask import Flask, request, jsonify import json +import os app = Flask(__name__) @@ -52,6 +53,50 @@ def service_status(): except subprocess.CalledProcessError as e: return {"Error": e.strip('\n')} +def read_gps_data_file(): + # Define the path to your file. This is a relative path example. Adjust it as needed. + file_path = '{{ gps_service_directory }}/gps_data' + + if os.path.exists(file_path): + with open(file_path, 'r') as file: + lines = file.readlines() + + # Assuming the data is in a specific format, you can parse it accordingly + lat = lines[0].split(':')[1] + lon = lines[1].split(':')[1] + speed = lines[2].split(':')[1] + + try: + lat = float(lat.strip('\n')) + lon = float(lon.strip('\n')) + speed = float(speed.strip('\n')) * 0.62 + except: + lat = 0 + lon = 0 + speed = 0 + + # Round latitude and longitude to 5 decimal places at most + lat_rounded = round(lat, 3) + lon_rounded = round(lon, 3) + speed_rounded = round(speed, 1) + data = { + "lat": lat_rounded, + "lon": lon_rounded, + "speed": speed_rounded + } + else: + data = { + "lat": 37.548, + "lon": -121.961, + "speed": 0 + } + + return jsonify(data) + +@app.route('/return_gps', methods=['GET']) +def return_gps(): + return read_gps_data_file() + @app.route('/start', methods=['GET']) def start(): try: diff --git a/templates/create_timelapse.sh.j2 b/templates/create_timelapse.sh.j2 index b962baa..05653eb 100644 --- a/templates/create_timelapse.sh.j2 +++ b/templates/create_timelapse.sh.j2 @@ -5,5 +5,6 @@ rm $RUN_FILE rm $WORKING_DIR/*temp*.jpg # create timelapse -/bin/ffmpeg -r 30 -pattern_type glob -i "$WORKING_DIR/*.jpg" -vf "scale=1920x1080" -vcodec libx264 /$WORKING_DIR/00-timelapse.mp4 & +echo "Creating Timelapse" +/bin/ffmpeg -r 30 -pattern_type glob -i "$WORKING_DIR/*.jpg" -vf "scale=1920x1080" -vcodec libx264 /$WORKING_DIR/00-timelapse.mp4 diff --git a/templates/index-service_control.php.j2 b/templates/index-service_control.php.j2 index 03b9598..01f0cf6 100644 --- a/templates/index-service_control.php.j2 +++ b/templates/index-service_control.php.j2 @@ -1,4 +1,5 @@ "; // Use file_get_contents or cURL to fetch the API data - $response = file_get_contents($apiUrl); - + try { + $response = file_get_contents($apiUrl); + } catch (Exception $e) { + $response = '{ "Message": "unknowable", "Status": "unknowable" }'; + } if ($response === FALSE) { $debug_string = $debug_string."Response ERROR!!
"; return "error"; // API error @@ -52,47 +56,44 @@ function runAPI($submitted_status) { return isset($data['Status']) ? $data['Status'] : 'unknown'; } +// | awk '{printf(\"%.5f\n\", $1)}' +// | awk '{printf(\"%.5f\n\", $1)}' + function getGPS(){ - if (!file_exists("{{ gps_service_directory }}/gps_data")) { - return "No GPS data available."; - } + // check the API + $gps_data = file_get_contents("http://172.17.0.1:5000/return_gps"); try { - $LAT = shell_exec("cat {{ gps_service_directory }}/gps_data | grep lat | cut -d ':' -f 2 | awk '{printf(\"%.5f\n\", $1)}')"); - $LON = shell_exec("cat {{ gps_service_directory }}/gps_data | grep lon | cut -d ':' -f 2 | awk '{printf(\"%.5f\n\", $1)}')"); - $SPEED = floatval(trim(shell_exec("cat {{ gps_service_directory }}/gps_data | grep speed | cut -d ':' -f 2)"))) * 0.62; - } catch (Exception $e) { - return "Failed to parse gps_data: ".$e; - } + $gps_data = json_decode($gps_data, true); - if (is_null($LAT)) { - return "No GPS data available."; + } catch (Exception $e){ + $gps_data = '{c}'; + } + + //set the vars + $LAT = $gps_data['lat']; + $LON = $gps_data['lon']; + $SPEED = $gps_data['speed']; + + if (is_null($LAT) || $LAT == 0) { + return "No GPS data available - null LAT"; } - if (is_null($LON)) { - return "No GPS data available."; + if (is_null($LON) || $LON == 0) { + return "No GPS data available - null LON"; } return $LAT.", ".$LON.", ".$SPEED."mph"; } - -// Check if the button was clicked via AJAX request -// if ($_SERVER['REQUEST_METHOD'] === 'POST') { -// $button_recent = true; -// $status_at_submit = $_POST['status']; -// echo "Set when clicked, second in code - ".$status_at_submit."
"; -// // Get the API result -// $apiResult = runAPI($status_at_submit); -// // Return the result as a JSON response -// echo json_encode(['status' => $apiResult]); -// exit; -// } - // URL of the external API $apiUrl = "http://172.17.0.1:5000/status"; // Use file_get_contents to fetch data from the API -$response = file_get_contents($apiUrl); +try { + $response = file_get_contents($apiUrl); +} catch (Exception $e) { + $response = '{ "Message": "unknowable", "Status": "unknowable" }'; +} // Check if the request was successful if ($response === FALSE) { @@ -132,6 +133,10 @@ switch ($status) { $button_text = "Service Stopping..."; $button_active = false; break; + case "unknowable": + $button_text = "Service Manager Down..."; + $button_active = false; + break; default: $button_text = "Error, No Status"; $button_active = false; diff --git a/templates/purge_thumbnails.sh.j2 b/templates/purge_thumbnails.sh.j2 new file mode 100644 index 0000000..75c354e --- /dev/null +++ b/templates/purge_thumbnails.sh.j2 @@ -0,0 +1,10 @@ +#!/bin/bash +TARGET_DIR="{{ working_folder }}/small_thumbs" +cd "$TARGET_DIR" || exit +# Find all files, sort them by modification time (oldest first), and delete all but the 50 most recent +ls -t | tail -n +51 | while read -r file; do + if [ -f "$file" ]; then + # echo "Deleting file: $file" + rm "$file" + fi +done \ No newline at end of file diff --git a/templates/smb.conf.j2 b/templates/smb.conf.j2 new file mode 100644 index 0000000..6331aa6 --- /dev/null +++ b/templates/smb.conf.j2 @@ -0,0 +1,14 @@ +# timelapse.conf +# timelapse share folder + + +[timelapse] + path = {{ working_folder }} + writable = yes + read only = no + only guest = yes + public = yes + guest ok = yes + guest only = yes + guest account = nobody + browsable = yes diff --git a/templates/timelapse.service.j2 b/templates/timelapse.service.j2 index 5d9ceb6..6ae0efc 100644 --- a/templates/timelapse.service.j2 +++ b/templates/timelapse.service.j2 @@ -3,7 +3,7 @@ Description=Timelapse service After=network.target [Service] -ExecStart=/opt/carputer/timelapse/timelapse_service.sh +ExecStart={{ working_folder }}/timelapse_service.sh Restart=always User=root Group=root diff --git a/templates/timelapse_service.sh.j2 b/templates/timelapse_service.sh.j2 index d3552e3..f89ef26 100644 --- a/templates/timelapse_service.sh.j2 +++ b/templates/timelapse_service.sh.j2 @@ -1,54 +1,60 @@ #!/bin/bash trap "source {{ working_folder }}/create_timelapse.sh" SIGINT SIGTERM - +echo "Start" # initialize all the variables # basic things BEGIN=$(date +%Y%m%d-%H%M%S) +echo $BEGIN WORKING_DIR="{{ working_folder }}/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) +mkdir -p $WORKING_DIR + +if ( nc -w 3 -z 10.18.1.1 8184 2>&1 >/dev/null ); then + # 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 + 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 +else + echo "No Gali API Data Available" +fi + RUN_FILE={{ working_folder }}/run -# 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 - # timelapse creation helpers -echo "To create timelapse, here's the ffpmeg script" >> $WORKING_DIR/info.txt -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 +#echo "To create timelapse, here's the ffpmeg script" >> $WORKING_DIR/info.txt +#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 +#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 touch $RUN_FILE +echo $RUN_FILE i=1 + + while [ -e $RUN_FILE ] 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 + if [ $SPEED_KPH > 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 - + echo $FILENAME + echo $COORDINATES # This variable is for the timestamp TIME=$(date +%c) @@ -67,17 +73,14 @@ do 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 + 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 sleep 1 ((i++)) done - +# end the loop