2026-08-01 16:54 Webaktualisierung durch Javascript und Bezeichnung der Eingänge in config.json anpassbar
This commit is contained in:
parent
10ce58541e
commit
2c63235b54
4 changed files with 128 additions and 51 deletions
|
|
@ -3,10 +3,15 @@
|
|||
"i2c_bus": "1",
|
||||
"ads1115_address_1": 72,
|
||||
"ads1115_address_2": 73,
|
||||
"name_adc1_ch0": "Hauptbatterie (U12V)",
|
||||
"factor_adc1_ch0": 5.0,
|
||||
"name_adc1_ch1": "Backup-Batterie",
|
||||
"factor_adc1_ch1": 5.0,
|
||||
"name_adc1_ch2": "Solar-Eingang",
|
||||
"factor_adc1_ch2": 1.0,
|
||||
"name_adc1_ch3": "Steuerspannung Intern",
|
||||
"factor_adc1_ch3": 1.0,
|
||||
"name_adc2_ch0": "Zusatzakku Funk",
|
||||
"factor_adc2_ch0": 1.0,
|
||||
"relais_pins": [
|
||||
{"id": 1, "name": "Relais 1 (Hauptschalter)", "gpio": "GPIO12"},
|
||||
|
|
|
|||
26
main.go
26
main.go
|
|
@ -22,10 +22,15 @@ type Config struct {
|
|||
I2CBus string `json:"i2c_bus"`
|
||||
ADS1115Address1 uint16 `json:"ads1115_address_1"`
|
||||
ADS1115Address2 uint16 `json:"ads1115_address_2"`
|
||||
NameAdc1Ch0 string `json:"name_adc1_ch0"`
|
||||
FactorAdc1Ch0 float64 `json:"factor_adc1_ch0"`
|
||||
NameAdc1Ch1 string `json:"name_adc1_ch1"`
|
||||
FactorAdc1Ch1 float64 `json:"factor_adc1_ch1"`
|
||||
NameAdc1Ch2 string `json:"name_adc1_ch2"`
|
||||
FactorAdc1Ch2 float64 `json:"factor_adc1_ch2"`
|
||||
NameAdc1Ch3 string `json:"name_adc1_ch3"`
|
||||
FactorAdc1Ch3 float64 `json:"factor_adc1_ch3"`
|
||||
NameAdc2Ch0 string `json:"name_adc2_ch0"`
|
||||
FactorAdc2Ch0 float64 `json:"factor_adc2_ch0"`
|
||||
Relais []RelaisConf `json:"relais_pins"`
|
||||
}
|
||||
|
|
@ -95,12 +100,31 @@ func main() {
|
|||
}
|
||||
|
||||
// Instanziierung des Webservers mit Übergabe der Callback-Funktionen
|
||||
server := web.NewServer(config.WebPort, readVoltageCallback, toggleRelaisCallback, getRelaisStateCallback, callbackGetVersion)
|
||||
|
||||
server := web.NewServer(
|
||||
config.WebPort,
|
||||
readVoltageCallback,
|
||||
toggleRelaisCallback,
|
||||
getRelaisStateCallback,
|
||||
callbackGetVersion,
|
||||
getVoltNamesCallback,
|
||||
)
|
||||
|
||||
fmt.Printf("Webserver gestartet auf Port %s\n", config.WebPort)
|
||||
log.Fatal(server.Start())
|
||||
}
|
||||
|
||||
// NEUES CALLBACK: Liefert die Namen der Kanäle an das Web-Paket
|
||||
func getVoltNamesCallback() []string {
|
||||
return []string{
|
||||
config.NameAdc1Ch0,
|
||||
config.NameAdc1Ch1,
|
||||
config.NameAdc1Ch2,
|
||||
config.NameAdc1Ch3,
|
||||
config.NameAdc2Ch0,
|
||||
}
|
||||
}
|
||||
|
||||
// Callback: Wird vom Webserver aufgerufen, um Kanäle zu messen
|
||||
// Kanäle 0-3 = ADS1115 Nr. 1 (A0-A3)
|
||||
// Kanal 4 = ADS1115 Nr. 2 (A0)
|
||||
|
|
|
|||
|
|
@ -1,27 +1,28 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type RelaisData struct {
|
||||
ID int
|
||||
Name string
|
||||
State bool
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
State bool `json:"state"`
|
||||
}
|
||||
|
||||
// TemplateData erweitert um alle 5 Spannungskanäle
|
||||
type TemplateData struct {
|
||||
V1 float64 // ADC 1 - Kanal 0
|
||||
V2 float64 // ADC 1 - Kanal 1
|
||||
V3 float64 // ADC 1 - Kanal 2
|
||||
V4 float64 // ADC 1 - Kanal 3
|
||||
V5 float64 // ADC 2 - Kanal 0
|
||||
Relais []RelaisData
|
||||
Version string
|
||||
BuildDate string
|
||||
type StatusResponse struct {
|
||||
V1 float64 `json:"v1"`
|
||||
V2 float64 `json:"v2"`
|
||||
V3 float64 `json:"v3"`
|
||||
V4 float64 `json:"v4"`
|
||||
V5 float64 `json:"v5"`
|
||||
Names []string `json:"names"` // Neu: Sendet die Namen als Array mit
|
||||
Relais []RelaisData `json:"relais"`
|
||||
Version string `json:"version"`
|
||||
BuildDate string `json:"build_date"`
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
|
|
@ -30,45 +31,37 @@ type Server struct {
|
|||
toggleFunc func(id int)
|
||||
getRelais func() []RelaisData
|
||||
versionFunc func() (string, string)
|
||||
getNamesFunc func() []string // Neu hinzugefügt
|
||||
}
|
||||
|
||||
func NewServer(port string, readVolt func(int) float64, toggle func(int), getRelais func() []RelaisData, getVersion func() (string, string)) *Server {
|
||||
func NewServer(port string, readVolt func(int) float64, toggle func(int), getRelais func() []RelaisData, getVersion func() (string, string), getNames func() []string) *Server {
|
||||
return &Server{
|
||||
port: port,
|
||||
readVoltFunc: readVolt,
|
||||
toggleFunc: toggle,
|
||||
getRelais: getRelais,
|
||||
versionFunc: getVersion,
|
||||
getNamesFunc: getNames, // Neu hinzugefügt
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) Start() error {
|
||||
http.HandleFunc("/", s.handleIndex)
|
||||
http.HandleFunc("/toggle", s.handleToggle)
|
||||
http.HandleFunc("/api/status", s.handleAPIStatus)
|
||||
http.HandleFunc("/api/toggle", s.handleAPIToggle)
|
||||
return http.ListenAndServe(s.port, nil)
|
||||
}
|
||||
|
||||
func (s *Server) handleIndex(w http.ResponseWriter, r *http.Request) {
|
||||
// Alle 5 Kanäle nacheinander über das Callback einlesen
|
||||
v1 := s.readVoltFunc(0)
|
||||
v2 := s.readVoltFunc(1)
|
||||
v3 := s.readVoltFunc(2)
|
||||
v4 := s.readVoltFunc(3)
|
||||
v5 := s.readVoltFunc(4)
|
||||
ver, date := s.versionFunc()
|
||||
|
||||
tmpl, err := template.ParseFiles("web/static/index.html")
|
||||
if err != nil {
|
||||
http.Error(w, "Template nicht gefunden: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
data := TemplateData{
|
||||
V1: v1,
|
||||
V2: v2,
|
||||
V3: v3,
|
||||
V4: v4,
|
||||
V5: v5,
|
||||
data := StatusResponse{
|
||||
Names: s.getNamesFunc(), // Namen für das erste Rendern bereitstellen
|
||||
Relais: s.getRelais(),
|
||||
Version: ver,
|
||||
BuildDate: date,
|
||||
|
|
@ -76,11 +69,29 @@ func (s *Server) handleIndex(w http.ResponseWriter, r *http.Request) {
|
|||
tmpl.Execute(w, data)
|
||||
}
|
||||
|
||||
func (s *Server) handleToggle(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *Server) handleAPIStatus(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
ver, date := s.versionFunc()
|
||||
resp := StatusResponse{
|
||||
V1: s.readVoltFunc(0),
|
||||
V2: s.readVoltFunc(1),
|
||||
V3: s.readVoltFunc(2),
|
||||
V4: s.readVoltFunc(3),
|
||||
V5: s.readVoltFunc(4),
|
||||
Names: s.getNamesFunc(),
|
||||
Relais: s.getRelais(),
|
||||
Version: ver,
|
||||
BuildDate: date,
|
||||
}
|
||||
json.NewEncoder(w).Encode(resp)
|
||||
}
|
||||
|
||||
func (s *Server) handleAPIToggle(w http.ResponseWriter, r *http.Request) {
|
||||
idStr := r.URL.Query().Get("id")
|
||||
id, err := strconv.Atoi(idStr)
|
||||
if err == nil {
|
||||
s.toggleFunc(id)
|
||||
}
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
s.handleAPIStatus(w, r)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,43 +9,43 @@
|
|||
.card { background: #1e293b; padding: 20px; margin: 15px 0; border-radius: 12px; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.3); border: 1px solid #334155; }
|
||||
.val { font-size: 2.5em; color: #38bdf8; font-weight: bold; font-family: monospace; }
|
||||
.grid { display: grid; grid-template-columns: 1fr; gap: 12px; margin-top: 15px; }
|
||||
.btn { width: 100%; padding: 14px; font-size: 1.1em; border: none; border-radius: 8px; cursor: pointer; font-weight: bold; transition: background 0.2s; color: white; }
|
||||
.btn { width: 100%; padding: 14px; font-size: 1.1em; border: none; border-radius: 8px; cursor: pointer; font-weight: bold; transition: background 0.2s, transform 0.1s; color: white; }
|
||||
.btn:active { transform: scale(0.98); }
|
||||
.btn-on { background: #ef4444; }
|
||||
.btn-on:hover { background: #dc2626; }
|
||||
.btn-off { background: #10b981; }
|
||||
.btn-off:hover { background: #059669; }
|
||||
.footer-nav { margin-top: 25px; font-size: 0.9em; color: #64748b; }
|
||||
</style>
|
||||
<meta http-equiv="refresh" content="3">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>U12V Funkstations-Monitor</h1>
|
||||
|
||||
<!-- Alle 5 Spannungen strikt untereinander -->
|
||||
<!-- Dynamische Überschriften aus dem Names-Array der config.json -->
|
||||
<div class="card">
|
||||
<h3>ADC 1 - Kanal 0</h3>
|
||||
<div class="val">{{printf "%.2f" .V1}} V</div>
|
||||
<h3>{{index .Names 0}}</h3>
|
||||
<div class="val" id="v1">--.-- V</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>ADC 1 - Kanal 1</h3>
|
||||
<div class="val">{{printf "%.2f" .V2}} V</div>
|
||||
<h3>{{index .Names 1}}</h3>
|
||||
<div class="val" id="v2">--.-- V</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>ADC 1 - Kanal 2</h3>
|
||||
<div class="val">{{printf "%.2f" .V3}} V</div>
|
||||
<h3>{{index .Names 2}}</h3>
|
||||
<div class="val" id="v3">--.-- V</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>ADC 1 - Kanal 3</h3>
|
||||
<div class="val">{{printf "%.2f" .V4}} V</div>
|
||||
<h3>{{index .Names 3}}</h3>
|
||||
<div class="val" id="v4">--.-- V</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>ADC 2 - Kanal 0</h3>
|
||||
<div class="val">{{printf "%.2f" .V5}} V</div>
|
||||
<h3>{{index .Names 4}}</h3>
|
||||
<div class="val" id="v5">--.-- V</div>
|
||||
</div>
|
||||
|
||||
<!-- Relaissteuerung -->
|
||||
|
|
@ -53,22 +53,59 @@
|
|||
<h3>Relaissteuerung</h3>
|
||||
<div class="grid">
|
||||
{{range .Relais}}
|
||||
<a href="/toggle?id={{.ID}}" style="text-decoration: none;">
|
||||
<button class="btn {{if .State}}btn-on{{else}}btn-off{{end}}">
|
||||
{{.Name}}: {{if .State}}AKTIV (Ausschalten){{else}}INAKTIV (Einschalten){{end}}
|
||||
</button>
|
||||
</a>
|
||||
<button class="btn btn-off" id="relais-btn-{{.ID}}" onclick="toggleRelais({{.ID}})">
|
||||
{{.Name}}: Laden...
|
||||
</button>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer-nav">
|
||||
Aktualisiert alle 3 Sekunden automatisch<br>
|
||||
Live-Aktualisierung aktiv (ohne Flackern)<br>
|
||||
<span style="font-size: 0.8em; color: #475569; margin-top: 5px; display: block;">
|
||||
Version v{{.Version}} ({{.BuildDate}})
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function updateUI(data) {
|
||||
document.getElementById('v1').innerText = data.v1.toFixed(2) + ' V';
|
||||
document.getElementById('v2').innerText = data.v2.toFixed(2) + ' V';
|
||||
document.getElementById('v3').innerText = data.v3.toFixed(2) + ' V';
|
||||
document.getElementById('v4').innerText = data.v4.toFixed(2) + ' V';
|
||||
document.getElementById('v5').innerText = data.v5.toFixed(2) + ' V';
|
||||
|
||||
data.relais.forEach(r => {
|
||||
const btn = document.getElementById(`relais-btn-${r.id}`);
|
||||
if (btn) {
|
||||
if (r.state) {
|
||||
btn.className = 'btn btn-on';
|
||||
btn.innerText = `${r.name}: AKTIV (Ausschalten)`;
|
||||
} else {
|
||||
btn.className = 'btn btn-off';
|
||||
btn.innerText = `${r.name}: INAKTIV (Einschalten)`;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadStatus() {
|
||||
fetch('/api/status')
|
||||
.then(response => response.json())
|
||||
.then(data => updateUI(data))
|
||||
.catch(err => console.error('Fehler beim Laden des Status:', err));
|
||||
}
|
||||
|
||||
function toggleRelais(id) {
|
||||
fetch(`/api/toggle?id=${id}`)
|
||||
.then(response => response.json())
|
||||
.then(data => updateUI(data))
|
||||
.catch(err => console.error('Fehler beim Schalten:', err));
|
||||
}
|
||||
|
||||
loadStatus();
|
||||
setInterval(loadStatus, 2000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Reference in a new issue