Compare commits
82 commits
main
...
dev-stable
| Author | SHA1 | Date | |
|---|---|---|---|
| 2514e7cbaa | |||
| e89c7a0bd2 | |||
| 77d4dd3a0a | |||
| ffd681a654 | |||
| 8b3ea9e89f | |||
| ef631b9c3f | |||
| 0739547520 | |||
| ec25774fc2 | |||
| 45db22a155 | |||
| 8d99133efd | |||
| 9e90100b9d | |||
| 7e5ead8f76 | |||
| bb63929a1e | |||
| d3be1e2984 | |||
| 30c4c40494 | |||
| fdd53445da | |||
| c88020974e | |||
| 7dbd004057 | |||
| cc3f42b710 | |||
| ea2ce70de3 | |||
| cb9c15b0e0 | |||
| 19db588525 | |||
| 55a08ee5a5 | |||
| 8e554ff849 | |||
| 9d1032d5f7 | |||
| 05129a734e | |||
| 5edacf8d44 | |||
| 5ff64fc340 | |||
| f90bc2c75b | |||
| 37800c312f | |||
| 46035e996d | |||
| c6f72d83c9 | |||
| 294f8b84ab | |||
| b72fce9038 | |||
| 698c1d3076 | |||
| 254fd49c08 | |||
| ee87f6346f | |||
| 85551f5110 | |||
| fe011be589 | |||
| 368bd1ae9d | |||
| 91cc993841 | |||
| 4e5339df5a | |||
| da664519f3 | |||
| 753511958c | |||
| 9824d03655 | |||
| c071de983f | |||
| e0872eed11 | |||
| 033049430b | |||
| f7589cafe0 | |||
| 57109b47bb | |||
| d9525d1af0 | |||
| 1da601fb23 | |||
| 444f7bffb0 | |||
| 1bad92763d | |||
| 45f990a1b8 | |||
| 5a45c280fb | |||
| ba87998c7a | |||
| 7c38625f49 | |||
| 18997abc80 | |||
| 61ad242506 | |||
| 733b059cf3 | |||
| e7dd6c62a8 | |||
| 2494e9d278 | |||
| 1bdb59d012 | |||
| 577ce1c885 | |||
| 6d37ad3af0 | |||
| cffdc504ca | |||
| 6c06ee2b52 | |||
| 5bb0d81df5 | |||
| c031baf491 | |||
| de000d134e | |||
| 01f072742f | |||
| cd0632281f | |||
| 4af3ec7276 | |||
| a0f25306a2 | |||
| 389923c24b | |||
| cc9c738de2 | |||
| d3df81c908 | |||
| eed441b0d6 | |||
| eee7ee410c | |||
| c9223ecffc | |||
| 1e5b6677d2 |
39 changed files with 1013 additions and 1 deletions
31
README.md
31
README.md
|
|
@ -1,12 +1,41 @@
|
||||||
|
<img src="assets/images/lomes.svg" alt="Meshtastic" width="100">
|
||||||
|
|
||||||
# LoMeS
|
# LoMeS
|
||||||
|
|
||||||
**LOcal MEshtastic Server** _in development_
|
**LOcal MEshtastic Server** _in development_
|
||||||
|
|
||||||
Idea is to:
|
Idea is to:
|
||||||
- develop an offline web app client and python backend, running in your local network and **nowhere** else.
|
- develop an offline Meshtastic server, running in your local network and **nowhere** else.
|
||||||
- create a web app, coded in `html`, `css`, `js` and `php` and make it a bit prettier than the original.
|
- create a web app, coded in `html`, `css`, `js` and `php` and make it a bit prettier than the original.
|
||||||
- code a backend utilising `flask`, `python`, `python3-meshtastic` and `python3-requests`.
|
- code a backend utilising `flask`, `python`, `python3-meshtastic` and `python3-requests`.
|
||||||
- make it as light but versatile as possible _let's see how this is gonna work out..._
|
- make it as light but versatile as possible _let's see how this is gonna work out..._
|
||||||
- and **maybe** connect to mobile clients in the far future.
|
- and **maybe** connect to mobile clients in the far future.
|
||||||
- check progress [here](https://lab.c95.org/fr4nz/LoMeS/src/branch/main/assets/notes/notes.md)
|
- check progress [here](https://lab.c95.org/fr4nz/LoMeS/src/branch/main/assets/notes/notes.md)
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Hardware
|
||||||
|
|
||||||
|
for now everything will be developed and tested on Raspberry Pi 3B and newer acting as server and as LoRa Modules RAK WisBlock 4631 with RAK 19007 Board as well as Heltec V3 connected via USB/UART.
|
||||||
|
|
||||||
|
## Hard & Firmware
|
||||||
|
|
||||||
|
- Raspberry Pi 4B | _RPI OS lite (Debian 13 | Trixie)_
|
||||||
|
- RAK Wisblock | _FW 2.6.11_
|
||||||
|
- Heltec V3 | _FW 2.6.11_
|
||||||
|
|
||||||
|
## Development Setup
|
||||||
|
|
||||||
|
1. Flash Raspberry Pi OS lite 64bit
|
||||||
|
2. Install git:
|
||||||
|
```bash
|
||||||
|
sudo apt install -y git
|
||||||
|
```
|
||||||
|
3. Clone repository
|
||||||
|
```bash
|
||||||
|
git clone https://lab.c95.org/fr4nz/LoMeS.git && cd LoMeS
|
||||||
|
```
|
||||||
|
4. Run development environment setup
|
||||||
|
```bash
|
||||||
|
chmod +x lomes-setup.sh && sudo ./lomes-setup.sh
|
||||||
|
```
|
||||||
|
5. Start developing
|
||||||
2
assets/config/deps/dependencies
Normal file
2
assets/config/deps/dependencies
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
#deps="openssl nginx nginx-common python3-meshtastic python3-flask python3-requests"
|
||||||
|
deps=("openssl" "nginx" "python3-meshtastic" "python3-flask" "python3-requests" "usbip" "python3-usb")
|
||||||
12
assets/config/flask/lomes-app.py
Normal file
12
assets/config/flask/lomes-app.py
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
#!/bin/python3
|
||||||
|
|
||||||
|
from flask import Flask, render_template
|
||||||
|
|
||||||
|
app = Flask(__name__, static_folder="/var/www/html", static_url_path="/var/www/html", send_from_directory("data/images, lomes.svg"))
|
||||||
|
|
||||||
|
@app.route("/")
|
||||||
|
def index():
|
||||||
|
return app.send_static_file("index.html")
|
||||||
|
|
||||||
|
|
||||||
|
app.run(host="127.0.0.1", port=5000)
|
||||||
14
assets/config/nginx/nginx_HTTP.conf
Normal file
14
assets/config/nginx/nginx_HTTP.conf
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
###########################################################
|
||||||
|
### ###
|
||||||
|
### MESHPI NGINX CONFIG ###
|
||||||
|
### ###
|
||||||
|
###########################################################
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name DOMAIN IPADDR;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:5000;
|
||||||
|
}
|
||||||
|
}
|
||||||
31
assets/config/nginx/nginx_SSL.conf
Normal file
31
assets/config/nginx/nginx_SSL.conf
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
###########################################################
|
||||||
|
### ###
|
||||||
|
### MESHPI NGINX CONFIG ###
|
||||||
|
### ###
|
||||||
|
###########################################################
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name DOMAIN IPADDR;
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
server_name DOMAIN IPADDR;
|
||||||
|
|
||||||
|
ssl_certificate CERTPATH.crt;
|
||||||
|
ssl_certificate_key KEYPATH.key;
|
||||||
|
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||||
|
|
||||||
|
ssl_session_cache shared:SSL:10m;
|
||||||
|
ssl_session_timeout 10m;
|
||||||
|
|
||||||
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:5000;
|
||||||
|
}
|
||||||
|
}
|
||||||
57
assets/images/favicon.ico
Normal file
57
assets/images/favicon.ico
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="23.797873mm"
|
||||||
|
height="12.931701mm"
|
||||||
|
viewBox="0 0 23.797873 12.931701"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
xml:space="preserve"
|
||||||
|
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||||
|
sodipodi:docname="favicon.ico"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#505050"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:zoom="11.782177"
|
||||||
|
inkscape:cx="30.6395"
|
||||||
|
inkscape:cy="19.308826"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1374"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="42"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="g3" /><defs
|
||||||
|
id="defs1" /><g
|
||||||
|
transform="matrix(0.21229796,0,0,0.12171574,-113.52852,-33.25759)"
|
||||||
|
id="g3"
|
||||||
|
style="clip-rule:evenodd;display:inline;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2">
|
||||||
|
<g
|
||||||
|
transform="matrix(0.579082,0,0,1.01004,460.975,-39.6867)"
|
||||||
|
id="g1">
|
||||||
|
<path
|
||||||
|
d="m 250.908,330.267 -57.782,84.738 -12.188,-8.311 63.864,-93.657 c 1.372,-2.013 3.651,-3.218 6.087,-3.221 2.437,-0.002 4.717,1.199 6.093,3.21 l 64.012,93.51 -12.173,8.333 z"
|
||||||
|
style="fill:#ffffff"
|
||||||
|
id="path1" />
|
||||||
|
</g><g
|
||||||
|
transform="matrix(0.579082,0,0,1.01004,441.72359,108.2802)"
|
||||||
|
id="g1-3"
|
||||||
|
style="clip-rule:evenodd;display:inline;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2">
|
||||||
|
<path
|
||||||
|
d="m 230.63238,183.77092 -57.782,84.738 -12.188,-8.311 63.864,-93.657 c 1.372,-2.013 3.651,-3.218 6.087,-3.221 2.437,-0.002 4.717,1.199 6.093,3.21 l 16.93449,24.73698 -12.173,8.333 z"
|
||||||
|
style="fill:#ffffff"
|
||||||
|
id="path1-5"
|
||||||
|
sodipodi:nodetypes="ccccccccc" />
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g></svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
BIN
assets/images/lomes.png
Normal file
BIN
assets/images/lomes.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
57
assets/images/lomes.svg
Normal file
57
assets/images/lomes.svg
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="23.797873mm"
|
||||||
|
height="12.931701mm"
|
||||||
|
viewBox="0 0 23.797873 12.931701"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
xml:space="preserve"
|
||||||
|
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||||
|
sodipodi:docname="lomes.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#505050"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:zoom="11.782177"
|
||||||
|
inkscape:cx="30.6395"
|
||||||
|
inkscape:cy="19.308826"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1374"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="42"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="g3" /><defs
|
||||||
|
id="defs1" /><g
|
||||||
|
transform="matrix(0.21229796,0,0,0.12171574,-113.52852,-33.25759)"
|
||||||
|
id="g3"
|
||||||
|
style="clip-rule:evenodd;display:inline;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2">
|
||||||
|
<g
|
||||||
|
transform="matrix(0.579082,0,0,1.01004,460.975,-39.6867)"
|
||||||
|
id="g1">
|
||||||
|
<path
|
||||||
|
d="m 250.908,330.267 -57.782,84.738 -12.188,-8.311 63.864,-93.657 c 1.372,-2.013 3.651,-3.218 6.087,-3.221 2.437,-0.002 4.717,1.199 6.093,3.21 l 64.012,93.51 -12.173,8.333 z"
|
||||||
|
style="fill:#ffffff"
|
||||||
|
id="path1" />
|
||||||
|
</g><g
|
||||||
|
transform="matrix(0.579082,0,0,1.01004,441.72359,108.2802)"
|
||||||
|
id="g1-3"
|
||||||
|
style="clip-rule:evenodd;display:inline;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2">
|
||||||
|
<path
|
||||||
|
d="m 230.63238,183.77092 -57.782,84.738 -12.188,-8.311 63.864,-93.657 c 1.372,-2.013 3.651,-3.218 6.087,-3.221 2.437,-0.002 4.717,1.199 6.093,3.21 l 16.93449,24.73698 -12.173,8.333 z"
|
||||||
|
style="fill:#ffffff"
|
||||||
|
id="path1-5"
|
||||||
|
sodipodi:nodetypes="ccccccccc" />
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g></svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
34
assets/notes/FrontToBackend.md
Normal file
34
assets/notes/FrontToBackend.md
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
[CRUD REST API](https://youtu.be/I07FyMH1DzI)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
[Meshtastic Python API Documentation](https://python.meshtastic.org/)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
To send commands from a Python script to a web server, you can use HTTP requests. The requests library is a popular and user-friendly option for making HTTP calls in Python.
|
||||||
|
For example, you can send a POST request with data to a specific URL:
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
data = {"key": "value"}
|
||||||
|
response = requests.post("http://example.com/api", data=data)
|
||||||
|
|
||||||
|
Alternatively, you can use the built-in urllib module, though requests is generally preferred for its simplicity and readability.
|
||||||
|
|
||||||
|
For sending data from a Raspberry Pi to a web server, you can use tools like curl in a cron job to periodically send data from a Python script to a server endpoint.
|
||||||
|
For instance:
|
||||||
|
|
||||||
|
curl -X POST -d "$(python /path/to/script.py)" http://example.com/receive.php
|
||||||
|
|
||||||
|
This approach allows the script to output data, which is then sent to the server via HTTP POST.
|
||||||
|
|
||||||
|
If you are building a web server in Python to receive commands, you can use the http.server module to create a simple server that handles incoming requests.
|
||||||
|
For example, a basic server can be started with:
|
||||||
|
|
||||||
|
python3 -m http.server 8000
|
||||||
|
|
||||||
|
This starts a server on port 8000, accessible via http://localhost:8000.
|
||||||
|
You can extend this server to process incoming commands by defining custom request handlers using BaseHTTPRequestHandler.
|
||||||
|
|
||||||
|
For more advanced use cases, frameworks like Flask or Django can be used to create robust web servers capable of handling complex command logic and data processing.
|
||||||
105
assets/notes/notes.md
Normal file
105
assets/notes/notes.md
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
# Notes
|
||||||
|
## To Do
|
||||||
|
1. [x] define actions and plan
|
||||||
|
2. [ ] create setup script
|
||||||
|
3. [ ] communicate with Meshtastic device via web interface
|
||||||
|
|
||||||
|
## Follow up
|
||||||
|
### Backend
|
||||||
|
[meshtastic-cli-receive-text](https://github.com/brad28b/meshtastic-cli-receive-text)
|
||||||
|
|
||||||
|
### Web app
|
||||||
|
[flask web app tutorial](https://www.digitalocean.com/community/tutorials/how-to-make-a-web-application-using-flask-in-python-3)
|
||||||
|
|
||||||
|
[w3schools How to's](https://www.w3schools.com/howto/default.asp)
|
||||||
|
|
||||||
|
## Timeline
|
||||||
|
|
||||||
|
### Backend
|
||||||
|
- Environment setup script in development
|
||||||
|
|
||||||
|
### Frontend
|
||||||
|
|
||||||
|
### Thoughts
|
||||||
|
- Play with usbip
|
||||||
|
|
||||||
|
To establish a connection between a website and a USB device connected to a Linux server, you cannot directly connect a web application to a USB device over a network using standard web protocols. However, you can achieve this functionality by creating a bridge between the web application and the USB device using a local service on the Linux server. Here’s how:
|
||||||
|
|
||||||
|
1. Use USB/IP to Share the USB Device Over the Network: The USB/IP project allows you to share a physical USB device connected to a Linux server over a TCP/IP network, making it appear as a local USB device on a client machine. This is the most direct method for network access.
|
||||||
|
- On the Server (where the USB device is physically connected):
|
||||||
|
- Install the usbip package.
|
||||||
|
- Load the required kernel modules: usbip_core and usbip_host by creating a `.conf` file in `/etc/modules-load.d/` and adding the modules.
|
||||||
|
- Start and enable the usbipd.service daemon.
|
||||||
|
- List the available USB devices using usbip list -l to find the device's bus ID.
|
||||||
|
- Bind the specific USB device to the USB/IP service using usbip bind -b <busid>. This makes the device available for remote access.
|
||||||
|
- On the Client (the machine running the web application):
|
||||||
|
- Ensure the vhci_hcd kernel module is loaded.
|
||||||
|
- List the available devices from the server using usbip list -r <server_ip_address>.
|
||||||
|
- Attach the device to the client using usbip attach -r <server_ip_address> -b <busid>. Once attached, the device will appear as a local USB device on the client machine.
|
||||||
|
2. Integrate the Attached Device with Your Web Application: Once the USB device is attached to the client machine via USB/IP, it will be accessible to any software running on that machine, including a web application. The web application can then communicate with the device using standard USB communication libraries (e.g., libusb for C/C++, pyusb for Python) just as if the device were directly connected to the client machine. The web server (e.g., Apache, Nginx) and the application framework (e.g., Node.js, Django) on the client machine handle the web interface, while the application code uses the local USB device.
|
||||||
|
3. Alternative: Use a Local Service (e.g., a Web API): Instead of attaching the device to the web server machine, you can create a separate local service (a daemon or a small application) on the server that communicates directly with the USB device. This service can then expose a simple HTTP API (e.g., using a lightweight framework like Flask or Express) that the web application can call to send commands to or receive data from the USB device. This approach keeps the USB device connection confined to the server and uses standard web protocols for communication.
|
||||||
|
|
||||||
|
In summary, the most practical approach is to use USB/IP to make the USB device appear as a local device on the machine hosting the web application, allowing the application to interact with it directly through standard USB libraries.
|
||||||
|
|
||||||
|
### Troubble Shooting
|
||||||
|
|
||||||
|
**usbip: error: could not connect to 192.168.1.8:3240: System error** adjust firewall? `vhci_hcd` on client machine?
|
||||||
|
|
||||||
|
So for the daemon - /etc/systemd/system/usbipd.service
|
||||||
|
```
|
||||||
|
[Unit]
|
||||||
|
Description=usbip host daemon
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
Restart=always
|
||||||
|
ExecStartPre=/usr/sbin/modprobe usbip-core
|
||||||
|
ExecStartPre=/usr/sbin/modprobe usbip-host
|
||||||
|
ExecStart=/usr/sbin/usbipd
|
||||||
|
ExecStopPost=/usr/sbin/rmmod usbip-host
|
||||||
|
ExecStopPost=/usr/sbin/rmmod usbip-core
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
And the template for the binds - /etc/systemd/system/usbip-bind@.service
|
||||||
|
|
||||||
|
```
|
||||||
|
[Unit]
|
||||||
|
Description=Bind USB device to usbipd
|
||||||
|
After=network-online.target usbipd.service
|
||||||
|
Wants=network-online.target usbipd.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
RemainAfterExit=yes
|
||||||
|
ExecStart=/usr/sbin/usbip bind --busid %i
|
||||||
|
ExecStop=/usr/sbin/usbip unbind --busid %i
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
Enable and start the daemon with:
|
||||||
|
```
|
||||||
|
systemctl enable usbipd
|
||||||
|
systemctl start usbuipd
|
||||||
|
```
|
||||||
|
|
||||||
|
Then add binds with:
|
||||||
|
|
||||||
|
```
|
||||||
|
systemctl enable usbip-bind@1-1.2.3
|
||||||
|
systemctl start usbip-bind@1-1.2.3
|
||||||
|
```
|
||||||
|
Replace 1-1.2.3 with the bind id of the USB device you want to share. You can use as many of these as devices you want to share and you can then bind and unbind each one individually and have then bind at machine startup (or not).
|
||||||
|
|
||||||
|
Find the bind ids with:
|
||||||
|
|
||||||
|
`usbip list -l`
|
||||||
|
Note that usbip binds seem to expire if not attached by a remote machine within around 10 mins - not very helpful!
|
||||||
|
|
||||||
|
I haven't yet decided how I'm going to tackle the client attach, possibly not with systemd. It would be really helpful if usbip had a bit more intelligence...
|
||||||
18
assets/shell/colors
Normal file
18
assets/shell/colors
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
## SOME COLOR
|
||||||
|
BLK="\e[0;30m"
|
||||||
|
RED="\e[0;31m"
|
||||||
|
GRN="\e[0;32m"
|
||||||
|
ORN="\e[0;33m"
|
||||||
|
BLU="\e[0;34m"
|
||||||
|
MGT="\e[0;35m"
|
||||||
|
CYN="\e[0;36m"
|
||||||
|
LGR="\e[0;37m"
|
||||||
|
DGR="\e[1;30m"
|
||||||
|
LRD="\e[1;31m"
|
||||||
|
LGN="\e[1;32m"
|
||||||
|
YEL="\e[1;33m"
|
||||||
|
LBL="\e[1;34m"
|
||||||
|
LPR="\e[1;35m"
|
||||||
|
LCY="\e[1;36m"
|
||||||
|
WHT="\e[1;37m"
|
||||||
|
CRS="\e[0m"
|
||||||
3
assets/shell/template.sh
Normal file
3
assets/shell/template.sh
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
### SHELL TEMPLATE
|
||||||
60
assets/test/test.sh
Executable file
60
assets/test/test.sh
Executable file
|
|
@ -0,0 +1,60 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
source assets/shell/colors
|
||||||
|
source assets/config/deps/dependencies
|
||||||
|
|
||||||
|
### DEPENDENCY CHECK & INSTALLER
|
||||||
|
|
||||||
|
echo -e "\n ${LCY}Dependency and Privilege Check running...${CRS}\n"
|
||||||
|
|
||||||
|
### PRIVILEGES
|
||||||
|
|
||||||
|
if (( $(id -u) == 0 )); then ### AM I ROOT?
|
||||||
|
echo -e " ${GRN}Privilege check passed...${CRS}\n"
|
||||||
|
|
||||||
|
else
|
||||||
|
echo -e " ${RED}Privilege check failed... Please run script with sudo or as root!${CRS}\n"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
### DEPENDENCIES
|
||||||
|
|
||||||
|
dep_check() {
|
||||||
|
local pkg="$1"
|
||||||
|
|
||||||
|
if dpkg -s "$pkg" >/dev/null 2>&1; then
|
||||||
|
echo "$pkg" >>/tmp/installed_dev
|
||||||
|
else
|
||||||
|
echo "$pkg" >>/tmp/missing_dev
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
for pkg in "${deps[@]}"; do
|
||||||
|
dep_check "$pkg"
|
||||||
|
done
|
||||||
|
|
||||||
|
installed="$(cat /tmp/installed_dev 2>/dev/null)"
|
||||||
|
missing="$(cat /tmp/missing_dev 2>/dev/null)"
|
||||||
|
|
||||||
|
if ! [ "$missing" ]; then
|
||||||
|
echo -e "\n ${GRN}Dependencies met. Proceeding...${CRS}\n"
|
||||||
|
else
|
||||||
|
echo -e "\n ${RED}Following dependencies are missing :${CRS}\n\n$missing"
|
||||||
|
while true; do
|
||||||
|
echo -e "\n ${YEL}Do you wish to install via APT?"
|
||||||
|
read -p " (Y/n) --> " install_dep
|
||||||
|
echo -e "${CRS}"
|
||||||
|
if [[ "$install_dep" = "" || "$install_dep" = "y" || "$install_dep" = "Y" ]]; then
|
||||||
|
sudo apt update && sudo apt install -y $missing --simulate
|
||||||
|
echo -e "\n ${GRN}Dependencies installed. Proceeding...${CRS}\n"
|
||||||
|
sudo rm /tmp/installed_dev /tmp/missing_dev 2>/dev/null
|
||||||
|
break
|
||||||
|
elif [[ "$install_dep" == "n" || "$install_dep" = "N" ]]; then
|
||||||
|
echo -e "\n ${RED}Missing dependencies... Exiting!${CRS}\n"
|
||||||
|
sudo rm /tmp/installed_dev /tmp/missing_dev 2>/dev/null
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo -e "\n ${YEL}Invalid response... Try again...\n\n ${GRN}Y ${YEL}= (Yes, install dependencies and continue)\n ${RED}N ${YEL}= (No, don't install dependencies and exit)${CRS}\n "
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
21
lomes-app.py
Executable file
21
lomes-app.py
Executable file
|
|
@ -0,0 +1,21 @@
|
||||||
|
#!/bin/python3
|
||||||
|
|
||||||
|
from flask import Flask, render_template
|
||||||
|
import datetime
|
||||||
|
import getpass
|
||||||
|
import os
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
@app.route("/")
|
||||||
|
def index():
|
||||||
|
user = getpass.getuser()
|
||||||
|
preTime = datetime.datetime.now()
|
||||||
|
time = preTime.strftime("%H:%M:%S - %a %d.%m.%Y")
|
||||||
|
uname_fields = ("System", "Hostname", "Kernel")
|
||||||
|
uname_info = os.uname()
|
||||||
|
sysinfo = "\n".join(f"{field}: {value} | " for field, value in zip(uname_fields, uname_info))
|
||||||
|
|
||||||
|
return render_template("index.html", user=user, time=time, sysinfo=sysinfo)
|
||||||
|
|
||||||
|
app.run(host="127.0.0.1", port=5000, debug=True)
|
||||||
276
lomes-setup.sh
Executable file
276
lomes-setup.sh
Executable file
|
|
@ -0,0 +1,276 @@
|
||||||
|
#!/bin/bash
|
||||||
|
trap "exit" INT
|
||||||
|
|
||||||
|
chmod +x lomes-app.py
|
||||||
|
mkdir -p /tmp/LoMeS
|
||||||
|
source assets/shell/colors
|
||||||
|
source assets/config/deps/dependencies
|
||||||
|
|
||||||
|
### DEPENDENCY CHECK & INSTALLER
|
||||||
|
|
||||||
|
echo -e "\n ${LCY}Dependency and Privilege Check running...${CRS}\n"
|
||||||
|
|
||||||
|
### PRIVILEGES
|
||||||
|
|
||||||
|
if (( $(id -u) == 0 )); then ### AM I ROOT ?
|
||||||
|
echo -e " ${LCY}- ${GRN}Privilege check passed...${CRS}\n"
|
||||||
|
|
||||||
|
else
|
||||||
|
echo -e " ${LCY}- ${RED}Privilege check failed... Please run script with sudo or as root!${CRS}\n"
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
### DEPENDENCIES
|
||||||
|
|
||||||
|
dep_check() { ### MISSING PKGS ?
|
||||||
|
local pkg="$1"
|
||||||
|
|
||||||
|
if dpkg -s "$pkg" >/dev/null 2>&1; then
|
||||||
|
echo "$pkg" >>/tmp/LoMeS/installed_dev
|
||||||
|
|
||||||
|
else
|
||||||
|
echo "$pkg" >>/tmp/LoMeS/missing_dev
|
||||||
|
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
for pkg in "${deps[@]}"; do
|
||||||
|
dep_check "$pkg"
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
|
installed="$(cat /tmp/LoMeS/installed_dev 2>/dev/null)"
|
||||||
|
missing="$(cat /tmp/LoMeS/missing_dev 2>/dev/null)"
|
||||||
|
|
||||||
|
if ! [ "$missing" ]; then
|
||||||
|
echo -e " ${LCY}- ${GRN}Dependencies met. Proceeding...${CRS}\n"
|
||||||
|
|
||||||
|
else ### INSTALLING PKGS !
|
||||||
|
echo -e " ${LCY}- ${RED}Following dependencies are missing :\n\n$missing${CRS}"
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
echo -e "\n ${YEL}Do you wish to install via APT?"
|
||||||
|
read -p " (Y/n) --> " install_dep
|
||||||
|
echo -e "${CRS}"
|
||||||
|
|
||||||
|
if [[ "$install_dep" = "" || "$install_dep" = "y" || "$install_dep" = "Y" ]]; then
|
||||||
|
sudo apt update > /dev/null 2>&1 && sudo apt install -y $missing --simulate > /dev/null 2>&1
|
||||||
|
echo -e " ${LCY}- ${GRN}Dependencies installed. Proceeding...${CRS}\n"
|
||||||
|
sudo rm /tmp/LoMeS/installed_dev /tmp/LoMeS/missing_dev 2>/dev/null
|
||||||
|
break
|
||||||
|
|
||||||
|
elif [[ "$install_dep" == "n" || "$install_dep" = "N" ]]; then
|
||||||
|
echo -e " ${RED}Missing dependencies... Exiting!${CRS}\n"
|
||||||
|
sudo rm /tmp/LoMeS/installed_dev /tmp/LoMeS/missing_dev 2>/dev/null
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
else
|
||||||
|
echo -e " ${YEL}Invalid response... Try again...\n\n ${GRN}Y ${YEL}= (Yes, install dependencies and continue)\n ${RED}N ${YEL}= (No, don't install dependencies and exit)${CRS}\n "
|
||||||
|
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
### NGINX SETUP & CONFIG
|
||||||
|
|
||||||
|
###### CONNECTION & INTERFACE
|
||||||
|
|
||||||
|
onif=$(/sbin/ip route get 162.249.72.1 | awk '{print $5}' | cut -d/ -f1)
|
||||||
|
|
||||||
|
while true; do ### SELECT NETWORK INTERFACE !
|
||||||
|
echo -e " ${YEL}What network interface will nginx be using?"
|
||||||
|
read -p " current = "$onif" --> " nif
|
||||||
|
echo -e "${CRS}"
|
||||||
|
|
||||||
|
if ! [ "$nif" ]; then
|
||||||
|
nif="$onif"
|
||||||
|
break
|
||||||
|
|
||||||
|
elif [ -d "/sys/class/net/$nif" ]; then
|
||||||
|
break
|
||||||
|
|
||||||
|
else
|
||||||
|
echo -e "\n ${LRD}Interface not found... Try again!${CRS}\n"
|
||||||
|
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
ip4=$(/sbin/ip -o -4 addr list "$nif" | awk '{print $4}' | cut -d/ -f1) ### GET IP FOR CHOSEN INTERFACE !
|
||||||
|
echo -e "\n ${YEL}Current hostname : ${LCY}$(hostname)${CRS}"
|
||||||
|
echo -e " ${YEL}Current ip address : ${LCY}$ip4 ${YEL}@ ${LCY}$nif${CRS}"
|
||||||
|
echo -e "\n ${YEL}This information will be used to configure ${LCY}nginx.conf ${YEL}during the next steps."
|
||||||
|
|
||||||
|
while true; do ### HOST NAME CHANGE ?
|
||||||
|
read -p " Would you like to change the hostname? (y/N) --> " conf_hostname
|
||||||
|
echo -e "${CRS}"
|
||||||
|
|
||||||
|
if [[ "$conf_hostname" = "" || "$conf_hostname" = "n" || "$conf_hostname" = "N" ]]; then
|
||||||
|
new_hostname=$(hostname)
|
||||||
|
break
|
||||||
|
|
||||||
|
elif [[ "$conf_hostname" = "y" || "$conf_hostname" = "Y" ]]; then
|
||||||
|
read -p " Enter new hostname --> " new_hostname
|
||||||
|
sudo sed -i.backup "s/$(hostname)/$new_hostname/g" /etc/hosts
|
||||||
|
sudo echo "$new_hostname" > /etc/hostname
|
||||||
|
sudo systemctl restart NetworkManager 2>/dev/null
|
||||||
|
hostname -F /etc/hostname 2>/dev/null
|
||||||
|
echo -e "\n ${GRN}Host name changed to ${LCY}$(hostname)${CRS}"
|
||||||
|
echo "HC" > /tmp/LoMeS/honacha
|
||||||
|
break
|
||||||
|
|
||||||
|
else
|
||||||
|
echo -e "\n ${YEL}Invalid response... Try again...\n\n Y = (Yes, set new hostname)\n N = (No, leave as is)${CRS}\n "
|
||||||
|
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
while true; do ### CERTIFICATION & CONFIGURATION HTTPS
|
||||||
|
echo -e "\n ${YEL}Configure nginx with SSL and create a self signed cetrificate?"
|
||||||
|
read -p " (Y/n) --> " nginxSSL
|
||||||
|
echo -e "${CRS}"
|
||||||
|
|
||||||
|
if [[ "$nginxSSL" = "" || "$nginxSSL" = "y" || "$nginxSSL" = "Y" ]]; then
|
||||||
|
echo -e " ${YEL}Enter path to certificates folder"
|
||||||
|
read -p " default = /etc/nginx/ssl --> " cert_path
|
||||||
|
echo -e "${CRS}"
|
||||||
|
|
||||||
|
if ! [ "$cert_path" ]; then
|
||||||
|
cert_path=/etc/nginx/ssl
|
||||||
|
|
||||||
|
elif [[ "$cert_path" = "." ]]; then
|
||||||
|
cert_path=$PWD
|
||||||
|
|
||||||
|
else
|
||||||
|
:
|
||||||
|
|
||||||
|
fi
|
||||||
|
echo -e " ${YEL}Enter file name for certificate and key"
|
||||||
|
read -p " default = $(hostname) --> " cert_name
|
||||||
|
echo -e "${CRS}"
|
||||||
|
|
||||||
|
if ! [ "$cert_name" ]; then
|
||||||
|
cert_name=$(hostname)
|
||||||
|
|
||||||
|
else
|
||||||
|
:
|
||||||
|
|
||||||
|
fi
|
||||||
|
sudo mkdir -p "$cert_path"
|
||||||
|
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout "$cert_path"/"$cert_name".key -out "$cert_path"/"$cert_name".crt
|
||||||
|
echo -e "\n ${GRN}SSL certificate files ${LCY}$cert_name.crt ${GRN}and ${LCY}$cert_name.key ${GRN}created and stored in ${LCY}$cert_path${CRS}"
|
||||||
|
sudo chmod 644 "$cert_path"/"$cert_name".crt
|
||||||
|
sudo chmod 600 "$cert_path"/"$cert_name".key
|
||||||
|
sudo cp assets/config/nginx/nginx_SSL.conf /etc/nginx/sites-enabled/$(hostname).conf
|
||||||
|
sudo sed -i "s/DOMAIN/$(hostname).local/g" /etc/nginx/sites-enabled/$(hostname).conf
|
||||||
|
sudo sed -i "s/IPADDR/$ip4/g" /etc/nginx/sites-enabled/$(hostname).conf
|
||||||
|
sudo sed -i "s|CERTPATH|$cert_path/$cert_name|" /etc/nginx/sites-enabled/$(hostname).conf
|
||||||
|
sudo sed -i "s|KEYPATH|$cert_path/$cert_name|" /etc/nginx/sites-enabled/$(hostname).conf
|
||||||
|
break
|
||||||
|
|
||||||
|
elif [[ "$nginxSSL" == "n" || "$nginxSSL" = "N" ]]; then
|
||||||
|
while true; do ### CONFIGURATION HTTP
|
||||||
|
echo -e " ${YEL}Configure nginx with HTTP?"
|
||||||
|
read -p " (Y/n) --> " nginxHTTP
|
||||||
|
echo -e "${CRS}"
|
||||||
|
|
||||||
|
if [[ "$nginxHTTP" = "" || "$nginxHTTP" = "y" || "$nginxHTTP" = "Y" ]]; then
|
||||||
|
sudo cp assets/config/nginx/nginx_HTTP.conf /etc/nginx/sites-enabled/$(hostname).conf
|
||||||
|
sudo sed -i "s/DOMAIN/$(hostname).local/g" /etc/nginx/sites-enabled/$(hostname).conf
|
||||||
|
sudo sed -i "s/IPADDR/$ip4/g" /etc/nginx/sites-enabled/$(hostname).conf
|
||||||
|
break
|
||||||
|
|
||||||
|
elif [[ "$nginxHTTP" = "n" || "$nginxHTTP" = "N" ]]; then
|
||||||
|
echo -e " ${LCY}- ${LRD}Keeping nginx default configuration!${CRS}"
|
||||||
|
break
|
||||||
|
|
||||||
|
else
|
||||||
|
echo -e " ${YEL}Invalid response... Try again...\n\n Y = (Yes, configure nginx)\n N = (No, skip nginx configuration)${CRS}\n "
|
||||||
|
break
|
||||||
|
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
break
|
||||||
|
|
||||||
|
else
|
||||||
|
echo -e "\n ${YEL}Invalid response... Try again...\n\n Y = (Yes, configure SSL certificate and continue)\n N = (No, leave unencrypted and continue)${CRS}\n "
|
||||||
|
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
###### NGINX MAINTENANCE
|
||||||
|
|
||||||
|
sudo rm -rf /var/www/html
|
||||||
|
|
||||||
|
if ! [ "$(sudo nginx -t > /dev/null 2>&1)" ]; then ### NGINX CONF CHECK
|
||||||
|
echo -e "\n ${LCY}- ${LGN}Nginx configuration checks out...${CRS}"
|
||||||
|
|
||||||
|
else
|
||||||
|
echo -e "\n ${LRD}Nginx configuration is malformed!${CRS}"
|
||||||
|
echo "nginx -t --> failed" >> /tmp/LoMeS/install_log
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! [ "$(sudo nginx -s reload > /dev/null 2>&1)" ]; then ### RELOAD NGINX
|
||||||
|
echo -e "\n ${LCY}- ${LGN}Nginx reloaded...${CRS}"
|
||||||
|
|
||||||
|
else
|
||||||
|
echo -e "\n ${LRD}Nginx couldn't reload!${CRS}"
|
||||||
|
echo "nginx -s reload --> failed" >> /tmp/LoMeS/install_log
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! [ -f "/tmp/LoMeS/install_log" ]; then ### LOG CHECK
|
||||||
|
echo -e "\n ${LCY}- ${LGN}No errors while setting up...${CRS}"
|
||||||
|
|
||||||
|
else
|
||||||
|
echo -e "\n ${LRD}Error occured during setup! Please check ${LCY}/tmp/LoMeS/install_log ${LRD}for details...${CRS}"
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "/tmp/LoMeS/honacha" ]; then ### REBOOT DUE TO HOST NAME CHANGE
|
||||||
|
echo -e "\n ${YEL}During setup you changed this machines host name. For the change to take effect, you should reboot! "
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
read -p " Would you like to reboot? (Y/n) --> " reboot
|
||||||
|
echo -e "${CRS}"
|
||||||
|
|
||||||
|
if [[ "$reboot" = "" || "$reboot" = "y" || "$reboot" = "Y" ]]; then
|
||||||
|
echo -e " ${YEL}Rebooting in 10sec..."
|
||||||
|
echo -e " ${YEL}to start flask manually, run ${LCY}python3 lomes-app.py ${YEL}from LoMeS directory${CRS}"
|
||||||
|
sleep 10s
|
||||||
|
sudo reboot
|
||||||
|
|
||||||
|
elif [[ "$reboot" = "n" || "$reboot" = "N" ]]; then
|
||||||
|
echo -e " ${YEL}to start flask manually, run ${LCY}python3 lomes-app.py ${YEL}from LoMeS directory${CRS}"
|
||||||
|
break
|
||||||
|
|
||||||
|
else
|
||||||
|
echo -e "\n ${YEL}Invalid response... Try again...\n\n Y = (Yes, reboot)\n N = (No, don't reboot)${CRS}\n "
|
||||||
|
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
echo -e "${YEL}" ### START FLASK ?
|
||||||
|
read -p " Would you like to start flask? (Y/n) --> " flask_start
|
||||||
|
echo -e "${CRS}"
|
||||||
|
|
||||||
|
if [[ "$flask_start" = "" || "$flask_start" = "y" || "$flask_start" = "Y" ]]; then
|
||||||
|
echo -e " ${YEL}Starting flask in 10sec..."
|
||||||
|
echo -e " ${YEL}to start flask manually, run ${LCY}python3 lomes-app.py ${YEL}from LoMeS directory${CRS}\n"
|
||||||
|
sleep 10s
|
||||||
|
python3 lomes-app.py
|
||||||
|
break
|
||||||
|
|
||||||
|
elif [[ "$flask_start" = "n" || "$flask_start" = "N" ]]; then
|
||||||
|
echo -e " ${YEL}to start flask manually, run ${LCY}python3 lomes-app.py ${YEL}from LoMeS directory${CRS}\n"
|
||||||
|
break
|
||||||
|
|
||||||
|
else
|
||||||
|
echo -e "\n ${YEL}Invalid response... Try again...\n\n Y = (Yes, to start flask)\n N = (No, start flask later)${CRS}\n "
|
||||||
|
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
fi
|
||||||
146
static/css/style.css
Normal file
146
static/css/style.css
Normal file
|
|
@ -0,0 +1,146 @@
|
||||||
|
*{
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: Gravity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Gravity';
|
||||||
|
src: url('font/Gravity-UltraLight.woff2') format('woff2'),
|
||||||
|
url('font/Gravity-UltraLight.woff') format('woff');
|
||||||
|
font-weight: 200;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Gravity';
|
||||||
|
src: url('font/Gravity-Light.woff2') format('woff2'),
|
||||||
|
url('font/Gravity-Light.woff') format('woff');
|
||||||
|
font-weight: 300;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Gravity';
|
||||||
|
src: url('font/Gravity-Bold.woff2') format('woff2'),
|
||||||
|
url('font/Gravity-Bold.woff') format('woff');
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Gravity';
|
||||||
|
src: url('font/Gravity-LightItalic.woff2') format('woff2'),
|
||||||
|
url('font/Gravity-LightItalic.woff') format('woff');
|
||||||
|
font-weight: 300;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Gravity';
|
||||||
|
src: url('font/Gravity-Italic.woff2') format('woff2'),
|
||||||
|
url('font/Gravity-Italic.woff') format('woff');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Gravity Book';
|
||||||
|
src: url('font/Gravity-Book.woff2') format('woff2'),
|
||||||
|
url('font/Gravity-Book.woff') format('woff');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Gravity';
|
||||||
|
src: url('font/Gravity-BoldItalic.woff2') format('woff2'),
|
||||||
|
url('font/Gravity-BoldItalic.woff') format('woff');
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Gravity Book';
|
||||||
|
src: url('font/Gravity-BookItalic.woff2') format('woff2'),
|
||||||
|
url('font/Gravity-BookItalic.woff') format('woff');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Gravity';
|
||||||
|
src: url('font/Gravity-Regular.woff2') format('woff2'),
|
||||||
|
url('font/Gravity-Regular.woff') format('woff');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Gravity';
|
||||||
|
src: url('font/Gravity-UltraLightItalic.woff2') format('woff2'),
|
||||||
|
url('font/Gravity-UltraLightItalic.woff') format('woff');
|
||||||
|
font-weight: 200;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: darkslategrey;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
height: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textBox {
|
||||||
|
padding-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 26px;
|
||||||
|
padding: 5px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
text-align: center;
|
||||||
|
color: lightgray;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 20px;
|
||||||
|
padding: 5px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
text-align: center;
|
||||||
|
color: lightgray;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 5px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
text-align: center;
|
||||||
|
color: lightgray;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 5px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
text-align: center;
|
||||||
|
color: lightgray;
|
||||||
|
}
|
||||||
BIN
static/font/Gravity-Bold.woff
Normal file
BIN
static/font/Gravity-Bold.woff
Normal file
Binary file not shown.
BIN
static/font/Gravity-Bold.woff2
Normal file
BIN
static/font/Gravity-Bold.woff2
Normal file
Binary file not shown.
BIN
static/font/Gravity-BoldItalic.woff
Normal file
BIN
static/font/Gravity-BoldItalic.woff
Normal file
Binary file not shown.
BIN
static/font/Gravity-BoldItalic.woff2
Normal file
BIN
static/font/Gravity-BoldItalic.woff2
Normal file
Binary file not shown.
BIN
static/font/Gravity-Book.woff
Normal file
BIN
static/font/Gravity-Book.woff
Normal file
Binary file not shown.
BIN
static/font/Gravity-Book.woff2
Normal file
BIN
static/font/Gravity-Book.woff2
Normal file
Binary file not shown.
BIN
static/font/Gravity-BookItalic.woff
Normal file
BIN
static/font/Gravity-BookItalic.woff
Normal file
Binary file not shown.
BIN
static/font/Gravity-BookItalic.woff2
Normal file
BIN
static/font/Gravity-BookItalic.woff2
Normal file
Binary file not shown.
BIN
static/font/Gravity-Italic.woff
Normal file
BIN
static/font/Gravity-Italic.woff
Normal file
Binary file not shown.
BIN
static/font/Gravity-Italic.woff2
Normal file
BIN
static/font/Gravity-Italic.woff2
Normal file
Binary file not shown.
BIN
static/font/Gravity-Light.woff
Normal file
BIN
static/font/Gravity-Light.woff
Normal file
Binary file not shown.
BIN
static/font/Gravity-Light.woff2
Normal file
BIN
static/font/Gravity-Light.woff2
Normal file
Binary file not shown.
BIN
static/font/Gravity-LightItalic.woff
Normal file
BIN
static/font/Gravity-LightItalic.woff
Normal file
Binary file not shown.
BIN
static/font/Gravity-LightItalic.woff2
Normal file
BIN
static/font/Gravity-LightItalic.woff2
Normal file
Binary file not shown.
BIN
static/font/Gravity-Regular.woff
Normal file
BIN
static/font/Gravity-Regular.woff
Normal file
Binary file not shown.
BIN
static/font/Gravity-Regular.woff2
Normal file
BIN
static/font/Gravity-Regular.woff2
Normal file
Binary file not shown.
BIN
static/font/Gravity-UltraLight.woff
Normal file
BIN
static/font/Gravity-UltraLight.woff
Normal file
Binary file not shown.
BIN
static/font/Gravity-UltraLight.woff2
Normal file
BIN
static/font/Gravity-UltraLight.woff2
Normal file
Binary file not shown.
BIN
static/font/Gravity-UltraLightItalic.woff
Normal file
BIN
static/font/Gravity-UltraLightItalic.woff
Normal file
Binary file not shown.
BIN
static/font/Gravity-UltraLightItalic.woff2
Normal file
BIN
static/font/Gravity-UltraLightItalic.woff2
Normal file
Binary file not shown.
57
static/images/favicon.ico
Normal file
57
static/images/favicon.ico
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="23.797873mm"
|
||||||
|
height="12.931701mm"
|
||||||
|
viewBox="0 0 23.797873 12.931701"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
xml:space="preserve"
|
||||||
|
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||||
|
sodipodi:docname="favicon.ico"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#505050"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:zoom="11.782177"
|
||||||
|
inkscape:cx="30.6395"
|
||||||
|
inkscape:cy="19.308826"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1374"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="42"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="g3" /><defs
|
||||||
|
id="defs1" /><g
|
||||||
|
transform="matrix(0.21229796,0,0,0.12171574,-113.52852,-33.25759)"
|
||||||
|
id="g3"
|
||||||
|
style="clip-rule:evenodd;display:inline;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2">
|
||||||
|
<g
|
||||||
|
transform="matrix(0.579082,0,0,1.01004,460.975,-39.6867)"
|
||||||
|
id="g1">
|
||||||
|
<path
|
||||||
|
d="m 250.908,330.267 -57.782,84.738 -12.188,-8.311 63.864,-93.657 c 1.372,-2.013 3.651,-3.218 6.087,-3.221 2.437,-0.002 4.717,1.199 6.093,3.21 l 64.012,93.51 -12.173,8.333 z"
|
||||||
|
style="fill:#ffffff"
|
||||||
|
id="path1" />
|
||||||
|
</g><g
|
||||||
|
transform="matrix(0.579082,0,0,1.01004,441.72359,108.2802)"
|
||||||
|
id="g1-3"
|
||||||
|
style="clip-rule:evenodd;display:inline;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2">
|
||||||
|
<path
|
||||||
|
d="m 230.63238,183.77092 -57.782,84.738 -12.188,-8.311 63.864,-93.657 c 1.372,-2.013 3.651,-3.218 6.087,-3.221 2.437,-0.002 4.717,1.199 6.093,3.21 l 16.93449,24.73698 -12.173,8.333 z"
|
||||||
|
style="fill:#ffffff"
|
||||||
|
id="path1-5"
|
||||||
|
sodipodi:nodetypes="ccccccccc" />
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g></svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
57
static/images/lomes.svg
Normal file
57
static/images/lomes.svg
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="23.797873mm"
|
||||||
|
height="12.931701mm"
|
||||||
|
viewBox="0 0 23.797873 12.931701"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
xml:space="preserve"
|
||||||
|
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||||
|
sodipodi:docname="lomes.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#eeeeee"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:showpageshadow="0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#505050"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:zoom="11.782177"
|
||||||
|
inkscape:cx="30.6395"
|
||||||
|
inkscape:cy="19.308826"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1374"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="42"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="g3" /><defs
|
||||||
|
id="defs1" /><g
|
||||||
|
transform="matrix(0.21229796,0,0,0.12171574,-113.52852,-33.25759)"
|
||||||
|
id="g3"
|
||||||
|
style="clip-rule:evenodd;display:inline;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2">
|
||||||
|
<g
|
||||||
|
transform="matrix(0.579082,0,0,1.01004,460.975,-39.6867)"
|
||||||
|
id="g1">
|
||||||
|
<path
|
||||||
|
d="m 250.908,330.267 -57.782,84.738 -12.188,-8.311 63.864,-93.657 c 1.372,-2.013 3.651,-3.218 6.087,-3.221 2.437,-0.002 4.717,1.199 6.093,3.21 l 64.012,93.51 -12.173,8.333 z"
|
||||||
|
style="fill:#ffffff"
|
||||||
|
id="path1" />
|
||||||
|
</g><g
|
||||||
|
transform="matrix(0.579082,0,0,1.01004,441.72359,108.2802)"
|
||||||
|
id="g1-3"
|
||||||
|
style="clip-rule:evenodd;display:inline;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2">
|
||||||
|
<path
|
||||||
|
d="m 230.63238,183.77092 -57.782,84.738 -12.188,-8.311 63.864,-93.657 c 1.372,-2.013 3.651,-3.218 6.087,-3.221 2.437,-0.002 4.717,1.199 6.093,3.21 l 16.93449,24.73698 -12.173,8.333 z"
|
||||||
|
style="fill:#ffffff"
|
||||||
|
id="path1-5"
|
||||||
|
sodipodi:nodetypes="ccccccccc" />
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g></svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
33
templates/index.html
Normal file
33
templates/index.html
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="content-type" content="text/html" charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width" initial-scale="1.0">
|
||||||
|
|
||||||
|
<title>LoMeS | Meshtastic</title>
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='images/lomes.svg') }}">
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<section>
|
||||||
|
<header>
|
||||||
|
<img class="icon" src="{{ url_for('static', filename='images/lomes.svg') }}">
|
||||||
|
<h1>Welcome to LoMeS</h1>
|
||||||
|
</header>
|
||||||
|
<div class="textBox">
|
||||||
|
<h3>A Local Meshtastic Server in development</h3><br>
|
||||||
|
<p>If you see this site, means "LoMeS" development environment is running!</p>
|
||||||
|
<p>Thanks for collaborating and trying to make things more real...</p>
|
||||||
|
</div>
|
||||||
|
<div class="textBox">
|
||||||
|
<h3>Configuration Summary</h3>
|
||||||
|
<div class="confBox">
|
||||||
|
<ul>
|
||||||
|
<li>User executing flask : {{user}}</li>
|
||||||
|
<li>Machine time : {{time}}</li>
|
||||||
|
<li>{{sysinfo}}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue