first successful test

This commit is contained in:
2025-08-24 19:49:45 -07:00
parent 96906161fd
commit cc24088bbe
9 changed files with 388 additions and 110 deletions

View File

@ -36,6 +36,8 @@ sd_unmounted: true
arm_arch: false arm_arch: false
refresh_special: true
streamer_packages: streamer_packages:
- ffmpeg - ffmpeg
- alsa-utils - alsa-utils

View File

@ -9,21 +9,54 @@ $button_recent = false;
$button_action = ""; $button_action = "";
$button_result = ""; $button_result = "";
$http_host = $_SERVER['HTTP_HOST']; $http_host = $_SERVER['HTTP_HOST'];
$debug_string = ""; $capture_duration = "3";
$elapsed_time = "0";
if (isset($_GET['action'])) { // Service Status API Checker
$debug_string = $debug_string."GET called, ".$_GET['action']."<br>"; $status_API_URL = "http://172.17.0.1:5000/status";
$button_recent = true;
$button_result = runAPI($_GET['action']); // Use file_get_contents to fetch data from the API
$debug_string = $debug_string."Button Result: ".$button_result."<br>"; try {
sleep(1); $status_API_response = file_get_contents($status_API_URL);
} catch (Exception $e) {
$status_API_response = '{ "Message": "unknowable", "Status": "unknowable" }';
}
// Check if the request was successful
if ($status_API_response === FALSE) {
$data = "Failed to fetch data.";
} }
function runAPI($submitted_status) { else {
if(!isset($debug_string)){ // Decode the JSON response (assuming the API returns JSON)
$debug_string = ""; $data = json_decode($status_API_response, true);
if (isset($data['Message'])) {
$message = $data['Message'];
} else {
$message = "No message found in the response.";
} }
$debug_string = $debug_string."runAPI called, ".$submitted_status."<br>"; if (isset($data['Status'])) {
$status = $data['Status'];
} else {
$status = "None";
}
if (isset($data['Elapsed_Time'])) {
$elapsed_time = $data['Elapsed_Time'];
} else {
$elapsed_time = "-1";
}
}
if (isset($_GET['new_duration'])){
$button_recent = true;
set_duration($_GET['new_duration']);
}
if (isset($_GET['action'])) {
$button_recent = true;
$button_result = service_manager($_GET['action']);
}
function service_manager($submitted_status) {
switch ($submitted_status) { switch ($submitted_status) {
case "stop": case "stop":
$apiUrl = "http://172.17.0.1:5000/stop"; $apiUrl = "http://172.17.0.1:5000/stop";
@ -35,8 +68,6 @@ function runAPI($submitted_status) {
$apiUrl = "http://172.17.0.1:5000/status"; $apiUrl = "http://172.17.0.1:5000/status";
break; break;
} }
// API URL
$debug_string = $debug_string."After switch, apiUrl is ".$apiUrl."<br>";
// Use file_get_contents or cURL to fetch the API data // Use file_get_contents or cURL to fetch the API data
try { try {
$response = file_get_contents($apiUrl); $response = file_get_contents($apiUrl);
@ -44,77 +75,15 @@ function runAPI($submitted_status) {
$response = '{ "Message": "unknowable", "Status": "unknowable" }'; $response = '{ "Message": "unknowable", "Status": "unknowable" }';
} }
if ($response === FALSE) { if ($response === FALSE) {
$debug_string = $debug_string."Response ERROR!!<br>";
return "error"; // API error return "error"; // API error
} }
// Decode the JSON response (assuming the API returns JSON) // Decode the JSON response (assuming the API returns JSON)
$data = json_decode($response, true); $data = json_decode($response, true);
$debug_string = $debug_string."Data from API call: ".$data['Message']."<br>";
// Assuming the API returns a single word result (like "success", "failure", etc.) // Assuming the API returns a single word result (like "success", "failure", etc.)
return isset($data['Status']) ? $data['Status'] : 'unknown'; return isset($data['Status']) ? $data['Status'] : 'unknown';
} }
// | awk '{printf(\"%.5f\n\", $1)}'
// | awk '{printf(\"%.5f\n\", $1)}'
function getGPS(){
// check the API
$gps_data = file_get_contents("http://172.17.0.1:5000/return_gps");
try {
$gps_data = json_decode($gps_data, true);
} 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) || $LON == 0) {
return "No GPS data available - null LON";
}
return $LAT.", ".$LON.", ".$SPEED."mph";
}
// URL of the external API
$apiUrl = "http://172.17.0.1:5000/status";
// Use file_get_contents to fetch data from the API
try {
$response = file_get_contents($apiUrl);
} catch (Exception $e) {
$response = '{ "Message": "unknowable", "Status": "unknowable" }';
}
// Check if the request was successful
if ($response === FALSE) {
$data = "Failed to fetch data.";
}
else {
// Decode the JSON response (assuming the API returns JSON)
$data = json_decode($response, true);
// If you want to display specific data, adjust this part
// For example, if the API returns a 'message' key, display it
if (isset($data['Message'])) {
$message = $data['Message'];
} else {
$message = "No message found in the response.";
}
if (isset($data['Status'])) {
$status = $data['Status'];
} else {
$status = "None";
}
}
switch ($status) { switch ($status) {
case "active": case "active":
$button_text = "Stop Service"; $button_text = "Stop Service";
@ -168,6 +137,133 @@ if($button_recent){
exit(); // Always include exit() after headers are sent exit(); // Always include exit() after headers are sent
} }
function duration_button_handler($button_ID, $function) {
// Duration Status API Checker
$duration_API_URL = "http://172.17.0.1:5000/check_duration";
// Use file_get_contents to fetch data from the API
try {
$duration_API_URL = file_get_contents($duration_API_URL);
} catch (Exception $e) {
$duration_API_URL = '{ "Message": "unknowable", "Status": "unknowable" }';
}
// Check if the request was successful
if ($duration_API_URL === FALSE) {
$raw_duration = "Failed to fetch data.";
}
else {
// Decode the JSON response (assuming the API returns JSON)
$raw_duration = json_decode($duration_API_URL, true);
if (isset($raw_duration['duration'])) {
$capture_duration = $raw_duration['duration'];
} else {
$capture_duration = "3";
}
}
// Service Status API Checker
$status_API_URL = "http://172.17.0.1:5000/status";
// Use file_get_contents to fetch data from the API
try {
$status_API_response = file_get_contents($status_API_URL);
} catch (Exception $e) {
$status_API_response = '{ "Message": "unknowable", "Status": "unknowable" }';
}
// Check if the request was successful
if ($status_API_response === FALSE) {
$data = "Failed to fetch data.";
}
else {
// Decode the JSON response (assuming the API returns JSON)
$data = json_decode($status_API_response, true);
if (isset($data['Message'])) {
$message = $data['Message'];
} else {
$message = "No message found in the response.";
}
if (isset($data['Status'])) {
$status = $data['Status'];
} else {
$status = "None";
}
}
# function 0 - return class string
# function 1 - return active string
switch ($function) {
case "0":
# these classes are weird because reasons
# specifically because the classes are named
# based on service status
# to decode this, inactive is green
# and deactivating is blue
# so all the selectable buttons are blue and the active one is green
if($button_ID == $capture_duration){
return "inactive";
}
else {
return "selected";
}
break;
case "1":
# this disables clicking the active button
if($status == "active") {
return " disabled";
}
else if($button_ID == $capture_duration){
return " disabled";
}
else {
return "";
}
break;
default:
break;
}
}
# this will be an integer from 0-3 for
# 30min, 1hr, 2hr, and 6rh respectively
# info = {
# "duration": new_duration
# }
function set_duration($duration) {
switch ($duration) {
case "0":
$apiUrl = "http://172.17.0.1:5000/store_duration?new_duration=0";
break;
case "1":
$apiUrl = "http://172.17.0.1:5000/store_duration?new_duration=1";
break;
case "2":
$apiUrl = "http://172.17.0.1:5000/store_duration?new_duration=2";
break;
case "3":
$apiUrl = "http://172.17.0.1:5000/store_duration?new_duration=3";
break;
default:
$apiUrl = "http://172.17.0.1:5000/status";
break;
}
try {
$response = file_get_contents($apiUrl);
} catch (Exception $e) {
$response = '{ "Message": "unknowable", "Status": "unknowable" }';
}
if ($response === FALSE) {
return "error"; // API error
}
}
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
@ -185,22 +281,53 @@ if($button_recent){
<div class="container"> <div class="container">
<div class="button-container"> <div class="button-container">
<button <?php echo $button_action; ?> class="<?php echo $status; ?>" id="phpButton"<?php if(!$button_active || $button_recent) {echo " disabled";} ?>>
<button <?php echo $button_action; ?> class="<?php echo $status; ?>" id="phpButton"<?php if(!$button_active || $button_recent) {echo " disabled";} ?>>
<?php echo $button_text; ?> <?php echo $button_text; ?>
</button> </button>
<p> <p>
</div> </div>
<p> <p>
<div class="button-container">
<table>
<tr>
<td>
<button onclick="location.href='/?new_duration=3'" class="<?php echo duration_button_handler(3, 0); ?>" id="6_hour_button" <?php echo duration_button_handler(3, 1); ?>>6 Hours</button>
</td>
<td>
<button onclick="location.href='/?new_duration=2'" class="<?php echo duration_button_handler(2, 0); ?>" id="2_hour_button" <?php echo duration_button_handler(2, 1); ?>>2 Hours</button>
</td>
</tr>
<tr>
<td>
<button onclick="location.href='/?new_duration=1'" class="<?php echo duration_button_handler(1, 0); ?>" id="1_hour_button" <?php echo duration_button_handler(1, 1); ?>>1 Hour</button>
</td>
<td>
<button onclick="location.href='/?new_duration=0'" class="<?php echo duration_button_handler(0, 0); ?>" id="30_min_button" <?php echo duration_button_handler(0, 1); ?>>30 min</button>
</td>
</tr>
</table>
</div>
<p>
<div class="api-data"> <div class="api-data">
<!-- PHP will inject the data here --> <!-- PHP will inject the data here -->
<?php <?php
echo "Full Message:<br>".htmlspecialchars($message)."<p>"; echo "Full Message:<br>".htmlspecialchars($message)."<p>";
if ($status == "active"){
echo "Time Elapsed:<br>".htmlspecialchars($elapsed_time)."s<p>";
}
echo "Current Date:<br>".date("F j, Y, g:i:s a")."<p>"; echo "Current Date:<br>".date("F j, Y, g:i:s a")."<p>";
echo "Current GPS Data:<br>".getGPS();
?> ?>
</div> </div>
<p>
</div> </div>
<script> <script>
// When the button is clicked, send an AJAX request to fetch new data // When the button is clicked, send an AJAX request to fetch new data
@ -220,13 +347,14 @@ if($button_recent){
}) })
}); });
</script> </script>
<script> <script>
// Automatically refresh the page every second // Automatically refresh the page every second
setTimeout(function() { setTimeout(function() {
location.reload(); location.reload();
}, <?php if($button_recent){ echo "5000"; } else{ echo "1000"; } ?>); // Refresh interval (1000 ms = 1 second) }, <?php if($button_recent){ echo "5000"; } else{ echo "1000"; } ?>); // Refresh interval (1000 ms = 1 second)
</script> </script>
</body> </body>
</html> </html>

View File

@ -69,7 +69,7 @@ button:hover {
background: radial-gradient(circle, #671281 0%, #8423a1 100%); background: radial-gradient(circle, #671281 0%, #8423a1 100%);
color: #bdc3c7; /* Dimmer text color */ color: #bdc3c7; /* Dimmer text color */
} }
.deactivating { .selected {
background-color: #003699; background-color: #003699;
background: radial-gradient(circle, #003699 0%, #337aff 100%); background: radial-gradient(circle, #003699 0%, #337aff 100%);
color: #bdc3c7; /* Dimmer text color */ color: #bdc3c7; /* Dimmer text color */

View File

@ -15,28 +15,28 @@
set_fact: set_fact:
cpu_architecture: "{{ cpu_architecture_output.stdout_lines[0] }}" cpu_architecture: "{{ cpu_architecture_output.stdout_lines[0] }}"
- name: Video Capture - Install Packages #- name: Video Capture - Install Packages
when: not refresh_special | bool # when: not refresh_special | bool
apt: # apt:
name: # name:
- "{{ streamer_packages_items }}" # - "{{ streamer_packages_items }}"
state: present # state: present
loop: "{{ streamer_packages }}" # loop: "{{ streamer_packages }}"
loop_control: # loop_control:
loop_var: streamer_packages_items # loop_var: streamer_packages_items
#
#- name: Video Capture - SD Card Handler
# when: mount_sd | bool
# include_tasks: sd_handler.yaml
#
#- name: Video Capture - Configure MediaMTX
# include_tasks: mediamtx.yaml
#
#- name: Video Capture - Configure Streaming
# include_tasks: streamer.yaml
- name: Video Capture - SD Card Handler - name: Video Capture - Configure service control
when: mount_sd | bool include_tasks: service_control.yaml
include_tasks: sd_handler.yaml
- name: Video Capture - Configure MediaMTX
include_tasks: mediamtx.yaml
- name: Video Capture - Configure Streaming
include_tasks: streamer.yaml
#- name: Video Capture - Configure service control
# include_tasks: service_control.yaml
... ...

View File

@ -60,6 +60,21 @@
dest: /etc/systemd/system/service_control.service dest: /etc/systemd/system/service_control.service
mode: 0644 mode: 0644
- name: video_capture - service timeout helper files
block:
- name: video_capture - create service_timeout.sh
template:
src: service_timeout.sh.j2
dest: "{{ service_control_folder }}/service_timeout.sh"
mode: 0755
- name: video_capture - create service_timeout.service
template:
src: service_timeout.service.j2
dest: /etc/systemd/system/service_timeout.service
mode: 0644
# daemon reload # daemon reload
- name: video_capture - service_control api - daemon reload - name: video_capture - service_control api - daemon reload
systemd: systemd:
@ -72,6 +87,11 @@
state: started state: started
enabled: yes enabled: yes
- name: video_capture - service_control api - enable and start service timeoue api
systemd:
name: service_timeout.service
state: started
enabled: yes
############################################### ###############################################
@ -102,24 +122,27 @@
owner: root owner: root
group: root group: root
# - name: service_control_website - template index.php #- name: service_control_website - template index.php
# template: # template:
# src: index-service_control.php.j2 # src: index-service_control.php.j2
# dest: "{{ service_control_web_folder }}/html/index.php" # dest: "{{ service_control_web_folder }}/html/index.php"
# mode: 0644 # mode: 0644
############################################### ###############################################
# Start service_control_website # Start service_control_website
############################################### ###############################################
# https://unix.stackexchange.com/questions/265704/start-stop-a-systemd-service-at-specific-times # https://unix.stackexchange.com/questions/265704/start-stop-a-systemd-service-at-specific-times
# i can create several conflicting services with various timeouts # i can create several conflicting services with various timeouts
- name: start service_control_website - name: start service_control_website
when: not refresh_special | bool
block: block:
- name: set container variables - name: set container variables
set_fact: set_fact:
container_name: "service_control_website" container_name: "service_control_website"
container_http_port: "8081" container_http_port: "8081"
extra_volumes: ""
- name: service_control_website - template config - name: service_control_website - template config
template: template:

View File

@ -37,7 +37,7 @@
# same with video, the lsusb ID is 534d:0021 # same with video, the lsusb ID is 534d:0021
- name: video_capture - get video device - name: video_capture - get video device
shell: "v4l2-ctl --list-devices -z {{ lsusb_device_ID }}| grep video | head -n 1" shell: "v4l2-ctl --list-devices -z {{ lsusb_device_ID }} | grep video | head -n 1 | awk '{print $1}'"
register: video_ID_0 register: video_ID_0
- name: video_capture - set video_device - name: video_capture - set video_device

View File

@ -4,6 +4,7 @@ from lxml import html
from flask import Flask, request, jsonify from flask import Flask, request, jsonify
import json import json
import os import os
from datetime import datetime, timedelta
app = Flask(__name__) app = Flask(__name__)
@ -49,10 +50,81 @@ def service_status():
result = subprocess.run(command, shell=True, check=True, capture_output=True, text=True) result = subprocess.run(command, shell=True, check=True, capture_output=True, text=True)
command = "systemctl status {{ service_control_name }} | grep Active | cut -d ':' -f 2 | cut -d ' ' -f 2" command = "systemctl status {{ service_control_name }} | grep Active | cut -d ':' -f 2 | cut -d ' ' -f 2"
status = subprocess.run(command, shell=True, check=True, capture_output=True, text=True) status = subprocess.run(command, shell=True, check=True, capture_output=True, text=True)
return {"Message": result.stdout.strip('\n'), "Status": status.stdout.strip('\n')} if status.stdout.strip('\n') == "active":
elapsed_time = check_time_elapsed(result.stdout.strip('\n'))
times_up(elapsed_time)
else:
elapsed_time = -1
return {"Elapsed_Time": elapsed_time, "Service Name": "{{ service_control_name }}", "Message": result.stdout.strip('\n'), "Status": status.stdout.strip('\n')}
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
return {"Error": e.strip('\n')} return {"Error": e.strip('\n')}
def save_duration(new_duration):
# this will be an integer from 0-3 for
# 30min, 1hr, 2hr, and 6rh respectively
seconds = 1800
if new_duration == "0":
seconds = 1800
if new_duration == "1":
seconds = 3600
if new_duration == "2":
seconds = 7200
if new_duration == "3":
seconds = 21600
info = {
"duration": new_duration,
"seconds": seconds
}
with open("duration.json", "w") as file:
json.dump(info, file, indent=4)
return {"status": "duration.json stored"}
def get_duration():
try:
with open('duration.json', 'r') as file:
data = json.load(file)
return (data)
except Exception as e:
return {"error": str(e)}, 500
def check_time_elapsed(time_str):
# Extract the time part from the string
datetime_extract = time_str.split("since ")[1].split(";")[0]
try:
time_format = "%a %Y-%m-%d %H:%M:%S %Z"
given_time = datetime.strptime(datetime_extract, time_format)
except ValueError as e:
raise ValueError(f"Error parsing the time string: {datetime_extract}" + str(e))
# Get the current time
now = datetime.now()
# Calculate the difference in seconds
delta = now - given_time
total_seconds = int(delta.total_seconds())
return total_seconds
def times_up(time_elapsed):
duration_json = get_duration()
stored_duration = duration_json["duration"]
if stored_duration == 0:
if time_elapsed > 1800:
stop_service()
elif stored_duration == 1:
if time_elapsed > 3600:
stop_service()
elif stored_duration == 2:
if time_elapsed > 7200:
stop_service()
elif stored_duration == 3:
if time_elapsed > 21600:
stop_service()
@app.route('/start', methods=['GET']) @app.route('/start', methods=['GET'])
def start(): def start():
try: try:
@ -77,6 +149,27 @@ def status():
print(e) print(e)
return jsonify({'error': e}), 400 return jsonify({'error': e}), 400
@app.route('/check_duration', methods=['GET'])
def check_duration():
try:
return jsonify(get_duration())
except ValueError as e:
print(e)
return jsonify({'error': e}), 400
@app.route('/store_duration', methods=['GET'])
def store_duration():
try:
new_duration = request.args.get('new_duration')
except ValueError as e:
return jsonify({'error': e}), 400
if new_duration:
try:
return jsonify(save_duration(new_duration))
except ValueError as e:
print(e)
return jsonify({'error': e}), 400
@app.route('/test', methods=['GET']) @app.route('/test', methods=['GET'])
def test(): def test():
return jsonify({'message': 'Hello there'}) return jsonify({'message': 'Hello there'})

View File

@ -0,0 +1,15 @@
[Unit]
Description=VCR Streaming Service Timeout Helper
After=network.target
[Service]
Type=simple
WorkingDirectory={{ service_control_folder }}
ExecStart=/bin/bash {{ service_control_folder }}/service_timeout.sh
Restart=always
User=root
Group=root
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,17 @@
#!/bin/bash
i=1
while [ $i ]
do
TIME_ELAPSED=$(curl -s http://172.17.0.1:5000/status | jq .Elapsed_Time)
TIMEOUT_VALUE=$(cat {{ streaming_working_folder }}/service_control/duration.json | jq .seconds)
if [[ $TIME_ELAPSED -gt $TIMEOUT_VALUE ]]; then
curl -s http://172.17.0.1:5000/stop
i=0
fi
sleep 1
done