Skip to content

Commit 28a616e

Browse files
authored
Merge pull request #8 from OpenIPC/improvements
Improvements
2 parents e48976b + c1707ce commit 28a616e

File tree

9 files changed

+289
-82
lines changed

9 files changed

+289
-82
lines changed

.github/workflows/release.yml

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name: Build and Release
33
on:
44
push:
55
tags:
6-
- 'v*.*.*'
6+
- 'v*.*.*' # This triggers the workflow on tags following semantic versioning
77

88
jobs:
99
build:
@@ -22,7 +22,7 @@ jobs:
2222

2323
- name: Get version from tag
2424
id: vars
25-
run: echo "VERSION=${GITHUB_REF#refs/tags/} >> $GITHUB_ENV"
25+
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
2626

2727
- name: Install dependencies
2828
run: |
@@ -33,9 +33,25 @@ jobs:
3333
run: |
3434
python setup.py sdist bdist_wheel
3535
36+
- name: Create Release
37+
id: create_release
38+
run: |
39+
if [[ "${GITHUB_REF}" == "refs/tags/v"* ]]; then
40+
TAG_NAME=${GITHUB_REF#refs/tags/}
41+
if [[ "${GITHUB_REF}" == "refs/tags/v"* ]]; then
42+
echo "Creating release for tag $TAG_NAME"
43+
echo "GITHUB_REF=$GITHUB_REF" >> $GITHUB_ENV
44+
fi
45+
else
46+
echo "Not a valid tag, skipping release creation."
47+
exit 0
48+
fi
49+
3650
- name: Upload Package to Release
51+
if: steps.create_release.outputs.tag_name != ''
3752
uses: softprops/action-gh-release@v1
3853
with:
54+
tag_name: ${{ steps.create_release.outputs.tag_name }}
3955
files: |
4056
dist/*
4157
env:

py_config_gs/.vscode/sftp.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "Radxa",
3+
"host": "10.0.1.14",
4+
"protocol": "sftp",
5+
"port": 22,
6+
"username": "root",
7+
"remotePath": "/root/workspace/py_config_gs/py_config_gs",
8+
"uploadOnSave": false,
9+
"useTempFile": false,
10+
"openSsh": false,
11+
"ignore": [
12+
"**/*.log",
13+
"**/*.tmp",
14+
".venv/**",
15+
"**/*.env"
16+
17+
]
18+
}

py_config_gs/app.py

Lines changed: 88 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,14 @@
22
from flask import Flask, render_template, request, Response, redirect, url_for, send_from_directory, flash
33
import json
44
import os
5+
import sys
56
import subprocess
67
from importlib.metadata import version
78

8-
9-
# read version for footer
10-
#app_version = version('py-config-gs')
9+
# Read version for footer
1110
with open('version.txt', 'r') as f:
1211
app_version = f.read().strip()
1312

14-
1513
# Configure logging
1614
logging.basicConfig(level=logging.DEBUG, # Set the log level to DEBUG
1715
format='%(asctime)s - %(levelname)s - %(message)s')
@@ -34,6 +32,7 @@
3432

3533
# Log the SETTINGS_FILE path
3634
logger.info(f'Settings file path: {SETTINGS_FILE}')
35+
3736
logger.info(f'App version: {app_version}')
3837

3938
# Load settings.json
@@ -49,21 +48,25 @@
4948
logger.debug(f'VIDEO_DIR is set to: {VIDEO_DIR}')
5049

5150
def stream_journal():
52-
"""Stream journalctl output in real-time."""
53-
process = subprocess.Popen(
54-
['journalctl', '-f'],
55-
stdout=subprocess.PIPE,
56-
stderr=subprocess.PIPE,
57-
text=True
58-
)
59-
60-
while True:
61-
output = process.stdout.readline()
62-
if output:
63-
yield f"data: {output}\n\n"
64-
else:
65-
break
66-
51+
if os.getenv('FLASK_ENV') != 'development':
52+
"""Stream journalctl output in real-time."""
53+
process = subprocess.Popen(
54+
['journalctl', '-f'],
55+
stdout=subprocess.PIPE,
56+
stderr=subprocess.PIPE,
57+
text=True
58+
)
59+
60+
while True:
61+
output = process.stdout.readline()
62+
if output:
63+
yield f"data: {output}\n\n"
64+
else:
65+
break
66+
else:
67+
logger.info('No data in DEVELOPMENT mode')
68+
yield "data: No data in DEVELOPMENT mode\n\n" # Send as part of stream instead of flashing
69+
6770
@app.route('/journal')
6871
def journal():
6972
return render_template('journal.html', version=app_version)
@@ -74,18 +77,18 @@ def stream():
7477

7578
@app.route('/')
7679
def home():
77-
# List of services that you want to control
78-
services = ['openipc',"wifibroadcast.service"]
80+
services = ['openipc', "wifibroadcast.service"]
7981
service_statuses = {}
82+
83+
# flash(f'Starting up...', 'info')
8084

81-
# Fetches the current status (enabled/disabled) for each service.
82-
for service in services:
83-
# Check if the service is enabled or disabled
84-
result = subprocess.run(['systemctl', 'is-enabled', service], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
85-
status = result.stdout.decode('utf-8').strip()
86-
service_statuses[service] = status
85+
if os.getenv('FLASK_ENV') != 'development':
86+
for service in services:
87+
result = subprocess.run(['systemctl', 'is-enabled', service], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
88+
status = result.stdout.decode('utf-8').strip()
89+
service_statuses[service] = status
8790

88-
return render_template('home.html', config_files=config_files, version=app_version, services=service_statuses )
91+
return render_template('home.html', config_files=config_files, version=app_version, services=service_statuses)
8992

9093
@app.route('/edit/<filename>', methods=['GET', 'POST'])
9194
def edit(filename):
@@ -117,13 +120,12 @@ def videos():
117120
video_files = [f for f in os.listdir(VIDEO_DIR) if f.endswith(('.mp4', '.mkv', '.avi'))]
118121
logger.debug(f'VIDEO_DIR: {VIDEO_DIR}')
119122
logger.debug(f'Video files found: {video_files}')
123+
flash(f'Loading from VIDEO_DIR: {VIDEO_DIR}','info')
120124
return render_template('videos.html', video_files=video_files, version=app_version)
121125

122-
123126
@app.route('/play/<filename>')
124127
def play(filename):
125128
try:
126-
# Ensure the file exists in the VIDEO_DIR and is served from there
127129
return send_from_directory(VIDEO_DIR, filename)
128130
except FileNotFoundError:
129131
logger.error(f'Video file not found: {filename}')
@@ -132,11 +134,17 @@ def play(filename):
132134
@app.route('/temperature')
133135
def get_temperature():
134136
try:
135-
soc_temp = int(open('/sys/class/thermal/thermal_zone0/temp').read().strip()) / 1000.0 # Convert to °C
136-
gpu_temp = int(open('/sys/class/thermal/thermal_zone1/temp').read().strip()) / 1000.0 # Convert to °C
137-
soc_temp_f = (soc_temp * 9/5) + 32
138-
gpu_temp_f = (gpu_temp * 9/5) + 32
139-
137+
soc_temp = 0
138+
gpu_temp = 0
139+
soc_temp_f = 0
140+
gpu_temp_f = 0
141+
142+
if os.getenv('FLASK_ENV') != 'development':
143+
soc_temp = int(open('/sys/class/thermal/thermal_zone0/temp').read().strip()) / 1000.0 # Convert to °C
144+
gpu_temp = int(open('/sys/class/thermal/thermal_zone1/temp').read().strip()) / 1000.0 # Convert to °C
145+
soc_temp_f = (soc_temp * 9/5) + 32
146+
gpu_temp_f = (gpu_temp * 9/5) + 32
147+
140148
return {
141149
'soc_temperature': f"{soc_temp:.1f}",
142150
'soc_temperature_f': f"{soc_temp_f:.1f}",
@@ -164,16 +172,13 @@ def backup():
164172
def run_command():
165173
selected_command = request.form.get('command')
166174

167-
# Construct the first command based on the dropdown value
168175
cli_command = f"echo cli -s {selected_command} > /dev/udp/localhost/14550"
169176
logger.debug(f'Running command: {cli_command}')
170177
flash(f'Running command: {cli_command}', 'info')
171178

172-
# Run the commands
173179
subprocess.run(cli_command, shell=True)
174180
subprocess.run("echo killall -1 majestic > /dev/udp/localhost/14550", shell=True)
175181

176-
# Redirect back to the home page after the commands are run
177182
return redirect(url_for('home'))
178183

179184
@app.route('/service_action', methods=['POST'])
@@ -199,7 +204,6 @@ def service_action():
199204

200205
return redirect(url_for('home'))
201206

202-
# Function to check allowed file extensions
203207
def allowed_file(filename):
204208
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
205209

@@ -220,8 +224,50 @@ def upload_file():
220224
return redirect(url_for('home'))
221225
return render_template('upload.html') # A separate template for file upload
222226

223-
def main():
224-
app.run(host='0.0.0.0', port=SERVER_PORT)
227+
@app.route('/settings', methods=['GET', 'POST'])
228+
def settings():
229+
# Load settings
230+
with open(SETTINGS_FILE, 'r') as f:
231+
settings = json.load(f)
232+
233+
# Initialize config_files from the loaded settings
234+
config_files = settings['config_files']
235+
236+
if request.method == 'POST':
237+
# Get data from the form
238+
config_files = request.form.getlist('config_files')
239+
video_dir = request.form.get('VIDEO_DIR')
240+
server_port = request.form.get('SERVER_PORT')
241+
242+
# Create a structured dictionary for saving
243+
settings_data = {
244+
"config_files": [
245+
{"name": config_files[i], "path": request.form[f'path_{i}']}
246+
for i in range(len(config_files))
247+
],
248+
"VIDEO_DIR": video_dir,
249+
"SERVER_PORT": int(server_port) # Ensure it's an integer
250+
}
251+
252+
# Save the settings to JSON
253+
with open(SETTINGS_FILE, 'w') as f:
254+
json.dump(settings_data, f, indent=4)
255+
logger.debug('Updated settings saved.')
256+
257+
# Restart the Flask app
258+
os.execv(sys.executable, ['python'] + sys.argv)
259+
260+
return redirect(url_for('home'))
261+
262+
return render_template('settings.html', config_files=config_files, settings=settings, version=app_version)
263+
264+
225265

226266
if __name__ == '__main__':
227-
main()
267+
# Load settings to get SERVER_PORT and debug mode from the settings file.
268+
with open(SETTINGS_FILE, 'r') as f:
269+
settings = json.load(f)
270+
271+
SERVER_PORT = settings['SERVER_PORT']
272+
DEBUG_MODE = os.getenv('FLASK_ENV') == 'development'
273+
app.run(port=SERVER_PORT, debug=DEBUG_MODE, host='0.0.0.0')

py_config_gs/static/css/styles.css

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,4 +252,72 @@ form {
252252
.config-content {
253253
gap: 10px; /* Reduce gap on smaller screens */
254254
}
255-
}
255+
}
256+
257+
258+
259+
/* Header container to hold the logo on the left and center the heading */
260+
.header-container {
261+
display: flex;
262+
align-items: center; /* Vertically center the logo and heading */
263+
justify-content: center; /* Initially center everything */
264+
position: relative; /* For the absolute positioning of the logo */
265+
margin-bottom: 20px;
266+
}
267+
268+
/* Logo styling */
269+
.header-logo {
270+
position: absolute;
271+
left: 0; /* Align the logo to the left */
272+
max-width: 60px; /* Set the logo size */
273+
height: auto;
274+
margin-left: 20px; /* Add space from the left edge */
275+
}
276+
277+
/* Center the heading */
278+
.header-container h1 {
279+
margin: 0;
280+
text-align: center;
281+
font-size: 24px;
282+
color: #333;
283+
flex-grow: 1; /* Let the heading take up the remaining space */
284+
}
285+
286+
/* Adjust the layout on smaller screens */
287+
@media (max-width: 768px) {
288+
.header-container {
289+
flex-direction: column; /* Stack the logo and heading vertically */
290+
}
291+
292+
.header-logo {
293+
position: static;
294+
margin-left: 0;
295+
margin-bottom: 10px; /* Add space below the logo when stacked */
296+
}
297+
}
298+
299+
/* Flash Message Styles */
300+
.flash-message {
301+
background-color: #d1ecf1; /* Light blue background */
302+
color: #0c5460; /* Dark blue text */
303+
304+
border: 1px solid #4046ed; /* Border to define the area */
305+
border-radius: 5px; /* Rounded corners */
306+
margin-bottom: 20px; /* Space below the flash message */
307+
308+
width: 100%; /* Fill the width of the container */
309+
max-width: 100%; /* Ensure it doesn't exceed the viewport width */
310+
height: 50px; /* Fixed height */
311+
overflow-y: auto; /* Allow vertical scrolling */
312+
313+
padding: 10px; /* Padding for inner content */
314+
box-sizing: border-box; /* Include padding in the height and width */
315+
}
316+
317+
/* Additional styles for message list */
318+
.flash-message ul {
319+
list-style-type: none; /* Remove default list styling */
320+
padding: 0; /* Remove padding */
321+
margin: 0; /* Remove margin */
322+
}
323+
151 KB
Loading

0 commit comments

Comments
 (0)