Add complete babycam setup: install script, Flask app, state machine, configs

- setup.sh: vollständiges Install- und Konfigurationsscript für Raspberry Pi OS Trixie
- app/main.py: Flask Webinterface mit MJPEG-Stream, WLAN-Verwaltung und Statusseite
- app/templates/index.html: Dark-Mode UI mit Live-Stream, WLAN-Scan und Verbinden
- scripts/network_state.py: State Machine für Modus A (Heimnetz) / B (Access Point)
- config/hostapd.conf: Access Point BabyCam (192.168.50.0/24)
- config/dnsmasq.conf: DHCP-Server für AP-Netzwerk
- systemd/: babycam-web und babycam-network als autostart Services
- README.md: Schnellstart-Anleitung und Projektstruktur ergänzt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Julian Vollmer 2026-05-18 14:38:26 +02:00
parent 476922f440
commit ef858f9040
9 changed files with 839 additions and 6 deletions

View File

@ -1,4 +1,48 @@
# Raspberry Pi Babycam Gesamtplan (Pi 3 + Camera Module 3 NoIR)
# Raspberry Pi Babycam
**Hardware:** Raspberry Pi 3 + Camera Module 3 NoIR Wide + USB WLAN Stick
**OS:** Raspberry Pi OS Trixie Lite (arm64)
---
## Schnellstart
```bash
git clone <repo-url> babycam
cd babycam
sudo bash setup.sh
```
Nach dem Neustart:
- Webinterface: http://babycam.local
- SSH: `ssh pi@babycam.local`
- AP-SSID: `BabyCam` / Passwort: `babycam123`
- AP-Webinterface: http://192.168.50.1
---
## Projektstruktur
```
babycam/
├── setup.sh # Komplettes Install- und Konfigurationsscript
├── app/
│ ├── main.py # Flask Webinterface (Stream, WLAN, Status)
│ └── templates/
│ └── index.html # Web-UI
├── scripts/
│ └── network_state.py # State Machine (Modus A/B/C)
├── config/
│ ├── hostapd.conf # Access Point Konfiguration
│ └── dnsmasq.conf # DHCP Server Konfiguration
└── systemd/
├── babycam-web.service # Flask als Systemdienst
└── babycam-network.service # State Machine als Systemdienst
```
---
## Gesamtplan (Pi 3 + Camera Module 3 NoIR)
## 1. Zielarchitektur
@ -137,10 +181,11 @@ Ergebnis:
Empfohlene Konfiguration:
libcamera-vid:
rpicam-vid (ehemals libcamera-vid):
- Auflösung: 1280x720
- Framerate: 20 fps
- H264 Stream über TCP
- Codec: MJPEG
- Ausgabe: stdout → Flask MJPEG-Stream
---
@ -171,9 +216,8 @@ Funktionen:
## 12. Systemdienste
- Webserver (Flask oder FastAPI)
- Kamera-Service
- optional Netzwerk-State-Manager
- `babycam-web.service` → Flask Webserver (Port 80, autostart)
- `babycam-network.service` → State Machine (prüft alle 30s den WLAN-Status)
---

183
app/main.py Normal file
View File

@ -0,0 +1,183 @@
#!/usr/bin/env python3
"""
Babycam Flask Webinterface
- Live-Stream via MJPEG (rpicam-vid)
- WLAN-Verwaltung (scan, connect)
- Systemstatus
"""
import subprocess
import json
import re
import time
from flask import Flask, Response, render_template, request, jsonify
app = Flask(__name__)
STREAM_WIDTH = 1280
STREAM_HEIGHT = 720
STREAM_FPS = 20
# ---------------------------------------------------------------------------
# Kamera-Stream
# ---------------------------------------------------------------------------
def generate_frames():
cmd = [
"rpicam-vid",
"-t", "0",
"--width", str(STREAM_WIDTH),
"--height", str(STREAM_HEIGHT),
"--framerate", str(STREAM_FPS),
"--codec", "mjpeg",
"-o", "-",
"--nopreview",
]
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
boundary = b"--frame\r\nContent-Type: image/jpeg\r\n\r\n"
buf = b""
try:
while True:
chunk = process.stdout.read(4096)
if not chunk:
break
buf += chunk
start = buf.find(b"\xff\xd8")
end = buf.find(b"\xff\xd9")
if start != -1 and end != -1 and end > start:
frame = buf[start:end + 2]
buf = buf[end + 2:]
yield boundary + frame + b"\r\n"
finally:
process.kill()
@app.route("/stream")
def stream():
return Response(
generate_frames(),
mimetype="multipart/x-mixed-replace; boundary=frame",
)
# ---------------------------------------------------------------------------
# WLAN-Verwaltung
# ---------------------------------------------------------------------------
def get_wifi_networks():
result = subprocess.run(
["nmcli", "-t", "-f", "SSID,SIGNAL,SECURITY", "device", "wifi", "list", "ifname", "wlan0"],
capture_output=True, text=True,
)
networks = []
seen = set()
for line in result.stdout.splitlines():
parts = line.split(":")
if len(parts) >= 2:
ssid = parts[0].strip()
if ssid and ssid not in seen:
seen.add(ssid)
signal = parts[1] if len(parts) > 1 else "0"
security = parts[2] if len(parts) > 2 else ""
networks.append({"ssid": ssid, "signal": signal, "security": security})
return sorted(networks, key=lambda x: int(x["signal"]) if x["signal"].isdigit() else 0, reverse=True)
def get_system_status():
# IP-Adressen
ip_result = subprocess.run(["hostname", "-I"], capture_output=True, text=True)
ips = ip_result.stdout.strip().split()
# Aktive WLAN-Verbindung
wifi_result = subprocess.run(
["nmcli", "-t", "-f", "GENERAL.CONNECTION", "device", "show", "wlan0"],
capture_output=True, text=True,
)
wifi_match = re.search(r"GENERAL\.CONNECTION:(.*)", wifi_result.stdout)
wifi_ssid = wifi_match.group(1).strip() if wifi_match else "nicht verbunden"
# AP-Status
ap_result = subprocess.run(["systemctl", "is-active", "hostapd"], capture_output=True, text=True)
ap_active = ap_result.stdout.strip() == "active"
# Kamera verfügbar?
cam_result = subprocess.run(["rpicam-hello", "--list-cameras"], capture_output=True, text=True)
cam_available = "No cameras" not in cam_result.stderr and "No cameras" not in cam_result.stdout
return {
"ips": ips,
"wifi_ssid": wifi_ssid,
"ap_active": ap_active,
"cam_available": cam_available,
"uptime": _get_uptime(),
}
def _get_uptime():
try:
with open("/proc/uptime") as f:
seconds = float(f.read().split()[0])
m, s = divmod(int(seconds), 60)
h, m = divmod(m, 60)
return f"{h}h {m}m"
except Exception:
return "unbekannt"
# ---------------------------------------------------------------------------
# Routen
# ---------------------------------------------------------------------------
@app.route("/")
def index():
status = get_system_status()
return render_template("index.html", status=status)
@app.route("/api/status")
def api_status():
return jsonify(get_system_status())
@app.route("/wifi/scan")
def wifi_scan():
# Kurz neu scannen
subprocess.run(["nmcli", "device", "wifi", "rescan", "ifname", "wlan0"],
capture_output=True)
time.sleep(2)
return jsonify(get_wifi_networks())
@app.route("/wifi/connect", methods=["POST"])
def wifi_connect():
data = request.get_json()
ssid = data.get("ssid", "").strip()
password = data.get("password", "").strip()
if not ssid:
return jsonify({"success": False, "error": "SSID fehlt"}), 400
cmd = ["nmcli", "device", "wifi", "connect", ssid, "ifname", "wlan0"]
if password:
cmd += ["password", password]
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
success = result.returncode == 0
return jsonify({
"success": success,
"message": result.stdout.strip() or result.stderr.strip(),
})
# ---------------------------------------------------------------------------
# Start
# ---------------------------------------------------------------------------
if __name__ == "__main__":
app.run(host="0.0.0.0", port=80, debug=False)

306
app/templates/index.html Normal file
View File

@ -0,0 +1,306 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Babycam</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
background: #0f0f0f;
color: #e0e0e0;
min-height: 100vh;
}
header {
background: #1a1a1a;
padding: 1rem 1.5rem;
display: flex;
align-items: center;
gap: 0.75rem;
border-bottom: 1px solid #2a2a2a;
}
header h1 { font-size: 1.2rem; font-weight: 600; }
header .dot {
width: 10px; height: 10px;
border-radius: 50%;
background: {{ '#4caf50' if status.cam_available else '#f44336' }};
}
.container { max-width: 900px; margin: 0 auto; padding: 1.5rem; }
/* Stream */
.stream-box {
background: #000;
border-radius: 12px;
overflow: hidden;
margin-bottom: 1.5rem;
aspect-ratio: 16/9;
display: flex;
align-items: center;
justify-content: center;
}
.stream-box img {
width: 100%;
height: 100%;
object-fit: cover;
}
.stream-box .no-cam {
color: #555;
font-size: 0.9rem;
text-align: center;
padding: 2rem;
}
/* Status-Karten */
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
margin-bottom: 1.5rem;
}
.card {
background: #1a1a1a;
border-radius: 10px;
padding: 1rem 1.25rem;
border: 1px solid #2a2a2a;
}
.card label {
font-size: 0.75rem;
color: #888;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.card .value {
font-size: 1rem;
font-weight: 500;
margin-top: 0.3rem;
word-break: break-all;
}
.badge {
display: inline-block;
padding: 0.2rem 0.6rem;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 500;
}
.badge.on { background: #1b4d2e; color: #4caf50; }
.badge.off { background: #3d1a1a; color: #f44336; }
/* WLAN-Panel */
.panel {
background: #1a1a1a;
border-radius: 10px;
border: 1px solid #2a2a2a;
overflow: hidden;
}
.panel-header {
padding: 1rem 1.25rem;
border-bottom: 1px solid #2a2a2a;
display: flex;
align-items: center;
justify-content: space-between;
}
.panel-header h2 { font-size: 1rem; font-weight: 600; }
button {
background: #2a2a2a;
color: #e0e0e0;
border: 1px solid #3a3a3a;
padding: 0.5rem 1rem;
border-radius: 6px;
cursor: pointer;
font-size: 0.85rem;
transition: background 0.15s;
}
button:hover { background: #333; }
button.primary { background: #1565c0; border-color: #1976d2; }
button.primary:hover { background: #1976d2; }
#network-list { padding: 0.5rem 0; min-height: 3rem; }
.network-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.65rem 1.25rem;
cursor: pointer;
transition: background 0.1s;
border-radius: 6px;
margin: 0.1rem 0.5rem;
}
.network-item:hover { background: #252525; }
.network-item.selected { background: #1a2a3a; }
.network-name { font-size: 0.9rem; }
.network-signal { font-size: 0.75rem; color: #888; }
.connect-form {
padding: 1rem 1.25rem;
border-top: 1px solid #2a2a2a;
display: none;
gap: 0.75rem;
flex-direction: column;
}
.connect-form.visible { display: flex; }
.connect-form input {
background: #0f0f0f;
border: 1px solid #3a3a3a;
color: #e0e0e0;
padding: 0.6rem 0.9rem;
border-radius: 6px;
font-size: 0.9rem;
width: 100%;
}
.connect-form input:focus {
outline: none;
border-color: #1976d2;
}
#status-msg {
font-size: 0.85rem;
padding: 0 1.25rem 1rem;
color: #888;
}
#status-msg.ok { color: #4caf50; }
#status-msg.err { color: #f44336; }
</style>
</head>
<body>
<header>
<div class="dot"></div>
<h1>Babycam</h1>
</header>
<div class="container">
<!-- Stream -->
<div class="stream-box">
{% if status.cam_available %}
<img src="/stream" alt="Kamera-Stream">
{% else %}
<div class="no-cam">
Kamera nicht verfügbar<br>
<small>Ribbon-Kabel verbunden?</small>
</div>
{% endif %}
</div>
<!-- Status-Karten -->
<div class="cards">
<div class="card">
<label>WLAN</label>
<div class="value">{{ status.wifi_ssid }}</div>
</div>
<div class="card">
<label>IP-Adressen</label>
<div class="value">{{ status.ips | join(", ") or "keine" }}</div>
</div>
<div class="card">
<label>Access Point</label>
<div class="value">
<span class="badge {{ 'on' if status.ap_active else 'off' }}">
{{ "aktiv" if status.ap_active else "aus" }}
</span>
</div>
</div>
<div class="card">
<label>Laufzeit</label>
<div class="value">{{ status.uptime }}</div>
</div>
</div>
<!-- WLAN-Verwaltung -->
<div class="panel">
<div class="panel-header">
<h2>WLAN konfigurieren</h2>
<button onclick="scanNetworks()">Scannen</button>
</div>
<div id="network-list"><p style="padding:1rem;color:#555;font-size:.85rem;">Auf Scannen klicken…</p></div>
<div class="connect-form" id="connect-form">
<div style="font-size:.85rem;color:#aaa">Verbinden mit: <strong id="selected-ssid"></strong></div>
<input type="password" id="wifi-password" placeholder="WLAN-Passwort">
<button class="primary" onclick="connectWifi()">Verbinden</button>
</div>
<div id="status-msg"></div>
</div>
</div>
<script>
let selectedSSID = null;
async function scanNetworks() {
const list = document.getElementById("network-list");
list.innerHTML = '<p style="padding:1rem;color:#555;font-size:.85rem;">Scanne…</p>';
document.getElementById("connect-form").classList.remove("visible");
const res = await fetch("/wifi/scan");
const networks = await res.json();
if (!networks.length) {
list.innerHTML = '<p style="padding:1rem;color:#555;font-size:.85rem;">Keine Netzwerke gefunden.</p>';
return;
}
list.innerHTML = networks.map(n => `
<div class="network-item" onclick="selectNetwork('${n.ssid.replace(/'/g,"\\'")}', this)">
<span class="network-name">${n.ssid}</span>
<span class="network-signal">${n.signal}% &nbsp; ${n.security || "offen"}</span>
</div>
`).join("");
}
function selectNetwork(ssid, el) {
document.querySelectorAll(".network-item").forEach(i => i.classList.remove("selected"));
el.classList.add("selected");
selectedSSID = ssid;
document.getElementById("selected-ssid").textContent = ssid;
document.getElementById("connect-form").classList.add("visible");
document.getElementById("wifi-password").focus();
}
async function connectWifi() {
if (!selectedSSID) return;
const password = document.getElementById("wifi-password").value;
const msg = document.getElementById("status-msg");
msg.className = "";
msg.textContent = "Verbinde…";
const res = await fetch("/wifi/connect", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ ssid: selectedSSID, password }),
});
const data = await res.json();
msg.className = data.success ? "ok" : "err";
msg.textContent = data.success ? "Verbunden!" : "Fehler: " + data.message;
if (data.success) {
setTimeout(() => location.reload(), 2000);
}
}
</script>
</body>
</html>

5
config/dnsmasq.conf Normal file
View File

@ -0,0 +1,5 @@
interface=wlan1
bind-interfaces
dhcp-range=192.168.50.20,192.168.50.200,255.255.255.0,24h
domain=local
address=/babycam.local/192.168.50.1

14
config/hostapd.conf Normal file
View File

@ -0,0 +1,14 @@
interface=wlan1
driver=nl80211
ssid=BabyCam
hw_mode=g
channel=6
wmm_enabled=0
macaddr_acl=0
auth_algs=1
ignore_broadcast_ssid=0
wpa=2
wpa_passphrase=babycam123
wpa_key_mgmt=WPA-PSK
wpa_pairwise=TKIP
rsn_pairwise=CCMP

91
scripts/network_state.py Normal file
View File

@ -0,0 +1,91 @@
#!/usr/bin/env python3
"""
Babycam Network State Machine
Verwaltet die drei Betriebsmodi:
A Heimnetz (wlan0 verbunden)
B Kein WLAN (AP auf wlan1)
C Setup-Modus (AP läuft, User konfiguriert WLAN)
"""
import subprocess
import time
import logging
import sys
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
handlers=[logging.StreamHandler(sys.stdout)],
)
log = logging.getLogger(__name__)
AP_INTERFACE = "wlan1"
CLIENT_INTERFACE = "wlan0"
AP_IP = "192.168.50.1"
CHECK_INTERVAL = 30 # Sekunden
def run(cmd, check=False):
result = subprocess.run(
cmd, shell=True, capture_output=True, text=True
)
if check and result.returncode != 0:
log.error(f"Fehler bei: {cmd}\n{result.stderr}")
return result
def is_wlan0_connected():
result = run(
f"nmcli -t -f GENERAL.STATE device show {CLIENT_INTERFACE}"
)
return "100 (connected)" in result.stdout
def is_ap_running():
result = run("systemctl is-active hostapd")
return result.stdout.strip() == "active"
def start_ap():
log.info("Starte Access Point (BabyCam)...")
run("ip addr add 192.168.50.1/24 dev wlan1 2>/dev/null || true")
run("ip link set wlan1 up")
run("systemctl start hostapd", check=True)
run("systemctl start dnsmasq", check=True)
log.info("Access Point gestartet.")
def stop_ap():
log.info("Stoppe Access Point...")
run("systemctl stop hostapd")
run("systemctl stop dnsmasq")
run("ip addr flush dev wlan1 2>/dev/null || true")
log.info("Access Point gestoppt.")
def main():
log.info("Babycam Network State Machine gestartet.")
current_mode = None
while True:
connected = is_wlan0_connected()
ap_running = is_ap_running()
if connected:
if current_mode != "A":
log.info("Modus A: Heimnetz aktiv.")
if ap_running:
stop_ap()
current_mode = "A"
else:
if current_mode != "B":
log.info("Modus B: Kein WLAN starte Access Point.")
if not ap_running:
start_ap()
current_mode = "B"
time.sleep(CHECK_INTERVAL)
if __name__ == "__main__":
main()

158
setup.sh Normal file
View File

@ -0,0 +1,158 @@
#!/usr/bin/env bash
# ==============================================================================
# Babycam Setup Script
# Raspberry Pi 3 + Camera Module 3 NoIR
# Raspberry Pi OS Trixie (arm64)
# ==============================================================================
set -e
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
INSTALL_DIR="/opt/babycam"
AP_INTERFACE="wlan1"
AP_IP="192.168.50.1"
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
info() { echo -e "${GREEN}[INFO]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; }
[[ $EUID -ne 0 ]] && error "Bitte als root ausführen: sudo bash setup.sh"
# ------------------------------------------------------------------------------
# 1. Pakete installieren
# ------------------------------------------------------------------------------
info "Pakete installieren..."
apt-get update -q
apt-get install -y \
hostapd \
dnsmasq \
python3-flask \
rpicam-apps \
avahi-daemon \
network-manager \
iptables
# ------------------------------------------------------------------------------
# 2. Kamera-Interface aktivieren
# ------------------------------------------------------------------------------
info "Kamera-Interface aktivieren..."
if ! grep -q "^camera_auto_detect=1" /boot/firmware/config.txt 2>/dev/null; then
echo "camera_auto_detect=1" >> /boot/firmware/config.txt
fi
if ! grep -q "^start_x=1" /boot/firmware/config.txt 2>/dev/null; then
echo "start_x=1" >> /boot/firmware/config.txt
fi
# ------------------------------------------------------------------------------
# 3. Projektdateien installieren
# ------------------------------------------------------------------------------
info "Projektdateien nach $INSTALL_DIR kopieren..."
mkdir -p "$INSTALL_DIR"
cp -r "$REPO_DIR/app" "$INSTALL_DIR/"
cp -r "$REPO_DIR/scripts" "$INSTALL_DIR/"
cp -r "$REPO_DIR/config" "$INSTALL_DIR/"
chmod +x "$INSTALL_DIR/scripts/network_state.py"
# ------------------------------------------------------------------------------
# 4. hostapd konfigurieren
# ------------------------------------------------------------------------------
info "hostapd konfigurieren..."
cp "$REPO_DIR/config/hostapd.conf" /etc/hostapd/hostapd.conf
sed -i 's|#DAEMON_CONF=.*|DAEMON_CONF="/etc/hostapd/hostapd.conf"|' /etc/default/hostapd
# hostapd nicht automatisch beim Boot starten (State Machine übernimmt das)
systemctl unmask hostapd
systemctl disable hostapd
# ------------------------------------------------------------------------------
# 5. dnsmasq konfigurieren
# ------------------------------------------------------------------------------
info "dnsmasq konfigurieren..."
# Originale Config sichern
if [ -f /etc/dnsmasq.conf ] && [ ! -f /etc/dnsmasq.conf.orig ]; then
mv /etc/dnsmasq.conf /etc/dnsmasq.conf.orig
fi
cp "$REPO_DIR/config/dnsmasq.conf" /etc/dnsmasq.conf
# dnsmasq nicht automatisch starten (State Machine übernimmt)
systemctl disable dnsmasq
# systemd-resolved: DNS-Stub deaktivieren damit Port 53 frei ist
if [ -f /etc/systemd/resolved.conf ]; then
sed -i 's|#DNSStubListener=yes|DNSStubListener=no|' /etc/systemd/resolved.conf
sed -i 's|DNSStubListener=yes|DNSStubListener=no|' /etc/systemd/resolved.conf
if ! grep -q "DNSStubListener=no" /etc/systemd/resolved.conf; then
echo "DNSStubListener=no" >> /etc/systemd/resolved.conf
fi
systemctl restart systemd-resolved
fi
# ------------------------------------------------------------------------------
# 6. NetworkManager: wlan1 als unmanaged markieren
# ------------------------------------------------------------------------------
info "wlan1 aus NetworkManager-Verwaltung nehmen..."
mkdir -p /etc/NetworkManager/conf.d
cat > /etc/NetworkManager/conf.d/99-unmanaged.conf << EOF
[keyfile]
unmanaged-devices=interface-name:wlan1
EOF
systemctl reload NetworkManager || systemctl restart NetworkManager
# ------------------------------------------------------------------------------
# 7. IP-Forwarding aktivieren (für Internet-Sharing AP → Heimnetz)
# ------------------------------------------------------------------------------
info "IP-Forwarding aktivieren..."
if ! grep -q "^net.ipv4.ip_forward=1" /etc/sysctl.conf; then
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
fi
sysctl -w net.ipv4.ip_forward=1 > /dev/null
# iptables NAT-Regel (wlan0 → wlan1 Sharing)
iptables -t nat -C POSTROUTING -o wlan0 -j MASQUERADE 2>/dev/null || \
iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
# Regel persistent speichern
apt-get install -y iptables-persistent -q || true
netfilter-persistent save 2>/dev/null || true
# ------------------------------------------------------------------------------
# 8. Systemd Services installieren
# ------------------------------------------------------------------------------
info "Systemd Services installieren..."
cp "$REPO_DIR/systemd/babycam-web.service" /etc/systemd/system/
cp "$REPO_DIR/systemd/babycam-network.service" /etc/systemd/system/
systemctl daemon-reload
systemctl enable babycam-web.service
systemctl enable babycam-network.service
# ------------------------------------------------------------------------------
# 9. Flask braucht Port 80 → als root oder via authbind
# ------------------------------------------------------------------------------
info "Flask Port 80 erlauben..."
apt-get install -y authbind -q
touch /etc/authbind/byport/80
chmod 500 /etc/authbind/byport/80
chown pi /etc/authbind/byport/80
# Service anpassen um authbind zu nutzen
sed -i 's|ExecStart=/usr/bin/python3|ExecStart=/usr/bin/authbind --deep /usr/bin/python3|' \
/etc/systemd/system/babycam-web.service
systemctl daemon-reload
# ------------------------------------------------------------------------------
# Fertig
# ------------------------------------------------------------------------------
echo ""
echo -e "${GREEN}========================================${NC}"
echo -e "${GREEN} Babycam Setup abgeschlossen!${NC}"
echo -e "${GREEN}========================================${NC}"
echo ""
echo " Webinterface: http://babycam.local"
echo " AP-SSID: BabyCam"
echo " AP-Passwort: babycam123"
echo " AP-IP: 192.168.50.1"
echo ""
warn "Kamera-Ribbon-Kabel verbunden? Kamera wird nach Neustart erkannt."
echo ""
info "Neustart in 5 Sekunden... (Ctrl+C zum Abbrechen)"
sleep 5
reboot

View File

@ -0,0 +1,16 @@
[Unit]
Description=Babycam Network State Machine
After=network.target NetworkManager.service
Wants=network.target
[Service]
ExecStart=/usr/bin/python3 /opt/babycam/scripts/network_state.py
WorkingDirectory=/opt/babycam
Restart=always
RestartSec=10
User=root
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,16 @@
[Unit]
Description=Babycam Webinterface
After=network.target
Wants=network.target
[Service]
ExecStart=/usr/bin/python3 /opt/babycam/app/main.py
WorkingDirectory=/opt/babycam
Restart=always
RestartSec=5
User=pi
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target