Compare commits

...

14 Commits
0.1.1 ... 0.3.0

Author SHA1 Message Date
5e0159047c separation of ui and static
Some checks failed
ci/woodpecker/tag/build/5 Pipeline failed
ci/woodpecker/tag/predeploy Pipeline was successful
ci/woodpecker/tag/deploy/2 unknown status
ci/woodpecker/tag/deploy/1 unknown status
ci/woodpecker/tag/build/2 Pipeline failed
ci/woodpecker/tag/deploy/4 unknown status
ci/woodpecker/tag/deploy/3 unknown status
ci/woodpecker/tag/build/3 Pipeline failed
ci/woodpecker/tag/deploy/5 unknown status
ci/woodpecker/tag/ingress unknown status
ci/woodpecker/tag/build/1 Pipeline failed
ci/woodpecker/tag/build/4 Pipeline failed
2025-12-01 14:00:48 +01:00
b23b624a86 homekit bridge name 2025-12-01 12:45:01 +01:00
9c099e44af drop homekit bridge build script 2025-12-01 11:06:49 +01:00
9c17a73605 build homekit-bridge image 2 2025-12-01 10:54:57 +01:00
a389edcd87 build homekit-bridge image 2025-12-01 10:53:33 +01:00
17c9bca8d1 forgotten file 2
All checks were successful
ci/woodpecker/tag/predeploy Pipeline was successful
ci/woodpecker/tag/build/4 Pipeline was successful
ci/woodpecker/tag/build/1 Pipeline was successful
ci/woodpecker/tag/build/3 Pipeline was successful
ci/woodpecker/tag/build/2 Pipeline was successful
ci/woodpecker/tag/deploy/4 Pipeline was successful
ci/woodpecker/tag/deploy/3 Pipeline was successful
ci/woodpecker/tag/deploy/2 Pipeline was successful
ci/woodpecker/tag/deploy/1 Pipeline was successful
ci/woodpecker/tag/ingress Pipeline was successful
2025-12-01 10:43:33 +01:00
c4fc21d760 forgotten file 2025-12-01 10:42:58 +01:00
e902d221ea fix configMap
All checks were successful
ci/woodpecker/tag/predeploy Pipeline was successful
ci/woodpecker/tag/build/1 Pipeline was successful
ci/woodpecker/tag/build/4 Pipeline was successful
ci/woodpecker/tag/build/2 Pipeline was successful
ci/woodpecker/tag/build/3 Pipeline was successful
ci/woodpecker/tag/deploy/3 Pipeline was successful
ci/woodpecker/tag/deploy/1 Pipeline was successful
ci/woodpecker/tag/deploy/2 Pipeline was successful
ci/woodpecker/tag/deploy/4 Pipeline was successful
2025-12-01 10:39:16 +01:00
e19bffc90c ci fix
All checks were successful
ci/woodpecker/tag/predeploy Pipeline was successful
ci/woodpecker/tag/build/4 Pipeline was successful
ci/woodpecker/tag/build/1 Pipeline was successful
ci/woodpecker/tag/build/3 Pipeline was successful
ci/woodpecker/tag/build/2 Pipeline was successful
ci/woodpecker/tag/deploy/1 Pipeline was successful
ci/woodpecker/tag/deploy/3 Pipeline was successful
ci/woodpecker/tag/deploy/2 Pipeline was successful
ci/woodpecker/tag/deploy/4 Pipeline was successful
2025-12-01 10:21:52 +01:00
5a13183123 homekit
All checks were successful
ci/woodpecker/push/build/3 Pipeline was successful
ci/woodpecker/push/build/1 Pipeline was successful
ci/woodpecker/push/predeploy Pipeline was successful
ci/woodpecker/push/build/2 Pipeline was successful
ci/woodpecker/push/build/4 Pipeline was successful
ci/woodpecker/push/deploy/3 Pipeline was successful
ci/woodpecker/push/deploy/1 Pipeline was successful
ci/woodpecker/push/deploy/2 Pipeline was successful
ci/woodpecker/push/deploy/4 Pipeline was successful
2025-11-30 21:56:52 +01:00
deb26c4945 homekit dockerfile
All checks were successful
ci/woodpecker/push/build/2 Pipeline was successful
ci/woodpecker/push/build/4 Pipeline was successful
ci/woodpecker/push/predeploy Pipeline was successful
ci/woodpecker/push/build/3 Pipeline was successful
ci/woodpecker/push/build/1 Pipeline was successful
ci/woodpecker/push/deploy/4 Pipeline was successful
ci/woodpecker/push/deploy/1 Pipeline was successful
ci/woodpecker/push/deploy/3 Pipeline was successful
ci/woodpecker/push/deploy/2 Pipeline was successful
2025-11-30 20:15:34 +01:00
c0e3ac1fe0 icons 3
All checks were successful
ci/woodpecker/push/build/3 Pipeline was successful
ci/woodpecker/push/build/1 Pipeline was successful
ci/woodpecker/push/build/4 Pipeline was successful
ci/woodpecker/push/build/2 Pipeline was successful
ci/woodpecker/push/predeploy Pipeline was successful
ci/woodpecker/push/deploy/1 Pipeline was successful
ci/woodpecker/push/deploy/2 Pipeline was successful
ci/woodpecker/tag/predeploy Pipeline was successful
ci/woodpecker/push/deploy/3 Pipeline was successful
ci/woodpecker/push/deploy/4 Pipeline was successful
ci/woodpecker/tag/build/4 Pipeline was successful
ci/woodpecker/tag/build/1 Pipeline was successful
ci/woodpecker/tag/build/3 Pipeline was successful
ci/woodpecker/tag/build/2 Pipeline was successful
ci/woodpecker/tag/deploy/2 Pipeline was successful
ci/woodpecker/tag/deploy/4 Pipeline was successful
ci/woodpecker/tag/deploy/1 Pipeline was successful
ci/woodpecker/tag/deploy/3 Pipeline was successful
2025-11-30 18:17:47 +01:00
370c16eb42 icons 2
All checks were successful
ci/woodpecker/push/deploy/3 Pipeline was successful
ci/woodpecker/push/build/2 Pipeline was successful
ci/woodpecker/push/predeploy Pipeline was successful
ci/woodpecker/push/build/3 Pipeline was successful
ci/woodpecker/push/build/4 Pipeline was successful
ci/woodpecker/push/build/1 Pipeline was successful
ci/woodpecker/push/deploy/1 Pipeline was successful
ci/woodpecker/tag/predeploy Pipeline was successful
ci/woodpecker/push/deploy/2 Pipeline was successful
ci/woodpecker/push/deploy/4 Pipeline was successful
ci/woodpecker/tag/build/1 Pipeline was successful
ci/woodpecker/tag/build/2 Pipeline was successful
ci/woodpecker/tag/build/4 Pipeline was successful
ci/woodpecker/tag/build/3 Pipeline was successful
ci/woodpecker/tag/deploy/3 Pipeline was successful
ci/woodpecker/tag/deploy/4 Pipeline was successful
ci/woodpecker/tag/deploy/2 Pipeline was successful
ci/woodpecker/tag/deploy/1 Pipeline was successful
2025-11-30 18:10:55 +01:00
fd1d5c4f31 icons
All checks were successful
ci/woodpecker/push/predeploy Pipeline was successful
ci/woodpecker/push/build/4 Pipeline was successful
ci/woodpecker/push/build/3 Pipeline was successful
ci/woodpecker/push/build/1 Pipeline was successful
ci/woodpecker/push/build/2 Pipeline was successful
ci/woodpecker/push/deploy/4 Pipeline was successful
ci/woodpecker/push/deploy/1 Pipeline was successful
ci/woodpecker/push/deploy/3 Pipeline was successful
ci/woodpecker/push/deploy/2 Pipeline was successful
ci/woodpecker/tag/predeploy Pipeline was successful
ci/woodpecker/tag/build/4 Pipeline was successful
ci/woodpecker/tag/build/1 Pipeline was successful
ci/woodpecker/tag/build/3 Pipeline was successful
ci/woodpecker/tag/build/2 Pipeline was successful
ci/woodpecker/tag/deploy/4 Pipeline was successful
ci/woodpecker/tag/deploy/1 Pipeline was successful
ci/woodpecker/tag/deploy/3 Pipeline was successful
ci/woodpecker/tag/deploy/2 Pipeline was successful
2025-11-30 18:01:12 +01:00
60 changed files with 642 additions and 54 deletions

View File

@@ -1,9 +1,13 @@
when:
event: [tag]
matrix:
APP:
- ui
- api
- abstraction
- rules
- static
steps:
build-${APP}:

View File

@@ -1,9 +1,17 @@
when:
event: [tag]
depends_on:
- build
- predeploy
matrix:
APP:
- ui
- api
- abstraction
- rules
- static
steps:
deploy-${APP}:
@@ -24,6 +32,3 @@ steps:
exclude:
- refs/tags/*-configchange
depends_on:
- build
- predeploy

23
.woodpecker/ingress.yml Normal file
View File

@@ -0,0 +1,23 @@
when:
event: [tag]
depends_on:
- deploy
steps:
apply_ingress:
image: quay.io/wollud1969/k8s-admin-helper:0.3.4
environment:
KUBE_CONFIG_CONTENT:
from_secret: kube_config
NAMESPACE: "homea2"
commands:
- printf "$KUBE_CONFIG_CONTENT" > /tmp/kubeconfig
- export KUBECONFIG=/tmp/kubeconfig
- kubectl apply -f deployment/ingress.yaml -n $NAMESPACE
when:
event: [tag]
ref:
exclude:
- refs/tags/*-configchange

View File

@@ -1,3 +1,6 @@
when:
event: [tag]
steps:
create_namespace:
image: quay.io/wollud1969/k8s-admin-helper:0.3.4

View File

@@ -10,7 +10,9 @@ ENV PYTHONDONTWRITEBYTECODE=1 \
MQTT_PORT=1883 \
REDIS_HOST=localhost \
REDIS_PORT=6379 \
REDIS_DB=0
REDIS_DB=0 \
REDIS_CHANNEL=ui:updates
# Create non-root user
RUN addgroup -g 10001 -S app && \

30
apps/homekit/Dockerfile Normal file
View File

@@ -0,0 +1,30 @@
FROM python:3.12-slim
# Environment defaults (can be overridden at runtime)
ENV PYTHONUNBUFFERED=1 \
HOMEKIT_NAME="Home Automation Bridge" \
HOMEKIT_PIN="031-45-154" \
HOMEKIT_PORT="51826" \
API_BASE="http://api:8001" \
HOMEKIT_API_TOKEN="" \
HOMEKIT_PERSIST_FILE="/data/homekit.state"
WORKDIR /app
# Copy only requirements first for better build caching
COPY apps/homekit/requirements.txt ./apps/homekit/requirements.txt
RUN pip install --no-cache-dir --upgrade pip \
&& pip install --no-cache-dir -r apps/homekit/requirements.txt
# Copy full source tree
COPY . /app
# Expose HomeKit TCP port (mDNS uses UDP 5353 via host network)
EXPOSE 51826/tcp
# Volume for persistent HomeKit state (pairings etc.)
VOLUME ["/data"]
# Start the HomeKit bridge
CMD ["python", "-m", "apps.homekit.main"]

View File

@@ -0,0 +1,28 @@
services:
homekit-bridge:
build:
context: ../..
dockerfile: apps/homekit/Dockerfile
container_name: homekit-bridge
# Required for mDNS/Bonjour to work properly
network_mode: host
environment:
- HOMEKIT_NAME=Hottis Home Automation Bridge
- HOMEKIT_PIN=031-45-154
- HOMEKIT_PORT=51826
- API_BASE=http://homea2-api-internal.hottis.de
- HOMEKIT_API_TOKEN=
- HOMEKIT_PERSIST_FILE=/data/homekit.state
volumes:
- homekit_data:/data
restart: unless-stopped
volumes:
homekit_data:
driver: local

View File

@@ -6,7 +6,7 @@ FROM python:3.14-alpine
# Prevent Python from writing .pyc files and enable unbuffered output
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
RULES_CONFIG=config/rules.yaml \
RULES_CONFIG=/app/config/rules.yaml \
MQTT_BROKER=172.16.2.16 \
MQTT_PORT=1883 \
REDIS_HOST=localhost \

15
apps/static/Dockerfile Normal file
View File

@@ -0,0 +1,15 @@
# Static assets Dockerfile (minimal webserver for /static only)
FROM nginx:1.27-alpine
WORKDIR /usr/share/nginx/html
# Remove default nginx content
RUN rm -rf ./*
# Copy only static assets from the UI project
COPY apps/ui/static/ ./
EXPOSE 80
# Use default nginx config; caller can mount custom config if needed

Binary file not shown.

After

Width:  |  Height:  |  Size: 618 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 827 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 884 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1018 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1018 B

View File

Before

Width:  |  Height:  |  Size: 244 B

After

Width:  |  Height:  |  Size: 244 B

View File

Before

Width:  |  Height:  |  Size: 721 B

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 519 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 547 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 641 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 695 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 808 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 808 B

View File

Before

Width:  |  Height:  |  Size: 244 B

After

Width:  |  Height:  |  Size: 244 B

View File

@@ -0,0 +1,43 @@
{
"name": "Home Automation",
"short_name": "Home",
"description": "Smart Home Automation System",
"start_url": "/",
"display": "standalone",
"background_color": "#667eea",
"theme_color": "#667eea",
"orientation": "portrait",
"icons": [
{
"src": "/static/apple-touch-icon-180x180.png",
"sizes": "180x180",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/static/apple-touch-icon-152x152.png",
"sizes": "152x152",
"type": "image/png"
},
{
"src": "/static/apple-touch-icon-120x120.png",
"sizes": "120x120",
"type": "image/png"
},
{
"src": "/static/apple-touch-icon-76x76.png",
"sizes": "76x76",
"type": "image/png"
},
{
"src": "/static/apple-touch-icon-32x32.png",
"sizes": "32x32",
"type": "image/png"
},
{
"src": "/static/apple-touch-icon-16x16.png",
"sizes": "16x16",
"type": "image/png"
}
]
}

View File

@@ -1,49 +1,41 @@
# UI Service Dockerfile
# FastAPI + Jinja2 + HTMX Dashboard
# UI Service Dockerfile (Application only, without static files)
FROM python:3.14-alpine
# Prevent Python from writing .pyc files and enable unbuffered output
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
UI_PORT=8002 \
API_BASE=http://api:8001 \
BASE_PATH=""
BASE_PATH="" \
STATIC_BASE=http://static:8080
# Create non-root user
RUN addgroup -g 10001 -S app && \
adduser -u 10001 -S app -G app
# Set working directory
WORKDIR /app
# Install system dependencies
RUN apk add --no-cache \
curl \
gcc \
musl-dev \
linux-headers
# Install Python dependencies
COPY apps/ui/requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
# Copy only Python code and templates, but exclude static assets
COPY apps/__init__.py /app/apps/__init__.py
COPY apps/ui/ /app/apps/ui/
COPY apps/ui/__init__.py /app/apps/ui/__init__.py
COPY apps/ui/main.py /app/apps/ui/main.py
COPY apps/ui/api_client.py /app/apps/ui/api_client.py
COPY apps/ui/templates/ /app/apps/ui/templates/
# Change ownership to app user
RUN chown -R app:app /app
# Switch to non-root user
USER app
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:${UI_PORT}/health || exit 1
# Expose port
EXPOSE 8002
# Run application
CMD ["python", "-m", "uvicorn", "apps.ui.main:app", "--host", "0.0.0.0", "--port", "8002"]

View File

@@ -5,7 +5,7 @@ import os
from pathlib import Path
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.responses import HTMLResponse, JSONResponse, FileResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
@@ -16,9 +16,11 @@ logger = logging.getLogger(__name__)
# Read configuration from environment variables
API_BASE = os.getenv("API_BASE", "http://localhost:8001")
BASE_PATH = os.getenv("BASE_PATH", "") # e.g., "/ui" for reverse proxy
STATIC_BASE = os.getenv("STATIC_BASE", "/static")
print(f"UI using API_BASE: {API_BASE}")
print(f"UI using BASE_PATH: {BASE_PATH}")
print(f"UI using STATIC_BASE: {STATIC_BASE}")
def api_url(path: str) -> str:
"""Helper function to construct API URLs.
@@ -43,12 +45,53 @@ app = FastAPI(
templates_dir = Path(__file__).parent / "templates"
templates = Jinja2Templates(directory=str(templates_dir))
# Make STATIC_BASE available in all templates
templates.env.globals["STATIC_BASE"] = STATIC_BASE
# Setup static files
static_dir = Path(__file__).parent / "static"
static_dir.mkdir(exist_ok=True)
app.mount("/static", StaticFiles(directory=str(static_dir)), name="static")
@app.get("/apple-touch-icon.png")
async def apple_touch_icon():
"""Serve Apple Touch Icon with proper headers."""
icon_path = static_dir / "apple-touch-icon.png"
return FileResponse(
path=icon_path,
media_type="image/png",
headers={
"Cache-Control": "public, max-age=31536000",
"Content-Type": "image/png"
}
)
@app.get("/favicon.ico")
async def favicon():
"""Serve favicon."""
icon_path = static_dir / "apple-touch-icon.png"
return FileResponse(
path=icon_path,
media_type="image/png"
)
@app.get("/manifest.json")
async def manifest():
"""Serve Web App Manifest with proper headers."""
manifest_path = static_dir / "manifest.json"
return FileResponse(
path=manifest_path,
media_type="application/manifest+json",
headers={
"Cache-Control": "public, max-age=86400",
"Content-Type": "application/manifest+json"
}
)
@app.get("/health")
async def health() -> JSONResponse:
"""Health check endpoint for Kubernetes/Docker.
@@ -60,7 +103,8 @@ async def health() -> JSONResponse:
"status": "ok",
"service": "ui",
"api_base": API_BASE,
"base_path": BASE_PATH
"base_path": BASE_PATH,
"static_base": STATIC_BASE,
})
@@ -89,7 +133,7 @@ async def rooms(request: Request) -> HTMLResponse:
"""
return templates.TemplateResponse("rooms.html", {
"request": request,
"api_base": API_BASE
"api_base": API_BASE,
})
@@ -107,7 +151,7 @@ async def room_detail(request: Request, room_name: str) -> HTMLResponse:
return templates.TemplateResponse("room.html", {
"request": request,
"api_base": API_BASE,
"room_name": room_name
"room_name": room_name,
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -4,7 +4,19 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Home Automation</title>
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
<!-- Apple Touch Icon -->
<link rel="apple-touch-icon" sizes="180x180" href="{{ STATIC_BASE }}/apple-touch-icon.png">
<link rel="apple-touch-icon" sizes="152x152" href="{{ STATIC_BASE }}/apple-touch-icon.png">
<link rel="apple-touch-icon" sizes="120x120" href="{{ STATIC_BASE }}/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="{{ STATIC_BASE }}/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="16x16" href="{{ STATIC_BASE }}/apple-touch-icon.png">
<link rel="icon" type="image/svg+xml" href="{{ STATIC_BASE }}/favicon.svg">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="Dashboard">
<meta name="theme-color" content="#667eea">
<link rel="manifest" href="{{ STATIC_BASE }}/manifest.json">
<style>
* {
margin: 0;

View File

@@ -4,6 +4,18 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Gerät - Home Automation</title>
<!-- Apple Touch Icon -->
<link rel="apple-touch-icon" sizes="180x180" href="{{ STATIC_BASE }}/apple-touch-icon.png">
<link rel="apple-touch-icon" sizes="152x152" href="{{ STATIC_BASE }}/apple-touch-icon.png">
<link rel="apple-touch-icon" sizes="120x120" href="{{ STATIC_BASE }}/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="{{ STATIC_BASE }}/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="16x16" href="{{ STATIC_BASE }}/apple-touch-icon.png">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="Gerät">
<meta name="theme-color" content="#667eea">
<link rel="manifest" href="{{ STATIC_BASE }}/manifest.json">
<style>
* {
margin: 0;
@@ -334,8 +346,8 @@
</script>
<!-- Load API client AFTER API_BASE is set -->
<script src="/static/types.js"></script>
<script src="/static/api-client.js"></script>
<script src="{{ STATIC_BASE }}/types.js"></script>
<script src="{{ STATIC_BASE }}/api-client.js"></script>
<script>
// Get device ID from URL

View File

@@ -6,11 +6,17 @@
<title>Garage - Home Automation</title>
<!-- Apple Touch Icon -->
<link rel="apple-touch-icon" sizes="180x180" href="/static/garage-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/static/garage-icon.png">
<link rel="apple-touch-icon" sizes="180x180" href="{{ STATIC_BASE }}/garage-icon-180x180.png">
<link rel="apple-touch-icon" sizes="152x152" href="{{ STATIC_BASE }}/garage-icon-152x152.png">
<link rel="apple-touch-icon" sizes="120x120" href="{{ STATIC_BASE }}/garage-icon-120x120.png">
<link rel="apple-touch-icon" sizes="76x76" href="{{ STATIC_BASE }}/garage-icon-76x76.png">
<link rel="icon" type="image/png" sizes="32x32" href="{{ STATIC_BASE }}/garage-icon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="{{ STATIC_BASE }}/garage-icon-16x16.png">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="Garage">
<meta name="theme-color" content="#667eea">
<link rel="manifest" href="{{ STATIC_BASE }}/manifest.json">
<style>
* {
margin: 0;
@@ -300,8 +306,8 @@
</script>
<!-- Load API client AFTER API_BASE is set -->
<script src="/static/types.js"></script>
<script src="/static/api-client.js"></script>
<script src="{{ STATIC_BASE }}/types.js"></script>
<script src="{{ STATIC_BASE }}/api-client.js"></script>
<script>
// Device IDs for garage devices

View File

@@ -4,6 +4,19 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Home Automation</title>
<!-- Apple Touch Icon -->
<link rel="apple-touch-icon" sizes="180x180" href="{{ STATIC_BASE }}/apple-touch-icon-180x180.png">
<link rel="apple-touch-icon" sizes="152x152" href="{{ STATIC_BASE }}/apple-touch-icon-152x152.png">
<link rel="apple-touch-icon" sizes="120x120" href="{{ STATIC_BASE }}/apple-touch-icon-120x120.png">
<link rel="apple-touch-icon" sizes="76x76" href="{{ STATIC_BASE }}/apple-touch-icon-76x76.png">
<link rel="icon" type="image/png" sizes="32x32" href="{{ STATIC_BASE }}/apple-touch-icon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="{{ STATIC_BASE }}/apple-touch-icon-16x16.png">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="Home Automation">
<meta name="theme-color" content="#667eea">
<link rel="manifest" href="{{ STATIC_BASE }}/manifest.json">
<style>
* {
margin: 0;

View File

@@ -4,6 +4,18 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ room_name }} - Home Automation</title>
<!-- Apple Touch Icon -->
<link rel="apple-touch-icon" sizes="180x180" href="{{ STATIC_BASE }}/apple-touch-icon.png">
<link rel="apple-touch-icon" sizes="152x152" href="{{ STATIC_BASE }}/apple-touch-icon.png">
<link rel="apple-touch-icon" sizes="120x120" href="{{ STATIC_BASE }}/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="{{ STATIC_BASE }}/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="16x16" href="{{ STATIC_BASE }}/apple-touch-icon.png">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="{{ room_name }}">
<meta name="theme-color" content="#667eea">
<link rel="manifest" href="{{ STATIC_BASE }}/manifest.json">
<style>
* {
margin: 0;
@@ -217,8 +229,8 @@
</script>
<!-- Load API client AFTER API_BASE is set -->
<script src="/static/types.js"></script>
<script src="/static/api-client.js"></script>
<script src="{{ STATIC_BASE }}/types.js"></script>
<script src="{{ STATIC_BASE }}/api-client.js"></script>
<script>
// Get room name from URL

View File

@@ -6,11 +6,17 @@
<title>Räume - Home Automation</title>
<!-- Apple Touch Icon -->
<link rel="apple-touch-icon" sizes="180x180" href="/static/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/static/apple-touch-icon.png">
<link rel="apple-touch-icon" sizes="180x180" href="{{ STATIC_BASE }}/apple-touch-icon-180x180.png">
<link rel="apple-touch-icon" sizes="152x152" href="{{ STATIC_BASE }}/apple-touch-icon-152x152.png">
<link rel="apple-touch-icon" sizes="120x120" href="{{ STATIC_BASE }}/apple-touch-icon-120x120.png">
<link rel="apple-touch-icon" sizes="76x76" href="{{ STATIC_BASE }}/apple-touch-icon-76x76.png">
<link rel="icon" type="image/png" sizes="32x32" href="{{ STATIC_BASE }}/apple-touch-icon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="{{ STATIC_BASE }}/apple-touch-icon-16x16.png">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="Home Automation">
<meta name="apple-mobile-web-app-title" content="Räume">
<meta name="theme-color" content="#667eea">
<link rel="manifest" href="{{ STATIC_BASE }}/manifest.json">
<style>
* {
margin: 0;
@@ -157,8 +163,8 @@
</script>
<!-- Load API client AFTER API_BASE is set -->
<script src="/static/types.js"></script>
<script src="/static/api-client.js"></script>
<script src="{{ STATIC_BASE }}/types.js"></script>
<script src="{{ STATIC_BASE }}/api-client.js"></script>
<script>
// Room icon mapping

30
create_icons.py Normal file
View File

@@ -0,0 +1,30 @@
"""
Script to create additional PNG icon sizes for better iOS compatibility
"""
import os
from pathlib import Path
from PIL import Image
def create_icon_sizes():
static_dir = Path("/Users/wn/Workspace/home-automation/apps/ui/static")
# Sizes that iOS might need
sizes = [16, 32, 57, 60, 72, 76, 114, 120, 144, 152, 180]
# Create home icons
base_icon = Image.open(static_dir / "apple-touch-icon.png")
for size in sizes:
resized = base_icon.resize((size, size), Image.Resampling.LANCZOS)
resized.save(static_dir / f"apple-touch-icon-{size}x{size}.png")
print(f"Created apple-touch-icon-{size}x{size}.png")
# Create garage icons
garage_icon = Image.open(static_dir / "garage-icon.png")
for size in sizes:
resized = garage_icon.resize((size, size), Image.Resampling.LANCZOS)
resized.save(static_dir / f"garage-icon-{size}x{size}.png")
print(f"Created garage-icon-{size}x{size}.png")
if __name__ == "__main__":
create_icon_sizes()

118
create_proper_icons.py Normal file
View File

@@ -0,0 +1,118 @@
"""
Script to create proper PNG icons with house and car symbols
"""
import os
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont
def create_proper_icons():
static_dir = Path("/Users/wn/Workspace/home-automation/apps/ui/static")
# Create home icon with house symbol
def create_home_icon(size):
img = Image.new('RGBA', (size, size), color=(102, 126, 234, 255)) # #667EEA
draw = ImageDraw.Draw(img)
# Calculate proportions
margin = size // 10
house_size = size - 2 * margin
# Draw house shape
# Base rectangle
base_height = house_size // 2
base_y = size - margin - base_height
draw.rectangle([margin, base_y, size - margin, size - margin], fill='white')
# Roof triangle
roof_height = house_size // 3
roof_points = [
(size // 2, margin), # top point
(margin, base_y), # bottom left
(size - margin, base_y) # bottom right
]
draw.polygon(roof_points, fill='white')
# Door
door_width = house_size // 6
door_height = base_height // 2
door_x = size // 2 - door_width // 2
door_y = size - margin - door_height
draw.rectangle([door_x, door_y, door_x + door_width, size - margin], fill=(102, 126, 234, 255))
# Window
window_size = house_size // 8
window_x = margin + house_size // 4
window_y = base_y + base_height // 4
draw.rectangle([window_x, window_y, window_x + window_size, window_y + window_size], fill=(102, 126, 234, 255))
return img
# Create car icon with car symbol
def create_car_icon(size):
img = Image.new('RGBA', (size, size), color=(102, 126, 234, 255)) # #667EEA
draw = ImageDraw.Draw(img)
# Calculate proportions
margin = size // 8
car_width = size - 2 * margin
car_height = car_width // 2
car_y = size // 2 - car_height // 2
# Draw car body
draw.rounded_rectangle([margin, car_y, size - margin, car_y + car_height],
radius=size//20, fill='white')
# Draw car roof
roof_margin = car_width // 4
roof_height = car_height // 2
roof_y = car_y - roof_height // 2
draw.rounded_rectangle([margin + roof_margin, roof_y,
size - margin - roof_margin, car_y + roof_height // 2],
radius=size//30, fill='white')
# Draw wheels
wheel_radius = car_height // 4
wheel_y = car_y + car_height - wheel_radius // 2
# Left wheel
left_wheel_x = margin + car_width // 4
draw.ellipse([left_wheel_x - wheel_radius, wheel_y - wheel_radius,
left_wheel_x + wheel_radius, wheel_y + wheel_radius],
fill=(102, 126, 234, 255))
# Right wheel
right_wheel_x = size - margin - car_width // 4
draw.ellipse([right_wheel_x - wheel_radius, wheel_y - wheel_radius,
right_wheel_x + wheel_radius, wheel_y + wheel_radius],
fill=(102, 126, 234, 255))
return img
# Sizes to create
sizes = [16, 32, 57, 60, 72, 76, 114, 120, 144, 152, 180]
# Create home icons
for size in sizes:
home_icon = create_home_icon(size)
home_icon.save(static_dir / f"apple-touch-icon-{size}x{size}.png")
print(f"Created apple-touch-icon-{size}x{size}.png")
# Also create the main apple-touch-icon.png
main_icon = create_home_icon(180)
main_icon.save(static_dir / "apple-touch-icon.png")
print("Created apple-touch-icon.png")
# Create garage icons
for size in sizes:
car_icon = create_car_icon(size)
car_icon.save(static_dir / f"garage-icon-{size}x{size}.png")
print(f"Created garage-icon-{size}x{size}.png")
# Also create the main garage-icon.png
main_garage = create_car_icon(180)
main_garage.save(static_dir / "garage-icon.png")
print("Created garage-icon.png")
if __name__ == "__main__":
create_proper_icons()

View File

@@ -52,11 +52,6 @@ spec:
configMapKeyRef:
name: home-automation-environment
key: SHARED_REDIS_DB
- name: REDIS_CHANNEL
valueFrom:
configMapKeyRef:
name: home-automation-environment
key: API_REDIS_CHANNEL
volumeMounts:
- name: config-volume
mountPath: /app/config

View File

@@ -14,10 +14,9 @@ data:
# UI specific environment variables
UI_UI_PORT: "8002"
UI_API_BASE: "https://homea2-api.hottis.de"
UI_STATIC_BASE: "http://homea2-static.hottis.de"
UI_BASE_PATH: "/"
# API specific environment variables
API_REDIS_CHANNEL: "ui:updates"
# Rules specific environment variables
RULES_RULES_CONFIG: "/app/config/rules.yaml"

View File

@@ -11,6 +11,7 @@ spec:
dnsNames:
- homea2.hottis.de
- homea2-api.hottis.de
- homea2-static.hottis.de
---
apiVersion: traefik.containo.us/v1alpha1
kind: TLSOption
@@ -59,4 +60,37 @@ spec:
services:
- name: api
port: 80
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: static
spec:
entryPoints:
- websecure
tls:
secretName: homea2-cert
routes:
- match: Host(`homea2-static.hottis.de`)
kind: Rule
services:
- name: static
port: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-internal
spec:
ingressClassName: traefik-internal
rules:
- host: homea2-api-internal.hottis.de
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api
port:
number: 80

View File

@@ -48,11 +48,6 @@ spec:
configMapKeyRef:
name: home-automation-environment
key: SHARED_REDIS_DB
- name: RULES_CONFIG
valueFrom:
configMapKeyRef:
name: home-automation-environment
key: RULES_RULES_CONFIG
volumeMounts:
- name: config-volume
mountPath: /app/config

View File

@@ -0,0 +1,61 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: static
namespace: homea2
labels:
app: static
component: home-automation
spec:
replicas: 1
selector:
matchLabels:
app: static
template:
metadata:
labels:
app: static
component: home-automation
spec:
containers:
- name: static
image: %IMAGE%
ports:
- containerPort: 80
name: http
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 10
periodSeconds: 30
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 10
resources:
limits:
cpu: 200m
memory: 128Mi
requests:
cpu: 50m
memory: 64Mi
---
apiVersion: v1
kind: Service
metadata:
name: static
namespace: homea2
labels:
app: static
component: home-automation
spec:
selector:
app: static
ports:
- port: 80
targetPort: 80
name: http
type: ClusterIP

77
icon-test.html Normal file
View File

@@ -0,0 +1,77 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Icon Test</title>
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
background: #f5f5f5;
}
.icon-container {
display: flex;
gap: 20px;
margin: 20px 0;
}
.icon-test {
background: white;
border-radius: 8px;
padding: 20px;
text-align: center;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.icon-test img {
display: block;
margin: 10px auto;
border-radius: 8px;
}
.icon-sizes {
display: flex;
gap: 10px;
flex-wrap: wrap;
justify-content: center;
margin-top: 10px;
}
</style>
</head>
<body>
<h1>Apple Touch Icon Test</h1>
<div class="icon-container">
<div class="icon-test">
<h3>Home Icon</h3>
<img src="apps/ui/static/apple-touch-icon.png" alt="Home Icon" width="120" height="120">
<p>Haupticon für die Home Automation App</p>
<div class="icon-sizes">
<img src="apps/ui/static/apple-touch-icon-76x76.png" alt="Home 76px" width="76" height="76">
<img src="apps/ui/static/apple-touch-icon-60x60.png" alt="Home 60px" width="60" height="60">
<img src="apps/ui/static/apple-touch-icon-32x32.png" alt="Home 32px" width="32" height="32">
</div>
</div>
<div class="icon-test">
<h3>Garage Icon</h3>
<img src="apps/ui/static/garage-icon.png" alt="Garage Icon" width="120" height="120">
<p>Icon für die Garage-Seite</p>
<div class="icon-sizes">
<img src="apps/ui/static/garage-icon-76x76.png" alt="Garage 76px" width="76" height="76">
<img src="apps/ui/static/garage-icon-60x60.png" alt="Garage 60px" width="60" height="60">
<img src="apps/ui/static/garage-icon-32x32.png" alt="Garage 32px" width="32" height="32">
</div>
</div>
</div>
<h2>iPhone Homescreen Test</h2>
<p>Um die Icons auf dem iPhone zu testen:</p>
<ol>
<li>Öffnen Sie Ihre Home Automation App im Safari</li>
<li>Tippen Sie auf das Teilen-Symbol</li>
<li>Wählen Sie "Zum Home-Bildschirm hinzufügen"</li>
<li>Das Icon sollte jetzt als Haus-Symbol erscheinen</li>
</ol>
<p><strong>Hinweis:</strong> Falls das alte Icon noch angezeigt wird, löschen Sie die bestehende App vom Homescreen und fügen Sie sie neu hinzu.</p>
</body>
</html>

29
test-icons.sh Executable file
View File

@@ -0,0 +1,29 @@
#!/bin/bash
# Script to test icon accessibility over HTTPS/mTLS
echo "Testing Apple Touch Icon accessibility..."
# Test base icons
echo "1. Testing main apple-touch-icon.png:"
curl -I "https://your-domain.com/static/apple-touch-icon-180x180.png" || echo "FAILED"
echo "2. Testing garage icon:"
curl -I "https://your-domain.com/static/garage-icon-180x180.png" || echo "FAILED"
echo "3. Testing manifest:"
curl -I "https://your-domain.com/manifest.json" || echo "FAILED"
echo "4. Testing favicon route:"
curl -I "https://your-domain.com/favicon.ico" || echo "FAILED"
echo "5. Testing apple-touch-icon route:"
curl -I "https://your-domain.com/apple-touch-icon.png" || echo "FAILED"
echo ""
echo "Testing mTLS with client certificate:"
echo "6. Testing with client cert:"
curl -I --cert client.crt --key client.key "https://your-domain.com/static/apple-touch-icon-180x180.png" || echo "FAILED"
echo ""
echo "Note: Replace 'your-domain.com' with your actual domain"
echo "Note: Use actual client certificate files if testing mTLS"