Compare commits
100 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
c0e3ac1fe0
|
|||
|
370c16eb42
|
|||
|
fd1d5c4f31
|
|||
|
51072424ed
|
|||
|
722f4f0a8c
|
|||
|
0acabc737e
|
|||
|
34b0cdef69
|
|||
|
68ca51a242
|
|||
|
6d0f38965d
|
|||
|
1078e4cd53
|
|||
|
0c2f3f2e83
|
|||
|
418f813e80
|
|||
|
2b2fd92923
|
|||
|
8fa81be750
|
|||
|
205baa7e01
|
|||
|
f3f9238d5f
|
|||
|
5decf79bee
|
|||
|
be2654ac98
|
|||
|
bb27296310
|
|||
|
63857671f9
|
|||
|
d008c9fd5a
|
|||
|
1eb0f84659
|
|||
|
51df63d9f2
|
|||
|
cdaa5deb58
|
|||
|
91ef285a6c
|
|||
|
9afa68a111
|
|||
|
1119bb529f
|
|||
|
26286ce194
|
|||
|
7913a0044d
|
|||
|
871d0dc890
|
|||
|
7409995780
|
|||
|
9d4f3ac560
|
|||
|
bbbd01fbac
|
|||
|
61134f8bfa
|
|||
|
b12bbc1eb0
|
|||
|
8425dda177
|
|||
|
eddcd20d19
|
|||
|
28bbff16aa
|
|||
|
02fe11754c
|
|||
|
59b2c566ad
|
|||
|
42d7aae10c
|
|||
|
83ab36884b
|
|||
|
4d6e1a9ffe
|
|||
|
1ad7df5c73
|
|||
|
927d13191d
|
|||
|
0a0edd2b5b
|
|||
|
5ddf9bbc53
|
|||
|
5a8fa5ff46
|
|||
|
d7d06718ec
|
|||
|
a92ee40224
|
|||
|
8226fb5aca
|
|||
|
426f63124b
|
|||
|
9010e9587f
|
|||
|
69b2742f2a
|
|||
|
e409e5fdd1
|
|||
|
5c97bb3c1e
|
|||
|
b4e0fc8ddd
|
|||
|
86409b26f0
|
|||
|
d9139e2693
|
|||
|
740ac6c9ad
|
|||
|
fec97e54c1
|
|||
|
743e84560d
|
|||
|
f25ab6a3a1
|
|||
|
b08a3f2564
|
|||
|
db43854156
|
|||
|
3d759bd3ff
|
|||
|
7193c2be7f
|
|||
|
02596f4796
|
|||
|
e316ec0f58
|
|||
|
18481d9970
|
|||
|
84fe6eea96
|
|||
|
84e401778e
|
|||
|
4ee3c13d3e
|
|||
|
d685366c09
|
|||
|
07b28e2f1f
|
|||
|
39bfb66098
|
|||
|
75860cd1c2
|
|||
|
bcbb58ea36
|
|||
|
b38ed75261
|
|||
|
feb055b2ea
|
|||
|
cce730b2fa
|
|||
|
a26901037d
|
|||
|
4889f5ed8b
|
|||
|
804e9bf742
|
|||
|
f60d5d03e9
|
|||
|
eff88e1d2f
|
|||
|
d027163087
|
|||
|
4051ca22a4
|
|||
|
2608e935b8
|
|||
|
51f3b4f227
|
|||
|
006359687f
|
|||
|
f26d304890
|
|||
|
6feec48ac6
|
|||
|
ed6ed66a37
|
|||
|
09498dd0e5
|
|||
|
41f5e06e30
|
|||
|
7769c6066a
|
|||
|
5f23e28cc0
|
|||
|
cc083c1055
|
|||
|
37b773143f
|
4
.gitignore
vendored
@@ -64,3 +64,7 @@ poetry.lock
|
|||||||
|
|
||||||
apps/homekit/homekit.state
|
apps/homekit/homekit.state
|
||||||
|
|
||||||
|
tools/ca/
|
||||||
|
tools/clients/
|
||||||
|
tools/certificates/
|
||||||
|
tools/certificates.tgz
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
steps:
|
|
||||||
build_ui:
|
|
||||||
image: plugins/kaniko
|
|
||||||
settings:
|
|
||||||
repo: ${FORGE_NAME}/${CI_REPO}-ui
|
|
||||||
registry:
|
|
||||||
from_secret: container_registry
|
|
||||||
tags: latest,${CI_COMMIT_TAG}
|
|
||||||
username:
|
|
||||||
from_secret: container_registry_username
|
|
||||||
password:
|
|
||||||
from_secret: container_registry_password
|
|
||||||
dockerfile: apps/ui/Dockerfile
|
|
||||||
when:
|
|
||||||
- event: [tag]
|
|
||||||
25
.woodpecker/build.yml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
matrix:
|
||||||
|
APP:
|
||||||
|
- ui
|
||||||
|
- api
|
||||||
|
- abstraction
|
||||||
|
- rules
|
||||||
|
|
||||||
|
steps:
|
||||||
|
build-${APP}:
|
||||||
|
image: plugins/kaniko
|
||||||
|
settings:
|
||||||
|
registry:
|
||||||
|
from_secret: local_registry
|
||||||
|
username:
|
||||||
|
from_secret: local_username
|
||||||
|
password:
|
||||||
|
from_secret: local_password
|
||||||
|
repo: ${FORGE_NAME}/${CI_REPO}/${APP}
|
||||||
|
auto_tag: true
|
||||||
|
dockerfile: apps/${APP}/Dockerfile
|
||||||
|
when:
|
||||||
|
event: [tag]
|
||||||
|
ref:
|
||||||
|
exclude:
|
||||||
|
- refs/tags/*-configchange
|
||||||
29
.woodpecker/deploy.yml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
matrix:
|
||||||
|
APP:
|
||||||
|
- ui
|
||||||
|
- api
|
||||||
|
- abstraction
|
||||||
|
- rules
|
||||||
|
|
||||||
|
steps:
|
||||||
|
deploy-${APP}:
|
||||||
|
image: quay.io/wollud1969/k8s-admin-helper:0.3.4
|
||||||
|
environment:
|
||||||
|
KUBE_CONFIG_CONTENT:
|
||||||
|
from_secret: kube_config
|
||||||
|
NAMESPACE: "homea2"
|
||||||
|
IMAGE: "${FORGE_NAME}/${CI_REPO}/${APP}:${CI_COMMIT_TAG}"
|
||||||
|
commands:
|
||||||
|
- printf "$KUBE_CONFIG_CONTENT" > /tmp/kubeconfig
|
||||||
|
- export KUBECONFIG=/tmp/kubeconfig
|
||||||
|
- echo "Deploying application ${APP} ($IMAGE) to namespace $NAMESPACE"
|
||||||
|
- cat deployment/${APP}-deployment.yaml | sed "s,%IMAGE%,$IMAGE,g" | kubectl apply -n $NAMESPACE -f -
|
||||||
|
when:
|
||||||
|
event: [tag]
|
||||||
|
ref:
|
||||||
|
exclude:
|
||||||
|
- refs/tags/*-configchange
|
||||||
|
|
||||||
|
depends_on:
|
||||||
|
- build
|
||||||
|
- predeploy
|
||||||
38
.woodpecker/predeploy.yml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
steps:
|
||||||
|
create_namespace:
|
||||||
|
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 create namespace $NAMESPACE || echo "Namespace $NAMESPACE already exists"
|
||||||
|
when:
|
||||||
|
event: [tag]
|
||||||
|
ref:
|
||||||
|
exclude:
|
||||||
|
- refs/tags/*-configchange
|
||||||
|
|
||||||
|
apply_configuration:
|
||||||
|
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 create configmap home-automation-config
|
||||||
|
--from-file=devices.yaml=config/devices.yaml
|
||||||
|
--from-file=groups.yaml=config/groups.yaml
|
||||||
|
--from-file=layout.yaml=config/layout.yaml
|
||||||
|
--from-file=rules.yaml=config/rules.yaml
|
||||||
|
--from-file=scenes.yaml=config/scenes.yaml
|
||||||
|
--namespace=$NAMESPACE
|
||||||
|
--dry-run=client -o yaml | kubectl apply -f -
|
||||||
|
- kubectl apply -f deployment/configmap.yaml -n $NAMESPACE
|
||||||
|
when:
|
||||||
|
event: [tag]
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ import uuid
|
|||||||
from aiomqtt import Client
|
from aiomqtt import Client
|
||||||
from pydantic import ValidationError
|
from pydantic import ValidationError
|
||||||
|
|
||||||
from packages.home_capabilities import LightState, ThermostatState, ContactState, TempHumidityState, RelayState
|
from packages.home_capabilities import LightState, ThermostatState, ContactState, TempHumidityState, RelayState, ThreePhasePowerState
|
||||||
from apps.abstraction.transformation import (
|
from apps.abstraction.transformation import (
|
||||||
transform_abstract_to_vendor,
|
transform_abstract_to_vendor,
|
||||||
transform_vendor_to_abstract
|
transform_vendor_to_abstract
|
||||||
@@ -231,6 +231,9 @@ async def handle_vendor_state(
|
|||||||
elif device_type in {"temp_humidity", "temp_humidity_sensor"}:
|
elif device_type in {"temp_humidity", "temp_humidity_sensor"}:
|
||||||
# Validate temperature & humidity sensor state
|
# Validate temperature & humidity sensor state
|
||||||
TempHumidityState.model_validate(abstract_payload)
|
TempHumidityState.model_validate(abstract_payload)
|
||||||
|
elif device_type == "three_phase_powermeter":
|
||||||
|
# Validate three-phase powermeter state
|
||||||
|
ThreePhasePowerState.model_validate(abstract_payload)
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
logger.error(f"Validation failed for {device_type} STATE {device_id}: {e}")
|
logger.error(f"Validation failed for {device_type} STATE {device_id}: {e}")
|
||||||
return
|
return
|
||||||
@@ -388,9 +391,19 @@ async def async_main() -> None:
|
|||||||
validate_devices(devices)
|
validate_devices(devices)
|
||||||
logger.info(f"Loaded {len(devices)} device(s) from configuration")
|
logger.info(f"Loaded {len(devices)} device(s) from configuration")
|
||||||
|
|
||||||
# Get Redis URL from config or environment variable or use default
|
# Build Redis URL from environment variables or config or use default
|
||||||
|
redis_host = os.environ.get("REDIS_HOST")
|
||||||
|
redis_port = os.environ.get("REDIS_PORT")
|
||||||
|
redis_db = os.environ.get("REDIS_DB")
|
||||||
|
|
||||||
|
if redis_host and redis_port and redis_db:
|
||||||
|
redis_url = f"redis://{redis_host}:{redis_port}/{redis_db}"
|
||||||
|
logger.info(f"Using Redis from environment variables: {redis_url}")
|
||||||
|
else:
|
||||||
|
# Fallback to config file
|
||||||
redis_config = config.get("redis", {})
|
redis_config = config.get("redis", {})
|
||||||
redis_url = redis_config.get("url") or os.environ.get("REDIS_URL", "redis://localhost:6379/0")
|
redis_url = redis_config.get("url") or "redis://localhost:6379/0"
|
||||||
|
logger.info(f"Using Redis from config file: {redis_url}")
|
||||||
|
|
||||||
# Connect to Redis with retry
|
# Connect to Redis with retry
|
||||||
redis_client = await get_redis_client(redis_url)
|
redis_client = await get_redis_client(redis_url)
|
||||||
|
|||||||
@@ -374,6 +374,103 @@ def _transform_relay_shelly_to_abstract(payload: str) -> dict[str, Any]:
|
|||||||
"""
|
"""
|
||||||
return {"power": payload.strip()}
|
return {"power": payload.strip()}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# HANDLER FUNCTIONS: relay - hottis_modbus technology
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
def _transform_relay_hottis_modbus_to_vendor(payload: dict[str, Any]) -> str:
|
||||||
|
"""Transform abstract relay payload to Hottis Modbus format.
|
||||||
|
|
||||||
|
Hottis Modbus expects plain text 'on' or 'off' (not JSON).
|
||||||
|
- power: 'on'/'off' -> 'on'/'off' (plain string)
|
||||||
|
|
||||||
|
Example:
|
||||||
|
- Abstract: {'power': 'on'}
|
||||||
|
- Hottis Modbus: 'on'
|
||||||
|
"""
|
||||||
|
power = payload.get("power", "off")
|
||||||
|
return power
|
||||||
|
|
||||||
|
|
||||||
|
def _transform_relay_hottis_modbus_to_abstract(payload: str) -> dict[str, Any]:
|
||||||
|
"""Transform Hottis Modbus relay payload to abstract format.
|
||||||
|
|
||||||
|
Hottis Modbus sends plain text 'on' or 'off' (not JSON).
|
||||||
|
- 'on'/'off' -> power: 'on'/'off'
|
||||||
|
|
||||||
|
Example:
|
||||||
|
- Hottis Modbus: 'on'
|
||||||
|
- Abstract: {'power': 'on'}
|
||||||
|
"""
|
||||||
|
return {"power": payload.strip()}
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# HANDLER FUNCTIONS: three_phase_powermeter - hottis_modbus technology
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
def _transform_three_phase_powermeter_hottis_modbus_to_vendor(payload: dict[str, Any]) -> dict[str, Any]:
|
||||||
|
"""Transform abstract three_phase_powermeter payload to hottis_modbus format.
|
||||||
|
|
||||||
|
energy: float = Field(..., description="Total energy in kWh")
|
||||||
|
total_power: float = Field(..., description="Total power in W")
|
||||||
|
phase1_power: float = Field(..., description="Power for phase 1 in W")
|
||||||
|
phase2_power: float = Field(..., description="Power for phase 2 in W")
|
||||||
|
phase3_power: float = Field(..., description="Power for phase 3 in W")
|
||||||
|
phase1_voltage: float = Field(..., description="Voltage for phase 1 in V")
|
||||||
|
phase2_voltage: float = Field(..., description="Voltage for phase 2 in V")
|
||||||
|
phase3_voltage: float = Field(..., description="Voltage for phase 3 in V")
|
||||||
|
phase1_current: float = Field(..., description="Current for phase 1 in A")
|
||||||
|
phase2_current: float = Field(..., description="Current for phase 2 in A")
|
||||||
|
phase3_current: float = Field(..., description="Current for phase 3 in A")
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
vendor_payload = {
|
||||||
|
"energy": payload.get("energy", 0.0),
|
||||||
|
"total_power": payload.get("total_power", 0.0),
|
||||||
|
"phase1_power": payload.get("phase1_power", 0.0),
|
||||||
|
"phase2_power": payload.get("phase2_power", 0.0),
|
||||||
|
"phase3_power": payload.get("phase3_power", 0.0),
|
||||||
|
"phase1_voltage": payload.get("phase1_voltage", 0.0),
|
||||||
|
"phase2_voltage": payload.get("phase2_voltage", 0.0),
|
||||||
|
"phase3_voltage": payload.get("phase3_voltage", 0.0),
|
||||||
|
"phase1_current": payload.get("phase1_current", 0.0),
|
||||||
|
"phase2_current": payload.get("phase2_current", 0.0),
|
||||||
|
"phase3_current": payload.get("phase3_current", 0.0),
|
||||||
|
}
|
||||||
|
|
||||||
|
return vendor_payload
|
||||||
|
|
||||||
|
|
||||||
|
def _transform_three_phase_powermeter_hottis_modbus_to_abstract(payload: str) -> dict[str, Any]:
|
||||||
|
"""Transform hottis_modbus three_phase_powermeter payload to abstract format.
|
||||||
|
|
||||||
|
Transformations:
|
||||||
|
- Direct mapping of all power meter fields
|
||||||
|
|
||||||
|
Example:
|
||||||
|
- hottis_modbus: {'energy': 123.45, 'total_power': 1500.0, 'phase1_power': 500.0, ...}
|
||||||
|
- Abstract: {'energy': 123.45, 'total_power': 1500.0, 'phase1_power': 500.0, ...}
|
||||||
|
"""
|
||||||
|
payload = json.loads(payload)
|
||||||
|
abstract_payload = {
|
||||||
|
"energy": payload.get("energy", 0.0),
|
||||||
|
"total_power": payload.get("total_power", 0.0),
|
||||||
|
"phase1_power": payload.get("phase1_power", 0.0),
|
||||||
|
"phase2_power": payload.get("phase2_power", 0.0),
|
||||||
|
"phase3_power": payload.get("phase3_power", 0.0),
|
||||||
|
"phase1_voltage": payload.get("phase1_voltage", 0.0),
|
||||||
|
"phase2_voltage": payload.get("phase2_voltage", 0.0),
|
||||||
|
"phase3_voltage": payload.get("phase3_voltage", 0.0),
|
||||||
|
"phase1_current": payload.get("phase1_current", 0.0),
|
||||||
|
"phase2_current": payload.get("phase2_current", 0.0),
|
||||||
|
"phase3_current": payload.get("phase3_current", 0.0),
|
||||||
|
}
|
||||||
|
|
||||||
|
return abstract_payload
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# HANDLER FUNCTIONS: max technology (Homegear MAX!)
|
# HANDLER FUNCTIONS: max technology (Homegear MAX!)
|
||||||
@@ -482,6 +579,12 @@ TRANSFORM_HANDLERS: dict[tuple[str, str, str], TransformHandler] = {
|
|||||||
("relay", "zigbee2mqtt", "to_abstract"): _transform_relay_zigbee2mqtt_to_abstract,
|
("relay", "zigbee2mqtt", "to_abstract"): _transform_relay_zigbee2mqtt_to_abstract,
|
||||||
("relay", "shelly", "to_vendor"): _transform_relay_shelly_to_vendor,
|
("relay", "shelly", "to_vendor"): _transform_relay_shelly_to_vendor,
|
||||||
("relay", "shelly", "to_abstract"): _transform_relay_shelly_to_abstract,
|
("relay", "shelly", "to_abstract"): _transform_relay_shelly_to_abstract,
|
||||||
|
("relay", "hottis_modbus", "to_vendor"): _transform_relay_hottis_modbus_to_vendor,
|
||||||
|
("relay", "hottis_modbus", "to_abstract"): _transform_relay_hottis_modbus_to_abstract,
|
||||||
|
|
||||||
|
# Three-Phase Powermeter transformations
|
||||||
|
("three_phase_powermeter", "hottis_modbus", "to_vendor"): _transform_three_phase_powermeter_hottis_modbus_to_vendor,
|
||||||
|
("three_phase_powermeter", "hottis_modbus", "to_abstract"): _transform_three_phase_powermeter_hottis_modbus_to_abstract,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ app.add_middleware(
|
|||||||
"http://localhost:8002",
|
"http://localhost:8002",
|
||||||
"http://172.19.1.11:8002",
|
"http://172.19.1.11:8002",
|
||||||
"http://127.0.0.1:8002",
|
"http://127.0.0.1:8002",
|
||||||
|
"https://homea2.hottis.de"
|
||||||
],
|
],
|
||||||
allow_credentials=True,
|
allow_credentials=True,
|
||||||
allow_methods=["*"],
|
allow_methods=["*"],
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import os
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from fastapi import FastAPI, Request
|
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.staticfiles import StaticFiles
|
||||||
from fastapi.templating import Jinja2Templates
|
from fastapi.templating import Jinja2Templates
|
||||||
|
|
||||||
@@ -49,6 +49,44 @@ static_dir.mkdir(exist_ok=True)
|
|||||||
app.mount("/static", StaticFiles(directory=str(static_dir)), name="static")
|
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")
|
@app.get("/health")
|
||||||
async def health() -> JSONResponse:
|
async def health() -> JSONResponse:
|
||||||
"""Health check endpoint for Kubernetes/Docker.
|
"""Health check endpoint for Kubernetes/Docker.
|
||||||
@@ -129,6 +167,22 @@ async def device_detail(request: Request, device_id: str) -> HTMLResponse:
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/garage", response_class=HTMLResponse)
|
||||||
|
async def garage(request: Request) -> HTMLResponse:
|
||||||
|
"""Render the garage page with car outlet devices.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request: The FastAPI request object
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
HTMLResponse: Rendered garage template
|
||||||
|
"""
|
||||||
|
return templates.TemplateResponse("garage.html", {
|
||||||
|
"request": request,
|
||||||
|
"api_base": API_BASE
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
@app.get("/dashboard", response_class=HTMLResponse)
|
@app.get("/dashboard", response_class=HTMLResponse)
|
||||||
async def dashboard(request: Request) -> HTMLResponse:
|
async def dashboard(request: Request) -> HTMLResponse:
|
||||||
"""Render the dashboard with rooms and devices.
|
"""Render the dashboard with rooms and devices.
|
||||||
|
|||||||
@@ -102,12 +102,14 @@ class HomeAutomationClient {
|
|||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async setDeviceState(deviceId, type, payload) {
|
async setDeviceState(deviceId, type, payload) {
|
||||||
|
const requestBody = { type, payload };
|
||||||
|
console.log('API setDeviceState request:', requestBody);
|
||||||
await fetch(this.api(`/devices/${deviceId}/set`), {
|
await fetch(this.api(`/devices/${deviceId}/set`), {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ type, payload })
|
body: JSON.stringify(requestBody)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,11 +152,15 @@ class HomeAutomationClient {
|
|||||||
this.eventSource.close();
|
this.eventSource.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.eventSource = new EventSource(this.api('/realtime'));
|
const realtimeUrl = this.api('/realtime');
|
||||||
|
console.log('Connecting to SSE endpoint:', realtimeUrl);
|
||||||
|
this.eventSource = new EventSource(realtimeUrl);
|
||||||
|
|
||||||
this.eventSource.onmessage = (event) => {
|
this.eventSource.onmessage = (event) => {
|
||||||
|
console.log('Raw SSE event received:', event.data);
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(event.data);
|
const data = JSON.parse(event.data);
|
||||||
|
console.log('Parsed SSE data:', data);
|
||||||
|
|
||||||
// Normalize event format: convert API format to unified format
|
// Normalize event format: convert API format to unified format
|
||||||
const normalizedEvent = {
|
const normalizedEvent = {
|
||||||
@@ -163,6 +169,7 @@ class HomeAutomationClient {
|
|||||||
state: data.payload || data.state // Support both formats
|
state: data.payload || data.state // Support both formats
|
||||||
};
|
};
|
||||||
|
|
||||||
|
console.log('Normalized SSE event:', normalizedEvent);
|
||||||
onEvent(normalizedEvent);
|
onEvent(normalizedEvent);
|
||||||
|
|
||||||
// Notify all registered listeners
|
// Notify all registered listeners
|
||||||
@@ -172,12 +179,17 @@ class HomeAutomationClient {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to parse SSE event:', error);
|
console.error('Failed to parse SSE event:', error, 'Raw data:', event.data);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.eventSource.onopen = (event) => {
|
||||||
|
console.log('SSE connection opened:', event);
|
||||||
|
};
|
||||||
|
|
||||||
this.eventSource.onerror = (error) => {
|
this.eventSource.onerror = (error) => {
|
||||||
console.error('SSE connection error:', error);
|
console.error('SSE connection error:', error);
|
||||||
|
console.log('EventSource readyState:', this.eventSource.readyState);
|
||||||
if (onError) {
|
if (onError) {
|
||||||
onError(error);
|
onError(error);
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
apps/ui/static/apple-touch-icon-114x114.png
Normal file
|
After Width: | Height: | Size: 618 B |
BIN
apps/ui/static/apple-touch-icon-120x120.png
Normal file
|
After Width: | Height: | Size: 639 B |
BIN
apps/ui/static/apple-touch-icon-144x144.png
Normal file
|
After Width: | Height: | Size: 827 B |
BIN
apps/ui/static/apple-touch-icon-152x152.png
Normal file
|
After Width: | Height: | Size: 884 B |
BIN
apps/ui/static/apple-touch-icon-16x16.png
Normal file
|
After Width: | Height: | Size: 153 B |
BIN
apps/ui/static/apple-touch-icon-180x180.png
Normal file
|
After Width: | Height: | Size: 1018 B |
BIN
apps/ui/static/apple-touch-icon-32x32.png
Normal file
|
After Width: | Height: | Size: 210 B |
BIN
apps/ui/static/apple-touch-icon-57x57.png
Normal file
|
After Width: | Height: | Size: 336 B |
BIN
apps/ui/static/apple-touch-icon-60x60.png
Normal file
|
After Width: | Height: | Size: 346 B |
BIN
apps/ui/static/apple-touch-icon-72x72.png
Normal file
|
After Width: | Height: | Size: 413 B |
BIN
apps/ui/static/apple-touch-icon-76x76.png
Normal file
|
After Width: | Height: | Size: 432 B |
BIN
apps/ui/static/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 1018 B |
4
apps/ui/static/apple-touch-icon.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg width="180" height="180" viewBox="0 0 180 180" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="180" height="180" rx="40" fill="#667EEA"/>
|
||||||
|
<text x="90" y="130" font-size="80" text-anchor="middle" fill="white">🏡</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 244 B |
BIN
apps/ui/static/garage-icon-114x114.png
Normal file
|
After Width: | Height: | Size: 519 B |
BIN
apps/ui/static/garage-icon-120x120.png
Normal file
|
After Width: | Height: | Size: 547 B |
BIN
apps/ui/static/garage-icon-144x144.png
Normal file
|
After Width: | Height: | Size: 641 B |
BIN
apps/ui/static/garage-icon-152x152.png
Normal file
|
After Width: | Height: | Size: 695 B |
BIN
apps/ui/static/garage-icon-16x16.png
Normal file
|
After Width: | Height: | Size: 126 B |
BIN
apps/ui/static/garage-icon-180x180.png
Normal file
|
After Width: | Height: | Size: 808 B |
BIN
apps/ui/static/garage-icon-32x32.png
Normal file
|
After Width: | Height: | Size: 192 B |
BIN
apps/ui/static/garage-icon-57x57.png
Normal file
|
After Width: | Height: | Size: 257 B |
BIN
apps/ui/static/garage-icon-60x60.png
Normal file
|
After Width: | Height: | Size: 271 B |
BIN
apps/ui/static/garage-icon-72x72.png
Normal file
|
After Width: | Height: | Size: 347 B |
BIN
apps/ui/static/garage-icon-76x76.png
Normal file
|
After Width: | Height: | Size: 368 B |
BIN
apps/ui/static/garage-icon.png
Normal file
|
After Width: | Height: | Size: 808 B |
4
apps/ui/static/garage-icon.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg width="180" height="180" viewBox="0 0 180 180" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="180" height="180" rx="40" fill="#667EEA"/>
|
||||||
|
<text x="90" y="130" font-size="80" text-anchor="middle" fill="white">🚗</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 244 B |
43
apps/ui/static/manifest.json
Normal 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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -4,7 +4,19 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Home Automation</title>
|
<title>Home Automation</title>
|
||||||
|
|
||||||
|
<!-- Apple Touch Icon -->
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/static/apple-touch-icon.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="152x152" href="/static/apple-touch-icon.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="120x120" href="/static/apple-touch-icon.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="/static/apple-touch-icon.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="/static/apple-touch-icon.png">
|
||||||
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
|
<link rel="icon" type="image/svg+xml" href="/static/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/manifest.json">
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|||||||
@@ -4,6 +4,18 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Gerät - Home Automation</title>
|
<title>Gerät - Home Automation</title>
|
||||||
|
|
||||||
|
<!-- Apple Touch Icon -->
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/static/apple-touch-icon.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="152x152" href="/static/apple-touch-icon.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="120x120" href="/static/apple-touch-icon.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="/static/apple-touch-icon.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="/static/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/manifest.json">
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@@ -217,6 +229,48 @@
|
|||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.phase-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phase-section h4 {
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phase-values {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phase-value {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background: rgba(102, 126, 234, 0.1);
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phase-value .value {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #667eea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phase-value .unit {
|
||||||
|
color: #666;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.phase-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.state-badge {
|
.state-badge {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 8px 20px;
|
padding: 8px 20px;
|
||||||
@@ -298,7 +352,8 @@
|
|||||||
<script>
|
<script>
|
||||||
// Get device ID from URL
|
// Get device ID from URL
|
||||||
const pathParts = window.location.pathname.split('/');
|
const pathParts = window.location.pathname.split('/');
|
||||||
const deviceId = pathParts[pathParts.length - 1];
|
const deviceId = decodeURIComponent(pathParts[pathParts.length - 1]);
|
||||||
|
console.log('Device ID from URL:', deviceId);
|
||||||
|
|
||||||
// Device data
|
// Device data
|
||||||
let deviceData = null;
|
let deviceData = null;
|
||||||
@@ -366,6 +421,7 @@
|
|||||||
'thermostat': 'Thermostat',
|
'thermostat': 'Thermostat',
|
||||||
'contact': 'Kontaktsensor',
|
'contact': 'Kontaktsensor',
|
||||||
'temp_humidity_sensor': 'Temperatur & Luftfeuchte',
|
'temp_humidity_sensor': 'Temperatur & Luftfeuchte',
|
||||||
|
'three_phase_powermeter': 'Dreiphasen-Stromzähler',
|
||||||
'relay': 'Schalter',
|
'relay': 'Schalter',
|
||||||
'outlet': 'Steckdose',
|
'outlet': 'Steckdose',
|
||||||
'cover': 'Jalousie'
|
'cover': 'Jalousie'
|
||||||
@@ -393,6 +449,9 @@
|
|||||||
case 'temp_humidity_sensor':
|
case 'temp_humidity_sensor':
|
||||||
renderTempHumidityDisplay(container);
|
renderTempHumidityDisplay(container);
|
||||||
break;
|
break;
|
||||||
|
case 'three_phase_powermeter':
|
||||||
|
renderThreePhasePowerDisplay(container);
|
||||||
|
break;
|
||||||
case 'cover':
|
case 'cover':
|
||||||
renderCoverControls(container);
|
renderCoverControls(container);
|
||||||
break;
|
break;
|
||||||
@@ -565,6 +624,93 @@
|
|||||||
container.appendChild(card);
|
container.appendChild(card);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderThreePhasePowerDisplay(container) {
|
||||||
|
const card = document.createElement('div');
|
||||||
|
card.className = 'card';
|
||||||
|
card.innerHTML = '<div class="card-title">Leistungsmessung</div>';
|
||||||
|
|
||||||
|
// Übersicht
|
||||||
|
const overviewGrid = document.createElement('div');
|
||||||
|
overviewGrid.className = 'state-grid';
|
||||||
|
overviewGrid.innerHTML = `
|
||||||
|
<div class="state-item">
|
||||||
|
<div class="state-value" id="total-power">${deviceState.total_power?.toFixed(0) || '--'} W</div>
|
||||||
|
<div class="state-label">Gesamtleistung</div>
|
||||||
|
</div>
|
||||||
|
<div class="state-item">
|
||||||
|
<div class="state-value" id="energy">${deviceState.energy?.toFixed(2) || '--'} kWh</div>
|
||||||
|
<div class="state-label">Energie</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
card.appendChild(overviewGrid);
|
||||||
|
|
||||||
|
// Phasen Details
|
||||||
|
const phaseCard = document.createElement('div');
|
||||||
|
phaseCard.className = 'card';
|
||||||
|
phaseCard.innerHTML = '<div class="card-title">Phasen</div>';
|
||||||
|
phaseCard.style.marginTop = '20px';
|
||||||
|
|
||||||
|
const phaseGrid = document.createElement('div');
|
||||||
|
phaseGrid.className = 'phase-grid';
|
||||||
|
phaseGrid.innerHTML = `
|
||||||
|
<div class="phase-section">
|
||||||
|
<h4>Phase 1</h4>
|
||||||
|
<div class="phase-values">
|
||||||
|
<div class="phase-value">
|
||||||
|
<span class="value" id="phase1-power">${deviceState.phase1_power?.toFixed(0) || '--'}</span>
|
||||||
|
<span class="unit">W</span>
|
||||||
|
</div>
|
||||||
|
<div class="phase-value">
|
||||||
|
<span class="value" id="phase1-voltage">${deviceState.phase1_voltage?.toFixed(1) || '--'}</span>
|
||||||
|
<span class="unit">V</span>
|
||||||
|
</div>
|
||||||
|
<div class="phase-value">
|
||||||
|
<span class="value" id="phase1-current">${deviceState.phase1_current?.toFixed(2) || '--'}</span>
|
||||||
|
<span class="unit">A</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="phase-section">
|
||||||
|
<h4>Phase 2</h4>
|
||||||
|
<div class="phase-values">
|
||||||
|
<div class="phase-value">
|
||||||
|
<span class="value" id="phase2-power">${deviceState.phase2_power?.toFixed(0) || '--'}</span>
|
||||||
|
<span class="unit">W</span>
|
||||||
|
</div>
|
||||||
|
<div class="phase-value">
|
||||||
|
<span class="value" id="phase2-voltage">${deviceState.phase2_voltage?.toFixed(1) || '--'}</span>
|
||||||
|
<span class="unit">V</span>
|
||||||
|
</div>
|
||||||
|
<div class="phase-value">
|
||||||
|
<span class="value" id="phase2-current">${deviceState.phase2_current?.toFixed(2) || '--'}</span>
|
||||||
|
<span class="unit">A</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="phase-section">
|
||||||
|
<h4>Phase 3</h4>
|
||||||
|
<div class="phase-values">
|
||||||
|
<div class="phase-value">
|
||||||
|
<span class="value" id="phase3-power">${deviceState.phase3_power?.toFixed(0) || '--'}</span>
|
||||||
|
<span class="unit">W</span>
|
||||||
|
</div>
|
||||||
|
<div class="phase-value">
|
||||||
|
<span class="value" id="phase3-voltage">${deviceState.phase3_voltage?.toFixed(1) || '--'}</span>
|
||||||
|
<span class="unit">V</span>
|
||||||
|
</div>
|
||||||
|
<div class="phase-value">
|
||||||
|
<span class="value" id="phase3-current">${deviceState.phase3_current?.toFixed(2) || '--'}</span>
|
||||||
|
<span class="unit">A</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
phaseCard.appendChild(phaseGrid);
|
||||||
|
|
||||||
|
container.appendChild(card);
|
||||||
|
container.appendChild(phaseCard);
|
||||||
|
}
|
||||||
|
|
||||||
function renderCoverControls(container) {
|
function renderCoverControls(container) {
|
||||||
const card = document.createElement('div');
|
const card = document.createElement('div');
|
||||||
card.className = 'card';
|
card.className = 'card';
|
||||||
@@ -707,9 +853,19 @@
|
|||||||
try {
|
try {
|
||||||
// Use API client's realtime connection
|
// Use API client's realtime connection
|
||||||
window.apiClient.connectRealtime((event) => {
|
window.apiClient.connectRealtime((event) => {
|
||||||
|
console.log('SSE event received:', event);
|
||||||
|
console.log('Current deviceId:', deviceId);
|
||||||
|
console.log('Event device_id:', event.device_id);
|
||||||
|
console.log('Device type:', deviceData.type);
|
||||||
if (event.device_id === deviceId && event.state) {
|
if (event.device_id === deviceId && event.state) {
|
||||||
|
console.log('Updating device state for:', deviceId);
|
||||||
|
console.log('Old state:', deviceState);
|
||||||
|
console.log('New state from event:', event.state);
|
||||||
deviceState = { ...deviceState, ...event.state };
|
deviceState = { ...deviceState, ...event.state };
|
||||||
|
console.log('Merged state:', deviceState);
|
||||||
updateUI();
|
updateUI();
|
||||||
|
} else {
|
||||||
|
console.log('SSE event ignored - not for this device or no state');
|
||||||
}
|
}
|
||||||
}, (error) => {
|
}, (error) => {
|
||||||
console.error('SSE connection error:', error);
|
console.error('SSE connection error:', error);
|
||||||
@@ -738,6 +894,9 @@
|
|||||||
case 'temp_humidity_sensor':
|
case 'temp_humidity_sensor':
|
||||||
updateTempHumidityUI();
|
updateTempHumidityUI();
|
||||||
break;
|
break;
|
||||||
|
case 'three_phase_powermeter':
|
||||||
|
updateThreePhasePowerUI();
|
||||||
|
break;
|
||||||
case 'cover':
|
case 'cover':
|
||||||
updateCoverUI();
|
updateCoverUI();
|
||||||
break;
|
break;
|
||||||
@@ -806,6 +965,42 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateThreePhasePowerUI() {
|
||||||
|
console.log('updateThreePhasePowerUI called with deviceState:', deviceState);
|
||||||
|
// Update overview
|
||||||
|
const totalPower = document.getElementById('total-power');
|
||||||
|
const energy = document.getElementById('energy');
|
||||||
|
|
||||||
|
console.log('Elements found - totalPower:', totalPower, 'energy:', energy);
|
||||||
|
|
||||||
|
if (totalPower && deviceState.total_power != null) {
|
||||||
|
console.log('Updating total power to:', deviceState.total_power);
|
||||||
|
totalPower.textContent = deviceState.total_power.toFixed(0) + ' W';
|
||||||
|
}
|
||||||
|
if (energy && deviceState.energy != null) {
|
||||||
|
console.log('Updating energy to:', deviceState.energy);
|
||||||
|
energy.textContent = deviceState.energy.toFixed(2) + ' kWh';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update phases
|
||||||
|
const phases = ['phase1', 'phase2', 'phase3'];
|
||||||
|
phases.forEach(phase => {
|
||||||
|
const power = document.getElementById(`${phase}-power`);
|
||||||
|
const voltage = document.getElementById(`${phase}-voltage`);
|
||||||
|
const current = document.getElementById(`${phase}-current`);
|
||||||
|
|
||||||
|
if (power && deviceState[`${phase}_power`] != null) {
|
||||||
|
power.textContent = deviceState[`${phase}_power`].toFixed(0);
|
||||||
|
}
|
||||||
|
if (voltage && deviceState[`${phase}_voltage`] != null) {
|
||||||
|
voltage.textContent = deviceState[`${phase}_voltage`].toFixed(1);
|
||||||
|
}
|
||||||
|
if (current && deviceState[`${phase}_current`] != null) {
|
||||||
|
current.textContent = deviceState[`${phase}_current`].toFixed(2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function updateCoverUI() {
|
function updateCoverUI() {
|
||||||
const slider = document.getElementById('position-slider');
|
const slider = document.getElementById('position-slider');
|
||||||
const value = document.getElementById('position-value');
|
const value = document.getElementById('position-value');
|
||||||
|
|||||||
699
apps/ui/templates/garage.html
Normal file
@@ -0,0 +1,699 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Garage - Home Automation</title>
|
||||||
|
|
||||||
|
<!-- Apple Touch Icon -->
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/static/garage-icon-180x180.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="152x152" href="/static/garage-icon-152x152.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="120x120" href="/static/garage-icon-120x120.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="76x76" href="/static/garage-icon-76x76.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="/static/garage-icon-32x32.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="/static/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="/manifest.json">
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.devices-container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-section {
|
||||||
|
background: rgba(255, 255, 255, 0.95);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-icon {
|
||||||
|
font-size: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-info {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-name {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
margin: 0 0 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-type {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: #f8f9fa;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.state-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.state-item {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.state-value {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #667eea;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.state-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-group {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-group:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-label {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-group {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-switch {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: 100px;
|
||||||
|
height: 50px;
|
||||||
|
background: #e0e0e0;
|
||||||
|
border-radius: 25px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.3s ease;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-switch.on {
|
||||||
|
background: #34c759;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-switch::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 4px;
|
||||||
|
left: 4px;
|
||||||
|
width: 42px;
|
||||||
|
height: 42px;
|
||||||
|
background: white;
|
||||||
|
border-radius: 50%;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-switch.on::after {
|
||||||
|
transform: translateX(50px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-switch:hover {
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-switch:active::after {
|
||||||
|
width: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-label {
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phase-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phase-section h4 {
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phase-values {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phase-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phase-value.full-width {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phase-value.half-width {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phase-value {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 6px 10px;
|
||||||
|
background: rgba(102, 126, 234, 0.1);
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phase-value .value {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #667eea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phase-value .unit {
|
||||||
|
color: #666;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.phase-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
padding: 8px;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-section {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.state-value {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phase-value {
|
||||||
|
padding: 4px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phase-values {
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phase-row {
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
text-align: center;
|
||||||
|
color: white;
|
||||||
|
font-size: 18px;
|
||||||
|
padding: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
background: rgba(255, 59, 48, 0.9);
|
||||||
|
color: white;
|
||||||
|
padding: 16px;
|
||||||
|
border-radius: 8px;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#error-container:empty {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div id="error-container"></div>
|
||||||
|
<div id="loading" class="loading">Lade Geräte...</div>
|
||||||
|
<div id="devices-container" class="devices-container" style="display: none;"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// API configuration from backend
|
||||||
|
window.API_BASE = '{{ api_base }}';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Load API client AFTER API_BASE is set -->
|
||||||
|
<script src="/static/types.js"></script>
|
||||||
|
<script src="/static/api-client.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Device IDs for garage devices
|
||||||
|
const GARAGE_DEVICES = [
|
||||||
|
'power_relay_caroutlet',
|
||||||
|
'powermeter_caroutlet'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Device states
|
||||||
|
const deviceStates = {};
|
||||||
|
let devicesData = {};
|
||||||
|
|
||||||
|
async function loadGarageDevices() {
|
||||||
|
const loading = document.getElementById('loading');
|
||||||
|
const container = document.getElementById('devices-container');
|
||||||
|
const errorContainer = document.getElementById('error-container');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Load all devices using API client
|
||||||
|
const allDevices = await window.apiClient.getDevices();
|
||||||
|
console.log('All devices loaded:', allDevices.length);
|
||||||
|
|
||||||
|
// Filter garage devices
|
||||||
|
const garageDevices = allDevices.filter(device =>
|
||||||
|
GARAGE_DEVICES.includes(device.device_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('Garage devices found:', garageDevices);
|
||||||
|
|
||||||
|
if (garageDevices.length === 0) {
|
||||||
|
throw new Error('Keine Garage-Geräte gefunden');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create device lookup
|
||||||
|
garageDevices.forEach(device => {
|
||||||
|
devicesData[device.device_id] = device;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Load device states
|
||||||
|
for (const device of garageDevices) {
|
||||||
|
try {
|
||||||
|
deviceStates[device.device_id] = await window.apiClient.getDeviceState(device.device_id);
|
||||||
|
console.log(`State for ${device.device_id}:`, deviceStates[device.device_id]);
|
||||||
|
} catch (err) {
|
||||||
|
console.warn(`Failed to load state for ${device.device_id}:`, err);
|
||||||
|
deviceStates[device.device_id] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loading.style.display = 'none';
|
||||||
|
container.style.display = 'grid';
|
||||||
|
|
||||||
|
// Render only the relay device (it will include the powermeter)
|
||||||
|
const relayDevice = garageDevices.find(d => d.device_id === 'power_relay_caroutlet');
|
||||||
|
if (relayDevice) {
|
||||||
|
const deviceSection = createDeviceSection(relayDevice);
|
||||||
|
container.appendChild(deviceSection);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start SSE for live updates
|
||||||
|
connectRealtime();
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading garage devices:', error);
|
||||||
|
loading.style.display = 'none';
|
||||||
|
errorContainer.innerHTML = `
|
||||||
|
<div class="error">
|
||||||
|
⚠️ Fehler beim Laden: ${error.message}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createDeviceSection(device) {
|
||||||
|
const fragment = document.createDocumentFragment();
|
||||||
|
|
||||||
|
// Create separate sections for each component
|
||||||
|
renderDeviceContent(fragment, device);
|
||||||
|
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderDeviceContent(container, device) {
|
||||||
|
// Render all content as separate device sections for Car Outlet
|
||||||
|
if (device.device_id === 'power_relay_caroutlet') {
|
||||||
|
// 1. Header section
|
||||||
|
const headerSection = document.createElement('div');
|
||||||
|
headerSection.className = 'device-section';
|
||||||
|
headerSection.innerHTML = `
|
||||||
|
<div style="display: flex; align-items: center; justify-content: center; gap: 12px;">
|
||||||
|
<div style="font-size: 32px;">⚡</div>
|
||||||
|
<div style="font-size: 20px; font-weight: 600; color: #333;">Car Outlet</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
container.appendChild(headerSection);
|
||||||
|
|
||||||
|
// 2. Control section
|
||||||
|
const controlSection = document.createElement('div');
|
||||||
|
controlSection.className = 'device-section';
|
||||||
|
controlSection.dataset.deviceId = device.device_id;
|
||||||
|
renderOutletControls(controlSection, device);
|
||||||
|
container.appendChild(controlSection);
|
||||||
|
|
||||||
|
// 3. Powermeter section
|
||||||
|
const powermeterDevice = Object.values(devicesData).find(d => d.device_id === 'powermeter_caroutlet');
|
||||||
|
if (powermeterDevice) {
|
||||||
|
const powermeterSection = document.createElement('div');
|
||||||
|
powermeterSection.className = 'device-section';
|
||||||
|
renderThreePhasePowerDisplay(powermeterSection, powermeterDevice);
|
||||||
|
container.appendChild(powermeterSection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderOutletControls(container, device) {
|
||||||
|
const controlGroup = document.createElement('div');
|
||||||
|
controlGroup.style.textAlign = 'center';
|
||||||
|
// controlGroup.style.marginBottom = '8px';
|
||||||
|
|
||||||
|
const state = deviceStates[device.device_id];
|
||||||
|
const currentPower = state?.power === 'on';
|
||||||
|
|
||||||
|
const toggleSwitch = document.createElement('button');
|
||||||
|
toggleSwitch.className = `toggle-switch ${currentPower ? 'on' : ''}`;
|
||||||
|
toggleSwitch.onclick = () => {
|
||||||
|
const currentState = deviceStates[device.device_id]?.power === 'on';
|
||||||
|
toggleOutlet(device.device_id, currentState ? 'off' : 'on');
|
||||||
|
};
|
||||||
|
|
||||||
|
const label = document.createElement('div');
|
||||||
|
label.className = 'toggle-label';
|
||||||
|
label.textContent = currentPower ? 'Ein' : 'Aus';
|
||||||
|
|
||||||
|
// Status display
|
||||||
|
// const stateDisplay = document.createElement('div');
|
||||||
|
// stateDisplay.style.marginTop = '16px';
|
||||||
|
// stateDisplay.style.fontSize = '18px';
|
||||||
|
// stateDisplay.style.fontWeight = '600';
|
||||||
|
// stateDisplay.style.color = currentPower ? '#34c759' : '#666';
|
||||||
|
// stateDisplay.textContent = `Status: ${currentPower ? 'Eingeschaltet' : 'Ausgeschaltet'}`;
|
||||||
|
|
||||||
|
controlGroup.appendChild(toggleSwitch);
|
||||||
|
controlGroup.appendChild(label);
|
||||||
|
// controlGroup.appendChild(stateDisplay);
|
||||||
|
|
||||||
|
container.appendChild(controlGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function renderThreePhasePowerDisplay(container, device) {
|
||||||
|
const state = deviceStates[device.device_id] || {};
|
||||||
|
|
||||||
|
// Leistungsmessung Title
|
||||||
|
// const title = document.createElement('h3');
|
||||||
|
// title.style.margin = '0 0 20px 0';
|
||||||
|
// title.style.fontSize = '18px';
|
||||||
|
// title.style.fontWeight = '600';
|
||||||
|
// title.style.color = '#333';
|
||||||
|
// title.textContent = 'Leistungsmessung';
|
||||||
|
// container.appendChild(title);
|
||||||
|
|
||||||
|
// Übersicht
|
||||||
|
const overviewGrid = document.createElement('div');
|
||||||
|
overviewGrid.className = 'state-grid';
|
||||||
|
overviewGrid.innerHTML = `
|
||||||
|
<div class="state-item">
|
||||||
|
<div class="state-value" id="total-power-${device.device_id}">${state.total_power?.toFixed(0) || '--'} W</div>
|
||||||
|
<div class="state-label">Gesamtleistung</div>
|
||||||
|
</div>
|
||||||
|
<div class="state-item">
|
||||||
|
<div class="state-value" id="energy-${device.device_id}">${state.energy?.toFixed(2) || '--'} kWh</div>
|
||||||
|
<div class="state-label">Energie</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
container.appendChild(overviewGrid);
|
||||||
|
|
||||||
|
// Phasen Title
|
||||||
|
const phaseTitle = document.createElement('h4');
|
||||||
|
phaseTitle.style.margin = '20px 0 8px 0';
|
||||||
|
phaseTitle.style.fontSize = '16px';
|
||||||
|
phaseTitle.style.fontWeight = '600';
|
||||||
|
phaseTitle.style.color = '#333';
|
||||||
|
// phaseTitle.textContent = 'Phasen';
|
||||||
|
container.appendChild(phaseTitle);
|
||||||
|
|
||||||
|
// Phasen Details
|
||||||
|
const phaseGrid = document.createElement('div');
|
||||||
|
phaseGrid.className = 'phase-grid';
|
||||||
|
phaseGrid.innerHTML = `
|
||||||
|
<div class="phase-section">
|
||||||
|
<h4>Phase 1</h4>
|
||||||
|
<div class="phase-values">
|
||||||
|
<div class="phase-value full-width">
|
||||||
|
<span class="value" id="phase1-power-${device.device_id}">${state.phase1_power?.toFixed(0) || '--'}</span>
|
||||||
|
<span class="unit">W</span>
|
||||||
|
</div>
|
||||||
|
<div class="phase-row">
|
||||||
|
<div class="phase-value half-width">
|
||||||
|
<span class="value" id="phase1-voltage-${device.device_id}">${state.phase1_voltage?.toFixed(1) || '--'}</span>
|
||||||
|
<span class="unit">V</span>
|
||||||
|
</div>
|
||||||
|
<div class="phase-value half-width">
|
||||||
|
<span class="value" id="phase1-current-${device.device_id}">${state.phase1_current?.toFixed(2) || '--'}</span>
|
||||||
|
<span class="unit">A</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="phase-section">
|
||||||
|
<h4>Phase 2</h4>
|
||||||
|
<div class="phase-values">
|
||||||
|
<div class="phase-value full-width">
|
||||||
|
<span class="value" id="phase2-power-${device.device_id}">${state.phase2_power?.toFixed(0) || '--'}</span>
|
||||||
|
<span class="unit">W</span>
|
||||||
|
</div>
|
||||||
|
<div class="phase-row">
|
||||||
|
<div class="phase-value half-width">
|
||||||
|
<span class="value" id="phase2-voltage-${device.device_id}">${state.phase2_voltage?.toFixed(1) || '--'}</span>
|
||||||
|
<span class="unit">V</span>
|
||||||
|
</div>
|
||||||
|
<div class="phase-value half-width">
|
||||||
|
<span class="value" id="phase2-current-${device.device_id}">${state.phase2_current?.toFixed(2) || '--'}</span>
|
||||||
|
<span class="unit">A</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="phase-section">
|
||||||
|
<h4>Phase 3</h4>
|
||||||
|
<div class="phase-values">
|
||||||
|
<div class="phase-value full-width">
|
||||||
|
<span class="value" id="phase3-power-${device.device_id}">${state.phase3_power?.toFixed(0) || '--'}</span>
|
||||||
|
<span class="unit">W</span>
|
||||||
|
</div>
|
||||||
|
<div class="phase-row">
|
||||||
|
<div class="phase-value half-width">
|
||||||
|
<span class="value" id="phase3-voltage-${device.device_id}">${state.phase3_voltage?.toFixed(1) || '--'}</span>
|
||||||
|
<span class="unit">V</span>
|
||||||
|
</div>
|
||||||
|
<div class="phase-value half-width">
|
||||||
|
<span class="value" id="phase3-current-${device.device_id}">${state.phase3_current?.toFixed(2) || '--'}</span>
|
||||||
|
<span class="unit">A</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
container.appendChild(phaseGrid);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function toggleOutlet(deviceId, newState) {
|
||||||
|
try {
|
||||||
|
const device = devicesData[deviceId];
|
||||||
|
await sendCommand(deviceId, {
|
||||||
|
type: device.type,
|
||||||
|
payload: { power: newState }
|
||||||
|
});
|
||||||
|
console.log(`Set ${deviceId} to ${newState}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error toggling outlet:', error);
|
||||||
|
alert('Fehler beim Schalten des Geräts: ' + error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendCommand(deviceId, payload) {
|
||||||
|
const device = devicesData[deviceId];
|
||||||
|
await window.apiClient.setDeviceState(deviceId, device.type, payload.payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
function connectRealtime() {
|
||||||
|
try {
|
||||||
|
window.apiClient.connectRealtime((event) => {
|
||||||
|
console.log('SSE event received:', event);
|
||||||
|
if (event.device_id && event.state && GARAGE_DEVICES.includes(event.device_id)) {
|
||||||
|
console.log('Updating garage device state for:', event.device_id);
|
||||||
|
deviceStates[event.device_id] = { ...deviceStates[event.device_id], ...event.state };
|
||||||
|
updateDeviceUI(event.device_id);
|
||||||
|
}
|
||||||
|
}, (error) => {
|
||||||
|
console.error('SSE connection error:', error);
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to connect to realtime events:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateDeviceUI(deviceId) {
|
||||||
|
const device = devicesData[deviceId];
|
||||||
|
if (!device) return;
|
||||||
|
|
||||||
|
const state = deviceStates[deviceId];
|
||||||
|
console.log(`Updating UI for ${deviceId}:`, state);
|
||||||
|
|
||||||
|
switch (device.type) {
|
||||||
|
case 'relay':
|
||||||
|
case 'outlet':
|
||||||
|
updateOutletUI(deviceId, state);
|
||||||
|
break;
|
||||||
|
case 'three_phase_powermeter':
|
||||||
|
updateThreePhasePowerUI(deviceId, state);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateOutletUI(deviceId, state) {
|
||||||
|
const section = document.querySelector(`[data-device-id="${deviceId}"]`);
|
||||||
|
if (!section) return;
|
||||||
|
|
||||||
|
const toggleSwitch = section.querySelector('.toggle-switch');
|
||||||
|
const label = section.querySelector('.toggle-label');
|
||||||
|
|
||||||
|
if (toggleSwitch && label && state.power) {
|
||||||
|
const isOn = state.power === 'on';
|
||||||
|
toggleSwitch.className = `toggle-switch ${isOn ? 'on' : ''}`;
|
||||||
|
label.textContent = isOn ? 'Ein' : 'Aus';
|
||||||
|
|
||||||
|
// Update state display in separate card
|
||||||
|
const cards = section.querySelectorAll('.card');
|
||||||
|
if (cards.length >= 3) { // Header, Control, State
|
||||||
|
const stateCard = cards[2];
|
||||||
|
stateCard.innerHTML = `
|
||||||
|
<div style="font-size: 18px; font-weight: 600; color: ${isOn ? '#34c759' : '#666'};">
|
||||||
|
Status: ${isOn ? 'Eingeschaltet' : 'Ausgeschaltet'}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateThreePhasePowerUI(deviceId, state) {
|
||||||
|
// Update overview
|
||||||
|
const totalPower = document.getElementById(`total-power-${deviceId}`);
|
||||||
|
const energy = document.getElementById(`energy-${deviceId}`);
|
||||||
|
|
||||||
|
if (totalPower && state.total_power != null) {
|
||||||
|
totalPower.textContent = state.total_power.toFixed(0) + ' W';
|
||||||
|
}
|
||||||
|
if (energy && state.energy != null) {
|
||||||
|
energy.textContent = state.energy.toFixed(2) + ' kWh';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update phases
|
||||||
|
const phases = ['phase1', 'phase2', 'phase3'];
|
||||||
|
phases.forEach(phase => {
|
||||||
|
const power = document.getElementById(`${phase}-power-${deviceId}`);
|
||||||
|
const voltage = document.getElementById(`${phase}-voltage-${deviceId}`);
|
||||||
|
const current = document.getElementById(`${phase}-current-${deviceId}`);
|
||||||
|
|
||||||
|
if (power && state[`${phase}_power`] != null) {
|
||||||
|
power.textContent = state[`${phase}_power`].toFixed(0);
|
||||||
|
}
|
||||||
|
if (voltage && state[`${phase}_voltage`] != null) {
|
||||||
|
voltage.textContent = state[`${phase}_voltage`].toFixed(1);
|
||||||
|
}
|
||||||
|
if (current && state[`${phase}_current`] != null) {
|
||||||
|
current.textContent = state[`${phase}_current`].toFixed(2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDeviceIcon(type) {
|
||||||
|
const icons = {
|
||||||
|
'relay': '⚡',
|
||||||
|
'outlet': '⚡',
|
||||||
|
'three_phase_powermeter': '📊'
|
||||||
|
};
|
||||||
|
return icons[type] || '📱';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTypeLabel(type) {
|
||||||
|
const labels = {
|
||||||
|
'relay': 'Relais',
|
||||||
|
'outlet': 'Steckdose',
|
||||||
|
'three_phase_powermeter': 'Dreiphasen-Stromzähler'
|
||||||
|
};
|
||||||
|
return labels[type] || 'Unbekannt';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup on page unload
|
||||||
|
window.addEventListener('beforeunload', () => {
|
||||||
|
window.apiClient.disconnectRealtime();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Load garage devices on page load
|
||||||
|
document.addEventListener('DOMContentLoaded', loadGarageDevices);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -4,6 +4,19 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Home Automation</title>
|
<title>Home Automation</title>
|
||||||
|
|
||||||
|
<!-- Apple Touch Icon -->
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/static/apple-touch-icon-180x180.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="152x152" href="/static/apple-touch-icon-152x152.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="120x120" href="/static/apple-touch-icon-120x120.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="76x76" href="/static/apple-touch-icon-76x76.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="/static/apple-touch-icon-32x32.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="/static/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/manifest.json">
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@@ -464,3 +477,4 @@
|
|||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,18 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>{{ room_name }} - Home Automation</title>
|
<title>{{ room_name }} - Home Automation</title>
|
||||||
|
|
||||||
|
<!-- Apple Touch Icon -->
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/static/apple-touch-icon.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="152x152" href="/static/apple-touch-icon.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="120x120" href="/static/apple-touch-icon.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="/static/apple-touch-icon.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="/static/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/manifest.json">
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@@ -231,6 +243,7 @@
|
|||||||
'thermostat': '🌡️',
|
'thermostat': '🌡️',
|
||||||
'contact': '🚪',
|
'contact': '🚪',
|
||||||
'temp_humidity_sensor': '🌡️',
|
'temp_humidity_sensor': '🌡️',
|
||||||
|
'three_phase_powermeter': '📊',
|
||||||
'relay': '💡',
|
'relay': '💡',
|
||||||
'outlet': '💡',
|
'outlet': '💡',
|
||||||
'cover': '🪟'
|
'cover': '🪟'
|
||||||
@@ -403,6 +416,15 @@
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'three_phase_powermeter':
|
||||||
|
if (state.total_power != null) {
|
||||||
|
html = `<div class="state-primary">${state.total_power.toFixed(0)} W</div>`;
|
||||||
|
if (state.energy != null) {
|
||||||
|
html += `<div class="state-secondary">${state.energy.toFixed(2)} kWh</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 'relay':
|
case 'relay':
|
||||||
case 'outlet':
|
case 'outlet':
|
||||||
if (state.power) {
|
if (state.power) {
|
||||||
|
|||||||
@@ -4,6 +4,19 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Räume - Home Automation</title>
|
<title>Räume - Home Automation</title>
|
||||||
|
|
||||||
|
<!-- Apple Touch Icon -->
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/static/apple-touch-icon-180x180.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="152x152" href="/static/apple-touch-icon-152x152.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="120x120" href="/static/apple-touch-icon-120x120.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="76x76" href="/static/apple-touch-icon-76x76.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="/static/apple-touch-icon-32x32.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="/static/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="Räume">
|
||||||
|
<meta name="theme-color" content="#667eea">
|
||||||
|
<link rel="manifest" href="/manifest.json">
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|||||||
@@ -1,14 +1,4 @@
|
|||||||
version: 1
|
version: 1
|
||||||
mqtt:
|
|
||||||
broker: "172.16.2.16"
|
|
||||||
port: 1883
|
|
||||||
client_id: "home-automation-abstraction"
|
|
||||||
username: null
|
|
||||||
password: null
|
|
||||||
keepalive: 60
|
|
||||||
redis:
|
|
||||||
url: "redis://172.23.1.116:6379/8"
|
|
||||||
channel: "ui:updates"
|
|
||||||
devices:
|
devices:
|
||||||
- device_id: lampe_semeniere_wohnzimmer
|
- device_id: lampe_semeniere_wohnzimmer
|
||||||
name: Semeniere
|
name: Semeniere
|
||||||
@@ -783,6 +773,23 @@ devices:
|
|||||||
set: "shellies/lichtterasse/relay/0/command"
|
set: "shellies/lichtterasse/relay/0/command"
|
||||||
state: "shellies/lichtterasse/relay/0"
|
state: "shellies/lichtterasse/relay/0"
|
||||||
|
|
||||||
|
- device_id: power_relay_caroutlet
|
||||||
|
name: Car Outlet
|
||||||
|
type: relay
|
||||||
|
cap_version: "relay@1.0.0"
|
||||||
|
technology: hottis_modbus
|
||||||
|
features:
|
||||||
|
power: true
|
||||||
|
topics:
|
||||||
|
set: "caroutlet/cmd"
|
||||||
|
state: "caroutlet/state"
|
||||||
|
|
||||||
|
- device_id: powermeter_caroutlet
|
||||||
|
name: Car Outlet
|
||||||
|
type: three_phase_powermeter
|
||||||
|
cap_version: "three_phase_powermeter@1.0.0"
|
||||||
|
technology: hottis_modbus
|
||||||
|
topics:
|
||||||
|
state: "caroutlet/powermeter"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -279,4 +279,15 @@ rooms:
|
|||||||
title: Licht Terasse
|
title: Licht Terasse
|
||||||
icon: 💡
|
icon: 💡
|
||||||
rank: 290
|
rank: 290
|
||||||
|
- name: Garage
|
||||||
|
devices:
|
||||||
|
- device_id: power_relay_caroutlet
|
||||||
|
title: Ladestrom
|
||||||
|
icon: ⚡
|
||||||
|
rank: 310
|
||||||
|
- device_id: powermeter_caroutlet
|
||||||
|
title: Ladestrom
|
||||||
|
icon: 📊
|
||||||
|
rank: 320
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
30
create_icons.py
Normal 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
@@ -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()
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
abstraction | 2025-11-18 12:04:42,875 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/42/1/SET_TEMPERATURE: 21
|
|
||||||
abstraction | 2025-11-18 12:04:42,914 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/45/1/SET_TEMPERATURE: 15
|
|
||||||
abstraction | 2025-11-18 12:04:42,950 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/46/1/SET_TEMPERATURE: 15
|
|
||||||
abstraction | 2025-11-18 12:04:42,987 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/39/1/SET_TEMPERATURE: 22
|
|
||||||
abstraction | 2025-11-18 12:04:43,029 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/41/1/SET_TEMPERATURE: 20
|
|
||||||
abstraction | 2025-11-18 12:04:43,071 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/48/1/SET_TEMPERATURE: 21
|
|
||||||
abstraction | 2025-11-18 12:04:43,108 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/52/1/STATE: false
|
|
||||||
abstraction | 2025-11-18 12:04:43,145 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/26/1/STATE: false
|
|
||||||
abstraction | 2025-11-18 12:04:43,182 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/27/1/STATE: false
|
|
||||||
abstraction | 2025-11-18 12:04:43,219 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/28/1/STATE: false
|
|
||||||
abstraction | 2025-11-18 12:04:43,256 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/29/1/STATE: false
|
|
||||||
abstraction | 2025-11-18 12:04:43,292 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/18/1/STATE: false
|
|
||||||
abstraction | 2025-11-18 12:04:43,331 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/22/1/STATE: false
|
|
||||||
abstraction | 2025-11-18 12:04:43,368 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/44/1/STATE: false
|
|
||||||
abstraction | 2025-11-18 12:04:48,498 - __main__ - DEBUG - MQTT message received on shellies/schrankesszimmer/relay/0: off
|
|
||||||
abstraction | 2025-11-18 12:04:52,989 - __main__ - DEBUG - MQTT message received on zigbee2mqtt/0x00158d0003f052b7: {"battery":100,"humidity":55.04,"linkquality":83,"power_outage_count":38416,"pressure":1002.6,"temperature":22.13,"voltage":3015}
|
|
||||||
abstraction | 2025-11-18 12:04:53,024 - __main__ - DEBUG - MQTT message received on zigbee2mqtt/0x00158d0003f052b7: {"battery":100,"humidity":54.82,"linkquality":83,"power_outage_count":38416,"pressure":1002.6,"temperature":22.13,"voltage":3015}
|
|
||||||
abstraction | 2025-11-18 12:04:53,061 - __main__ - DEBUG - MQTT message received on zigbee2mqtt/0x00158d0003f052b7: {"battery":100,"humidity":54.82,"linkquality":83,"power_outage_count":38416,"pressure":1002.4,"temperature":22.13,"voltage":3015}
|
|
||||||
abstraction | 2025-11-18 12:05:03,058 - __main__ - DEBUG - MQTT message received on shellies/lichtterasse/relay/0: off
|
|
||||||
abstraction | 2025-11-18 12:05:08,209 - __main__ - DEBUG - MQTT message received on shellies/wohnzimmer-regal/relay/0: off
|
|
||||||
abstraction | 2025-11-18 12:05:10,881 - __main__ - DEBUG - MQTT message received on shellies/LightKitchenSink/relay/0: on
|
|
||||||
abstraction | 2025-11-18 12:05:12,622 - __main__ - DEBUG - MQTT message received on zigbee2mqtt/0x00158d00083299bb: {"battery":63,"humidity":47.69,"linkquality":87,"power_outage_count":4906,"pressure":1009.9,"temperature":19.74,"voltage":2945}
|
|
||||||
abstraction | 2025-11-18 12:05:12,656 - __main__ - DEBUG - MQTT message received on zigbee2mqtt/0x00158d00083299bb: {"battery":63,"humidity":47.69,"linkquality":87,"power_outage_count":4906,"pressure":1009.9,"temperature":19.74,"voltage":2945}
|
|
||||||
abstraction | 2025-11-18 12:05:12,690 - __main__ - DEBUG - MQTT message received on zigbee2mqtt/0x00158d00083299bb: {"battery":63,"humidity":47.69,"linkquality":87,"power_outage_count":4906,"pressure":1009.7,"temperature":19.74,"voltage":2945}
|
|
||||||
abstraction | 2025-11-18 12:05:18,507 - __main__ - DEBUG - MQTT message received on shellies/schrankesszimmer/relay/0: off
|
|
||||||
@@ -1,261 +0,0 @@
|
|||||||
abstraction | 2025-11-18 12:04:40,901 - asyncio - DEBUG - Using selector: EpollSelector
|
|
||||||
abstraction | 2025-11-18 12:04:40,952 - __main__ - INFO - Loaded configuration from /app/config/devices.yaml
|
|
||||||
abstraction | 2025-11-18 12:04:40,953 - __main__ - INFO - Loaded 64 device(s): lampe_semeniere_wohnzimmer, stehlampe_esszimmer_spiegel, stehlampe_esszimmer_schrank, grosse_lampe_wohnzimmer, lampe_naehtischchen_wohnzimmer, kleine_lampe_rechts_esszimmer, kleine_lampe_links_esszimmer, leselampe_esszimmer, medusalampe_schlafzimmer, sportlicht_am_fernseher_studierzimmer, deckenlampe_schlafzimmer, bettlicht_wolfgang, bettlicht_patty, schranklicht_hinten_patty, schranklicht_vorne_patty, leselampe_patty, deckenlampe_esszimmer, standlampe_esszimmer, haustuer, deckenlampe_flur_oben, kueche_deckenlampe, sportlicht_tisch, sportlicht_regal, licht_flur_oben_am_spiegel, experimentlabtest, thermostat_wolfgang, thermostat_kueche, thermostat_schlafzimmer, thermostat_esszimmer, thermostat_wohnzimmer, thermostat_patty, thermostat_bad_oben, thermostat_bad_unten, sterne_wohnzimmer, kontakt_schlafzimmer_strasse, kontakt_esszimmer_strasse_rechts, kontakt_esszimmer_strasse_links, kontakt_wohnzimmer_garten_rechts, kontakt_wohnzimmer_garten_links, kontakt_kueche_garten_fenster, kontakt_kueche_garten_tuer, kontakt_kueche_strasse_rechts, kontakt_kueche_strasse_links, kontakt_patty_garten_rechts, kontakt_patty_garten_links, kontakt_patty_strasse, kontakt_wolfgang_garten, kontakt_bad_oben_strasse, kontakt_bad_unten_strasse, sensor_schlafzimmer, sensor_wohnzimmer, sensor_kueche, sensor_arbeitszimmer_patty, sensor_arbeitszimmer_wolfgang, sensor_bad_oben, sensor_bad_unten, sensor_flur, sensor_waschkueche, sensor_sportzimmer, licht_spuele_kueche, licht_schrank_esszimmer, licht_regal_wohnzimmer, licht_flur_schrank, licht_terasse
|
|
||||||
abstraction | 2025-11-18 12:04:40,953 - __main__ - INFO - Loaded 64 device(s) from configuration
|
|
||||||
abstraction | 2025-11-18 12:04:41,003 - __main__ - INFO - Connected to Redis: redis://172.23.1.116:6379/8
|
|
||||||
abstraction | 2025-11-18 12:04:41,003 - __main__ - INFO - Abstraction worker started
|
|
||||||
abstraction | 2025-11-18 12:04:41,003 - __main__ - INFO - Connecting to MQTT broker: 172.23.1.102:1883
|
|
||||||
abstraction | 2025-11-18 12:04:41,053 - __main__ - INFO - Connected to MQTT broker as home-automation-abstraction-b39304
|
|
||||||
abstraction | 2025-11-18 12:04:41,072 - __main__ - INFO - Subscribed to abstract SET: home/relay/lampe_semeniere_wohnzimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,091 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b8000015480b
|
|
||||||
abstraction | 2025-11-18 12:04:41,107 - __main__ - INFO - Subscribed to abstract SET: home/light/stehlampe_esszimmer_spiegel/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,125 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x001788010d06ea09
|
|
||||||
abstraction | 2025-11-18 12:04:41,141 - __main__ - INFO - Subscribed to abstract SET: home/light/stehlampe_esszimmer_schrank/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,159 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x001788010d09176c
|
|
||||||
abstraction | 2025-11-18 12:04:41,176 - __main__ - INFO - Subscribed to abstract SET: home/relay/grosse_lampe_wohnzimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,192 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b80000151aca
|
|
||||||
abstraction | 2025-11-18 12:04:41,209 - __main__ - INFO - Subscribed to abstract SET: home/relay/lampe_naehtischchen_wohnzimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,225 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x842e14fffee560ee
|
|
||||||
abstraction | 2025-11-18 12:04:41,242 - __main__ - INFO - Subscribed to abstract SET: home/relay/kleine_lampe_rechts_esszimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,259 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b80000156645
|
|
||||||
abstraction | 2025-11-18 12:04:41,276 - __main__ - INFO - Subscribed to abstract SET: home/relay/kleine_lampe_links_esszimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,293 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b80000153099
|
|
||||||
abstraction | 2025-11-18 12:04:41,310 - __main__ - INFO - Subscribed to abstract SET: home/light/leselampe_esszimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,327 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xec1bbdfffe7b84f2
|
|
||||||
abstraction | 2025-11-18 12:04:41,344 - __main__ - INFO - Subscribed to abstract SET: home/relay/medusalampe_schlafzimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,361 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b80000154c7c
|
|
||||||
abstraction | 2025-11-18 12:04:41,378 - __main__ - INFO - Subscribed to abstract SET: home/light/sportlicht_am_fernseher_studierzimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,395 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x842e14fffe76a23a
|
|
||||||
abstraction | 2025-11-18 12:04:41,415 - __main__ - INFO - Subscribed to abstract SET: home/light/deckenlampe_schlafzimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,432 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x0017880108a406a7
|
|
||||||
abstraction | 2025-11-18 12:04:41,449 - __main__ - INFO - Subscribed to abstract SET: home/light/bettlicht_wolfgang/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,466 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00178801081570bf
|
|
||||||
abstraction | 2025-11-18 12:04:41,484 - __main__ - INFO - Subscribed to abstract SET: home/light/bettlicht_patty/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,500 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x0017880108158b32
|
|
||||||
abstraction | 2025-11-18 12:04:41,518 - __main__ - INFO - Subscribed to abstract SET: home/light/schranklicht_hinten_patty/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,535 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x0017880106e29571
|
|
||||||
abstraction | 2025-11-18 12:04:41,552 - __main__ - INFO - Subscribed to abstract SET: home/relay/schranklicht_vorne_patty/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,569 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b80000154cf5
|
|
||||||
abstraction | 2025-11-18 12:04:41,586 - __main__ - INFO - Subscribed to abstract SET: home/light/leselampe_patty/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,603 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x001788010600ec9d
|
|
||||||
abstraction | 2025-11-18 12:04:41,620 - __main__ - INFO - Subscribed to abstract SET: home/light/deckenlampe_esszimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,637 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x0017880108a03e45
|
|
||||||
abstraction | 2025-11-18 12:04:41,655 - __main__ - INFO - Subscribed to abstract SET: home/light/standlampe_esszimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,674 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xbc33acfffe21f547
|
|
||||||
abstraction | 2025-11-18 12:04:41,692 - __main__ - INFO - Subscribed to abstract SET: home/light/haustuer/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,711 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xec1bbdfffea6a3da
|
|
||||||
abstraction | 2025-11-18 12:04:41,728 - __main__ - INFO - Subscribed to abstract SET: home/light/deckenlampe_flur_oben/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,746 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x001788010d2123a7
|
|
||||||
abstraction | 2025-11-18 12:04:41,764 - __main__ - INFO - Subscribed to abstract SET: home/light/kueche_deckenlampe/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,781 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x001788010d2c40c4
|
|
||||||
abstraction | 2025-11-18 12:04:41,798 - __main__ - INFO - Subscribed to abstract SET: home/light/sportlicht_tisch/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,814 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b8be2409f31b
|
|
||||||
abstraction | 2025-11-18 12:04:41,831 - __main__ - INFO - Subscribed to abstract SET: home/light/sportlicht_regal/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,848 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b8be2409f569
|
|
||||||
abstraction | 2025-11-18 12:04:41,865 - __main__ - INFO - Subscribed to abstract SET: home/light/licht_flur_oben_am_spiegel/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,883 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x842e14fffefe4ba4
|
|
||||||
abstraction | 2025-11-18 12:04:41,899 - __main__ - INFO - Subscribed to abstract SET: home/light/experimentlabtest/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,918 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b80000195038
|
|
||||||
abstraction | 2025-11-18 12:04:41,936 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_wolfgang/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,955 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x540f57fffe7e3cfe
|
|
||||||
abstraction | 2025-11-18 12:04:41,974 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_kueche/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,991 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x94deb8fffe2e5c06
|
|
||||||
abstraction | 2025-11-18 12:04:42,008 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_schlafzimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:42,025 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/42/1/SET_TEMPERATURE
|
|
||||||
abstraction | 2025-11-18 12:04:42,042 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_esszimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:42,059 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/45/1/SET_TEMPERATURE
|
|
||||||
abstraction | 2025-11-18 12:04:42,080 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_wohnzimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:42,097 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/46/1/SET_TEMPERATURE
|
|
||||||
abstraction | 2025-11-18 12:04:42,114 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_patty/set
|
|
||||||
abstraction | 2025-11-18 12:04:42,131 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/39/1/SET_TEMPERATURE
|
|
||||||
abstraction | 2025-11-18 12:04:42,150 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_bad_oben/set
|
|
||||||
abstraction | 2025-11-18 12:04:42,171 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/41/1/SET_TEMPERATURE
|
|
||||||
abstraction | 2025-11-18 12:04:42,189 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_bad_unten/set
|
|
||||||
abstraction | 2025-11-18 12:04:42,207 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/48/1/SET_TEMPERATURE
|
|
||||||
abstraction | 2025-11-18 12:04:42,224 - __main__ - INFO - Subscribed to abstract SET: home/relay/sterne_wohnzimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:42,240 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b80000155fc2
|
|
||||||
abstraction | 2025-11-18 12:04:42,240 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_schlafzimmer_strasse
|
|
||||||
abstraction | 2025-11-18 12:04:42,258 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/52/1/STATE
|
|
||||||
abstraction | 2025-11-18 12:04:42,258 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_esszimmer_strasse_rechts
|
|
||||||
abstraction | 2025-11-18 12:04:42,275 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/26/1/STATE
|
|
||||||
abstraction | 2025-11-18 12:04:42,275 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_esszimmer_strasse_links
|
|
||||||
abstraction | 2025-11-18 12:04:42,293 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/27/1/STATE
|
|
||||||
abstraction | 2025-11-18 12:04:42,293 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_wohnzimmer_garten_rechts
|
|
||||||
abstraction | 2025-11-18 12:04:42,313 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/28/1/STATE
|
|
||||||
abstraction | 2025-11-18 12:04:42,313 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_wohnzimmer_garten_links
|
|
||||||
abstraction | 2025-11-18 12:04:42,331 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/29/1/STATE
|
|
||||||
abstraction | 2025-11-18 12:04:42,331 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_kueche_garten_fenster
|
|
||||||
abstraction | 2025-11-18 12:04:42,351 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d008b332785
|
|
||||||
abstraction | 2025-11-18 12:04:42,351 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_kueche_garten_tuer
|
|
||||||
abstraction | 2025-11-18 12:04:42,371 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d008b332788
|
|
||||||
abstraction | 2025-11-18 12:04:42,371 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_kueche_strasse_rechts
|
|
||||||
abstraction | 2025-11-18 12:04:42,390 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d008b151803
|
|
||||||
abstraction | 2025-11-18 12:04:42,390 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_kueche_strasse_links
|
|
||||||
abstraction | 2025-11-18 12:04:42,408 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d008b331d0b
|
|
||||||
abstraction | 2025-11-18 12:04:42,408 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_patty_garten_rechts
|
|
||||||
abstraction | 2025-11-18 12:04:42,424 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/18/1/STATE
|
|
||||||
abstraction | 2025-11-18 12:04:42,424 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_patty_garten_links
|
|
||||||
abstraction | 2025-11-18 12:04:42,441 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/22/1/STATE
|
|
||||||
abstraction | 2025-11-18 12:04:42,441 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_patty_strasse
|
|
||||||
abstraction | 2025-11-18 12:04:42,462 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d000af457cf
|
|
||||||
abstraction | 2025-11-18 12:04:42,462 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_wolfgang_garten
|
|
||||||
abstraction | 2025-11-18 12:04:42,479 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d008b3328da
|
|
||||||
abstraction | 2025-11-18 12:04:42,480 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_bad_oben_strasse
|
|
||||||
abstraction | 2025-11-18 12:04:42,496 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d008b333aec
|
|
||||||
abstraction | 2025-11-18 12:04:42,496 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_bad_unten_strasse
|
|
||||||
abstraction | 2025-11-18 12:04:42,513 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/44/1/STATE
|
|
||||||
abstraction | 2025-11-18 12:04:42,513 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_schlafzimmer
|
|
||||||
abstraction | 2025-11-18 12:04:42,532 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d00043292dc
|
|
||||||
abstraction | 2025-11-18 12:04:42,532 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_wohnzimmer
|
|
||||||
abstraction | 2025-11-18 12:04:42,552 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d0008975707
|
|
||||||
abstraction | 2025-11-18 12:04:42,552 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_kueche
|
|
||||||
abstraction | 2025-11-18 12:04:42,571 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d00083299bb
|
|
||||||
abstraction | 2025-11-18 12:04:42,571 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_arbeitszimmer_patty
|
|
||||||
abstraction | 2025-11-18 12:04:42,589 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d0003f052b7
|
|
||||||
abstraction | 2025-11-18 12:04:42,589 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_arbeitszimmer_wolfgang
|
|
||||||
abstraction | 2025-11-18 12:04:42,608 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d000543fb99
|
|
||||||
abstraction | 2025-11-18 12:04:42,608 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_bad_oben
|
|
||||||
abstraction | 2025-11-18 12:04:42,625 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d00093e8987
|
|
||||||
abstraction | 2025-11-18 12:04:42,625 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_bad_unten
|
|
||||||
abstraction | 2025-11-18 12:04:42,645 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d00093e662a
|
|
||||||
abstraction | 2025-11-18 12:04:42,645 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_flur
|
|
||||||
abstraction | 2025-11-18 12:04:42,664 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d000836ccc6
|
|
||||||
abstraction | 2025-11-18 12:04:42,664 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_waschkueche
|
|
||||||
abstraction | 2025-11-18 12:04:42,682 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d000449f3bc
|
|
||||||
abstraction | 2025-11-18 12:04:42,682 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_sportzimmer
|
|
||||||
abstraction | 2025-11-18 12:04:42,699 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d0009421422
|
|
||||||
abstraction | 2025-11-18 12:04:42,716 - __main__ - INFO - Subscribed to abstract SET: home/relay/licht_spuele_kueche/set
|
|
||||||
abstraction | 2025-11-18 12:04:42,734 - __main__ - INFO - Subscribed to vendor STATE: shellies/LightKitchenSink/relay/0
|
|
||||||
abstraction | 2025-11-18 12:04:42,751 - __main__ - INFO - Subscribed to abstract SET: home/relay/licht_schrank_esszimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:42,770 - __main__ - INFO - Subscribed to vendor STATE: shellies/schrankesszimmer/relay/0
|
|
||||||
abstraction | 2025-11-18 12:04:42,790 - __main__ - INFO - Subscribed to abstract SET: home/relay/licht_regal_wohnzimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:42,807 - __main__ - INFO - Subscribed to vendor STATE: shellies/wohnzimmer-regal/relay/0
|
|
||||||
abstraction | 2025-11-18 12:04:42,823 - __main__ - INFO - Subscribed to abstract SET: home/relay/licht_flur_schrank/set
|
|
||||||
abstraction | 2025-11-18 12:04:42,841 - __main__ - INFO - Subscribed to vendor STATE: shellies/schrankflur/relay/0
|
|
||||||
abstraction | 2025-11-18 12:04:42,858 - __main__ - INFO - Subscribed to abstract SET: home/relay/licht_terasse/set
|
|
||||||
abstraction | 2025-11-18 12:04:42,875 - __main__ - INFO - Subscribed to vendor STATE: shellies/lichtterasse/relay/0
|
|
||||||
abstraction | 2025-11-18 12:04:42,875 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/42/1/SET_TEMPERATURE: 21
|
|
||||||
abstraction | 2025-11-18 12:04:42,875 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=thermostat, tech=max, payload=21
|
|
||||||
abstraction | 2025-11-18 12:04:42,876 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=thermostat, tech=max, payload={'target': 21.0, 'mode': 'heat'}
|
|
||||||
abstraction | 2025-11-18 12:04:42,876 - __main__ - INFO - ← abstract STATE thermostat_schlafzimmer: home/thermostat/thermostat_schlafzimmer/state → {"target": 21.0, "mode": "heat"}
|
|
||||||
abstraction | 2025-11-18 12:04:42,897 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "thermostat_schlafzimmer", "payload": {"target": 21.0, "mode": "heat"}, "ts": "2025-11-18T12:04:42.897310+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:42,914 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/45/1/SET_TEMPERATURE: 15
|
|
||||||
abstraction | 2025-11-18 12:04:42,914 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=thermostat, tech=max, payload=15
|
|
||||||
abstraction | 2025-11-18 12:04:42,914 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=thermostat, tech=max, payload={'target': 15.0, 'mode': 'heat'}
|
|
||||||
abstraction | 2025-11-18 12:04:42,914 - __main__ - INFO - ← abstract STATE thermostat_esszimmer: home/thermostat/thermostat_esszimmer/state → {"target": 15.0, "mode": "heat"}
|
|
||||||
abstraction | 2025-11-18 12:04:42,934 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "thermostat_esszimmer", "payload": {"target": 15.0, "mode": "heat"}, "ts": "2025-11-18T12:04:42.934255+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:42,950 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/46/1/SET_TEMPERATURE: 15
|
|
||||||
abstraction | 2025-11-18 12:04:42,950 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=thermostat, tech=max, payload=15
|
|
||||||
abstraction | 2025-11-18 12:04:42,950 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=thermostat, tech=max, payload={'target': 15.0, 'mode': 'heat'}
|
|
||||||
abstraction | 2025-11-18 12:04:42,951 - __main__ - INFO - ← abstract STATE thermostat_wohnzimmer: home/thermostat/thermostat_wohnzimmer/state → {"target": 15.0, "mode": "heat"}
|
|
||||||
abstraction | 2025-11-18 12:04:42,970 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "thermostat_wohnzimmer", "payload": {"target": 15.0, "mode": "heat"}, "ts": "2025-11-18T12:04:42.970936+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:42,987 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/39/1/SET_TEMPERATURE: 22
|
|
||||||
abstraction | 2025-11-18 12:04:42,988 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=thermostat, tech=max, payload=22
|
|
||||||
abstraction | 2025-11-18 12:04:42,988 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=thermostat, tech=max, payload={'target': 22.0, 'mode': 'heat'}
|
|
||||||
abstraction | 2025-11-18 12:04:42,988 - __main__ - INFO - ← abstract STATE thermostat_patty: home/thermostat/thermostat_patty/state → {"target": 22.0, "mode": "heat"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,009 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "thermostat_patty", "payload": {"target": 22.0, "mode": "heat"}, "ts": "2025-11-18T12:04:43.009673+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,029 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/41/1/SET_TEMPERATURE: 20
|
|
||||||
abstraction | 2025-11-18 12:04:43,029 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=thermostat, tech=max, payload=20
|
|
||||||
abstraction | 2025-11-18 12:04:43,029 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=thermostat, tech=max, payload={'target': 20.0, 'mode': 'heat'}
|
|
||||||
abstraction | 2025-11-18 12:04:43,029 - __main__ - INFO - ← abstract STATE thermostat_bad_oben: home/thermostat/thermostat_bad_oben/state → {"target": 20.0, "mode": "heat"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,053 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "thermostat_bad_oben", "payload": {"target": 20.0, "mode": "heat"}, "ts": "2025-11-18T12:04:43.053895+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,071 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/48/1/SET_TEMPERATURE: 21
|
|
||||||
abstraction | 2025-11-18 12:04:43,071 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=thermostat, tech=max, payload=21
|
|
||||||
abstraction | 2025-11-18 12:04:43,071 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=thermostat, tech=max, payload={'target': 21.0, 'mode': 'heat'}
|
|
||||||
abstraction | 2025-11-18 12:04:43,072 - __main__ - INFO - ← abstract STATE thermostat_bad_unten: home/thermostat/thermostat_bad_unten/state → {"target": 21.0, "mode": "heat"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,092 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "thermostat_bad_unten", "payload": {"target": 21.0, "mode": "heat"}, "ts": "2025-11-18T12:04:43.092210+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,108 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/52/1/STATE: false
|
|
||||||
abstraction | 2025-11-18 12:04:43,108 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=false
|
|
||||||
abstraction | 2025-11-18 12:04:43,108 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'closed'}
|
|
||||||
abstraction | 2025-11-18 12:04:43,109 - __main__ - INFO - ← abstract STATE kontakt_schlafzimmer_strasse: home/contact/kontakt_schlafzimmer_strasse/state → {"contact": "closed"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,128 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_schlafzimmer_strasse", "payload": {"contact": "closed"}, "ts": "2025-11-18T12:04:43.128506+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,145 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/26/1/STATE: false
|
|
||||||
abstraction | 2025-11-18 12:04:43,145 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=false
|
|
||||||
abstraction | 2025-11-18 12:04:43,145 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'closed'}
|
|
||||||
abstraction | 2025-11-18 12:04:43,146 - __main__ - INFO - ← abstract STATE kontakt_esszimmer_strasse_rechts: home/contact/kontakt_esszimmer_strasse_rechts/state → {"contact": "closed"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,165 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_esszimmer_strasse_rechts", "payload": {"contact": "closed"}, "ts": "2025-11-18T12:04:43.165958+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,182 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/27/1/STATE: false
|
|
||||||
abstraction | 2025-11-18 12:04:43,182 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=false
|
|
||||||
abstraction | 2025-11-18 12:04:43,183 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'closed'}
|
|
||||||
abstraction | 2025-11-18 12:04:43,183 - __main__ - INFO - ← abstract STATE kontakt_esszimmer_strasse_links: home/contact/kontakt_esszimmer_strasse_links/state → {"contact": "closed"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,202 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_esszimmer_strasse_links", "payload": {"contact": "closed"}, "ts": "2025-11-18T12:04:43.202580+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,219 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/28/1/STATE: false
|
|
||||||
abstraction | 2025-11-18 12:04:43,219 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=false
|
|
||||||
abstraction | 2025-11-18 12:04:43,219 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'closed'}
|
|
||||||
abstraction | 2025-11-18 12:04:43,220 - __main__ - INFO - ← abstract STATE kontakt_wohnzimmer_garten_rechts: home/contact/kontakt_wohnzimmer_garten_rechts/state → {"contact": "closed"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,239 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_wohnzimmer_garten_rechts", "payload": {"contact": "closed"}, "ts": "2025-11-18T12:04:43.239653+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,256 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/29/1/STATE: false
|
|
||||||
abstraction | 2025-11-18 12:04:43,256 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=false
|
|
||||||
abstraction | 2025-11-18 12:04:43,256 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'closed'}
|
|
||||||
abstraction | 2025-11-18 12:04:43,257 - __main__ - INFO - ← abstract STATE kontakt_wohnzimmer_garten_links: home/contact/kontakt_wohnzimmer_garten_links/state → {"contact": "closed"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,275 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_wohnzimmer_garten_links", "payload": {"contact": "closed"}, "ts": "2025-11-18T12:04:43.275832+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,292 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/18/1/STATE: false
|
|
||||||
abstraction | 2025-11-18 12:04:43,292 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=false
|
|
||||||
abstraction | 2025-11-18 12:04:43,292 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'closed'}
|
|
||||||
abstraction | 2025-11-18 12:04:43,293 - __main__ - INFO - ← abstract STATE kontakt_patty_garten_rechts: home/contact/kontakt_patty_garten_rechts/state → {"contact": "closed"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,314 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_patty_garten_rechts", "payload": {"contact": "closed"}, "ts": "2025-11-18T12:04:43.314579+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,331 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/22/1/STATE: false
|
|
||||||
abstraction | 2025-11-18 12:04:43,331 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=false
|
|
||||||
abstraction | 2025-11-18 12:04:43,331 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'closed'}
|
|
||||||
abstraction | 2025-11-18 12:04:43,332 - __main__ - INFO - ← abstract STATE kontakt_patty_garten_links: home/contact/kontakt_patty_garten_links/state → {"contact": "closed"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,351 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_patty_garten_links", "payload": {"contact": "closed"}, "ts": "2025-11-18T12:04:43.351704+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,368 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/44/1/STATE: false
|
|
||||||
abstraction | 2025-11-18 12:04:43,368 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=false
|
|
||||||
abstraction | 2025-11-18 12:04:43,368 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'closed'}
|
|
||||||
abstraction | 2025-11-18 12:04:43,369 - __main__ - INFO - ← abstract STATE kontakt_bad_unten_strasse: home/contact/kontakt_bad_unten_strasse/state → {"contact": "closed"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,388 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_bad_unten_strasse", "payload": {"contact": "closed"}, "ts": "2025-11-18T12:04:43.388390+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:48,498 - __main__ - DEBUG - MQTT message received on shellies/schrankesszimmer/relay/0: off
|
|
||||||
abstraction | 2025-11-18 12:04:48,498 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 12:04:48,498 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 12:04:48,498 - __main__ - INFO - ← abstract STATE licht_schrank_esszimmer: home/relay/licht_schrank_esszimmer/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 12:04:48,518 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_schrank_esszimmer", "payload": {"power": "off"}, "ts": "2025-11-18T12:04:48.518525+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:52,989 - __main__ - DEBUG - MQTT message received on zigbee2mqtt/0x00158d0003f052b7: {"battery":100,"humidity":55.04,"linkquality":83,"power_outage_count":38416,"pressure":1002.6,"temperature":22.13,"voltage":3015}
|
|
||||||
abstraction | 2025-11-18 12:04:52,989 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={"battery":100,"humidity":55.04,"linkquality":83,"power_outage_count":38416,"pressure":1002.6,"temperature":22.13,"voltage":3015}
|
|
||||||
abstraction | 2025-11-18 12:04:52,989 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={'battery': 100, 'humidity': 55.04, 'linkquality': 83, 'power_outage_count': 38416, 'pressure': 1002.6, 'temperature': 22.13, 'voltage': 3015}
|
|
||||||
abstraction | 2025-11-18 12:04:52,989 - __main__ - INFO - ← abstract STATE sensor_arbeitszimmer_patty: home/temp_humidity/sensor_arbeitszimmer_patty/state → {"battery": 100, "humidity": 55.04, "linkquality": 83, "power_outage_count": 38416, "pressure": 1002.6, "temperature": 22.13, "voltage": 3015}
|
|
||||||
abstraction | 2025-11-18 12:04:53,009 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "sensor_arbeitszimmer_patty", "payload": {"battery": 100, "humidity": 55.04, "linkquality": 83, "power_outage_count": 38416, "pressure": 1002.6, "temperature": 22.13, "voltage": 3015}, "ts": "2025-11-18T12:04:53.009776+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:53,024 - __main__ - DEBUG - MQTT message received on zigbee2mqtt/0x00158d0003f052b7: {"battery":100,"humidity":54.82,"linkquality":83,"power_outage_count":38416,"pressure":1002.6,"temperature":22.13,"voltage":3015}
|
|
||||||
abstraction | 2025-11-18 12:04:53,025 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={"battery":100,"humidity":54.82,"linkquality":83,"power_outage_count":38416,"pressure":1002.6,"temperature":22.13,"voltage":3015}
|
|
||||||
abstraction | 2025-11-18 12:04:53,025 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={'battery': 100, 'humidity': 54.82, 'linkquality': 83, 'power_outage_count': 38416, 'pressure': 1002.6, 'temperature': 22.13, 'voltage': 3015}
|
|
||||||
abstraction | 2025-11-18 12:04:53,025 - __main__ - INFO - ← abstract STATE sensor_arbeitszimmer_patty: home/temp_humidity/sensor_arbeitszimmer_patty/state → {"battery": 100, "humidity": 54.82, "linkquality": 83, "power_outage_count": 38416, "pressure": 1002.6, "temperature": 22.13, "voltage": 3015}
|
|
||||||
abstraction | 2025-11-18 12:04:53,044 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "sensor_arbeitszimmer_patty", "payload": {"battery": 100, "humidity": 54.82, "linkquality": 83, "power_outage_count": 38416, "pressure": 1002.6, "temperature": 22.13, "voltage": 3015}, "ts": "2025-11-18T12:04:53.044379+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:53,061 - __main__ - DEBUG - MQTT message received on zigbee2mqtt/0x00158d0003f052b7: {"battery":100,"humidity":54.82,"linkquality":83,"power_outage_count":38416,"pressure":1002.4,"temperature":22.13,"voltage":3015}
|
|
||||||
abstraction | 2025-11-18 12:04:53,061 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={"battery":100,"humidity":54.82,"linkquality":83,"power_outage_count":38416,"pressure":1002.4,"temperature":22.13,"voltage":3015}
|
|
||||||
abstraction | 2025-11-18 12:04:53,061 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={'battery': 100, 'humidity': 54.82, 'linkquality': 83, 'power_outage_count': 38416, 'pressure': 1002.4, 'temperature': 22.13, 'voltage': 3015}
|
|
||||||
abstraction | 2025-11-18 12:04:53,061 - __main__ - INFO - ← abstract STATE sensor_arbeitszimmer_patty: home/temp_humidity/sensor_arbeitszimmer_patty/state → {"battery": 100, "humidity": 54.82, "linkquality": 83, "power_outage_count": 38416, "pressure": 1002.4, "temperature": 22.13, "voltage": 3015}
|
|
||||||
abstraction | 2025-11-18 12:04:53,084 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "sensor_arbeitszimmer_patty", "payload": {"battery": 100, "humidity": 54.82, "linkquality": 83, "power_outage_count": 38416, "pressure": 1002.4, "temperature": 22.13, "voltage": 3015}, "ts": "2025-11-18T12:04:53.083988+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:05:03,058 - __main__ - DEBUG - MQTT message received on shellies/lichtterasse/relay/0: off
|
|
||||||
abstraction | 2025-11-18 12:05:03,058 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 12:05:03,058 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 12:05:03,058 - __main__ - INFO - ← abstract STATE licht_terasse: home/relay/licht_terasse/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 12:05:03,075 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_terasse", "payload": {"power": "off"}, "ts": "2025-11-18T12:05:03.075262+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:05:08,209 - __main__ - DEBUG - MQTT message received on shellies/wohnzimmer-regal/relay/0: off
|
|
||||||
abstraction | 2025-11-18 12:05:08,210 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 12:05:08,210 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 12:05:08,210 - __main__ - INFO - ← abstract STATE licht_regal_wohnzimmer: home/relay/licht_regal_wohnzimmer/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 12:05:08,228 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_regal_wohnzimmer", "payload": {"power": "off"}, "ts": "2025-11-18T12:05:08.228758+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:05:10,881 - __main__ - DEBUG - MQTT message received on shellies/LightKitchenSink/relay/0: on
|
|
||||||
abstraction | 2025-11-18 12:05:10,881 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=on
|
|
||||||
abstraction | 2025-11-18 12:05:10,881 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'on'}
|
|
||||||
abstraction | 2025-11-18 12:05:10,881 - __main__ - INFO - ← abstract STATE licht_spuele_kueche: home/relay/licht_spuele_kueche/state → {"power": "on"}
|
|
||||||
abstraction | 2025-11-18 12:05:10,899 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_spuele_kueche", "payload": {"power": "on"}, "ts": "2025-11-18T12:05:10.899207+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:05:12,622 - __main__ - DEBUG - MQTT message received on zigbee2mqtt/0x00158d00083299bb: {"battery":63,"humidity":47.69,"linkquality":87,"power_outage_count":4906,"pressure":1009.9,"temperature":19.74,"voltage":2945}
|
|
||||||
abstraction | 2025-11-18 12:05:12,622 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={"battery":63,"humidity":47.69,"linkquality":87,"power_outage_count":4906,"pressure":1009.9,"temperature":19.74,"voltage":2945}
|
|
||||||
abstraction | 2025-11-18 12:05:12,622 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={'battery': 63, 'humidity': 47.69, 'linkquality': 87, 'power_outage_count': 4906, 'pressure': 1009.9, 'temperature': 19.74, 'voltage': 2945}
|
|
||||||
abstraction | 2025-11-18 12:05:12,622 - __main__ - INFO - ← abstract STATE sensor_kueche: home/temp_humidity/sensor_kueche/state → {"battery": 63, "humidity": 47.69, "linkquality": 87, "power_outage_count": 4906, "pressure": 1009.9, "temperature": 19.74, "voltage": 2945}
|
|
||||||
abstraction | 2025-11-18 12:05:12,640 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "sensor_kueche", "payload": {"battery": 63, "humidity": 47.69, "linkquality": 87, "power_outage_count": 4906, "pressure": 1009.9, "temperature": 19.74, "voltage": 2945}, "ts": "2025-11-18T12:05:12.640129+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:05:12,656 - __main__ - DEBUG - MQTT message received on zigbee2mqtt/0x00158d00083299bb: {"battery":63,"humidity":47.69,"linkquality":87,"power_outage_count":4906,"pressure":1009.9,"temperature":19.74,"voltage":2945}
|
|
||||||
abstraction | 2025-11-18 12:05:12,656 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={"battery":63,"humidity":47.69,"linkquality":87,"power_outage_count":4906,"pressure":1009.9,"temperature":19.74,"voltage":2945}
|
|
||||||
abstraction | 2025-11-18 12:05:12,656 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={'battery': 63, 'humidity': 47.69, 'linkquality': 87, 'power_outage_count': 4906, 'pressure': 1009.9, 'temperature': 19.74, 'voltage': 2945}
|
|
||||||
abstraction | 2025-11-18 12:05:12,657 - __main__ - INFO - ← abstract STATE sensor_kueche: home/temp_humidity/sensor_kueche/state → {"battery": 63, "humidity": 47.69, "linkquality": 87, "power_outage_count": 4906, "pressure": 1009.9, "temperature": 19.74, "voltage": 2945}
|
|
||||||
abstraction | 2025-11-18 12:05:12,674 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "sensor_kueche", "payload": {"battery": 63, "humidity": 47.69, "linkquality": 87, "power_outage_count": 4906, "pressure": 1009.9, "temperature": 19.74, "voltage": 2945}, "ts": "2025-11-18T12:05:12.674372+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:05:12,690 - __main__ - DEBUG - MQTT message received on zigbee2mqtt/0x00158d00083299bb: {"battery":63,"humidity":47.69,"linkquality":87,"power_outage_count":4906,"pressure":1009.7,"temperature":19.74,"voltage":2945}
|
|
||||||
abstraction | 2025-11-18 12:05:12,690 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={"battery":63,"humidity":47.69,"linkquality":87,"power_outage_count":4906,"pressure":1009.7,"temperature":19.74,"voltage":2945}
|
|
||||||
abstraction | 2025-11-18 12:05:12,690 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={'battery': 63, 'humidity': 47.69, 'linkquality': 87, 'power_outage_count': 4906, 'pressure': 1009.7, 'temperature': 19.74, 'voltage': 2945}
|
|
||||||
abstraction | 2025-11-18 12:05:12,690 - __main__ - INFO - ← abstract STATE sensor_kueche: home/temp_humidity/sensor_kueche/state → {"battery": 63, "humidity": 47.69, "linkquality": 87, "power_outage_count": 4906, "pressure": 1009.7, "temperature": 19.74, "voltage": 2945}
|
|
||||||
abstraction | 2025-11-18 12:05:12,708 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "sensor_kueche", "payload": {"battery": 63, "humidity": 47.69, "linkquality": 87, "power_outage_count": 4906, "pressure": 1009.7, "temperature": 19.74, "voltage": 2945}, "ts": "2025-11-18T12:05:12.708715+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:05:18,507 - __main__ - DEBUG - MQTT message received on shellies/schrankesszimmer/relay/0: off
|
|
||||||
abstraction | 2025-11-18 12:05:18,508 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 12:05:18,508 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 12:05:18,508 - __main__ - INFO - ← abstract STATE licht_schrank_esszimmer: home/relay/licht_schrank_esszimmer/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 12:05:18,526 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_schrank_esszimmer", "payload": {"power": "off"}, "ts": "2025-11-18T12:05:18.525971+00:00"}
|
|
||||||
@@ -1,311 +0,0 @@
|
|||||||
rules | 2025-11-18 12:04:40,835 - asyncio - DEBUG - Using selector: EpollSelector
|
|
||||||
rules | 2025-11-18 12:04:40,835 - __main__ - INFO - ============================================================
|
|
||||||
rules | 2025-11-18 12:04:40,835 - __main__ - INFO - Rules Engine Starting
|
|
||||||
rules | 2025-11-18 12:04:40,835 - __main__ - INFO - ============================================================
|
|
||||||
rules | 2025-11-18 12:04:40,835 - __main__ - INFO - Config: /app/config/rules.yaml
|
|
||||||
rules | 2025-11-18 12:04:40,835 - __main__ - INFO - MQTT: 172.23.1.102:1883
|
|
||||||
rules | 2025-11-18 12:04:40,835 - __main__ - INFO - Redis: redis://172.23.1.116:6379/8
|
|
||||||
rules | 2025-11-18 12:04:40,836 - __main__ - INFO - ============================================================
|
|
||||||
rules | 2025-11-18 12:04:40,836 - __main__ - INFO - Loading rules configuration from /app/config/rules.yaml
|
|
||||||
rules | 2025-11-18 12:04:40,841 - __main__ - INFO - Loaded 6 rule(s) from configuration
|
|
||||||
rules | 2025-11-18 12:04:40,841 - __main__ - INFO - - window_setback_esszimmer (type: window_setback@1.0) [DISABLED]
|
|
||||||
rules | 2025-11-18 12:04:40,842 - __main__ - INFO - - window_setback_kueche (type: window_setback@1.0) [DISABLED]
|
|
||||||
rules | 2025-11-18 12:04:40,842 - __main__ - INFO - - window_setback_patty (type: window_setback@1.0) [DISABLED]
|
|
||||||
rules | 2025-11-18 12:04:40,842 - __main__ - INFO - - window_setback_schlafzimmer (type: window_setback@1.0) [DISABLED]
|
|
||||||
rules | 2025-11-18 12:04:40,842 - __main__ - INFO - - window_setback_wohnzimmer (type: window_setback@1.0) [DISABLED]
|
|
||||||
rules | 2025-11-18 12:04:40,846 - __main__ - INFO - - window_setback_wolfgang (type: window_setback@1.0)
|
|
||||||
rules | 2025-11-18 12:04:40,846 - __main__ - INFO - Successfully loaded 1 rule implementation(s) (5 disabled)
|
|
||||||
rules | 2025-11-18 12:04:40,846 - __main__ - INFO - Rule window_setback_wolfgang validated: 1 contacts, 1 thermostats
|
|
||||||
rules | 2025-11-18 12:04:40,846 - __main__ - DEBUG - Rule window_setback_wolfgang subscribes to 2 topic(s)
|
|
||||||
rules | 2025-11-18 12:04:40,847 - __main__ - INFO - Total MQTT subscriptions needed: 2
|
|
||||||
rules | 2025-11-18 12:04:40,847 - __main__ - INFO - Starting event processing loop
|
|
||||||
abstraction | 2025-11-18 12:04:40,901 - asyncio - DEBUG - Using selector: EpollSelector
|
|
||||||
abstraction | 2025-11-18 12:04:40,952 - __main__ - INFO - Loaded configuration from /app/config/devices.yaml
|
|
||||||
abstraction | 2025-11-18 12:04:40,953 - __main__ - INFO - Loaded 64 device(s): lampe_semeniere_wohnzimmer, stehlampe_esszimmer_spiegel, stehlampe_esszimmer_schrank, grosse_lampe_wohnzimmer, lampe_naehtischchen_wohnzimmer, kleine_lampe_rechts_esszimmer, kleine_lampe_links_esszimmer, leselampe_esszimmer, medusalampe_schlafzimmer, sportlicht_am_fernseher_studierzimmer, deckenlampe_schlafzimmer, bettlicht_wolfgang, bettlicht_patty, schranklicht_hinten_patty, schranklicht_vorne_patty, leselampe_patty, deckenlampe_esszimmer, standlampe_esszimmer, haustuer, deckenlampe_flur_oben, kueche_deckenlampe, sportlicht_tisch, sportlicht_regal, licht_flur_oben_am_spiegel, experimentlabtest, thermostat_wolfgang, thermostat_kueche, thermostat_schlafzimmer, thermostat_esszimmer, thermostat_wohnzimmer, thermostat_patty, thermostat_bad_oben, thermostat_bad_unten, sterne_wohnzimmer, kontakt_schlafzimmer_strasse, kontakt_esszimmer_strasse_rechts, kontakt_esszimmer_strasse_links, kontakt_wohnzimmer_garten_rechts, kontakt_wohnzimmer_garten_links, kontakt_kueche_garten_fenster, kontakt_kueche_garten_tuer, kontakt_kueche_strasse_rechts, kontakt_kueche_strasse_links, kontakt_patty_garten_rechts, kontakt_patty_garten_links, kontakt_patty_strasse, kontakt_wolfgang_garten, kontakt_bad_oben_strasse, kontakt_bad_unten_strasse, sensor_schlafzimmer, sensor_wohnzimmer, sensor_kueche, sensor_arbeitszimmer_patty, sensor_arbeitszimmer_wolfgang, sensor_bad_oben, sensor_bad_unten, sensor_flur, sensor_waschkueche, sensor_sportzimmer, licht_spuele_kueche, licht_schrank_esszimmer, licht_regal_wohnzimmer, licht_flur_schrank, licht_terasse
|
|
||||||
abstraction | 2025-11-18 12:04:40,953 - __main__ - INFO - Loaded 64 device(s) from configuration
|
|
||||||
rules | 2025-11-18 12:04:40,999 - __main__ - INFO - Connecting to MQTT broker 172.23.1.102:1883 (client_id=rule_engine-0d8cce)
|
|
||||||
abstraction | 2025-11-18 12:04:41,003 - __main__ - INFO - Connected to Redis: redis://172.23.1.116:6379/8
|
|
||||||
abstraction | 2025-11-18 12:04:41,003 - __main__ - INFO - Abstraction worker started
|
|
||||||
abstraction | 2025-11-18 12:04:41,003 - __main__ - INFO - Connecting to MQTT broker: 172.23.1.102:1883
|
|
||||||
rules | 2025-11-18 12:04:41,051 - __main__ - INFO - Connected to MQTT broker 172.23.1.102:1883
|
|
||||||
abstraction | 2025-11-18 12:04:41,053 - __main__ - INFO - Connected to MQTT broker as home-automation-abstraction-b39304
|
|
||||||
abstraction | 2025-11-18 12:04:41,072 - __main__ - INFO - Subscribed to abstract SET: home/relay/lampe_semeniere_wohnzimmer/set
|
|
||||||
rules | 2025-11-18 12:04:41,084 - __main__ - INFO - Subscribed to 2 topic(s): home/thermostat/thermostat_wolfgang/state, home/contact/kontakt_wolfgang_garten/state
|
|
||||||
rules | 2025-11-18 12:04:41,085 - __main__ - DEBUG - Received event: {'topic': 'home/thermostat/thermostat_wolfgang/state', 'type': 'state', 'cap': 'thermostat', 'device_id': 'thermostat_wolfgang', 'payload': {'target': 23.0, 'current': 23.5, 'mode': 'heat'}, 'ts': '2025-11-18T12:04:41.085220'}
|
|
||||||
abstraction | 2025-11-18 12:04:41,091 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b8000015480b
|
|
||||||
rules | 2025-11-18 12:04:41,085 - __main__ - DEBUG - Filtering for cap=thermostat, device_id=thermostat_wolfgang
|
|
||||||
rules | 2025-11-18 12:04:41,085 - __main__ - DEBUG - Rule window_setback_wolfgang: checking thermostats ['thermostat_wolfgang']
|
|
||||||
rules | 2025-11-18 12:04:41,086 - __main__ - INFO - Event thermostat/thermostat_wolfgang: 1 matching rule(s)
|
|
||||||
abstraction | 2025-11-18 12:04:41,107 - __main__ - INFO - Subscribed to abstract SET: home/light/stehlampe_esszimmer_spiegel/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,125 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x001788010d06ea09
|
|
||||||
abstraction | 2025-11-18 12:04:41,141 - __main__ - INFO - Subscribed to abstract SET: home/light/stehlampe_esszimmer_schrank/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,159 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x001788010d09176c
|
|
||||||
abstraction | 2025-11-18 12:04:41,176 - __main__ - INFO - Subscribed to abstract SET: home/relay/grosse_lampe_wohnzimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,192 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b80000151aca
|
|
||||||
rules | 2025-11-18 12:04:41,197 - __main__ - DEBUG - Rule window_setback_wolfgang: Updated current target for thermostat_wolfgang: 23.0°C
|
|
||||||
rules | 2025-11-18 12:04:41,197 - __main__ - DEBUG - Received event: {'topic': 'home/contact/kontakt_wolfgang_garten/state', 'type': 'state', 'cap': 'contact', 'device_id': 'kontakt_wolfgang_garten', 'payload': {'contact': 'closed', 'battery': 100, 'linkquality': 32, 'device_temperature': 28, 'voltage': 3025}, 'ts': '2025-11-18T12:04:41.197402'}
|
|
||||||
rules | 2025-11-18 12:04:41,198 - __main__ - DEBUG - Filtering for cap=contact, device_id=kontakt_wolfgang_garten
|
|
||||||
rules | 2025-11-18 12:04:41,198 - __main__ - DEBUG - Rule window_setback_wolfgang: checking contacts ['kontakt_wolfgang_garten']
|
|
||||||
rules | 2025-11-18 12:04:41,199 - __main__ - INFO - Event contact/kontakt_wolfgang_garten: 1 matching rule(s)
|
|
||||||
abstraction | 2025-11-18 12:04:41,209 - __main__ - INFO - Subscribed to abstract SET: home/relay/lampe_naehtischchen_wohnzimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,225 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x842e14fffee560ee
|
|
||||||
rules | 2025-11-18 12:04:41,233 - __main__ - INFO - Rule window_setback_wolfgang: Window closed, restoring 1 thermostats to previous temperatures
|
|
||||||
abstraction | 2025-11-18 12:04:41,242 - __main__ - INFO - Subscribed to abstract SET: home/relay/kleine_lampe_rechts_esszimmer/set
|
|
||||||
rules | 2025-11-18 12:04:41,250 - __main__ - WARNING - No previous target found for thermostat_wolfgang, cannot restore
|
|
||||||
abstraction | 2025-11-18 12:04:41,259 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b80000156645
|
|
||||||
abstraction | 2025-11-18 12:04:41,276 - __main__ - INFO - Subscribed to abstract SET: home/relay/kleine_lampe_links_esszimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,293 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b80000153099
|
|
||||||
abstraction | 2025-11-18 12:04:41,310 - __main__ - INFO - Subscribed to abstract SET: home/light/leselampe_esszimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,327 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xec1bbdfffe7b84f2
|
|
||||||
abstraction | 2025-11-18 12:04:41,344 - __main__ - INFO - Subscribed to abstract SET: home/relay/medusalampe_schlafzimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,361 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b80000154c7c
|
|
||||||
abstraction | 2025-11-18 12:04:41,378 - __main__ - INFO - Subscribed to abstract SET: home/light/sportlicht_am_fernseher_studierzimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,395 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x842e14fffe76a23a
|
|
||||||
abstraction | 2025-11-18 12:04:41,415 - __main__ - INFO - Subscribed to abstract SET: home/light/deckenlampe_schlafzimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,432 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x0017880108a406a7
|
|
||||||
abstraction | 2025-11-18 12:04:41,449 - __main__ - INFO - Subscribed to abstract SET: home/light/bettlicht_wolfgang/set
|
|
||||||
api | INFO: Started server process [1]
|
|
||||||
api | INFO: Waiting for application startup.
|
|
||||||
abstraction | 2025-11-18 12:04:41,466 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00178801081570bf
|
|
||||||
abstraction | 2025-11-18 12:04:41,484 - __main__ - INFO - Subscribed to abstract SET: home/light/bettlicht_patty/set
|
|
||||||
api | INFO: Application startup complete.
|
|
||||||
abstraction | 2025-11-18 12:04:41,500 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x0017880108158b32
|
|
||||||
api | INFO: Uvicorn running on http://0.0.0.0:8001 (Press CTRL+C to quit)
|
|
||||||
abstraction | 2025-11-18 12:04:41,518 - __main__ - INFO - Subscribed to abstract SET: home/light/schranklicht_hinten_patty/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,535 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x0017880106e29571
|
|
||||||
abstraction | 2025-11-18 12:04:41,552 - __main__ - INFO - Subscribed to abstract SET: home/relay/schranklicht_vorne_patty/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,569 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b80000154cf5
|
|
||||||
abstraction | 2025-11-18 12:04:41,586 - __main__ - INFO - Subscribed to abstract SET: home/light/leselampe_patty/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,603 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x001788010600ec9d
|
|
||||||
abstraction | 2025-11-18 12:04:41,620 - __main__ - INFO - Subscribed to abstract SET: home/light/deckenlampe_esszimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,637 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x0017880108a03e45
|
|
||||||
abstraction | 2025-11-18 12:04:41,655 - __main__ - INFO - Subscribed to abstract SET: home/light/standlampe_esszimmer/set
|
|
||||||
ui | UI using API_BASE: http://172.19.1.11:8001
|
|
||||||
ui | UI using BASE_PATH: /
|
|
||||||
ui | INFO: Started server process [1]
|
|
||||||
ui | INFO: Waiting for application startup.
|
|
||||||
ui | INFO: Application startup complete.
|
|
||||||
abstraction | 2025-11-18 12:04:41,674 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xbc33acfffe21f547
|
|
||||||
ui | INFO: Uvicorn running on http://0.0.0.0:8002 (Press CTRL+C to quit)
|
|
||||||
abstraction | 2025-11-18 12:04:41,692 - __main__ - INFO - Subscribed to abstract SET: home/light/haustuer/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,711 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xec1bbdfffea6a3da
|
|
||||||
abstraction | 2025-11-18 12:04:41,728 - __main__ - INFO - Subscribed to abstract SET: home/light/deckenlampe_flur_oben/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,746 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x001788010d2123a7
|
|
||||||
abstraction | 2025-11-18 12:04:41,764 - __main__ - INFO - Subscribed to abstract SET: home/light/kueche_deckenlampe/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,781 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x001788010d2c40c4
|
|
||||||
abstraction | 2025-11-18 12:04:41,798 - __main__ - INFO - Subscribed to abstract SET: home/light/sportlicht_tisch/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,814 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b8be2409f31b
|
|
||||||
abstraction | 2025-11-18 12:04:41,831 - __main__ - INFO - Subscribed to abstract SET: home/light/sportlicht_regal/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,848 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b8be2409f569
|
|
||||||
abstraction | 2025-11-18 12:04:41,865 - __main__ - INFO - Subscribed to abstract SET: home/light/licht_flur_oben_am_spiegel/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,883 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x842e14fffefe4ba4
|
|
||||||
abstraction | 2025-11-18 12:04:41,899 - __main__ - INFO - Subscribed to abstract SET: home/light/experimentlabtest/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,918 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b80000195038
|
|
||||||
abstraction | 2025-11-18 12:04:41,936 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_wolfgang/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,955 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x540f57fffe7e3cfe
|
|
||||||
abstraction | 2025-11-18 12:04:41,974 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_kueche/set
|
|
||||||
abstraction | 2025-11-18 12:04:41,991 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x94deb8fffe2e5c06
|
|
||||||
abstraction | 2025-11-18 12:04:42,008 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_schlafzimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:42,025 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/42/1/SET_TEMPERATURE
|
|
||||||
abstraction | 2025-11-18 12:04:42,042 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_esszimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:42,059 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/45/1/SET_TEMPERATURE
|
|
||||||
abstraction | 2025-11-18 12:04:42,080 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_wohnzimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:42,097 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/46/1/SET_TEMPERATURE
|
|
||||||
abstraction | 2025-11-18 12:04:42,114 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_patty/set
|
|
||||||
abstraction | 2025-11-18 12:04:42,131 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/39/1/SET_TEMPERATURE
|
|
||||||
abstraction | 2025-11-18 12:04:42,150 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_bad_oben/set
|
|
||||||
abstraction | 2025-11-18 12:04:42,171 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/41/1/SET_TEMPERATURE
|
|
||||||
abstraction | 2025-11-18 12:04:42,189 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_bad_unten/set
|
|
||||||
abstraction | 2025-11-18 12:04:42,207 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/48/1/SET_TEMPERATURE
|
|
||||||
abstraction | 2025-11-18 12:04:42,224 - __main__ - INFO - Subscribed to abstract SET: home/relay/sterne_wohnzimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:42,240 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b80000155fc2
|
|
||||||
abstraction | 2025-11-18 12:04:42,240 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_schlafzimmer_strasse
|
|
||||||
abstraction | 2025-11-18 12:04:42,258 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/52/1/STATE
|
|
||||||
abstraction | 2025-11-18 12:04:42,258 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_esszimmer_strasse_rechts
|
|
||||||
abstraction | 2025-11-18 12:04:42,275 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/26/1/STATE
|
|
||||||
abstraction | 2025-11-18 12:04:42,275 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_esszimmer_strasse_links
|
|
||||||
abstraction | 2025-11-18 12:04:42,293 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/27/1/STATE
|
|
||||||
abstraction | 2025-11-18 12:04:42,293 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_wohnzimmer_garten_rechts
|
|
||||||
abstraction | 2025-11-18 12:04:42,313 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/28/1/STATE
|
|
||||||
abstraction | 2025-11-18 12:04:42,313 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_wohnzimmer_garten_links
|
|
||||||
abstraction | 2025-11-18 12:04:42,331 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/29/1/STATE
|
|
||||||
abstraction | 2025-11-18 12:04:42,331 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_kueche_garten_fenster
|
|
||||||
abstraction | 2025-11-18 12:04:42,351 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d008b332785
|
|
||||||
abstraction | 2025-11-18 12:04:42,351 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_kueche_garten_tuer
|
|
||||||
abstraction | 2025-11-18 12:04:42,371 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d008b332788
|
|
||||||
abstraction | 2025-11-18 12:04:42,371 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_kueche_strasse_rechts
|
|
||||||
abstraction | 2025-11-18 12:04:42,390 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d008b151803
|
|
||||||
abstraction | 2025-11-18 12:04:42,390 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_kueche_strasse_links
|
|
||||||
abstraction | 2025-11-18 12:04:42,408 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d008b331d0b
|
|
||||||
abstraction | 2025-11-18 12:04:42,408 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_patty_garten_rechts
|
|
||||||
abstraction | 2025-11-18 12:04:42,424 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/18/1/STATE
|
|
||||||
abstraction | 2025-11-18 12:04:42,424 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_patty_garten_links
|
|
||||||
abstraction | 2025-11-18 12:04:42,441 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/22/1/STATE
|
|
||||||
abstraction | 2025-11-18 12:04:42,441 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_patty_strasse
|
|
||||||
abstraction | 2025-11-18 12:04:42,462 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d000af457cf
|
|
||||||
abstraction | 2025-11-18 12:04:42,462 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_wolfgang_garten
|
|
||||||
abstraction | 2025-11-18 12:04:42,479 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d008b3328da
|
|
||||||
abstraction | 2025-11-18 12:04:42,480 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_bad_oben_strasse
|
|
||||||
abstraction | 2025-11-18 12:04:42,496 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d008b333aec
|
|
||||||
abstraction | 2025-11-18 12:04:42,496 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_bad_unten_strasse
|
|
||||||
abstraction | 2025-11-18 12:04:42,513 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/44/1/STATE
|
|
||||||
abstraction | 2025-11-18 12:04:42,513 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_schlafzimmer
|
|
||||||
abstraction | 2025-11-18 12:04:42,532 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d00043292dc
|
|
||||||
abstraction | 2025-11-18 12:04:42,532 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_wohnzimmer
|
|
||||||
abstraction | 2025-11-18 12:04:42,552 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d0008975707
|
|
||||||
abstraction | 2025-11-18 12:04:42,552 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_kueche
|
|
||||||
abstraction | 2025-11-18 12:04:42,571 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d00083299bb
|
|
||||||
abstraction | 2025-11-18 12:04:42,571 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_arbeitszimmer_patty
|
|
||||||
abstraction | 2025-11-18 12:04:42,589 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d0003f052b7
|
|
||||||
abstraction | 2025-11-18 12:04:42,589 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_arbeitszimmer_wolfgang
|
|
||||||
abstraction | 2025-11-18 12:04:42,608 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d000543fb99
|
|
||||||
abstraction | 2025-11-18 12:04:42,608 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_bad_oben
|
|
||||||
abstraction | 2025-11-18 12:04:42,625 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d00093e8987
|
|
||||||
abstraction | 2025-11-18 12:04:42,625 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_bad_unten
|
|
||||||
abstraction | 2025-11-18 12:04:42,645 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d00093e662a
|
|
||||||
abstraction | 2025-11-18 12:04:42,645 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_flur
|
|
||||||
abstraction | 2025-11-18 12:04:42,664 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d000836ccc6
|
|
||||||
abstraction | 2025-11-18 12:04:42,664 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_waschkueche
|
|
||||||
abstraction | 2025-11-18 12:04:42,682 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d000449f3bc
|
|
||||||
abstraction | 2025-11-18 12:04:42,682 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_sportzimmer
|
|
||||||
abstraction | 2025-11-18 12:04:42,699 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d0009421422
|
|
||||||
abstraction | 2025-11-18 12:04:42,716 - __main__ - INFO - Subscribed to abstract SET: home/relay/licht_spuele_kueche/set
|
|
||||||
abstraction | 2025-11-18 12:04:42,734 - __main__ - INFO - Subscribed to vendor STATE: shellies/LightKitchenSink/relay/0
|
|
||||||
abstraction | 2025-11-18 12:04:42,751 - __main__ - INFO - Subscribed to abstract SET: home/relay/licht_schrank_esszimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:42,770 - __main__ - INFO - Subscribed to vendor STATE: shellies/schrankesszimmer/relay/0
|
|
||||||
abstraction | 2025-11-18 12:04:42,790 - __main__ - INFO - Subscribed to abstract SET: home/relay/licht_regal_wohnzimmer/set
|
|
||||||
abstraction | 2025-11-18 12:04:42,807 - __main__ - INFO - Subscribed to vendor STATE: shellies/wohnzimmer-regal/relay/0
|
|
||||||
abstraction | 2025-11-18 12:04:42,823 - __main__ - INFO - Subscribed to abstract SET: home/relay/licht_flur_schrank/set
|
|
||||||
abstraction | 2025-11-18 12:04:42,841 - __main__ - INFO - Subscribed to vendor STATE: shellies/schrankflur/relay/0
|
|
||||||
abstraction | 2025-11-18 12:04:42,858 - __main__ - INFO - Subscribed to abstract SET: home/relay/licht_terasse/set
|
|
||||||
abstraction | 2025-11-18 12:04:42,875 - __main__ - INFO - Subscribed to vendor STATE: shellies/lichtterasse/relay/0
|
|
||||||
abstraction | 2025-11-18 12:04:42,875 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/42/1/SET_TEMPERATURE: 21
|
|
||||||
abstraction | 2025-11-18 12:04:42,875 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=thermostat, tech=max, payload=21
|
|
||||||
abstraction | 2025-11-18 12:04:42,876 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=thermostat, tech=max, payload={'target': 21.0, 'mode': 'heat'}
|
|
||||||
abstraction | 2025-11-18 12:04:42,876 - __main__ - INFO - ← abstract STATE thermostat_schlafzimmer: home/thermostat/thermostat_schlafzimmer/state → {"target": 21.0, "mode": "heat"}
|
|
||||||
abstraction | 2025-11-18 12:04:42,897 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "thermostat_schlafzimmer", "payload": {"target": 21.0, "mode": "heat"}, "ts": "2025-11-18T12:04:42.897310+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:42,914 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/45/1/SET_TEMPERATURE: 15
|
|
||||||
abstraction | 2025-11-18 12:04:42,914 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=thermostat, tech=max, payload=15
|
|
||||||
abstraction | 2025-11-18 12:04:42,914 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=thermostat, tech=max, payload={'target': 15.0, 'mode': 'heat'}
|
|
||||||
abstraction | 2025-11-18 12:04:42,914 - __main__ - INFO - ← abstract STATE thermostat_esszimmer: home/thermostat/thermostat_esszimmer/state → {"target": 15.0, "mode": "heat"}
|
|
||||||
abstraction | 2025-11-18 12:04:42,934 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "thermostat_esszimmer", "payload": {"target": 15.0, "mode": "heat"}, "ts": "2025-11-18T12:04:42.934255+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:42,950 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/46/1/SET_TEMPERATURE: 15
|
|
||||||
abstraction | 2025-11-18 12:04:42,950 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=thermostat, tech=max, payload=15
|
|
||||||
abstraction | 2025-11-18 12:04:42,950 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=thermostat, tech=max, payload={'target': 15.0, 'mode': 'heat'}
|
|
||||||
abstraction | 2025-11-18 12:04:42,951 - __main__ - INFO - ← abstract STATE thermostat_wohnzimmer: home/thermostat/thermostat_wohnzimmer/state → {"target": 15.0, "mode": "heat"}
|
|
||||||
abstraction | 2025-11-18 12:04:42,970 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "thermostat_wohnzimmer", "payload": {"target": 15.0, "mode": "heat"}, "ts": "2025-11-18T12:04:42.970936+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:42,987 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/39/1/SET_TEMPERATURE: 22
|
|
||||||
abstraction | 2025-11-18 12:04:42,988 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=thermostat, tech=max, payload=22
|
|
||||||
abstraction | 2025-11-18 12:04:42,988 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=thermostat, tech=max, payload={'target': 22.0, 'mode': 'heat'}
|
|
||||||
abstraction | 2025-11-18 12:04:42,988 - __main__ - INFO - ← abstract STATE thermostat_patty: home/thermostat/thermostat_patty/state → {"target": 22.0, "mode": "heat"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,009 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "thermostat_patty", "payload": {"target": 22.0, "mode": "heat"}, "ts": "2025-11-18T12:04:43.009673+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,029 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/41/1/SET_TEMPERATURE: 20
|
|
||||||
abstraction | 2025-11-18 12:04:43,029 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=thermostat, tech=max, payload=20
|
|
||||||
abstraction | 2025-11-18 12:04:43,029 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=thermostat, tech=max, payload={'target': 20.0, 'mode': 'heat'}
|
|
||||||
abstraction | 2025-11-18 12:04:43,029 - __main__ - INFO - ← abstract STATE thermostat_bad_oben: home/thermostat/thermostat_bad_oben/state → {"target": 20.0, "mode": "heat"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,053 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "thermostat_bad_oben", "payload": {"target": 20.0, "mode": "heat"}, "ts": "2025-11-18T12:04:43.053895+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,071 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/48/1/SET_TEMPERATURE: 21
|
|
||||||
abstraction | 2025-11-18 12:04:43,071 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=thermostat, tech=max, payload=21
|
|
||||||
abstraction | 2025-11-18 12:04:43,071 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=thermostat, tech=max, payload={'target': 21.0, 'mode': 'heat'}
|
|
||||||
abstraction | 2025-11-18 12:04:43,072 - __main__ - INFO - ← abstract STATE thermostat_bad_unten: home/thermostat/thermostat_bad_unten/state → {"target": 21.0, "mode": "heat"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,092 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "thermostat_bad_unten", "payload": {"target": 21.0, "mode": "heat"}, "ts": "2025-11-18T12:04:43.092210+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,108 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/52/1/STATE: false
|
|
||||||
abstraction | 2025-11-18 12:04:43,108 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=false
|
|
||||||
abstraction | 2025-11-18 12:04:43,108 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'closed'}
|
|
||||||
abstraction | 2025-11-18 12:04:43,109 - __main__ - INFO - ← abstract STATE kontakt_schlafzimmer_strasse: home/contact/kontakt_schlafzimmer_strasse/state → {"contact": "closed"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,128 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_schlafzimmer_strasse", "payload": {"contact": "closed"}, "ts": "2025-11-18T12:04:43.128506+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,145 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/26/1/STATE: false
|
|
||||||
abstraction | 2025-11-18 12:04:43,145 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=false
|
|
||||||
abstraction | 2025-11-18 12:04:43,145 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'closed'}
|
|
||||||
abstraction | 2025-11-18 12:04:43,146 - __main__ - INFO - ← abstract STATE kontakt_esszimmer_strasse_rechts: home/contact/kontakt_esszimmer_strasse_rechts/state → {"contact": "closed"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,165 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_esszimmer_strasse_rechts", "payload": {"contact": "closed"}, "ts": "2025-11-18T12:04:43.165958+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,182 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/27/1/STATE: false
|
|
||||||
abstraction | 2025-11-18 12:04:43,182 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=false
|
|
||||||
abstraction | 2025-11-18 12:04:43,183 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'closed'}
|
|
||||||
abstraction | 2025-11-18 12:04:43,183 - __main__ - INFO - ← abstract STATE kontakt_esszimmer_strasse_links: home/contact/kontakt_esszimmer_strasse_links/state → {"contact": "closed"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,202 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_esszimmer_strasse_links", "payload": {"contact": "closed"}, "ts": "2025-11-18T12:04:43.202580+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,219 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/28/1/STATE: false
|
|
||||||
abstraction | 2025-11-18 12:04:43,219 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=false
|
|
||||||
abstraction | 2025-11-18 12:04:43,219 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'closed'}
|
|
||||||
abstraction | 2025-11-18 12:04:43,220 - __main__ - INFO - ← abstract STATE kontakt_wohnzimmer_garten_rechts: home/contact/kontakt_wohnzimmer_garten_rechts/state → {"contact": "closed"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,239 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_wohnzimmer_garten_rechts", "payload": {"contact": "closed"}, "ts": "2025-11-18T12:04:43.239653+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,256 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/29/1/STATE: false
|
|
||||||
abstraction | 2025-11-18 12:04:43,256 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=false
|
|
||||||
abstraction | 2025-11-18 12:04:43,256 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'closed'}
|
|
||||||
abstraction | 2025-11-18 12:04:43,257 - __main__ - INFO - ← abstract STATE kontakt_wohnzimmer_garten_links: home/contact/kontakt_wohnzimmer_garten_links/state → {"contact": "closed"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,275 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_wohnzimmer_garten_links", "payload": {"contact": "closed"}, "ts": "2025-11-18T12:04:43.275832+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,292 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/18/1/STATE: false
|
|
||||||
abstraction | 2025-11-18 12:04:43,292 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=false
|
|
||||||
abstraction | 2025-11-18 12:04:43,292 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'closed'}
|
|
||||||
abstraction | 2025-11-18 12:04:43,293 - __main__ - INFO - ← abstract STATE kontakt_patty_garten_rechts: home/contact/kontakt_patty_garten_rechts/state → {"contact": "closed"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,314 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_patty_garten_rechts", "payload": {"contact": "closed"}, "ts": "2025-11-18T12:04:43.314579+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,331 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/22/1/STATE: false
|
|
||||||
abstraction | 2025-11-18 12:04:43,331 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=false
|
|
||||||
abstraction | 2025-11-18 12:04:43,331 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'closed'}
|
|
||||||
abstraction | 2025-11-18 12:04:43,332 - __main__ - INFO - ← abstract STATE kontakt_patty_garten_links: home/contact/kontakt_patty_garten_links/state → {"contact": "closed"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,351 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_patty_garten_links", "payload": {"contact": "closed"}, "ts": "2025-11-18T12:04:43.351704+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,368 - __main__ - DEBUG - MQTT message received on homegear/instance1/plain/44/1/STATE: false
|
|
||||||
abstraction | 2025-11-18 12:04:43,368 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=false
|
|
||||||
abstraction | 2025-11-18 12:04:43,368 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'closed'}
|
|
||||||
abstraction | 2025-11-18 12:04:43,369 - __main__ - INFO - ← abstract STATE kontakt_bad_unten_strasse: home/contact/kontakt_bad_unten_strasse/state → {"contact": "closed"}
|
|
||||||
abstraction | 2025-11-18 12:04:43,388 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_bad_unten_strasse", "payload": {"contact": "closed"}, "ts": "2025-11-18T12:04:43.388390+00:00"}
|
|
||||||
api | INFO: 172.16.3.98:60163 - "GET /realtime HTTP/1.1" 200 OK
|
|
||||||
ui | INFO: 127.0.0.1:35036 - "GET /health HTTP/1.1" 200 OK
|
|
||||||
api | INFO: 172.16.3.98:60172 - "GET /realtime HTTP/1.1" 200 OK
|
|
||||||
abstraction | 2025-11-18 12:04:48,498 - __main__ - DEBUG - MQTT message received on shellies/schrankesszimmer/relay/0: off
|
|
||||||
abstraction | 2025-11-18 12:04:48,498 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 12:04:48,498 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 12:04:48,498 - __main__ - INFO - ← abstract STATE licht_schrank_esszimmer: home/relay/licht_schrank_esszimmer/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 12:04:48,518 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_schrank_esszimmer", "payload": {"power": "off"}, "ts": "2025-11-18T12:04:48.518525+00:00"}
|
|
||||||
api | INFO: 172.16.3.98:60187 - "GET /realtime HTTP/1.1" 200 OK
|
|
||||||
abstraction | 2025-11-18 12:04:52,989 - __main__ - DEBUG - MQTT message received on zigbee2mqtt/0x00158d0003f052b7: {"battery":100,"humidity":55.04,"linkquality":83,"power_outage_count":38416,"pressure":1002.6,"temperature":22.13,"voltage":3015}
|
|
||||||
abstraction | 2025-11-18 12:04:52,989 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={"battery":100,"humidity":55.04,"linkquality":83,"power_outage_count":38416,"pressure":1002.6,"temperature":22.13,"voltage":3015}
|
|
||||||
abstraction | 2025-11-18 12:04:52,989 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={'battery': 100, 'humidity': 55.04, 'linkquality': 83, 'power_outage_count': 38416, 'pressure': 1002.6, 'temperature': 22.13, 'voltage': 3015}
|
|
||||||
abstraction | 2025-11-18 12:04:52,989 - __main__ - INFO - ← abstract STATE sensor_arbeitszimmer_patty: home/temp_humidity/sensor_arbeitszimmer_patty/state → {"battery": 100, "humidity": 55.04, "linkquality": 83, "power_outage_count": 38416, "pressure": 1002.6, "temperature": 22.13, "voltage": 3015}
|
|
||||||
abstraction | 2025-11-18 12:04:53,009 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "sensor_arbeitszimmer_patty", "payload": {"battery": 100, "humidity": 55.04, "linkquality": 83, "power_outage_count": 38416, "pressure": 1002.6, "temperature": 22.13, "voltage": 3015}, "ts": "2025-11-18T12:04:53.009776+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:53,024 - __main__ - DEBUG - MQTT message received on zigbee2mqtt/0x00158d0003f052b7: {"battery":100,"humidity":54.82,"linkquality":83,"power_outage_count":38416,"pressure":1002.6,"temperature":22.13,"voltage":3015}
|
|
||||||
abstraction | 2025-11-18 12:04:53,025 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={"battery":100,"humidity":54.82,"linkquality":83,"power_outage_count":38416,"pressure":1002.6,"temperature":22.13,"voltage":3015}
|
|
||||||
abstraction | 2025-11-18 12:04:53,025 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={'battery': 100, 'humidity': 54.82, 'linkquality': 83, 'power_outage_count': 38416, 'pressure': 1002.6, 'temperature': 22.13, 'voltage': 3015}
|
|
||||||
abstraction | 2025-11-18 12:04:53,025 - __main__ - INFO - ← abstract STATE sensor_arbeitszimmer_patty: home/temp_humidity/sensor_arbeitszimmer_patty/state → {"battery": 100, "humidity": 54.82, "linkquality": 83, "power_outage_count": 38416, "pressure": 1002.6, "temperature": 22.13, "voltage": 3015}
|
|
||||||
abstraction | 2025-11-18 12:04:53,044 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "sensor_arbeitszimmer_patty", "payload": {"battery": 100, "humidity": 54.82, "linkquality": 83, "power_outage_count": 38416, "pressure": 1002.6, "temperature": 22.13, "voltage": 3015}, "ts": "2025-11-18T12:04:53.044379+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:04:53,061 - __main__ - DEBUG - MQTT message received on zigbee2mqtt/0x00158d0003f052b7: {"battery":100,"humidity":54.82,"linkquality":83,"power_outage_count":38416,"pressure":1002.4,"temperature":22.13,"voltage":3015}
|
|
||||||
abstraction | 2025-11-18 12:04:53,061 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={"battery":100,"humidity":54.82,"linkquality":83,"power_outage_count":38416,"pressure":1002.4,"temperature":22.13,"voltage":3015}
|
|
||||||
abstraction | 2025-11-18 12:04:53,061 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={'battery': 100, 'humidity': 54.82, 'linkquality': 83, 'power_outage_count': 38416, 'pressure': 1002.4, 'temperature': 22.13, 'voltage': 3015}
|
|
||||||
abstraction | 2025-11-18 12:04:53,061 - __main__ - INFO - ← abstract STATE sensor_arbeitszimmer_patty: home/temp_humidity/sensor_arbeitszimmer_patty/state → {"battery": 100, "humidity": 54.82, "linkquality": 83, "power_outage_count": 38416, "pressure": 1002.4, "temperature": 22.13, "voltage": 3015}
|
|
||||||
abstraction | 2025-11-18 12:04:53,084 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "sensor_arbeitszimmer_patty", "payload": {"battery": 100, "humidity": 54.82, "linkquality": 83, "power_outage_count": 38416, "pressure": 1002.4, "temperature": 22.13, "voltage": 3015}, "ts": "2025-11-18T12:04:53.083988+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:05:03,058 - __main__ - DEBUG - MQTT message received on shellies/lichtterasse/relay/0: off
|
|
||||||
abstraction | 2025-11-18 12:05:03,058 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 12:05:03,058 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 12:05:03,058 - __main__ - INFO - ← abstract STATE licht_terasse: home/relay/licht_terasse/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 12:05:03,075 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_terasse", "payload": {"power": "off"}, "ts": "2025-11-18T12:05:03.075262+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:05:08,209 - __main__ - DEBUG - MQTT message received on shellies/wohnzimmer-regal/relay/0: off
|
|
||||||
abstraction | 2025-11-18 12:05:08,210 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 12:05:08,210 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 12:05:08,210 - __main__ - INFO - ← abstract STATE licht_regal_wohnzimmer: home/relay/licht_regal_wohnzimmer/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 12:05:08,228 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_regal_wohnzimmer", "payload": {"power": "off"}, "ts": "2025-11-18T12:05:08.228758+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:05:10,881 - __main__ - DEBUG - MQTT message received on shellies/LightKitchenSink/relay/0: on
|
|
||||||
abstraction | 2025-11-18 12:05:10,881 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=on
|
|
||||||
abstraction | 2025-11-18 12:05:10,881 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'on'}
|
|
||||||
abstraction | 2025-11-18 12:05:10,881 - __main__ - INFO - ← abstract STATE licht_spuele_kueche: home/relay/licht_spuele_kueche/state → {"power": "on"}
|
|
||||||
abstraction | 2025-11-18 12:05:10,899 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_spuele_kueche", "payload": {"power": "on"}, "ts": "2025-11-18T12:05:10.899207+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:05:12,622 - __main__ - DEBUG - MQTT message received on zigbee2mqtt/0x00158d00083299bb: {"battery":63,"humidity":47.69,"linkquality":87,"power_outage_count":4906,"pressure":1009.9,"temperature":19.74,"voltage":2945}
|
|
||||||
abstraction | 2025-11-18 12:05:12,622 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={"battery":63,"humidity":47.69,"linkquality":87,"power_outage_count":4906,"pressure":1009.9,"temperature":19.74,"voltage":2945}
|
|
||||||
abstraction | 2025-11-18 12:05:12,622 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={'battery': 63, 'humidity': 47.69, 'linkquality': 87, 'power_outage_count': 4906, 'pressure': 1009.9, 'temperature': 19.74, 'voltage': 2945}
|
|
||||||
abstraction | 2025-11-18 12:05:12,622 - __main__ - INFO - ← abstract STATE sensor_kueche: home/temp_humidity/sensor_kueche/state → {"battery": 63, "humidity": 47.69, "linkquality": 87, "power_outage_count": 4906, "pressure": 1009.9, "temperature": 19.74, "voltage": 2945}
|
|
||||||
abstraction | 2025-11-18 12:05:12,640 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "sensor_kueche", "payload": {"battery": 63, "humidity": 47.69, "linkquality": 87, "power_outage_count": 4906, "pressure": 1009.9, "temperature": 19.74, "voltage": 2945}, "ts": "2025-11-18T12:05:12.640129+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:05:12,656 - __main__ - DEBUG - MQTT message received on zigbee2mqtt/0x00158d00083299bb: {"battery":63,"humidity":47.69,"linkquality":87,"power_outage_count":4906,"pressure":1009.9,"temperature":19.74,"voltage":2945}
|
|
||||||
abstraction | 2025-11-18 12:05:12,656 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={"battery":63,"humidity":47.69,"linkquality":87,"power_outage_count":4906,"pressure":1009.9,"temperature":19.74,"voltage":2945}
|
|
||||||
abstraction | 2025-11-18 12:05:12,656 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={'battery': 63, 'humidity': 47.69, 'linkquality': 87, 'power_outage_count': 4906, 'pressure': 1009.9, 'temperature': 19.74, 'voltage': 2945}
|
|
||||||
abstraction | 2025-11-18 12:05:12,657 - __main__ - INFO - ← abstract STATE sensor_kueche: home/temp_humidity/sensor_kueche/state → {"battery": 63, "humidity": 47.69, "linkquality": 87, "power_outage_count": 4906, "pressure": 1009.9, "temperature": 19.74, "voltage": 2945}
|
|
||||||
abstraction | 2025-11-18 12:05:12,674 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "sensor_kueche", "payload": {"battery": 63, "humidity": 47.69, "linkquality": 87, "power_outage_count": 4906, "pressure": 1009.9, "temperature": 19.74, "voltage": 2945}, "ts": "2025-11-18T12:05:12.674372+00:00"}
|
|
||||||
abstraction | 2025-11-18 12:05:12,690 - __main__ - DEBUG - MQTT message received on zigbee2mqtt/0x00158d00083299bb: {"battery":63,"humidity":47.69,"linkquality":87,"power_outage_count":4906,"pressure":1009.7,"temperature":19.74,"voltage":2945}
|
|
||||||
abstraction | 2025-11-18 12:05:12,690 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={"battery":63,"humidity":47.69,"linkquality":87,"power_outage_count":4906,"pressure":1009.7,"temperature":19.74,"voltage":2945}
|
|
||||||
abstraction | 2025-11-18 12:05:12,690 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={'battery': 63, 'humidity': 47.69, 'linkquality': 87, 'power_outage_count': 4906, 'pressure': 1009.7, 'temperature': 19.74, 'voltage': 2945}
|
|
||||||
abstraction | 2025-11-18 12:05:12,690 - __main__ - INFO - ← abstract STATE sensor_kueche: home/temp_humidity/sensor_kueche/state → {"battery": 63, "humidity": 47.69, "linkquality": 87, "power_outage_count": 4906, "pressure": 1009.7, "temperature": 19.74, "voltage": 2945}
|
|
||||||
abstraction | 2025-11-18 12:05:12,708 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "sensor_kueche", "payload": {"battery": 63, "humidity": 47.69, "linkquality": 87, "power_outage_count": 4906, "pressure": 1009.7, "temperature": 19.74, "voltage": 2945}, "ts": "2025-11-18T12:05:12.708715+00:00"}
|
|
||||||
ui | INFO: 127.0.0.1:35638 - "GET /health HTTP/1.1" 200 OK
|
|
||||||
abstraction | 2025-11-18 12:05:18,507 - __main__ - DEBUG - MQTT message received on shellies/schrankesszimmer/relay/0: off
|
|
||||||
abstraction | 2025-11-18 12:05:18,508 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 12:05:18,508 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 12:05:18,508 - __main__ - INFO - ← abstract STATE licht_schrank_esszimmer: home/relay/licht_schrank_esszimmer/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 12:05:18,526 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_schrank_esszimmer", "payload": {"power": "off"}, "ts": "2025-11-18T12:05:18.525971+00:00"}
|
|
||||||
@@ -1,268 +0,0 @@
|
|||||||
abstraction | 2025-11-18 10:23:59,179 - asyncio - DEBUG - Using selector: EpollSelector
|
|
||||||
abstraction | 2025-11-18 10:23:59,240 - __main__ - INFO - Loaded configuration from /app/config/devices.yaml
|
|
||||||
abstraction | 2025-11-18 10:23:59,240 - __main__ - INFO - Loaded 64 device(s): lampe_semeniere_wohnzimmer, stehlampe_esszimmer_spiegel, stehlampe_esszimmer_schrank, grosse_lampe_wohnzimmer, lampe_naehtischchen_wohnzimmer, kleine_lampe_rechts_esszimmer, kleine_lampe_links_esszimmer, leselampe_esszimmer, medusalampe_schlafzimmer, sportlicht_am_fernseher_studierzimmer, deckenlampe_schlafzimmer, bettlicht_wolfgang, bettlicht_patty, schranklicht_hinten_patty, schranklicht_vorne_patty, leselampe_patty, deckenlampe_esszimmer, standlampe_esszimmer, haustuer, deckenlampe_flur_oben, kueche_deckenlampe, sportlicht_tisch, sportlicht_regal, licht_flur_oben_am_spiegel, experimentlabtest, thermostat_wolfgang, thermostat_kueche, thermostat_schlafzimmer, thermostat_esszimmer, thermostat_wohnzimmer, thermostat_patty, thermostat_bad_oben, thermostat_bad_unten, sterne_wohnzimmer, kontakt_schlafzimmer_strasse, kontakt_esszimmer_strasse_rechts, kontakt_esszimmer_strasse_links, kontakt_wohnzimmer_garten_rechts, kontakt_wohnzimmer_garten_links, kontakt_kueche_garten_fenster, kontakt_kueche_garten_tuer, kontakt_kueche_strasse_rechts, kontakt_kueche_strasse_links, kontakt_patty_garten_rechts, kontakt_patty_garten_links, kontakt_patty_strasse, kontakt_wolfgang_garten, kontakt_bad_oben_strasse, kontakt_bad_unten_strasse, sensor_schlafzimmer, sensor_wohnzimmer, sensor_kueche, sensor_arbeitszimmer_patty, sensor_arbeitszimmer_wolfgang, sensor_bad_oben, sensor_bad_unten, sensor_flur, sensor_waschkueche, sensor_sportzimmer, licht_spuele_kueche, licht_schrank_esszimmer, licht_regal_wohnzimmer, licht_flur_schrank, licht_terasse
|
|
||||||
abstraction | 2025-11-18 10:23:59,241 - __main__ - INFO - Loaded 64 device(s) from configuration
|
|
||||||
abstraction | 2025-11-18 10:23:59,292 - __main__ - INFO - Connected to Redis: redis://172.23.1.116:6379/8
|
|
||||||
abstraction | 2025-11-18 10:23:59,292 - __main__ - INFO - Abstraction worker started
|
|
||||||
abstraction | 2025-11-18 10:23:59,293 - __main__ - INFO - Connecting to MQTT broker: 172.23.1.102:1883
|
|
||||||
abstraction | 2025-11-18 10:23:59,341 - __main__ - INFO - Connected to MQTT broker as home-automation-abstraction-2cfdfa
|
|
||||||
abstraction | 2025-11-18 10:23:59,359 - __main__ - INFO - Subscribed to abstract SET: home/relay/lampe_semeniere_wohnzimmer/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,377 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b8000015480b
|
|
||||||
abstraction | 2025-11-18 10:23:59,394 - __main__ - INFO - Subscribed to abstract SET: home/light/stehlampe_esszimmer_spiegel/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,411 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x001788010d06ea09
|
|
||||||
abstraction | 2025-11-18 10:23:59,428 - __main__ - INFO - Subscribed to abstract SET: home/light/stehlampe_esszimmer_schrank/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,444 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x001788010d09176c
|
|
||||||
abstraction | 2025-11-18 10:23:59,460 - __main__ - INFO - Subscribed to abstract SET: home/relay/grosse_lampe_wohnzimmer/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,477 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b80000151aca
|
|
||||||
abstraction | 2025-11-18 10:23:59,493 - __main__ - INFO - Subscribed to abstract SET: home/relay/lampe_naehtischchen_wohnzimmer/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,510 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x842e14fffee560ee
|
|
||||||
abstraction | 2025-11-18 10:23:59,526 - __main__ - INFO - Subscribed to abstract SET: home/relay/kleine_lampe_rechts_esszimmer/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,543 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b80000156645
|
|
||||||
abstraction | 2025-11-18 10:23:59,560 - __main__ - INFO - Subscribed to abstract SET: home/relay/kleine_lampe_links_esszimmer/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,578 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b80000153099
|
|
||||||
abstraction | 2025-11-18 10:23:59,595 - __main__ - INFO - Subscribed to abstract SET: home/light/leselampe_esszimmer/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,612 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xec1bbdfffe7b84f2
|
|
||||||
abstraction | 2025-11-18 10:23:59,630 - __main__ - INFO - Subscribed to abstract SET: home/relay/medusalampe_schlafzimmer/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,647 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b80000154c7c
|
|
||||||
abstraction | 2025-11-18 10:23:59,665 - __main__ - INFO - Subscribed to abstract SET: home/light/sportlicht_am_fernseher_studierzimmer/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,682 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x842e14fffe76a23a
|
|
||||||
abstraction | 2025-11-18 10:23:59,700 - __main__ - INFO - Subscribed to abstract SET: home/light/deckenlampe_schlafzimmer/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,717 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x0017880108a406a7
|
|
||||||
abstraction | 2025-11-18 10:23:59,735 - __main__ - INFO - Subscribed to abstract SET: home/light/bettlicht_wolfgang/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,753 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00178801081570bf
|
|
||||||
abstraction | 2025-11-18 10:23:59,770 - __main__ - INFO - Subscribed to abstract SET: home/light/bettlicht_patty/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,788 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x0017880108158b32
|
|
||||||
abstraction | 2025-11-18 10:23:59,807 - __main__ - INFO - Subscribed to abstract SET: home/light/schranklicht_hinten_patty/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,825 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x0017880106e29571
|
|
||||||
abstraction | 2025-11-18 10:23:59,844 - __main__ - INFO - Subscribed to abstract SET: home/relay/schranklicht_vorne_patty/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,862 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b80000154cf5
|
|
||||||
abstraction | 2025-11-18 10:23:59,881 - __main__ - INFO - Subscribed to abstract SET: home/light/leselampe_patty/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,901 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x001788010600ec9d
|
|
||||||
abstraction | 2025-11-18 10:23:59,920 - __main__ - INFO - Subscribed to abstract SET: home/light/deckenlampe_esszimmer/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,940 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x0017880108a03e45
|
|
||||||
abstraction | 2025-11-18 10:23:59,959 - __main__ - INFO - Subscribed to abstract SET: home/light/standlampe_esszimmer/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,979 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xbc33acfffe21f547
|
|
||||||
abstraction | 2025-11-18 10:23:59,999 - __main__ - INFO - Subscribed to abstract SET: home/light/haustuer/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,016 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xec1bbdfffea6a3da
|
|
||||||
abstraction | 2025-11-18 10:24:00,034 - __main__ - INFO - Subscribed to abstract SET: home/light/deckenlampe_flur_oben/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,053 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x001788010d2123a7
|
|
||||||
abstraction | 2025-11-18 10:24:00,072 - __main__ - INFO - Subscribed to abstract SET: home/light/kueche_deckenlampe/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,090 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x001788010d2c40c4
|
|
||||||
abstraction | 2025-11-18 10:24:00,108 - __main__ - INFO - Subscribed to abstract SET: home/light/sportlicht_tisch/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,127 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b8be2409f31b
|
|
||||||
abstraction | 2025-11-18 10:24:00,145 - __main__ - INFO - Subscribed to abstract SET: home/light/sportlicht_regal/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,163 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b8be2409f569
|
|
||||||
abstraction | 2025-11-18 10:24:00,183 - __main__ - INFO - Subscribed to abstract SET: home/light/licht_flur_oben_am_spiegel/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,201 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x842e14fffefe4ba4
|
|
||||||
abstraction | 2025-11-18 10:24:00,218 - __main__ - INFO - Subscribed to abstract SET: home/light/experimentlabtest/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,237 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b80000195038
|
|
||||||
abstraction | 2025-11-18 10:24:00,255 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_wolfgang/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,271 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x540f57fffe7e3cfe
|
|
||||||
abstraction | 2025-11-18 10:24:00,292 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_kueche/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,313 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x94deb8fffe2e5c06
|
|
||||||
abstraction | 2025-11-18 10:24:00,334 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_schlafzimmer/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,356 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/42/1/SET_TEMPERATURE
|
|
||||||
abstraction | 2025-11-18 10:24:00,377 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_esszimmer/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,398 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/45/1/SET_TEMPERATURE
|
|
||||||
abstraction | 2025-11-18 10:24:00,420 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_wohnzimmer/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,440 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/46/1/SET_TEMPERATURE
|
|
||||||
abstraction | 2025-11-18 10:24:00,457 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_patty/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,475 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/39/1/SET_TEMPERATURE
|
|
||||||
abstraction | 2025-11-18 10:24:00,493 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_bad_oben/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,509 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/41/1/SET_TEMPERATURE
|
|
||||||
abstraction | 2025-11-18 10:24:00,530 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_bad_unten/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,551 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/48/1/SET_TEMPERATURE
|
|
||||||
abstraction | 2025-11-18 10:24:00,572 - __main__ - INFO - Subscribed to abstract SET: home/relay/sterne_wohnzimmer/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,593 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b80000155fc2
|
|
||||||
abstraction | 2025-11-18 10:24:00,593 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_schlafzimmer_strasse
|
|
||||||
abstraction | 2025-11-18 10:24:00,614 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/52/1/STATE
|
|
||||||
abstraction | 2025-11-18 10:24:00,614 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_esszimmer_strasse_rechts
|
|
||||||
abstraction | 2025-11-18 10:24:00,630 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/26/1/STATE
|
|
||||||
abstraction | 2025-11-18 10:24:00,630 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_esszimmer_strasse_links
|
|
||||||
abstraction | 2025-11-18 10:24:00,647 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/27/1/STATE
|
|
||||||
abstraction | 2025-11-18 10:24:00,647 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_wohnzimmer_garten_rechts
|
|
||||||
abstraction | 2025-11-18 10:24:00,668 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/28/1/STATE
|
|
||||||
abstraction | 2025-11-18 10:24:00,668 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_wohnzimmer_garten_links
|
|
||||||
abstraction | 2025-11-18 10:24:00,691 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/29/1/STATE
|
|
||||||
abstraction | 2025-11-18 10:24:00,691 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_kueche_garten_fenster
|
|
||||||
abstraction | 2025-11-18 10:24:00,708 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d008b332785
|
|
||||||
abstraction | 2025-11-18 10:24:00,708 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_kueche_garten_tuer
|
|
||||||
abstraction | 2025-11-18 10:24:00,728 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d008b332788
|
|
||||||
abstraction | 2025-11-18 10:24:00,728 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_kueche_strasse_rechts
|
|
||||||
abstraction | 2025-11-18 10:24:00,747 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d008b151803
|
|
||||||
abstraction | 2025-11-18 10:24:00,747 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_kueche_strasse_links
|
|
||||||
abstraction | 2025-11-18 10:24:00,767 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d008b331d0b
|
|
||||||
abstraction | 2025-11-18 10:24:00,767 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_patty_garten_rechts
|
|
||||||
abstraction | 2025-11-18 10:24:00,784 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/18/1/STATE
|
|
||||||
abstraction | 2025-11-18 10:24:00,784 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_patty_garten_links
|
|
||||||
abstraction | 2025-11-18 10:24:00,802 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/22/1/STATE
|
|
||||||
abstraction | 2025-11-18 10:24:00,802 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_patty_strasse
|
|
||||||
abstraction | 2025-11-18 10:24:00,821 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d000af457cf
|
|
||||||
abstraction | 2025-11-18 10:24:00,821 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_wolfgang_garten
|
|
||||||
abstraction | 2025-11-18 10:24:00,838 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d008b3328da
|
|
||||||
abstraction | 2025-11-18 10:24:00,838 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_bad_oben_strasse
|
|
||||||
abstraction | 2025-11-18 10:24:00,855 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d008b333aec
|
|
||||||
abstraction | 2025-11-18 10:24:00,855 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_bad_unten_strasse
|
|
||||||
abstraction | 2025-11-18 10:24:00,872 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/44/1/STATE
|
|
||||||
abstraction | 2025-11-18 10:24:00,872 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_schlafzimmer
|
|
||||||
abstraction | 2025-11-18 10:24:00,891 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d00043292dc
|
|
||||||
abstraction | 2025-11-18 10:24:00,891 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_wohnzimmer
|
|
||||||
abstraction | 2025-11-18 10:24:00,907 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d0008975707
|
|
||||||
abstraction | 2025-11-18 10:24:00,907 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_kueche
|
|
||||||
abstraction | 2025-11-18 10:24:00,925 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d00083299bb
|
|
||||||
abstraction | 2025-11-18 10:24:00,925 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_arbeitszimmer_patty
|
|
||||||
abstraction | 2025-11-18 10:24:00,947 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d0003f052b7
|
|
||||||
abstraction | 2025-11-18 10:24:00,947 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_arbeitszimmer_wolfgang
|
|
||||||
abstraction | 2025-11-18 10:24:00,969 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d000543fb99
|
|
||||||
abstraction | 2025-11-18 10:24:00,969 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_bad_oben
|
|
||||||
abstraction | 2025-11-18 10:24:00,986 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d00093e8987
|
|
||||||
abstraction | 2025-11-18 10:24:00,986 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_bad_unten
|
|
||||||
abstraction | 2025-11-18 10:24:01,004 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d00093e662a
|
|
||||||
abstraction | 2025-11-18 10:24:01,004 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_flur
|
|
||||||
abstraction | 2025-11-18 10:24:01,022 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d000836ccc6
|
|
||||||
abstraction | 2025-11-18 10:24:01,022 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_waschkueche
|
|
||||||
abstraction | 2025-11-18 10:24:01,038 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d000449f3bc
|
|
||||||
abstraction | 2025-11-18 10:24:01,038 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_sportzimmer
|
|
||||||
abstraction | 2025-11-18 10:24:01,058 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d0009421422
|
|
||||||
abstraction | 2025-11-18 10:24:01,074 - __main__ - INFO - Subscribed to abstract SET: home/relay/licht_spuele_kueche/set
|
|
||||||
abstraction | 2025-11-18 10:24:01,090 - __main__ - INFO - Subscribed to vendor STATE: shellies/LightKitchenSink/relay/0
|
|
||||||
abstraction | 2025-11-18 10:24:01,107 - __main__ - INFO - Subscribed to abstract SET: home/relay/licht_schrank_esszimmer/set
|
|
||||||
abstraction | 2025-11-18 10:24:01,122 - __main__ - INFO - Subscribed to vendor STATE: shellies/schrankesszimmer/relay/0
|
|
||||||
abstraction | 2025-11-18 10:24:01,139 - __main__ - INFO - Subscribed to abstract SET: home/relay/licht_regal_wohnzimmer/set
|
|
||||||
abstraction | 2025-11-18 10:24:01,155 - __main__ - INFO - Subscribed to vendor STATE: shellies/wohnzimmer-regal/relay/0
|
|
||||||
abstraction | 2025-11-18 10:24:01,172 - __main__ - INFO - Subscribed to abstract SET: home/relay/licht_flur_schrank/set
|
|
||||||
abstraction | 2025-11-18 10:24:01,189 - __main__ - INFO - Subscribed to vendor STATE: shellies/schrankflur/relay/0
|
|
||||||
abstraction | 2025-11-18 10:24:01,205 - __main__ - INFO - Subscribed to abstract SET: home/relay/licht_terasse/set
|
|
||||||
abstraction | 2025-11-18 10:24:01,222 - __main__ - INFO - Subscribed to vendor STATE: shellies/lichtterasse/relay/0
|
|
||||||
abstraction | 2025-11-18 10:24:01,222 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=thermostat, tech=max, payload=21
|
|
||||||
abstraction | 2025-11-18 10:24:01,222 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=thermostat, tech=max, payload={'target': 21.0, 'mode': 'heat'}
|
|
||||||
abstraction | 2025-11-18 10:24:01,222 - __main__ - INFO - ← abstract STATE thermostat_schlafzimmer: home/thermostat/thermostat_schlafzimmer/state → {"target": 21.0, "mode": "heat"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,243 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "thermostat_schlafzimmer", "payload": {"target": 21.0, "mode": "heat"}, "ts": "2025-11-18T10:24:01.243641+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,260 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=thermostat, tech=max, payload=15
|
|
||||||
abstraction | 2025-11-18 10:24:01,260 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=thermostat, tech=max, payload={'target': 15.0, 'mode': 'heat'}
|
|
||||||
abstraction | 2025-11-18 10:24:01,260 - __main__ - INFO - ← abstract STATE thermostat_esszimmer: home/thermostat/thermostat_esszimmer/state → {"target": 15.0, "mode": "heat"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,280 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "thermostat_esszimmer", "payload": {"target": 15.0, "mode": "heat"}, "ts": "2025-11-18T10:24:01.280285+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,296 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=thermostat, tech=max, payload=15
|
|
||||||
abstraction | 2025-11-18 10:24:01,296 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=thermostat, tech=max, payload={'target': 15.0, 'mode': 'heat'}
|
|
||||||
abstraction | 2025-11-18 10:24:01,296 - __main__ - INFO - ← abstract STATE thermostat_wohnzimmer: home/thermostat/thermostat_wohnzimmer/state → {"target": 15.0, "mode": "heat"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,317 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "thermostat_wohnzimmer", "payload": {"target": 15.0, "mode": "heat"}, "ts": "2025-11-18T10:24:01.317708+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,334 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=thermostat, tech=max, payload=22
|
|
||||||
abstraction | 2025-11-18 10:24:01,334 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=thermostat, tech=max, payload={'target': 22.0, 'mode': 'heat'}
|
|
||||||
abstraction | 2025-11-18 10:24:01,334 - __main__ - INFO - ← abstract STATE thermostat_patty: home/thermostat/thermostat_patty/state → {"target": 22.0, "mode": "heat"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,357 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "thermostat_patty", "payload": {"target": 22.0, "mode": "heat"}, "ts": "2025-11-18T10:24:01.357082+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,373 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=thermostat, tech=max, payload=20
|
|
||||||
abstraction | 2025-11-18 10:24:01,373 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=thermostat, tech=max, payload={'target': 20.0, 'mode': 'heat'}
|
|
||||||
abstraction | 2025-11-18 10:24:01,373 - __main__ - INFO - ← abstract STATE thermostat_bad_oben: home/thermostat/thermostat_bad_oben/state → {"target": 20.0, "mode": "heat"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,395 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "thermostat_bad_oben", "payload": {"target": 20.0, "mode": "heat"}, "ts": "2025-11-18T10:24:01.395470+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,411 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=thermostat, tech=max, payload=5
|
|
||||||
abstraction | 2025-11-18 10:24:01,411 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=thermostat, tech=max, payload={'target': 5.0, 'mode': 'heat'}
|
|
||||||
abstraction | 2025-11-18 10:24:01,411 - __main__ - INFO - ← abstract STATE thermostat_bad_unten: home/thermostat/thermostat_bad_unten/state → {"target": 5.0, "mode": "heat"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,431 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "thermostat_bad_unten", "payload": {"target": 5.0, "mode": "heat"}, "ts": "2025-11-18T10:24:01.431068+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,448 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=false
|
|
||||||
abstraction | 2025-11-18 10:24:01,448 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'closed'}
|
|
||||||
abstraction | 2025-11-18 10:24:01,449 - __main__ - INFO - ← abstract STATE kontakt_schlafzimmer_strasse: home/contact/kontakt_schlafzimmer_strasse/state → {"contact": "closed"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,472 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_schlafzimmer_strasse", "payload": {"contact": "closed"}, "ts": "2025-11-18T10:24:01.472456+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,491 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=false
|
|
||||||
abstraction | 2025-11-18 10:24:01,491 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'closed'}
|
|
||||||
abstraction | 2025-11-18 10:24:01,491 - __main__ - INFO - ← abstract STATE kontakt_esszimmer_strasse_rechts: home/contact/kontakt_esszimmer_strasse_rechts/state → {"contact": "closed"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,733 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_esszimmer_strasse_rechts", "payload": {"contact": "closed"}, "ts": "2025-11-18T10:24:01.733873+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,750 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=false
|
|
||||||
abstraction | 2025-11-18 10:24:01,750 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'closed'}
|
|
||||||
abstraction | 2025-11-18 10:24:01,750 - __main__ - INFO - ← abstract STATE kontakt_esszimmer_strasse_links: home/contact/kontakt_esszimmer_strasse_links/state → {"contact": "closed"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,771 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_esszimmer_strasse_links", "payload": {"contact": "closed"}, "ts": "2025-11-18T10:24:01.771380+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,788 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=false
|
|
||||||
abstraction | 2025-11-18 10:24:01,788 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'closed'}
|
|
||||||
abstraction | 2025-11-18 10:24:01,788 - __main__ - INFO - ← abstract STATE kontakt_wohnzimmer_garten_rechts: home/contact/kontakt_wohnzimmer_garten_rechts/state → {"contact": "closed"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,808 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_wohnzimmer_garten_rechts", "payload": {"contact": "closed"}, "ts": "2025-11-18T10:24:01.808516+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,825 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=false
|
|
||||||
abstraction | 2025-11-18 10:24:01,825 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'closed'}
|
|
||||||
abstraction | 2025-11-18 10:24:01,825 - __main__ - INFO - ← abstract STATE kontakt_wohnzimmer_garten_links: home/contact/kontakt_wohnzimmer_garten_links/state → {"contact": "closed"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,844 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_wohnzimmer_garten_links", "payload": {"contact": "closed"}, "ts": "2025-11-18T10:24:01.844046+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,860 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=false
|
|
||||||
abstraction | 2025-11-18 10:24:01,861 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'closed'}
|
|
||||||
abstraction | 2025-11-18 10:24:01,861 - __main__ - INFO - ← abstract STATE kontakt_patty_garten_rechts: home/contact/kontakt_patty_garten_rechts/state → {"contact": "closed"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,881 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_patty_garten_rechts", "payload": {"contact": "closed"}, "ts": "2025-11-18T10:24:01.881922+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,898 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=false
|
|
||||||
abstraction | 2025-11-18 10:24:01,898 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'closed'}
|
|
||||||
abstraction | 2025-11-18 10:24:01,898 - __main__ - INFO - ← abstract STATE kontakt_patty_garten_links: home/contact/kontakt_patty_garten_links/state → {"contact": "closed"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,922 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_patty_garten_links", "payload": {"contact": "closed"}, "ts": "2025-11-18T10:24:01.922254+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,940 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=true
|
|
||||||
abstraction | 2025-11-18 10:24:01,940 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'open'}
|
|
||||||
abstraction | 2025-11-18 10:24:01,940 - __main__ - INFO - ← abstract STATE kontakt_bad_unten_strasse: home/contact/kontakt_bad_unten_strasse/state → {"contact": "open"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,959 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_bad_unten_strasse", "payload": {"contact": "open"}, "ts": "2025-11-18T10:24:01.959678+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:02,354 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 10:24:02,354 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 10:24:02,354 - __main__ - INFO - ← abstract STATE licht_terasse: home/relay/licht_terasse/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 10:24:02,373 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_terasse", "payload": {"power": "off"}, "ts": "2025-11-18T10:24:02.373461+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:07,440 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 10:24:07,440 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 10:24:07,441 - __main__ - INFO - ← abstract STATE licht_regal_wohnzimmer: home/relay/licht_regal_wohnzimmer/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 10:24:07,459 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_regal_wohnzimmer", "payload": {"power": "off"}, "ts": "2025-11-18T10:24:07.459082+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:08,817 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={'battery': 100, 'humidity': 54.37, 'linkquality': 83, 'power_outage_count': 38416, 'pressure': 1004.2, 'temperature': 22.16, 'voltage': 3015}
|
|
||||||
abstraction | 2025-11-18 10:24:08,817 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={'battery': 100, 'humidity': 54.37, 'linkquality': 83, 'power_outage_count': 38416, 'pressure': 1004.2, 'temperature': 22.16, 'voltage': 3015}
|
|
||||||
abstraction | 2025-11-18 10:24:08,817 - __main__ - INFO - ← abstract STATE sensor_arbeitszimmer_patty: home/temp_humidity/sensor_arbeitszimmer_patty/state → {"battery": 100, "humidity": 54.37, "linkquality": 83, "power_outage_count": 38416, "pressure": 1004.2, "temperature": 22.16, "voltage": 3015}
|
|
||||||
abstraction | 2025-11-18 10:24:08,835 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "sensor_arbeitszimmer_patty", "payload": {"battery": 100, "humidity": 54.37, "linkquality": 83, "power_outage_count": 38416, "pressure": 1004.2, "temperature": 22.16, "voltage": 3015}, "ts": "2025-11-18T10:24:08.835488+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:08,852 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={'battery': 100, 'humidity': 54.22, 'linkquality': 83, 'power_outage_count': 38416, 'pressure': 1004.2, 'temperature': 22.16, 'voltage': 3015}
|
|
||||||
abstraction | 2025-11-18 10:24:08,852 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={'battery': 100, 'humidity': 54.22, 'linkquality': 83, 'power_outage_count': 38416, 'pressure': 1004.2, 'temperature': 22.16, 'voltage': 3015}
|
|
||||||
abstraction | 2025-11-18 10:24:08,852 - __main__ - INFO - ← abstract STATE sensor_arbeitszimmer_patty: home/temp_humidity/sensor_arbeitszimmer_patty/state → {"battery": 100, "humidity": 54.22, "linkquality": 83, "power_outage_count": 38416, "pressure": 1004.2, "temperature": 22.16, "voltage": 3015}
|
|
||||||
abstraction | 2025-11-18 10:24:08,870 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "sensor_arbeitszimmer_patty", "payload": {"battery": 100, "humidity": 54.22, "linkquality": 83, "power_outage_count": 38416, "pressure": 1004.2, "temperature": 22.16, "voltage": 3015}, "ts": "2025-11-18T10:24:08.870674+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:08,887 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={'battery': 100, 'humidity': 54.22, 'linkquality': 83, 'power_outage_count': 38416, 'pressure': 1003.9, 'temperature': 22.16, 'voltage': 3015}
|
|
||||||
abstraction | 2025-11-18 10:24:08,887 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={'battery': 100, 'humidity': 54.22, 'linkquality': 83, 'power_outage_count': 38416, 'pressure': 1003.9, 'temperature': 22.16, 'voltage': 3015}
|
|
||||||
abstraction | 2025-11-18 10:24:08,887 - __main__ - INFO - ← abstract STATE sensor_arbeitszimmer_patty: home/temp_humidity/sensor_arbeitszimmer_patty/state → {"battery": 100, "humidity": 54.22, "linkquality": 83, "power_outage_count": 38416, "pressure": 1003.9, "temperature": 22.16, "voltage": 3015}
|
|
||||||
abstraction | 2025-11-18 10:24:08,907 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "sensor_arbeitszimmer_patty", "payload": {"battery": 100, "humidity": 54.22, "linkquality": 83, "power_outage_count": 38416, "pressure": 1003.9, "temperature": 22.16, "voltage": 3015}, "ts": "2025-11-18T10:24:08.907729+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:10,178 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=on
|
|
||||||
abstraction | 2025-11-18 10:24:10,178 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'on'}
|
|
||||||
abstraction | 2025-11-18 10:24:10,178 - __main__ - INFO - ← abstract STATE licht_spuele_kueche: home/relay/licht_spuele_kueche/state → {"power": "on"}
|
|
||||||
abstraction | 2025-11-18 10:24:10,196 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_spuele_kueche", "payload": {"power": "on"}, "ts": "2025-11-18T10:24:10.196762+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:17,815 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 10:24:17,815 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 10:24:17,815 - __main__ - INFO - ← abstract STATE licht_schrank_esszimmer: home/relay/licht_schrank_esszimmer/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 10:24:17,834 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_schrank_esszimmer", "payload": {"power": "off"}, "ts": "2025-11-18T10:24:17.834042+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:32,370 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 10:24:32,370 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 10:24:32,370 - __main__ - INFO - ← abstract STATE licht_terasse: home/relay/licht_terasse/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 10:24:32,405 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_terasse", "payload": {"power": "off"}, "ts": "2025-11-18T10:24:32.405754+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:37,447 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 10:24:37,447 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 10:24:37,447 - __main__ - INFO - ← abstract STATE licht_regal_wohnzimmer: home/relay/licht_regal_wohnzimmer/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 10:24:37,465 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_regal_wohnzimmer", "payload": {"power": "off"}, "ts": "2025-11-18T10:24:37.465220+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:40,188 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=on
|
|
||||||
abstraction | 2025-11-18 10:24:40,189 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'on'}
|
|
||||||
abstraction | 2025-11-18 10:24:40,189 - __main__ - INFO - ← abstract STATE licht_spuele_kueche: home/relay/licht_spuele_kueche/state → {"power": "on"}
|
|
||||||
abstraction | 2025-11-18 10:24:40,207 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_spuele_kueche", "payload": {"power": "on"}, "ts": "2025-11-18T10:24:40.207222+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:47,833 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 10:24:47,833 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 10:24:47,833 - __main__ - INFO - ← abstract STATE licht_schrank_esszimmer: home/relay/licht_schrank_esszimmer/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 10:24:47,868 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_schrank_esszimmer", "payload": {"power": "off"}, "ts": "2025-11-18T10:24:47.868787+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:25:02,363 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 10:25:02,363 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 10:25:02,363 - __main__ - INFO - ← abstract STATE licht_terasse: home/relay/licht_terasse/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 10:25:02,381 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_terasse", "payload": {"power": "off"}, "ts": "2025-11-18T10:25:02.381792+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:25:07,447 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 10:25:07,448 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 10:25:07,448 - __main__ - INFO - ← abstract STATE licht_regal_wohnzimmer: home/relay/licht_regal_wohnzimmer/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 10:25:07,465 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_regal_wohnzimmer", "payload": {"power": "off"}, "ts": "2025-11-18T10:25:07.465566+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:25:10,185 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=on
|
|
||||||
abstraction | 2025-11-18 10:25:10,185 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'on'}
|
|
||||||
abstraction | 2025-11-18 10:25:10,185 - __main__ - INFO - ← abstract STATE licht_spuele_kueche: home/relay/licht_spuele_kueche/state → {"power": "on"}
|
|
||||||
abstraction | 2025-11-18 10:25:10,202 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_spuele_kueche", "payload": {"power": "on"}, "ts": "2025-11-18T10:25:10.202372+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:25:17,820 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 10:25:17,820 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 10:25:17,820 - __main__ - INFO - ← abstract STATE licht_schrank_esszimmer: home/relay/licht_schrank_esszimmer/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 10:25:17,838 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_schrank_esszimmer", "payload": {"power": "off"}, "ts": "2025-11-18T10:25:17.838140+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:25:32,361 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 10:25:32,361 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 10:25:32,361 - __main__ - INFO - ← abstract STATE licht_terasse: home/relay/licht_terasse/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 10:25:32,379 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_terasse", "payload": {"power": "off"}, "ts": "2025-11-18T10:25:32.379286+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:25:37,455 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 10:25:37,455 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 10:25:37,455 - __main__ - INFO - ← abstract STATE licht_regal_wohnzimmer: home/relay/licht_regal_wohnzimmer/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 10:25:37,473 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_regal_wohnzimmer", "payload": {"power": "off"}, "ts": "2025-11-18T10:25:37.473171+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:25:40,193 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=on
|
|
||||||
abstraction | 2025-11-18 10:25:40,194 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'on'}
|
|
||||||
abstraction | 2025-11-18 10:25:40,194 - __main__ - INFO - ← abstract STATE licht_spuele_kueche: home/relay/licht_spuele_kueche/state → {"power": "on"}
|
|
||||||
abstraction | 2025-11-18 10:25:40,211 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_spuele_kueche", "payload": {"power": "on"}, "ts": "2025-11-18T10:25:40.211493+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:25:47,821 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 10:25:47,821 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 10:25:47,821 - __main__ - INFO - ← abstract STATE licht_schrank_esszimmer: home/relay/licht_schrank_esszimmer/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 10:25:47,838 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_schrank_esszimmer", "payload": {"power": "off"}, "ts": "2025-11-18T10:25:47.838508+00:00"}
|
|
||||||
@@ -1,332 +0,0 @@
|
|||||||
✔ home-automation-abstraction Built 0.0s
|
|
||||||
✔ home-automation-api Built 0.0s
|
|
||||||
✔ home-automation-rules Built 0.0s
|
|
||||||
✔ home-automation-ui Built 0.0s
|
|
||||||
Attaching to abstraction, api, rules, ui
|
|
||||||
abstraction | 2025-11-18 10:23:59,179 - asyncio - DEBUG - Using selector: EpollSelector
|
|
||||||
rules | 2025-11-18 10:23:59,207 - asyncio - DEBUG - Using selector: EpollSelector
|
|
||||||
rules | 2025-11-18 10:23:59,208 - __main__ - INFO - ============================================================
|
|
||||||
rules | 2025-11-18 10:23:59,208 - __main__ - INFO - Rules Engine Starting
|
|
||||||
rules | 2025-11-18 10:23:59,209 - __main__ - INFO - ============================================================
|
|
||||||
rules | 2025-11-18 10:23:59,209 - __main__ - INFO - Config: /app/config/rules.yaml
|
|
||||||
rules | 2025-11-18 10:23:59,210 - __main__ - INFO - MQTT: 172.23.1.102:1883
|
|
||||||
rules | 2025-11-18 10:23:59,210 - __main__ - INFO - Redis: redis://172.23.1.116:6379/8
|
|
||||||
rules | 2025-11-18 10:23:59,210 - __main__ - INFO - ============================================================
|
|
||||||
rules | 2025-11-18 10:23:59,211 - __main__ - INFO - Loading rules configuration from /app/config/rules.yaml
|
|
||||||
rules | 2025-11-18 10:23:59,217 - __main__ - INFO - Loaded 6 rule(s) from configuration
|
|
||||||
rules | 2025-11-18 10:23:59,218 - __main__ - INFO - - window_setback_esszimmer (type: window_setback@1.0) [DISABLED]
|
|
||||||
rules | 2025-11-18 10:23:59,218 - __main__ - INFO - - window_setback_kueche (type: window_setback@1.0) [DISABLED]
|
|
||||||
rules | 2025-11-18 10:23:59,219 - __main__ - INFO - - window_setback_patty (type: window_setback@1.0) [DISABLED]
|
|
||||||
rules | 2025-11-18 10:23:59,219 - __main__ - INFO - - window_setback_schlafzimmer (type: window_setback@1.0) [DISABLED]
|
|
||||||
rules | 2025-11-18 10:23:59,220 - __main__ - INFO - - window_setback_wohnzimmer (type: window_setback@1.0) [DISABLED]
|
|
||||||
rules | 2025-11-18 10:23:59,225 - __main__ - INFO - - window_setback_wolfgang (type: window_setback@1.0)
|
|
||||||
rules | 2025-11-18 10:23:59,225 - __main__ - INFO - Successfully loaded 1 rule implementation(s) (5 disabled)
|
|
||||||
rules | 2025-11-18 10:23:59,226 - __main__ - INFO - Rule window_setback_wolfgang validated: 1 contacts, 1 thermostats
|
|
||||||
rules | 2025-11-18 10:23:59,226 - __main__ - DEBUG - Rule window_setback_wolfgang subscribes to 2 topic(s)
|
|
||||||
rules | 2025-11-18 10:23:59,227 - __main__ - INFO - Total MQTT subscriptions needed: 2
|
|
||||||
rules | 2025-11-18 10:23:59,227 - __main__ - INFO - Starting event processing loop
|
|
||||||
abstraction | 2025-11-18 10:23:59,240 - __main__ - INFO - Loaded configuration from /app/config/devices.yaml
|
|
||||||
abstraction | 2025-11-18 10:23:59,240 - __main__ - INFO - Loaded 64 device(s): lampe_semeniere_wohnzimmer, stehlampe_esszimmer_spiegel, stehlampe_esszimmer_schrank, grosse_lampe_wohnzimmer, lampe_naehtischchen_wohnzimmer, kleine_lampe_rechts_esszimmer, kleine_lampe_links_esszimmer, leselampe_esszimmer, medusalampe_schlafzimmer, sportlicht_am_fernseher_studierzimmer, deckenlampe_schlafzimmer, bettlicht_wolfgang, bettlicht_patty, schranklicht_hinten_patty, schranklicht_vorne_patty, leselampe_patty, deckenlampe_esszimmer, standlampe_esszimmer, haustuer, deckenlampe_flur_oben, kueche_deckenlampe, sportlicht_tisch, sportlicht_regal, licht_flur_oben_am_spiegel, experimentlabtest, thermostat_wolfgang, thermostat_kueche, thermostat_schlafzimmer, thermostat_esszimmer, thermostat_wohnzimmer, thermostat_patty, thermostat_bad_oben, thermostat_bad_unten, sterne_wohnzimmer, kontakt_schlafzimmer_strasse, kontakt_esszimmer_strasse_rechts, kontakt_esszimmer_strasse_links, kontakt_wohnzimmer_garten_rechts, kontakt_wohnzimmer_garten_links, kontakt_kueche_garten_fenster, kontakt_kueche_garten_tuer, kontakt_kueche_strasse_rechts, kontakt_kueche_strasse_links, kontakt_patty_garten_rechts, kontakt_patty_garten_links, kontakt_patty_strasse, kontakt_wolfgang_garten, kontakt_bad_oben_strasse, kontakt_bad_unten_strasse, sensor_schlafzimmer, sensor_wohnzimmer, sensor_kueche, sensor_arbeitszimmer_patty, sensor_arbeitszimmer_wolfgang, sensor_bad_oben, sensor_bad_unten, sensor_flur, sensor_waschkueche, sensor_sportzimmer, licht_spuele_kueche, licht_schrank_esszimmer, licht_regal_wohnzimmer, licht_flur_schrank, licht_terasse
|
|
||||||
abstraction | 2025-11-18 10:23:59,241 - __main__ - INFO - Loaded 64 device(s) from configuration
|
|
||||||
abstraction | 2025-11-18 10:23:59,292 - __main__ - INFO - Connected to Redis: redis://172.23.1.116:6379/8
|
|
||||||
abstraction | 2025-11-18 10:23:59,292 - __main__ - INFO - Abstraction worker started
|
|
||||||
abstraction | 2025-11-18 10:23:59,293 - __main__ - INFO - Connecting to MQTT broker: 172.23.1.102:1883
|
|
||||||
abstraction | 2025-11-18 10:23:59,341 - __main__ - INFO - Connected to MQTT broker as home-automation-abstraction-2cfdfa
|
|
||||||
abstraction | 2025-11-18 10:23:59,359 - __main__ - INFO - Subscribed to abstract SET: home/relay/lampe_semeniere_wohnzimmer/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,377 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b8000015480b
|
|
||||||
rules | 2025-11-18 10:23:59,378 - __main__ - INFO - Connecting to MQTT broker 172.23.1.102:1883 (client_id=rule_engine-782522)
|
|
||||||
abstraction | 2025-11-18 10:23:59,394 - __main__ - INFO - Subscribed to abstract SET: home/light/stehlampe_esszimmer_spiegel/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,411 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x001788010d06ea09
|
|
||||||
abstraction | 2025-11-18 10:23:59,428 - __main__ - INFO - Subscribed to abstract SET: home/light/stehlampe_esszimmer_schrank/set
|
|
||||||
rules | 2025-11-18 10:23:59,431 - __main__ - INFO - Connected to MQTT broker 172.23.1.102:1883
|
|
||||||
abstraction | 2025-11-18 10:23:59,444 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x001788010d09176c
|
|
||||||
abstraction | 2025-11-18 10:23:59,460 - __main__ - INFO - Subscribed to abstract SET: home/relay/grosse_lampe_wohnzimmer/set
|
|
||||||
rules | 2025-11-18 10:23:59,466 - __main__ - INFO - Subscribed to 2 topic(s): home/thermostat/thermostat_wolfgang/state, home/contact/kontakt_wolfgang_garten/state
|
|
||||||
rules | 2025-11-18 10:23:59,467 - __main__ - DEBUG - Received event: {'topic': 'home/thermostat/thermostat_wolfgang/state', 'type': 'state', 'cap': 'thermostat', 'device_id': 'thermostat_wolfgang', 'payload': {'target': 23.0, 'current': 22.5, 'mode': 'heat'}, 'ts': '2025-11-18T10:23:59.467177'}
|
|
||||||
rules | 2025-11-18 10:23:59,467 - __main__ - DEBUG - Filtering for cap=thermostat, device_id=thermostat_wolfgang
|
|
||||||
rules | 2025-11-18 10:23:59,468 - __main__ - DEBUG - Rule window_setback_wolfgang: checking thermostats ['thermostat_wolfgang']
|
|
||||||
rules | 2025-11-18 10:23:59,468 - __main__ - INFO - Event thermostat/thermostat_wolfgang: 1 matching rule(s)
|
|
||||||
abstraction | 2025-11-18 10:23:59,477 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b80000151aca
|
|
||||||
abstraction | 2025-11-18 10:23:59,493 - __main__ - INFO - Subscribed to abstract SET: home/relay/lampe_naehtischchen_wohnzimmer/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,510 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x842e14fffee560ee
|
|
||||||
abstraction | 2025-11-18 10:23:59,526 - __main__ - INFO - Subscribed to abstract SET: home/relay/kleine_lampe_rechts_esszimmer/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,543 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b80000156645
|
|
||||||
abstraction | 2025-11-18 10:23:59,560 - __main__ - INFO - Subscribed to abstract SET: home/relay/kleine_lampe_links_esszimmer/set
|
|
||||||
rules | 2025-11-18 10:23:59,572 - __main__ - DEBUG - Rule window_setback_wolfgang: Updated current target for thermostat_wolfgang: 23.0°C
|
|
||||||
rules | 2025-11-18 10:23:59,573 - __main__ - DEBUG - Received event: {'topic': 'home/contact/kontakt_wolfgang_garten/state', 'type': 'state', 'cap': 'contact', 'device_id': 'kontakt_wolfgang_garten', 'payload': {'contact': 'closed', 'battery': 100, 'linkquality': 32, 'device_temperature': 26, 'voltage': 3025}, 'ts': '2025-11-18T10:23:59.573073'}
|
|
||||||
rules | 2025-11-18 10:23:59,573 - __main__ - DEBUG - Filtering for cap=contact, device_id=kontakt_wolfgang_garten
|
|
||||||
rules | 2025-11-18 10:23:59,573 - __main__ - DEBUG - Rule window_setback_wolfgang: checking contacts ['kontakt_wolfgang_garten']
|
|
||||||
rules | 2025-11-18 10:23:59,574 - __main__ - INFO - Event contact/kontakt_wolfgang_garten: 1 matching rule(s)
|
|
||||||
abstraction | 2025-11-18 10:23:59,578 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b80000153099
|
|
||||||
abstraction | 2025-11-18 10:23:59,595 - __main__ - INFO - Subscribed to abstract SET: home/light/leselampe_esszimmer/set
|
|
||||||
rules | 2025-11-18 10:23:59,610 - __main__ - INFO - Rule window_setback_wolfgang: Window closed, restoring 1 thermostats to previous temperatures
|
|
||||||
abstraction | 2025-11-18 10:23:59,612 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xec1bbdfffe7b84f2
|
|
||||||
rules | 2025-11-18 10:23:59,627 - __main__ - WARNING - No previous target found for thermostat_wolfgang, cannot restore
|
|
||||||
abstraction | 2025-11-18 10:23:59,630 - __main__ - INFO - Subscribed to abstract SET: home/relay/medusalampe_schlafzimmer/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,647 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b80000154c7c
|
|
||||||
abstraction | 2025-11-18 10:23:59,665 - __main__ - INFO - Subscribed to abstract SET: home/light/sportlicht_am_fernseher_studierzimmer/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,682 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x842e14fffe76a23a
|
|
||||||
abstraction | 2025-11-18 10:23:59,700 - __main__ - INFO - Subscribed to abstract SET: home/light/deckenlampe_schlafzimmer/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,717 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x0017880108a406a7
|
|
||||||
api | INFO: Started server process [1]
|
|
||||||
api | INFO: Waiting for application startup.
|
|
||||||
abstraction | 2025-11-18 10:23:59,735 - __main__ - INFO - Subscribed to abstract SET: home/light/bettlicht_wolfgang/set
|
|
||||||
api | INFO: Application startup complete.
|
|
||||||
abstraction | 2025-11-18 10:23:59,753 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00178801081570bf
|
|
||||||
api | INFO: Uvicorn running on http://0.0.0.0:8001 (Press CTRL+C to quit)
|
|
||||||
abstraction | 2025-11-18 10:23:59,770 - __main__ - INFO - Subscribed to abstract SET: home/light/bettlicht_patty/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,788 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x0017880108158b32
|
|
||||||
abstraction | 2025-11-18 10:23:59,807 - __main__ - INFO - Subscribed to abstract SET: home/light/schranklicht_hinten_patty/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,825 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x0017880106e29571
|
|
||||||
abstraction | 2025-11-18 10:23:59,844 - __main__ - INFO - Subscribed to abstract SET: home/relay/schranklicht_vorne_patty/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,862 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b80000154cf5
|
|
||||||
abstraction | 2025-11-18 10:23:59,881 - __main__ - INFO - Subscribed to abstract SET: home/light/leselampe_patty/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,901 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x001788010600ec9d
|
|
||||||
abstraction | 2025-11-18 10:23:59,920 - __main__ - INFO - Subscribed to abstract SET: home/light/deckenlampe_esszimmer/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,940 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x0017880108a03e45
|
|
||||||
abstraction | 2025-11-18 10:23:59,959 - __main__ - INFO - Subscribed to abstract SET: home/light/standlampe_esszimmer/set
|
|
||||||
abstraction | 2025-11-18 10:23:59,979 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xbc33acfffe21f547
|
|
||||||
ui | UI using API_BASE: http://172.19.1.11:8001
|
|
||||||
ui | UI using BASE_PATH: /
|
|
||||||
ui | INFO: Started server process [1]
|
|
||||||
ui | INFO: Waiting for application startup.
|
|
||||||
ui | INFO: Application startup complete.
|
|
||||||
abstraction | 2025-11-18 10:23:59,999 - __main__ - INFO - Subscribed to abstract SET: home/light/haustuer/set
|
|
||||||
ui | INFO: Uvicorn running on http://0.0.0.0:8002 (Press CTRL+C to quit)
|
|
||||||
abstraction | 2025-11-18 10:24:00,016 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xec1bbdfffea6a3da
|
|
||||||
abstraction | 2025-11-18 10:24:00,034 - __main__ - INFO - Subscribed to abstract SET: home/light/deckenlampe_flur_oben/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,053 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x001788010d2123a7
|
|
||||||
abstraction | 2025-11-18 10:24:00,072 - __main__ - INFO - Subscribed to abstract SET: home/light/kueche_deckenlampe/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,090 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x001788010d2c40c4
|
|
||||||
abstraction | 2025-11-18 10:24:00,108 - __main__ - INFO - Subscribed to abstract SET: home/light/sportlicht_tisch/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,127 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b8be2409f31b
|
|
||||||
abstraction | 2025-11-18 10:24:00,145 - __main__ - INFO - Subscribed to abstract SET: home/light/sportlicht_regal/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,163 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b8be2409f569
|
|
||||||
abstraction | 2025-11-18 10:24:00,183 - __main__ - INFO - Subscribed to abstract SET: home/light/licht_flur_oben_am_spiegel/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,201 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x842e14fffefe4ba4
|
|
||||||
abstraction | 2025-11-18 10:24:00,218 - __main__ - INFO - Subscribed to abstract SET: home/light/experimentlabtest/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,237 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b80000195038
|
|
||||||
abstraction | 2025-11-18 10:24:00,255 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_wolfgang/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,271 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x540f57fffe7e3cfe
|
|
||||||
abstraction | 2025-11-18 10:24:00,292 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_kueche/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,313 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x94deb8fffe2e5c06
|
|
||||||
abstraction | 2025-11-18 10:24:00,334 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_schlafzimmer/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,356 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/42/1/SET_TEMPERATURE
|
|
||||||
abstraction | 2025-11-18 10:24:00,377 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_esszimmer/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,398 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/45/1/SET_TEMPERATURE
|
|
||||||
abstraction | 2025-11-18 10:24:00,420 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_wohnzimmer/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,440 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/46/1/SET_TEMPERATURE
|
|
||||||
abstraction | 2025-11-18 10:24:00,457 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_patty/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,475 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/39/1/SET_TEMPERATURE
|
|
||||||
abstraction | 2025-11-18 10:24:00,493 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_bad_oben/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,509 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/41/1/SET_TEMPERATURE
|
|
||||||
abstraction | 2025-11-18 10:24:00,530 - __main__ - INFO - Subscribed to abstract SET: home/thermostat/thermostat_bad_unten/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,551 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/48/1/SET_TEMPERATURE
|
|
||||||
abstraction | 2025-11-18 10:24:00,572 - __main__ - INFO - Subscribed to abstract SET: home/relay/sterne_wohnzimmer/set
|
|
||||||
abstraction | 2025-11-18 10:24:00,593 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0xf0d1b80000155fc2
|
|
||||||
abstraction | 2025-11-18 10:24:00,593 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_schlafzimmer_strasse
|
|
||||||
abstraction | 2025-11-18 10:24:00,614 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/52/1/STATE
|
|
||||||
abstraction | 2025-11-18 10:24:00,614 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_esszimmer_strasse_rechts
|
|
||||||
abstraction | 2025-11-18 10:24:00,630 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/26/1/STATE
|
|
||||||
abstraction | 2025-11-18 10:24:00,630 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_esszimmer_strasse_links
|
|
||||||
abstraction | 2025-11-18 10:24:00,647 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/27/1/STATE
|
|
||||||
abstraction | 2025-11-18 10:24:00,647 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_wohnzimmer_garten_rechts
|
|
||||||
abstraction | 2025-11-18 10:24:00,668 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/28/1/STATE
|
|
||||||
abstraction | 2025-11-18 10:24:00,668 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_wohnzimmer_garten_links
|
|
||||||
abstraction | 2025-11-18 10:24:00,691 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/29/1/STATE
|
|
||||||
abstraction | 2025-11-18 10:24:00,691 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_kueche_garten_fenster
|
|
||||||
abstraction | 2025-11-18 10:24:00,708 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d008b332785
|
|
||||||
abstraction | 2025-11-18 10:24:00,708 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_kueche_garten_tuer
|
|
||||||
abstraction | 2025-11-18 10:24:00,728 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d008b332788
|
|
||||||
abstraction | 2025-11-18 10:24:00,728 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_kueche_strasse_rechts
|
|
||||||
abstraction | 2025-11-18 10:24:00,747 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d008b151803
|
|
||||||
abstraction | 2025-11-18 10:24:00,747 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_kueche_strasse_links
|
|
||||||
abstraction | 2025-11-18 10:24:00,767 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d008b331d0b
|
|
||||||
abstraction | 2025-11-18 10:24:00,767 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_patty_garten_rechts
|
|
||||||
abstraction | 2025-11-18 10:24:00,784 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/18/1/STATE
|
|
||||||
abstraction | 2025-11-18 10:24:00,784 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_patty_garten_links
|
|
||||||
abstraction | 2025-11-18 10:24:00,802 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/22/1/STATE
|
|
||||||
abstraction | 2025-11-18 10:24:00,802 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_patty_strasse
|
|
||||||
abstraction | 2025-11-18 10:24:00,821 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d000af457cf
|
|
||||||
abstraction | 2025-11-18 10:24:00,821 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_wolfgang_garten
|
|
||||||
abstraction | 2025-11-18 10:24:00,838 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d008b3328da
|
|
||||||
abstraction | 2025-11-18 10:24:00,838 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_bad_oben_strasse
|
|
||||||
abstraction | 2025-11-18 10:24:00,855 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d008b333aec
|
|
||||||
abstraction | 2025-11-18 10:24:00,855 - __main__ - INFO - Skipping SET subscription for read-only device: kontakt_bad_unten_strasse
|
|
||||||
api | INFO: 172.16.3.98:51428 - "GET /realtime HTTP/1.1" 200 OK
|
|
||||||
abstraction | 2025-11-18 10:24:00,872 - __main__ - INFO - Subscribed to vendor STATE: homegear/instance1/plain/44/1/STATE
|
|
||||||
abstraction | 2025-11-18 10:24:00,872 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_schlafzimmer
|
|
||||||
abstraction | 2025-11-18 10:24:00,891 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d00043292dc
|
|
||||||
abstraction | 2025-11-18 10:24:00,891 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_wohnzimmer
|
|
||||||
api | INFO: 172.16.3.98:51429 - "GET /realtime HTTP/1.1" 200 OK
|
|
||||||
abstraction | 2025-11-18 10:24:00,907 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d0008975707
|
|
||||||
abstraction | 2025-11-18 10:24:00,907 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_kueche
|
|
||||||
abstraction | 2025-11-18 10:24:00,925 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d00083299bb
|
|
||||||
abstraction | 2025-11-18 10:24:00,925 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_arbeitszimmer_patty
|
|
||||||
abstraction | 2025-11-18 10:24:00,947 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d0003f052b7
|
|
||||||
abstraction | 2025-11-18 10:24:00,947 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_arbeitszimmer_wolfgang
|
|
||||||
abstraction | 2025-11-18 10:24:00,969 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d000543fb99
|
|
||||||
abstraction | 2025-11-18 10:24:00,969 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_bad_oben
|
|
||||||
abstraction | 2025-11-18 10:24:00,986 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d00093e8987
|
|
||||||
abstraction | 2025-11-18 10:24:00,986 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_bad_unten
|
|
||||||
abstraction | 2025-11-18 10:24:01,004 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d00093e662a
|
|
||||||
abstraction | 2025-11-18 10:24:01,004 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_flur
|
|
||||||
abstraction | 2025-11-18 10:24:01,022 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d000836ccc6
|
|
||||||
abstraction | 2025-11-18 10:24:01,022 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_waschkueche
|
|
||||||
abstraction | 2025-11-18 10:24:01,038 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d000449f3bc
|
|
||||||
abstraction | 2025-11-18 10:24:01,038 - __main__ - INFO - Skipping SET subscription for read-only device: sensor_sportzimmer
|
|
||||||
abstraction | 2025-11-18 10:24:01,058 - __main__ - INFO - Subscribed to vendor STATE: zigbee2mqtt/0x00158d0009421422
|
|
||||||
abstraction | 2025-11-18 10:24:01,074 - __main__ - INFO - Subscribed to abstract SET: home/relay/licht_spuele_kueche/set
|
|
||||||
abstraction | 2025-11-18 10:24:01,090 - __main__ - INFO - Subscribed to vendor STATE: shellies/LightKitchenSink/relay/0
|
|
||||||
abstraction | 2025-11-18 10:24:01,107 - __main__ - INFO - Subscribed to abstract SET: home/relay/licht_schrank_esszimmer/set
|
|
||||||
abstraction | 2025-11-18 10:24:01,122 - __main__ - INFO - Subscribed to vendor STATE: shellies/schrankesszimmer/relay/0
|
|
||||||
abstraction | 2025-11-18 10:24:01,139 - __main__ - INFO - Subscribed to abstract SET: home/relay/licht_regal_wohnzimmer/set
|
|
||||||
abstraction | 2025-11-18 10:24:01,155 - __main__ - INFO - Subscribed to vendor STATE: shellies/wohnzimmer-regal/relay/0
|
|
||||||
abstraction | 2025-11-18 10:24:01,172 - __main__ - INFO - Subscribed to abstract SET: home/relay/licht_flur_schrank/set
|
|
||||||
abstraction | 2025-11-18 10:24:01,189 - __main__ - INFO - Subscribed to vendor STATE: shellies/schrankflur/relay/0
|
|
||||||
abstraction | 2025-11-18 10:24:01,205 - __main__ - INFO - Subscribed to abstract SET: home/relay/licht_terasse/set
|
|
||||||
abstraction | 2025-11-18 10:24:01,222 - __main__ - INFO - Subscribed to vendor STATE: shellies/lichtterasse/relay/0
|
|
||||||
abstraction | 2025-11-18 10:24:01,222 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=thermostat, tech=max, payload=21
|
|
||||||
abstraction | 2025-11-18 10:24:01,222 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=thermostat, tech=max, payload={'target': 21.0, 'mode': 'heat'}
|
|
||||||
abstraction | 2025-11-18 10:24:01,222 - __main__ - INFO - ← abstract STATE thermostat_schlafzimmer: home/thermostat/thermostat_schlafzimmer/state → {"target": 21.0, "mode": "heat"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,243 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "thermostat_schlafzimmer", "payload": {"target": 21.0, "mode": "heat"}, "ts": "2025-11-18T10:24:01.243641+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,260 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=thermostat, tech=max, payload=15
|
|
||||||
abstraction | 2025-11-18 10:24:01,260 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=thermostat, tech=max, payload={'target': 15.0, 'mode': 'heat'}
|
|
||||||
abstraction | 2025-11-18 10:24:01,260 - __main__ - INFO - ← abstract STATE thermostat_esszimmer: home/thermostat/thermostat_esszimmer/state → {"target": 15.0, "mode": "heat"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,280 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "thermostat_esszimmer", "payload": {"target": 15.0, "mode": "heat"}, "ts": "2025-11-18T10:24:01.280285+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,296 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=thermostat, tech=max, payload=15
|
|
||||||
abstraction | 2025-11-18 10:24:01,296 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=thermostat, tech=max, payload={'target': 15.0, 'mode': 'heat'}
|
|
||||||
abstraction | 2025-11-18 10:24:01,296 - __main__ - INFO - ← abstract STATE thermostat_wohnzimmer: home/thermostat/thermostat_wohnzimmer/state → {"target": 15.0, "mode": "heat"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,317 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "thermostat_wohnzimmer", "payload": {"target": 15.0, "mode": "heat"}, "ts": "2025-11-18T10:24:01.317708+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,334 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=thermostat, tech=max, payload=22
|
|
||||||
abstraction | 2025-11-18 10:24:01,334 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=thermostat, tech=max, payload={'target': 22.0, 'mode': 'heat'}
|
|
||||||
abstraction | 2025-11-18 10:24:01,334 - __main__ - INFO - ← abstract STATE thermostat_patty: home/thermostat/thermostat_patty/state → {"target": 22.0, "mode": "heat"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,357 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "thermostat_patty", "payload": {"target": 22.0, "mode": "heat"}, "ts": "2025-11-18T10:24:01.357082+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,373 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=thermostat, tech=max, payload=20
|
|
||||||
abstraction | 2025-11-18 10:24:01,373 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=thermostat, tech=max, payload={'target': 20.0, 'mode': 'heat'}
|
|
||||||
abstraction | 2025-11-18 10:24:01,373 - __main__ - INFO - ← abstract STATE thermostat_bad_oben: home/thermostat/thermostat_bad_oben/state → {"target": 20.0, "mode": "heat"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,395 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "thermostat_bad_oben", "payload": {"target": 20.0, "mode": "heat"}, "ts": "2025-11-18T10:24:01.395470+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,411 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=thermostat, tech=max, payload=5
|
|
||||||
abstraction | 2025-11-18 10:24:01,411 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=thermostat, tech=max, payload={'target': 5.0, 'mode': 'heat'}
|
|
||||||
abstraction | 2025-11-18 10:24:01,411 - __main__ - INFO - ← abstract STATE thermostat_bad_unten: home/thermostat/thermostat_bad_unten/state → {"target": 5.0, "mode": "heat"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,431 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "thermostat_bad_unten", "payload": {"target": 5.0, "mode": "heat"}, "ts": "2025-11-18T10:24:01.431068+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,448 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=false
|
|
||||||
abstraction | 2025-11-18 10:24:01,448 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'closed'}
|
|
||||||
abstraction | 2025-11-18 10:24:01,449 - __main__ - INFO - ← abstract STATE kontakt_schlafzimmer_strasse: home/contact/kontakt_schlafzimmer_strasse/state → {"contact": "closed"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,472 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_schlafzimmer_strasse", "payload": {"contact": "closed"}, "ts": "2025-11-18T10:24:01.472456+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,491 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=false
|
|
||||||
abstraction | 2025-11-18 10:24:01,491 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'closed'}
|
|
||||||
abstraction | 2025-11-18 10:24:01,491 - __main__ - INFO - ← abstract STATE kontakt_esszimmer_strasse_rechts: home/contact/kontakt_esszimmer_strasse_rechts/state → {"contact": "closed"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,733 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_esszimmer_strasse_rechts", "payload": {"contact": "closed"}, "ts": "2025-11-18T10:24:01.733873+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,750 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=false
|
|
||||||
abstraction | 2025-11-18 10:24:01,750 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'closed'}
|
|
||||||
abstraction | 2025-11-18 10:24:01,750 - __main__ - INFO - ← abstract STATE kontakt_esszimmer_strasse_links: home/contact/kontakt_esszimmer_strasse_links/state → {"contact": "closed"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,771 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_esszimmer_strasse_links", "payload": {"contact": "closed"}, "ts": "2025-11-18T10:24:01.771380+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,788 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=false
|
|
||||||
abstraction | 2025-11-18 10:24:01,788 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'closed'}
|
|
||||||
abstraction | 2025-11-18 10:24:01,788 - __main__ - INFO - ← abstract STATE kontakt_wohnzimmer_garten_rechts: home/contact/kontakt_wohnzimmer_garten_rechts/state → {"contact": "closed"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,808 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_wohnzimmer_garten_rechts", "payload": {"contact": "closed"}, "ts": "2025-11-18T10:24:01.808516+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,825 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=false
|
|
||||||
abstraction | 2025-11-18 10:24:01,825 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'closed'}
|
|
||||||
abstraction | 2025-11-18 10:24:01,825 - __main__ - INFO - ← abstract STATE kontakt_wohnzimmer_garten_links: home/contact/kontakt_wohnzimmer_garten_links/state → {"contact": "closed"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,844 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_wohnzimmer_garten_links", "payload": {"contact": "closed"}, "ts": "2025-11-18T10:24:01.844046+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,860 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=false
|
|
||||||
abstraction | 2025-11-18 10:24:01,861 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'closed'}
|
|
||||||
abstraction | 2025-11-18 10:24:01,861 - __main__ - INFO - ← abstract STATE kontakt_patty_garten_rechts: home/contact/kontakt_patty_garten_rechts/state → {"contact": "closed"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,881 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_patty_garten_rechts", "payload": {"contact": "closed"}, "ts": "2025-11-18T10:24:01.881922+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,898 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=false
|
|
||||||
abstraction | 2025-11-18 10:24:01,898 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'closed'}
|
|
||||||
abstraction | 2025-11-18 10:24:01,898 - __main__ - INFO - ← abstract STATE kontakt_patty_garten_links: home/contact/kontakt_patty_garten_links/state → {"contact": "closed"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,922 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_patty_garten_links", "payload": {"contact": "closed"}, "ts": "2025-11-18T10:24:01.922254+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,940 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=contact, tech=max, payload=true
|
|
||||||
abstraction | 2025-11-18 10:24:01,940 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=contact, tech=max, payload={'contact': 'open'}
|
|
||||||
abstraction | 2025-11-18 10:24:01,940 - __main__ - INFO - ← abstract STATE kontakt_bad_unten_strasse: home/contact/kontakt_bad_unten_strasse/state → {"contact": "open"}
|
|
||||||
abstraction | 2025-11-18 10:24:01,959 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "kontakt_bad_unten_strasse", "payload": {"contact": "open"}, "ts": "2025-11-18T10:24:01.959678+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:02,354 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 10:24:02,354 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 10:24:02,354 - __main__ - INFO - ← abstract STATE licht_terasse: home/relay/licht_terasse/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 10:24:02,373 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_terasse", "payload": {"power": "off"}, "ts": "2025-11-18T10:24:02.373461+00:00"}
|
|
||||||
ui | INFO: 127.0.0.1:49192 - "GET /health HTTP/1.1" 200 OK
|
|
||||||
api | INFO: 172.16.3.98:51450 - "GET /realtime HTTP/1.1" 200 OK
|
|
||||||
abstraction | 2025-11-18 10:24:07,440 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 10:24:07,440 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 10:24:07,441 - __main__ - INFO - ← abstract STATE licht_regal_wohnzimmer: home/relay/licht_regal_wohnzimmer/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 10:24:07,459 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_regal_wohnzimmer", "payload": {"power": "off"}, "ts": "2025-11-18T10:24:07.459082+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:08,817 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={'battery': 100, 'humidity': 54.37, 'linkquality': 83, 'power_outage_count': 38416, 'pressure': 1004.2, 'temperature': 22.16, 'voltage': 3015}
|
|
||||||
abstraction | 2025-11-18 10:24:08,817 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={'battery': 100, 'humidity': 54.37, 'linkquality': 83, 'power_outage_count': 38416, 'pressure': 1004.2, 'temperature': 22.16, 'voltage': 3015}
|
|
||||||
abstraction | 2025-11-18 10:24:08,817 - __main__ - INFO - ← abstract STATE sensor_arbeitszimmer_patty: home/temp_humidity/sensor_arbeitszimmer_patty/state → {"battery": 100, "humidity": 54.37, "linkquality": 83, "power_outage_count": 38416, "pressure": 1004.2, "temperature": 22.16, "voltage": 3015}
|
|
||||||
abstraction | 2025-11-18 10:24:08,835 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "sensor_arbeitszimmer_patty", "payload": {"battery": 100, "humidity": 54.37, "linkquality": 83, "power_outage_count": 38416, "pressure": 1004.2, "temperature": 22.16, "voltage": 3015}, "ts": "2025-11-18T10:24:08.835488+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:08,852 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={'battery': 100, 'humidity': 54.22, 'linkquality': 83, 'power_outage_count': 38416, 'pressure': 1004.2, 'temperature': 22.16, 'voltage': 3015}
|
|
||||||
abstraction | 2025-11-18 10:24:08,852 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={'battery': 100, 'humidity': 54.22, 'linkquality': 83, 'power_outage_count': 38416, 'pressure': 1004.2, 'temperature': 22.16, 'voltage': 3015}
|
|
||||||
abstraction | 2025-11-18 10:24:08,852 - __main__ - INFO - ← abstract STATE sensor_arbeitszimmer_patty: home/temp_humidity/sensor_arbeitszimmer_patty/state → {"battery": 100, "humidity": 54.22, "linkquality": 83, "power_outage_count": 38416, "pressure": 1004.2, "temperature": 22.16, "voltage": 3015}
|
|
||||||
abstraction | 2025-11-18 10:24:08,870 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "sensor_arbeitszimmer_patty", "payload": {"battery": 100, "humidity": 54.22, "linkquality": 83, "power_outage_count": 38416, "pressure": 1004.2, "temperature": 22.16, "voltage": 3015}, "ts": "2025-11-18T10:24:08.870674+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:08,887 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={'battery': 100, 'humidity': 54.22, 'linkquality': 83, 'power_outage_count': 38416, 'pressure': 1003.9, 'temperature': 22.16, 'voltage': 3015}
|
|
||||||
abstraction | 2025-11-18 10:24:08,887 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=temp_humidity_sensor, tech=zigbee2mqtt, payload={'battery': 100, 'humidity': 54.22, 'linkquality': 83, 'power_outage_count': 38416, 'pressure': 1003.9, 'temperature': 22.16, 'voltage': 3015}
|
|
||||||
abstraction | 2025-11-18 10:24:08,887 - __main__ - INFO - ← abstract STATE sensor_arbeitszimmer_patty: home/temp_humidity/sensor_arbeitszimmer_patty/state → {"battery": 100, "humidity": 54.22, "linkquality": 83, "power_outage_count": 38416, "pressure": 1003.9, "temperature": 22.16, "voltage": 3015}
|
|
||||||
abstraction | 2025-11-18 10:24:08,907 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "sensor_arbeitszimmer_patty", "payload": {"battery": 100, "humidity": 54.22, "linkquality": 83, "power_outage_count": 38416, "pressure": 1003.9, "temperature": 22.16, "voltage": 3015}, "ts": "2025-11-18T10:24:08.907729+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:10,178 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=on
|
|
||||||
abstraction | 2025-11-18 10:24:10,178 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'on'}
|
|
||||||
abstraction | 2025-11-18 10:24:10,178 - __main__ - INFO - ← abstract STATE licht_spuele_kueche: home/relay/licht_spuele_kueche/state → {"power": "on"}
|
|
||||||
abstraction | 2025-11-18 10:24:10,196 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_spuele_kueche", "payload": {"power": "on"}, "ts": "2025-11-18T10:24:10.196762+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:17,815 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 10:24:17,815 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 10:24:17,815 - __main__ - INFO - ← abstract STATE licht_schrank_esszimmer: home/relay/licht_schrank_esszimmer/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 10:24:17,834 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_schrank_esszimmer", "payload": {"power": "off"}, "ts": "2025-11-18T10:24:17.834042+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:32,370 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 10:24:32,370 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 10:24:32,370 - __main__ - INFO - ← abstract STATE licht_terasse: home/relay/licht_terasse/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 10:24:32,405 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_terasse", "payload": {"power": "off"}, "ts": "2025-11-18T10:24:32.405754+00:00"}
|
|
||||||
ui | INFO: 127.0.0.1:39276 - "GET /health HTTP/1.1" 200 OK
|
|
||||||
abstraction | 2025-11-18 10:24:37,447 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 10:24:37,447 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 10:24:37,447 - __main__ - INFO - ← abstract STATE licht_regal_wohnzimmer: home/relay/licht_regal_wohnzimmer/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 10:24:37,465 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_regal_wohnzimmer", "payload": {"power": "off"}, "ts": "2025-11-18T10:24:37.465220+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:40,188 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=on
|
|
||||||
abstraction | 2025-11-18 10:24:40,189 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'on'}
|
|
||||||
abstraction | 2025-11-18 10:24:40,189 - __main__ - INFO - ← abstract STATE licht_spuele_kueche: home/relay/licht_spuele_kueche/state → {"power": "on"}
|
|
||||||
abstraction | 2025-11-18 10:24:40,207 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_spuele_kueche", "payload": {"power": "on"}, "ts": "2025-11-18T10:24:40.207222+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:24:47,833 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 10:24:47,833 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 10:24:47,833 - __main__ - INFO - ← abstract STATE licht_schrank_esszimmer: home/relay/licht_schrank_esszimmer/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 10:24:47,868 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_schrank_esszimmer", "payload": {"power": "off"}, "ts": "2025-11-18T10:24:47.868787+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:25:02,363 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 10:25:02,363 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 10:25:02,363 - __main__ - INFO - ← abstract STATE licht_terasse: home/relay/licht_terasse/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 10:25:02,381 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_terasse", "payload": {"power": "off"}, "ts": "2025-11-18T10:25:02.381792+00:00"}
|
|
||||||
ui | INFO: 127.0.0.1:37108 - "GET /health HTTP/1.1" 200 OK
|
|
||||||
abstraction | 2025-11-18 10:25:07,447 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 10:25:07,448 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 10:25:07,448 - __main__ - INFO - ← abstract STATE licht_regal_wohnzimmer: home/relay/licht_regal_wohnzimmer/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 10:25:07,465 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_regal_wohnzimmer", "payload": {"power": "off"}, "ts": "2025-11-18T10:25:07.465566+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:25:10,185 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=on
|
|
||||||
abstraction | 2025-11-18 10:25:10,185 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'on'}
|
|
||||||
abstraction | 2025-11-18 10:25:10,185 - __main__ - INFO - ← abstract STATE licht_spuele_kueche: home/relay/licht_spuele_kueche/state → {"power": "on"}
|
|
||||||
abstraction | 2025-11-18 10:25:10,202 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_spuele_kueche", "payload": {"power": "on"}, "ts": "2025-11-18T10:25:10.202372+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:25:17,820 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 10:25:17,820 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 10:25:17,820 - __main__ - INFO - ← abstract STATE licht_schrank_esszimmer: home/relay/licht_schrank_esszimmer/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 10:25:17,838 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_schrank_esszimmer", "payload": {"power": "off"}, "ts": "2025-11-18T10:25:17.838140+00:00"}
|
|
||||||
api | INFO: 172.16.3.98:51763 - "GET /docs HTTP/1.1" 200 OK
|
|
||||||
api | /usr/local/lib/python3.14/site-packages/fastapi/openapi/utils.py:207: UserWarning: Duplicate Operation ID get_device_layout_devices__device_id__layout_get for function get_device_layout at /app/apps/api/main.py
|
|
||||||
api | warnings.warn(message, stacklevel=1)
|
|
||||||
api | /usr/local/lib/python3.14/site-packages/fastapi/openapi/utils.py:207: UserWarning: Duplicate Operation ID get_device_state_devices__device_id__state_get for function get_device_state at /app/apps/api/main.py
|
|
||||||
api | warnings.warn(message, stacklevel=1)
|
|
||||||
api | INFO: 172.16.3.98:51763 - "GET /openapi.json HTTP/1.1" 200 OK
|
|
||||||
api | INFO: 172.16.3.98:51763 - "GET /devices/states HTTP/1.1" 200 OK
|
|
||||||
abstraction | 2025-11-18 10:25:32,361 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 10:25:32,361 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 10:25:32,361 - __main__ - INFO - ← abstract STATE licht_terasse: home/relay/licht_terasse/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 10:25:32,379 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_terasse", "payload": {"power": "off"}, "ts": "2025-11-18T10:25:32.379286+00:00"}
|
|
||||||
ui | INFO: 127.0.0.1:41510 - "GET /health HTTP/1.1" 200 OK
|
|
||||||
abstraction | 2025-11-18 10:25:37,455 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 10:25:37,455 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 10:25:37,455 - __main__ - INFO - ← abstract STATE licht_regal_wohnzimmer: home/relay/licht_regal_wohnzimmer/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 10:25:37,473 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_regal_wohnzimmer", "payload": {"power": "off"}, "ts": "2025-11-18T10:25:37.473171+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:25:40,193 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=on
|
|
||||||
abstraction | 2025-11-18 10:25:40,194 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'on'}
|
|
||||||
abstraction | 2025-11-18 10:25:40,194 - __main__ - INFO - ← abstract STATE licht_spuele_kueche: home/relay/licht_spuele_kueche/state → {"power": "on"}
|
|
||||||
abstraction | 2025-11-18 10:25:40,211 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_spuele_kueche", "payload": {"power": "on"}, "ts": "2025-11-18T10:25:40.211493+00:00"}
|
|
||||||
abstraction | 2025-11-18 10:25:47,821 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract IN: type=relay, tech=shelly, payload=off
|
|
||||||
abstraction | 2025-11-18 10:25:47,821 - apps.abstraction.transformation - DEBUG - transform_vendor_to_abstract OUT: type=relay, tech=shelly, payload={'power': 'off'}
|
|
||||||
abstraction | 2025-11-18 10:25:47,821 - __main__ - INFO - ← abstract STATE licht_schrank_esszimmer: home/relay/licht_schrank_esszimmer/state → {"power": "off"}
|
|
||||||
abstraction | 2025-11-18 10:25:47,838 - __main__ - INFO - ← Redis PUBLISH ui:updates → {"type": "state", "device_id": "licht_schrank_esszimmer", "payload": {"power": "off"}, "ts": "2025-11-18T10:25:47.838508+00:00"}
|
|
||||||
73
deployment/abstraction-deployment.yaml
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: abstraction
|
||||||
|
labels:
|
||||||
|
app: abstraction
|
||||||
|
component: home-automation
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: abstraction
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
reloader.stakater.com/auto: "true"
|
||||||
|
configmap.reloader.stakater.com/reload: "home-automation-environment,home-automation-config"
|
||||||
|
labels:
|
||||||
|
app: abstraction
|
||||||
|
component: home-automation
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: abstraction
|
||||||
|
image: %IMAGE%
|
||||||
|
env:
|
||||||
|
- name: MQTT_BROKER
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: home-automation-environment
|
||||||
|
key: SHARED_MQTT_BROKER
|
||||||
|
- name: MQTT_PORT
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: home-automation-environment
|
||||||
|
key: SHARED_MQTT_PORT
|
||||||
|
- name: REDIS_HOST
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: home-automation-environment
|
||||||
|
key: SHARED_REDIS_HOST
|
||||||
|
- name: REDIS_PORT
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: home-automation-environment
|
||||||
|
key: SHARED_REDIS_PORT
|
||||||
|
- name: REDIS_DB
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: home-automation-environment
|
||||||
|
key: SHARED_REDIS_DB
|
||||||
|
volumeMounts:
|
||||||
|
- name: config-volume
|
||||||
|
mountPath: /app/config
|
||||||
|
readOnly: true
|
||||||
|
livenessProbe:
|
||||||
|
exec:
|
||||||
|
command:
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- "ps aux | grep -v grep | grep python"
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 10
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpu: 500m
|
||||||
|
memory: 512Mi
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 128Mi
|
||||||
|
volumes:
|
||||||
|
- name: config-volume
|
||||||
|
configMap:
|
||||||
|
name: home-automation-config
|
||||||
102
deployment/api-deployment.yaml
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: api
|
||||||
|
namespace: homea2
|
||||||
|
labels:
|
||||||
|
app: api
|
||||||
|
component: home-automation
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: api
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
reloader.stakater.com/auto: "true"
|
||||||
|
configmap.reloader.stakater.com/reload: "home-automation-environment,home-automation-config"
|
||||||
|
labels:
|
||||||
|
app: api
|
||||||
|
component: home-automation
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: api
|
||||||
|
image: %IMAGE%
|
||||||
|
ports:
|
||||||
|
- containerPort: 8001
|
||||||
|
name: http
|
||||||
|
env:
|
||||||
|
- name: MQTT_BROKER
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: home-automation-environment
|
||||||
|
key: SHARED_MQTT_BROKER
|
||||||
|
- name: MQTT_PORT
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: home-automation-environment
|
||||||
|
key: SHARED_MQTT_PORT
|
||||||
|
- name: REDIS_HOST
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: home-automation-environment
|
||||||
|
key: SHARED_REDIS_HOST
|
||||||
|
- name: REDIS_PORT
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: home-automation-environment
|
||||||
|
key: SHARED_REDIS_PORT
|
||||||
|
- name: REDIS_DB
|
||||||
|
valueFrom:
|
||||||
|
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
|
||||||
|
readOnly: true
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health
|
||||||
|
port: 8001
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 10
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health
|
||||||
|
port: 8001
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 5
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpu: 1000m
|
||||||
|
memory: 1Gi
|
||||||
|
requests:
|
||||||
|
cpu: 200m
|
||||||
|
memory: 256Mi
|
||||||
|
volumes:
|
||||||
|
- name: config-volume
|
||||||
|
configMap:
|
||||||
|
name: home-automation-config
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: api
|
||||||
|
labels:
|
||||||
|
app: api
|
||||||
|
component: home-automation
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: api
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
targetPort: 8001
|
||||||
|
name: http
|
||||||
|
type: ClusterIP
|
||||||
23
deployment/configmap.yaml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: home-automation-environment
|
||||||
|
namespace: homea2
|
||||||
|
data:
|
||||||
|
# Default environment variables
|
||||||
|
SHARED_MQTT_BROKER: "emqx01-anonymous-cluster-internal.broker.svc.cluster.local"
|
||||||
|
SHARED_MQTT_PORT: "1883"
|
||||||
|
SHARED_REDIS_HOST: "redis-master.redis.svc.cluster.local"
|
||||||
|
SHARED_REDIS_PORT: "6379"
|
||||||
|
SHARED_REDIS_DB: "8"
|
||||||
|
|
||||||
|
# UI specific environment variables
|
||||||
|
UI_UI_PORT: "8002"
|
||||||
|
UI_API_BASE: "https://homea2-api.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"
|
||||||
62
deployment/ingress.yaml
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
apiVersion: cert-manager.io/v1
|
||||||
|
kind: Certificate
|
||||||
|
metadata:
|
||||||
|
name: homea2-cert
|
||||||
|
spec:
|
||||||
|
secretName: homea2-cert
|
||||||
|
issuerRef:
|
||||||
|
name: letsencrypt-production-http
|
||||||
|
kind: ClusterIssuer
|
||||||
|
commonName: homea2.hottis.de
|
||||||
|
dnsNames:
|
||||||
|
- homea2.hottis.de
|
||||||
|
- homea2-api.hottis.de
|
||||||
|
---
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: TLSOption
|
||||||
|
metadata:
|
||||||
|
name: mtls-required
|
||||||
|
spec:
|
||||||
|
clientAuth:
|
||||||
|
clientAuthType: RequireAndVerifyClientCert
|
||||||
|
secretNames:
|
||||||
|
- mtls-ca-cert
|
||||||
|
---
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: ui
|
||||||
|
spec:
|
||||||
|
entryPoints:
|
||||||
|
- websecure
|
||||||
|
tls:
|
||||||
|
secretName: homea2-cert
|
||||||
|
options:
|
||||||
|
name: mtls-required
|
||||||
|
namespace: homea2
|
||||||
|
routes:
|
||||||
|
- match: Host(`homea2.hottis.de`)
|
||||||
|
kind: Rule
|
||||||
|
services:
|
||||||
|
- name: ui
|
||||||
|
port: 80
|
||||||
|
---
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: api
|
||||||
|
spec:
|
||||||
|
entryPoints:
|
||||||
|
- websecure
|
||||||
|
tls:
|
||||||
|
secretName: homea2-cert
|
||||||
|
options:
|
||||||
|
name: mtls-required
|
||||||
|
namespace: homea2
|
||||||
|
routes:
|
||||||
|
- match: Host(`homea2-api.hottis.de`)
|
||||||
|
kind: Rule
|
||||||
|
services:
|
||||||
|
- name: api
|
||||||
|
port: 80
|
||||||
|
|
||||||
78
deployment/rules-deployment.yaml
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: rules
|
||||||
|
labels:
|
||||||
|
app: rules
|
||||||
|
component: home-automation
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: rules
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
reloader.stakater.com/auto: "true"
|
||||||
|
configmap.reloader.stakater.com/reload: "home-automation-environment,home-automation-config"
|
||||||
|
labels:
|
||||||
|
app: rules
|
||||||
|
component: home-automation
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: rules
|
||||||
|
image: %IMAGE%
|
||||||
|
env:
|
||||||
|
- name: MQTT_BROKER
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: home-automation-environment
|
||||||
|
key: SHARED_MQTT_BROKER
|
||||||
|
- name: MQTT_PORT
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: home-automation-environment
|
||||||
|
key: SHARED_MQTT_PORT
|
||||||
|
- name: REDIS_HOST
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: home-automation-environment
|
||||||
|
key: SHARED_REDIS_HOST
|
||||||
|
- name: REDIS_PORT
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: home-automation-environment
|
||||||
|
key: SHARED_REDIS_PORT
|
||||||
|
- name: REDIS_DB
|
||||||
|
valueFrom:
|
||||||
|
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
|
||||||
|
readOnly: true
|
||||||
|
livenessProbe:
|
||||||
|
exec:
|
||||||
|
command:
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- "ps aux | grep -v grep | grep python"
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 10
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpu: 500m
|
||||||
|
memory: 512Mi
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 128Mi
|
||||||
|
volumes:
|
||||||
|
- name: config-volume
|
||||||
|
configMap:
|
||||||
|
name: home-automation-config
|
||||||
79
deployment/ui-deployment.yaml
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: ui
|
||||||
|
namespace: homea2
|
||||||
|
labels:
|
||||||
|
app: ui
|
||||||
|
component: home-automation
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: ui
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
reloader.stakater.com/auto: "true"
|
||||||
|
configmap.reloader.stakater.com/reload: "home-automation-environment"
|
||||||
|
labels:
|
||||||
|
app: ui
|
||||||
|
component: home-automation
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: ui
|
||||||
|
image: %IMAGE%
|
||||||
|
ports:
|
||||||
|
- containerPort: 8002
|
||||||
|
name: http
|
||||||
|
env:
|
||||||
|
- name: UI_PORT
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: home-automation-environment
|
||||||
|
key: UI_UI_PORT
|
||||||
|
- name: API_BASE
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: home-automation-environment
|
||||||
|
key: UI_API_BASE
|
||||||
|
- name: BASE_PATH
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: home-automation-environment
|
||||||
|
key: UI_BASE_PATH
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: 8002
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 10
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: 8002
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 5
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpu: 500m
|
||||||
|
memory: 512Mi
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 128Mi
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: ui
|
||||||
|
labels:
|
||||||
|
app: ui
|
||||||
|
component: home-automation
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: ui
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
targetPort: 8002
|
||||||
|
name: http
|
||||||
|
type: ClusterIP
|
||||||
59
icon-generator.html
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Icon Generator</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<canvas id="homeCanvas" width="180" height="180" style="border: 1px solid #ccc;"></canvas>
|
||||||
|
<canvas id="garageCanvas" width="180" height="180" style="border: 1px solid #ccc;"></canvas>
|
||||||
|
<br><br>
|
||||||
|
<button onclick="downloadHome()">Download Home Icon</button>
|
||||||
|
<button onclick="downloadGarage()">Download Garage Icon</button>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Home Icon
|
||||||
|
const homeCanvas = document.getElementById('homeCanvas');
|
||||||
|
const homeCtx = homeCanvas.getContext('2d');
|
||||||
|
|
||||||
|
// Fill background
|
||||||
|
homeCtx.fillStyle = '#667EEA';
|
||||||
|
homeCtx.fillRect(0, 0, 180, 180);
|
||||||
|
|
||||||
|
// Add house emoji
|
||||||
|
homeCtx.font = '80px Arial';
|
||||||
|
homeCtx.fillStyle = 'white';
|
||||||
|
homeCtx.textAlign = 'center';
|
||||||
|
homeCtx.textBaseline = 'middle';
|
||||||
|
homeCtx.fillText('🏡', 90, 90);
|
||||||
|
|
||||||
|
// Garage Icon
|
||||||
|
const garageCanvas = document.getElementById('garageCanvas');
|
||||||
|
const garageCtx = garageCanvas.getContext('2d');
|
||||||
|
|
||||||
|
// Fill background
|
||||||
|
garageCtx.fillStyle = '#667EEA';
|
||||||
|
garageCtx.fillRect(0, 0, 180, 180);
|
||||||
|
|
||||||
|
// Add car emoji
|
||||||
|
garageCtx.font = '80px Arial';
|
||||||
|
garageCtx.fillStyle = 'white';
|
||||||
|
garageCtx.textAlign = 'center';
|
||||||
|
garageCtx.textBaseline = 'middle';
|
||||||
|
garageCtx.fillText('🚗', 90, 90);
|
||||||
|
|
||||||
|
function downloadHome() {
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.download = 'apple-touch-icon.png';
|
||||||
|
link.href = homeCanvas.toDataURL();
|
||||||
|
link.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadGarage() {
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.download = 'garage-icon.png';
|
||||||
|
link.href = garageCanvas.toDataURL();
|
||||||
|
link.click();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
77
icon-test.html
Normal 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>
|
||||||
@@ -10,6 +10,9 @@ from packages.home_capabilities.temp_humidity_sensor import CAP_VERSION as TEMP_
|
|||||||
from packages.home_capabilities.temp_humidity_sensor import TempHumidityState
|
from packages.home_capabilities.temp_humidity_sensor import TempHumidityState
|
||||||
from packages.home_capabilities.relay import CAP_VERSION as RELAY_VERSION
|
from packages.home_capabilities.relay import CAP_VERSION as RELAY_VERSION
|
||||||
from packages.home_capabilities.relay import RelayState
|
from packages.home_capabilities.relay import RelayState
|
||||||
|
from packages.home_capabilities.three_phase_powermeter import CAP_VERSION as THREE_PHASE_POWERMETER_VERSION
|
||||||
|
from packages.home_capabilities.three_phase_powermeter import ThreePhasePowerState
|
||||||
|
|
||||||
from packages.home_capabilities.layout import (
|
from packages.home_capabilities.layout import (
|
||||||
DeviceTile,
|
DeviceTile,
|
||||||
Room,
|
Room,
|
||||||
@@ -56,4 +59,5 @@ __all__ = [
|
|||||||
"get_scene_by_id",
|
"get_scene_by_id",
|
||||||
"load_groups",
|
"load_groups",
|
||||||
"load_scenes",
|
"load_scenes",
|
||||||
|
"ThreePhasePowerState",
|
||||||
]
|
]
|
||||||
|
|||||||
29
packages/home_capabilities/three_phase_powermeter.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
class ThreePhasePowerState(BaseModel):
|
||||||
|
"""
|
||||||
|
State model for a three-phase power meter.
|
||||||
|
|
||||||
|
Required fields:
|
||||||
|
- energy: Total energy in kWh
|
||||||
|
- total_power: Total power in W
|
||||||
|
- phase1_power, phase2_power, phase3_power: Power per phase in W
|
||||||
|
- phase1_voltage, phase2_voltage, phase3_voltage: Voltage per phase in V
|
||||||
|
- phase1_current, phase2_current, phase3_current: Current per phase in A
|
||||||
|
"""
|
||||||
|
energy: float = Field(..., description="Total energy in kWh")
|
||||||
|
total_power: float = Field(..., description="Total power in W")
|
||||||
|
phase1_power: float = Field(..., description="Power for phase 1 in W")
|
||||||
|
phase2_power: float = Field(..., description="Power for phase 2 in W")
|
||||||
|
phase3_power: float = Field(..., description="Power for phase 3 in W")
|
||||||
|
phase1_voltage: float = Field(..., description="Voltage for phase 1 in V")
|
||||||
|
phase2_voltage: float = Field(..., description="Voltage for phase 2 in V")
|
||||||
|
phase3_voltage: float = Field(..., description="Voltage for phase 3 in V")
|
||||||
|
phase1_current: float = Field(..., description="Current for phase 1 in A")
|
||||||
|
phase2_current: float = Field(..., description="Current for phase 2 in A")
|
||||||
|
phase3_current: float = Field(..., description="Current for phase 3 in A")
|
||||||
|
|
||||||
|
|
||||||
|
# Capability metadata
|
||||||
|
CAP_VERSION = "three_phase_powermeter@1.0.0"
|
||||||
|
DISPLAY_NAME = "Three-Phase Power Meter"
|
||||||
29
test-icons.sh
Executable 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"
|
||||||
@@ -1,190 +0,0 @@
|
|||||||
# Device Simulator
|
|
||||||
|
|
||||||
Unified MQTT device simulator für das Home Automation System.
|
|
||||||
|
|
||||||
## Übersicht
|
|
||||||
|
|
||||||
Dieser Simulator ersetzt die einzelnen Simulatoren (`sim_test_lampe.py`, `sim_thermo.py`) und vereint alle Device-Typen in einer einzigen Anwendung.
|
|
||||||
|
|
||||||
## Unterstützte Geräte
|
|
||||||
|
|
||||||
### Lampen (3 Geräte)
|
|
||||||
- `test_lampe_1` - Mit Power und Brightness
|
|
||||||
- `test_lampe_2` - Mit Power und Brightness
|
|
||||||
- `test_lampe_3` - Mit Power und Brightness
|
|
||||||
|
|
||||||
**Features:**
|
|
||||||
- `power`: "on" oder "off"
|
|
||||||
- `brightness`: 0-100
|
|
||||||
|
|
||||||
### Thermostaten (1 Gerät)
|
|
||||||
- `test_thermo_1` - Vollständiger Thermostat mit Temperatur-Simulation
|
|
||||||
|
|
||||||
**Features:**
|
|
||||||
- `mode`: "off", "heat", oder "auto"
|
|
||||||
- `target`: Soll-Temperatur (5.0-30.0°C)
|
|
||||||
- `current`: Ist-Temperatur (wird simuliert)
|
|
||||||
- `battery`: Batteriestand (90%)
|
|
||||||
- `window_open`: Fensterstatus (false)
|
|
||||||
|
|
||||||
**Temperatur-Simulation:**
|
|
||||||
- Alle 5 Sekunden wird die Ist-Temperatur angepasst
|
|
||||||
- **HEAT/AUTO Mode**: Drift zu `target` (+0.2°C pro Intervall)
|
|
||||||
- **OFF Mode**: Drift zu Ambient-Temperatur 18°C (-0.2°C pro Intervall)
|
|
||||||
|
|
||||||
## MQTT-Konfiguration
|
|
||||||
|
|
||||||
- **Broker**: 172.16.2.16:1883 (konfigurierbar via ENV)
|
|
||||||
- **QoS**: 1 für alle Publishes
|
|
||||||
- **Retained**: Ja für alle State-Messages
|
|
||||||
- **Client ID**: device_simulator
|
|
||||||
|
|
||||||
### Topics
|
|
||||||
|
|
||||||
Für jedes Gerät:
|
|
||||||
- Subscribe: `vendor/{device_id}/set` (QoS 1)
|
|
||||||
- Publish: `vendor/{device_id}/state` (QoS 1, retained)
|
|
||||||
|
|
||||||
## Verwendung
|
|
||||||
|
|
||||||
### Starten
|
|
||||||
|
|
||||||
```bash
|
|
||||||
poetry run python tools/device_simulator.py
|
|
||||||
```
|
|
||||||
|
|
||||||
Oder im Hintergrund:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
poetry run python tools/device_simulator.py > /tmp/simulator.log 2>&1 &
|
|
||||||
```
|
|
||||||
|
|
||||||
### Umgebungsvariablen
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export MQTT_BROKER="172.16.2.16" # MQTT Broker Host
|
|
||||||
export MQTT_PORT="1883" # MQTT Broker Port
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testen
|
|
||||||
|
|
||||||
Ein umfassendes Test-Skript ist verfügbar:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./tools/test_device_simulator.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
Das Test-Skript:
|
|
||||||
1. Stoppt alle laufenden Services
|
|
||||||
2. Startet Abstraction Layer, API und Simulator
|
|
||||||
3. Testet alle Lampen-Operationen
|
|
||||||
4. Testet alle Thermostat-Operationen
|
|
||||||
5. Verifiziert MQTT State Messages
|
|
||||||
6. Zeigt Simulator-Logs
|
|
||||||
|
|
||||||
## Beispiele
|
|
||||||
|
|
||||||
### Lampe einschalten
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X POST http://localhost:8001/devices/test_lampe_1/set \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"type":"light","payload":{"power":"on"}}'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Helligkeit setzen
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X POST http://localhost:8001/devices/test_lampe_1/set \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"type":"light","payload":{"brightness":75}}'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Thermostat Mode setzen
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X POST http://localhost:8001/devices/test_thermo_1/set \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"type":"thermostat","payload":{"mode":"heat","target":22.5}}'
|
|
||||||
```
|
|
||||||
|
|
||||||
### State abfragen via MQTT
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Lampe
|
|
||||||
mosquitto_sub -h 172.16.2.16 -t 'vendor/test_lampe_1/state' -C 1
|
|
||||||
|
|
||||||
# Thermostat
|
|
||||||
mosquitto_sub -h 172.16.2.16 -t 'vendor/test_thermo_1/state' -C 1
|
|
||||||
```
|
|
||||||
|
|
||||||
## Architektur
|
|
||||||
|
|
||||||
```
|
|
||||||
Browser/API
|
|
||||||
↓ POST /devices/{id}/set
|
|
||||||
API Server (Port 8001)
|
|
||||||
↓ MQTT: home/{type}/{id}/set
|
|
||||||
Abstraction Layer
|
|
||||||
↓ MQTT: vendor/{id}/set
|
|
||||||
Device Simulator
|
|
||||||
↓ MQTT: vendor/{id}/state (retained)
|
|
||||||
Abstraction Layer
|
|
||||||
↓ MQTT: home/{type}/{id}/state (retained)
|
|
||||||
↓ Redis Pub/Sub: ui:updates
|
|
||||||
UI / Dashboard
|
|
||||||
```
|
|
||||||
|
|
||||||
## Logs
|
|
||||||
|
|
||||||
Der Simulator loggt alle Aktivitäten:
|
|
||||||
- Startup und MQTT-Verbindung
|
|
||||||
- Empfangene SET-Commands
|
|
||||||
- State-Änderungen
|
|
||||||
- Temperature-Drift (Thermostaten)
|
|
||||||
- Publizierte State-Messages
|
|
||||||
|
|
||||||
Log-Level: INFO
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Simulator startet nicht
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Prüfe ob Port bereits belegt
|
|
||||||
lsof -ti:1883
|
|
||||||
|
|
||||||
# Prüfe MQTT Broker
|
|
||||||
mosquitto_sub -h 172.16.2.16 -t '#' -C 1
|
|
||||||
```
|
|
||||||
|
|
||||||
### Keine State-Updates
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Prüfe Simulator-Log
|
|
||||||
tail -f /tmp/simulator.log
|
|
||||||
|
|
||||||
# Prüfe MQTT Topics
|
|
||||||
mosquitto_sub -h 172.16.2.16 -t 'vendor/#' -v
|
|
||||||
```
|
|
||||||
|
|
||||||
### API antwortet nicht
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Prüfe ob API läuft
|
|
||||||
curl http://localhost:8001/devices
|
|
||||||
|
|
||||||
# Prüfe API-Log
|
|
||||||
tail -f /tmp/api.log
|
|
||||||
```
|
|
||||||
|
|
||||||
## Integration
|
|
||||||
|
|
||||||
Der Simulator integriert sich nahtlos in das Home Automation System:
|
|
||||||
|
|
||||||
1. **Abstraction Layer** empfängt Commands und sendet sie an Simulator
|
|
||||||
2. **Simulator** reagiert und publiziert neuen State
|
|
||||||
3. **Abstraction Layer** empfängt State und publiziert zu Redis
|
|
||||||
4. **UI** empfängt Updates via SSE und aktualisiert Dashboard
|
|
||||||
|
|
||||||
Alle Komponenten arbeiten vollständig asynchron über MQTT.
|
|
||||||
693
tools/certificates.asc
Normal file
@@ -0,0 +1,693 @@
|
|||||||
|
-----BEGIN PGP MESSAGE-----
|
||||||
|
|
||||||
|
jA0ECQMIvcxCfrertFn90v8AAID+ASqxcvgoVWTsLJvACtmb7XYOQWfNQINjnHsz
|
||||||
|
8mdEMR18Qyj/z981Yo/MgjY4V+jDChHdaTRGNnWkQ08lvGax3pN9NfxTsfc//OoZ
|
||||||
|
cTxm/VutVyTh5u/kXezTr0RObDZUSIEg0VnoogxFJodPzkQxR/W3sCn+d/GnDROH
|
||||||
|
udh/MNiS/a5Q9JdNSu9mmMKIrvkdxJn5JplgzrRjXpX3LqxaJwr2jMIz0u1MnsUM
|
||||||
|
4GqDgof+WYI/fHKUie9dMxQdhYDYOFdCSjEZ2NOPQkID/XCR2tQ/GbrCpCVRUA2Z
|
||||||
|
tjCk3S0CflVYUyw2yPfVEP6QPGgfWcdjBgdX1reWCYC5sKMnW39k2k9BUfJ9EDNx
|
||||||
|
jvWwJ82xMwTjCmNzWUTznSP5EX1VEV8IockytkNiqwecwmW9XIkNbw/KN+SOxlfj
|
||||||
|
4wvOGvueYVUePf+3xveghXwCD7kjGmYkKweU241177suqIRvfGsOiiKSTqVHAfHH
|
||||||
|
zPvVsz6ix5QPsze0SSPNHFR6025s89OEzdOgjHbTmdVaDsnxdg7NCyGgYpXK0CwY
|
||||||
|
Dmusoe4dVJzPVBvyGCse8SQ9dFWuR5joa4N2HxgfwO/69kIqmSWWYL4Tvgp1J90i
|
||||||
|
HcM0xH2QJFQpWEkLkUaxDGrdkSHkzS5kmZpjkbVpf1qihdIfnSENVc/xkZoHgZKx
|
||||||
|
G2Zp6sVtTdWgYJhF884ygCvXUwlRyYIs+XgVN0zpr2UmWNrGa1WBGdbr62ZcF/DJ
|
||||||
|
c6aMrpOel6u0GDYsrIsTpiCmD99P/VHYXgxqE80+WL1nJh52PrBy8giqDDl57BSH
|
||||||
|
aW3xCke0eF7/hrhohHbRM3pIi7700gr3CJU03BqLpaGFLo2YF88L9N+Udylr+bBx
|
||||||
|
hQG4gqWoKp/lKx+9zn214w/h01FA1c+gbM187JGh5KTYZeIpbftgpC55UH3n/Awy
|
||||||
|
dL78XRfPm6EDjIhjmD1sNIw6P3wk7YiO58lfT2zLjVM9Pslh7VMUiOWJHcKmY9vP
|
||||||
|
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
|
||||||
|
weJlzvZk7gLaVw6ey1/Io/uNQw==
|
||||||
|
=7Zeo
|
||||||
|
-----END PGP MESSAGE-----
|
||||||
72
tools/create-client-cert.sh
Executable file
@@ -0,0 +1,72 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Check if client name and password are provided
|
||||||
|
if [ $# -lt 2 ]; then
|
||||||
|
echo "Usage: $0 <client-name> <p12-password>"
|
||||||
|
echo "Example: $0 john.doe mySecurePassword123"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
CLIENT_NAME="$1"
|
||||||
|
P12_PASSWORD="$2"
|
||||||
|
|
||||||
|
# Check if CA exists
|
||||||
|
if [ ! -f "certificates/ca/ca.crt" ] || [ ! -f "certificates/ca/ca.key" ]; then
|
||||||
|
echo "Error: CA not found. Please run setup-ca.sh first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "=== Creating Client Certificate ==="
|
||||||
|
echo "Client Name: $CLIENT_NAME"
|
||||||
|
|
||||||
|
# Create client directory
|
||||||
|
mkdir -p certificates/clients/$CLIENT_NAME
|
||||||
|
|
||||||
|
# Generate client private key
|
||||||
|
echo "Generating client private key..."
|
||||||
|
openssl genrsa -out certificates/clients/$CLIENT_NAME/$CLIENT_NAME.key 2048
|
||||||
|
# Generate client certificate signing request
|
||||||
|
echo "Generating client certificate signing request..."
|
||||||
|
openssl req -new -key certificates/clients/$CLIENT_NAME/$CLIENT_NAME.key \
|
||||||
|
-out certificates/clients/$CLIENT_NAME/$CLIENT_NAME.csr \
|
||||||
|
-subj "/DC=de/DC=hottis/DC=homea2/CN=$CLIENT_NAME"
|
||||||
|
|
||||||
|
# Sign the client certificate
|
||||||
|
echo "Signing client certificate..."
|
||||||
|
openssl x509 -req -in certificates/clients/$CLIENT_NAME/$CLIENT_NAME.csr \
|
||||||
|
-CA certificates/ca/ca.crt -CAkey certificates/ca/ca.key -CAcreateserial \
|
||||||
|
-out certificates/clients/$CLIENT_NAME/$CLIENT_NAME.crt \
|
||||||
|
-days 365 -sha256
|
||||||
|
|
||||||
|
# Create PKCS#12 bundle
|
||||||
|
echo "Creating PKCS#12 bundle..."
|
||||||
|
openssl pkcs12 -export \
|
||||||
|
-out certificates/clients/$CLIENT_NAME/$CLIENT_NAME.p12 \
|
||||||
|
-inkey certificates/clients/$CLIENT_NAME/$CLIENT_NAME.key \
|
||||||
|
-in certificates/clients/$CLIENT_NAME/$CLIENT_NAME.crt \
|
||||||
|
-certfile certificates/ca/ca.crt \
|
||||||
|
-name "$CLIENT_NAME Home Automation Client" \
|
||||||
|
-passout pass:$P12_PASSWORD
|
||||||
|
|
||||||
|
# Set appropriate permissions
|
||||||
|
chmod 400 certificates/clients/$CLIENT_NAME/$CLIENT_NAME.key
|
||||||
|
chmod 644 certificates/clients/$CLIENT_NAME/$CLIENT_NAME.crt
|
||||||
|
chmod 644 certificates/clients/$CLIENT_NAME/$CLIENT_NAME.p12
|
||||||
|
|
||||||
|
# Verify client certificate
|
||||||
|
echo "Verifying client certificate..."
|
||||||
|
openssl x509 -noout -text -in certificates/clients/$CLIENT_NAME/$CLIENT_NAME.crt
|
||||||
|
echo ""
|
||||||
|
echo "=== Client Certificate Created ==="
|
||||||
|
echo "Client Certificate: certificates/clients/$CLIENT_NAME/$CLIENT_NAME.crt"
|
||||||
|
echo "Client Private Key: certificates/clients/$CLIENT_NAME/$CLIENT_NAME.key"
|
||||||
|
echo "PKCS#12 Bundle: certificates/clients/$CLIENT_NAME/$CLIENT_NAME.p12"
|
||||||
|
echo ""
|
||||||
|
echo "Installation Instructions:"
|
||||||
|
echo "1. Import the PKCS#12 file into your browser/application"
|
||||||
|
echo "2. The bundle contains both the client certificate and CA certificate"
|
||||||
|
echo "3. Password for PKCS#12 file: $P12_PASSWORD"
|
||||||
|
echo ""
|
||||||
|
echo "For testing with curl:"
|
||||||
|
echo "curl --cert certificates/clients/$CLIENT_NAME/$CLIENT_NAME.crt --key certificates/clients/$CLIENT_NAME/$CLIENT_NAME.key https://homea2.hottis.de/"
|
||||||
12
tools/decrypt-certificates.sh
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
gpg --decrypt --pinentry-mode=loopback --output certificates.tgz certificates.asc
|
||||||
|
|
||||||
|
tar -xzvf certificates.tgz ./certificates/
|
||||||
|
|
||||||
|
rm certificates.tgz
|
||||||
|
|
||||||
@@ -1,291 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Unified Device Simulator for Home Automation.
|
|
||||||
|
|
||||||
Simulates multiple device types:
|
|
||||||
- Lights (test_lampe_1, test_lampe_2, test_lampe_3)
|
|
||||||
- Thermostats (test_thermo_1)
|
|
||||||
|
|
||||||
Each device:
|
|
||||||
- Subscribes to vendor/{device_id}/set
|
|
||||||
- Maintains local state
|
|
||||||
- Publishes state changes to vendor/{device_id}/state (retained, QoS 1)
|
|
||||||
- Thermostats simulate temperature drift every 5 seconds
|
|
||||||
"""
|
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import json
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import signal
|
|
||||||
import sys
|
|
||||||
import uuid
|
|
||||||
from datetime import datetime
|
|
||||||
from typing import Dict, Any
|
|
||||||
|
|
||||||
from aiomqtt import Client, MqttError
|
|
||||||
|
|
||||||
# Configure logging
|
|
||||||
logging.basicConfig(
|
|
||||||
level=logging.INFO,
|
|
||||||
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
|
||||||
)
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
# Configuration
|
|
||||||
BROKER_HOST = os.getenv("MQTT_BROKER", "172.16.2.16")
|
|
||||||
BROKER_PORT = int(os.getenv("MQTT_PORT", "1883"))
|
|
||||||
DRIFT_INTERVAL = 5 # seconds for thermostat temperature drift
|
|
||||||
|
|
||||||
# Device configurations
|
|
||||||
LIGHT_DEVICES = ["test_lampe_1", "test_lampe_2", "test_lampe_3"]
|
|
||||||
THERMOSTAT_DEVICES = ["test_thermo_1"]
|
|
||||||
|
|
||||||
|
|
||||||
class DeviceSimulator:
|
|
||||||
"""Unified simulator for lights and thermostats."""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
# Light states
|
|
||||||
self.light_states: Dict[str, Dict[str, Any]] = {
|
|
||||||
"test_lampe_1": {"power": "off", "brightness": 50},
|
|
||||||
"test_lampe_2": {"power": "off", "brightness": 50},
|
|
||||||
"test_lampe_3": {"power": "off", "brightness": 50}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Thermostat states
|
|
||||||
self.thermostat_states: Dict[str, Dict[str, Any]] = {
|
|
||||||
"test_thermo_1": {
|
|
||||||
"mode": "auto",
|
|
||||||
"target": 21.0,
|
|
||||||
"current": 20.5,
|
|
||||||
"battery": 90,
|
|
||||||
"window_open": False
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.client = None
|
|
||||||
self.running = True
|
|
||||||
self.drift_task = None
|
|
||||||
|
|
||||||
async def publish_state(self, device_id: str, device_type: str):
|
|
||||||
"""Publish device state to MQTT (retained, QoS 1)."""
|
|
||||||
if not self.client:
|
|
||||||
return
|
|
||||||
|
|
||||||
if device_type == "light":
|
|
||||||
state = self.light_states.get(device_id)
|
|
||||||
elif device_type == "thermostat":
|
|
||||||
state = self.thermostat_states.get(device_id)
|
|
||||||
else:
|
|
||||||
logger.warning(f"Unknown device type: {device_type}")
|
|
||||||
return
|
|
||||||
|
|
||||||
if not state:
|
|
||||||
logger.warning(f"Unknown device: {device_id}")
|
|
||||||
return
|
|
||||||
|
|
||||||
state_topic = f"vendor/{device_id}/state"
|
|
||||||
payload = json.dumps(state)
|
|
||||||
|
|
||||||
await self.client.publish(
|
|
||||||
state_topic,
|
|
||||||
payload=payload,
|
|
||||||
qos=1,
|
|
||||||
retain=True
|
|
||||||
)
|
|
||||||
logger.info(f"[{device_id}] Published state: {payload}")
|
|
||||||
|
|
||||||
async def handle_light_set(self, device_id: str, payload: dict):
|
|
||||||
"""Handle SET command for light device."""
|
|
||||||
if device_id not in self.light_states:
|
|
||||||
logger.warning(f"Unknown light device: {device_id}")
|
|
||||||
return
|
|
||||||
|
|
||||||
state = self.light_states[device_id]
|
|
||||||
updated = False
|
|
||||||
|
|
||||||
if "power" in payload:
|
|
||||||
old_power = state["power"]
|
|
||||||
state["power"] = payload["power"]
|
|
||||||
if old_power != state["power"]:
|
|
||||||
updated = True
|
|
||||||
logger.info(f"[{device_id}] Power: {old_power} -> {state['power']}")
|
|
||||||
|
|
||||||
if "brightness" in payload:
|
|
||||||
old_brightness = state["brightness"]
|
|
||||||
state["brightness"] = int(payload["brightness"])
|
|
||||||
if old_brightness != state["brightness"]:
|
|
||||||
updated = True
|
|
||||||
logger.info(f"[{device_id}] Brightness: {old_brightness} -> {state['brightness']}")
|
|
||||||
|
|
||||||
if updated:
|
|
||||||
await self.publish_state(device_id, "light")
|
|
||||||
|
|
||||||
async def handle_thermostat_set(self, device_id: str, payload: dict):
|
|
||||||
"""Handle SET command for thermostat device."""
|
|
||||||
if device_id not in self.thermostat_states:
|
|
||||||
logger.warning(f"Unknown thermostat device: {device_id}")
|
|
||||||
return
|
|
||||||
|
|
||||||
state = self.thermostat_states[device_id]
|
|
||||||
updated = False
|
|
||||||
|
|
||||||
if "mode" in payload:
|
|
||||||
new_mode = payload["mode"]
|
|
||||||
if new_mode in ["off", "heat", "auto"]:
|
|
||||||
old_mode = state["mode"]
|
|
||||||
state["mode"] = new_mode
|
|
||||||
if old_mode != new_mode:
|
|
||||||
updated = True
|
|
||||||
logger.info(f"[{device_id}] Mode: {old_mode} -> {new_mode}")
|
|
||||||
else:
|
|
||||||
logger.warning(f"[{device_id}] Invalid mode: {new_mode}")
|
|
||||||
|
|
||||||
if "target" in payload:
|
|
||||||
try:
|
|
||||||
new_target = float(payload["target"])
|
|
||||||
if 5.0 <= new_target <= 30.0:
|
|
||||||
old_target = state["target"]
|
|
||||||
state["target"] = new_target
|
|
||||||
if old_target != new_target:
|
|
||||||
updated = True
|
|
||||||
logger.info(f"[{device_id}] Target: {old_target}°C -> {new_target}°C")
|
|
||||||
else:
|
|
||||||
logger.warning(f"[{device_id}] Target out of range: {new_target}")
|
|
||||||
except (ValueError, TypeError):
|
|
||||||
logger.warning(f"[{device_id}] Invalid target value: {payload['target']}")
|
|
||||||
|
|
||||||
if updated:
|
|
||||||
await self.publish_state(device_id, "thermostat")
|
|
||||||
|
|
||||||
def apply_temperature_drift(self, device_id: str):
|
|
||||||
"""
|
|
||||||
Simulate temperature drift for thermostat.
|
|
||||||
Max change: ±0.2°C per interval.
|
|
||||||
"""
|
|
||||||
if device_id not in self.thermostat_states:
|
|
||||||
return
|
|
||||||
|
|
||||||
state = self.thermostat_states[device_id]
|
|
||||||
|
|
||||||
if state["mode"] == "off":
|
|
||||||
# Drift towards ambient (18°C)
|
|
||||||
ambient = 18.0
|
|
||||||
diff = ambient - state["current"]
|
|
||||||
else:
|
|
||||||
# Drift towards target
|
|
||||||
diff = state["target"] - state["current"]
|
|
||||||
|
|
||||||
# Apply max ±0.2°C drift
|
|
||||||
if abs(diff) < 0.1:
|
|
||||||
state["current"] = round(state["current"] + diff, 1)
|
|
||||||
elif diff > 0:
|
|
||||||
state["current"] = round(state["current"] + 0.2, 1)
|
|
||||||
else:
|
|
||||||
state["current"] = round(state["current"] - 0.2, 1)
|
|
||||||
|
|
||||||
logger.info(f"[{device_id}] Temperature drift: current={state['current']}°C (target={state['target']}°C, mode={state['mode']})")
|
|
||||||
|
|
||||||
async def thermostat_drift_loop(self):
|
|
||||||
"""Background loop for thermostat temperature drift."""
|
|
||||||
while self.running:
|
|
||||||
await asyncio.sleep(DRIFT_INTERVAL)
|
|
||||||
|
|
||||||
for device_id in THERMOSTAT_DEVICES:
|
|
||||||
self.apply_temperature_drift(device_id)
|
|
||||||
await self.publish_state(device_id, "thermostat")
|
|
||||||
|
|
||||||
async def handle_message(self, message):
|
|
||||||
"""Handle incoming MQTT message."""
|
|
||||||
try:
|
|
||||||
# Extract device_id from topic (vendor/{device_id}/set)
|
|
||||||
topic_parts = message.topic.value.split('/')
|
|
||||||
if len(topic_parts) != 3 or topic_parts[0] != "vendor" or topic_parts[2] != "set":
|
|
||||||
logger.warning(f"Unexpected topic format: {message.topic}")
|
|
||||||
return
|
|
||||||
|
|
||||||
device_id = topic_parts[1]
|
|
||||||
payload = json.loads(message.payload.decode())
|
|
||||||
|
|
||||||
logger.info(f"[{device_id}] Received SET: {payload}")
|
|
||||||
|
|
||||||
# Determine device type and handle accordingly
|
|
||||||
if device_id in self.light_states:
|
|
||||||
await self.handle_light_set(device_id, payload)
|
|
||||||
elif device_id in self.thermostat_states:
|
|
||||||
await self.handle_thermostat_set(device_id, payload)
|
|
||||||
else:
|
|
||||||
logger.warning(f"Unknown device: {device_id}")
|
|
||||||
|
|
||||||
except json.JSONDecodeError as e:
|
|
||||||
logger.error(f"Invalid JSON: {e}")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error handling message: {e}")
|
|
||||||
|
|
||||||
async def run(self):
|
|
||||||
"""Main simulator loop."""
|
|
||||||
# Generate unique client ID to avoid collisions
|
|
||||||
base_client_id = "device_simulator"
|
|
||||||
client_suffix = os.environ.get("MQTT_CLIENT_ID_SUFFIX") or uuid.uuid4().hex[:6]
|
|
||||||
unique_client_id = f"{base_client_id}-{client_suffix}"
|
|
||||||
|
|
||||||
try:
|
|
||||||
async with Client(
|
|
||||||
hostname=BROKER_HOST,
|
|
||||||
port=BROKER_PORT,
|
|
||||||
identifier=unique_client_id
|
|
||||||
) as client:
|
|
||||||
self.client = client
|
|
||||||
logger.info(f"✅ Connected to MQTT broker {BROKER_HOST}:{BROKER_PORT}")
|
|
||||||
|
|
||||||
# Publish initial states
|
|
||||||
for device_id in LIGHT_DEVICES:
|
|
||||||
await self.publish_state(device_id, "light")
|
|
||||||
logger.info(f"💡 Light simulator started: {device_id}")
|
|
||||||
|
|
||||||
for device_id in THERMOSTAT_DEVICES:
|
|
||||||
await self.publish_state(device_id, "thermostat")
|
|
||||||
logger.info(f"🌡️ Thermostat simulator started: {device_id}")
|
|
||||||
|
|
||||||
# Subscribe to all SET topics
|
|
||||||
all_devices = LIGHT_DEVICES + THERMOSTAT_DEVICES
|
|
||||||
for device_id in all_devices:
|
|
||||||
set_topic = f"vendor/{device_id}/set"
|
|
||||||
await client.subscribe(set_topic, qos=1)
|
|
||||||
logger.info(f"👂 Subscribed to {set_topic}")
|
|
||||||
|
|
||||||
# Start thermostat drift loop
|
|
||||||
self.drift_task = asyncio.create_task(self.thermostat_drift_loop())
|
|
||||||
|
|
||||||
# Listen for messages
|
|
||||||
async for message in client.messages:
|
|
||||||
await self.handle_message(message)
|
|
||||||
|
|
||||||
# Cancel drift loop on disconnect
|
|
||||||
if self.drift_task:
|
|
||||||
self.drift_task.cancel()
|
|
||||||
|
|
||||||
except MqttError as e:
|
|
||||||
logger.error(f"❌ MQTT Error: {e}")
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
logger.info("⚠️ Interrupted by user")
|
|
||||||
finally:
|
|
||||||
self.running = False
|
|
||||||
if self.drift_task:
|
|
||||||
self.drift_task.cancel()
|
|
||||||
logger.info("👋 Simulator stopped")
|
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
|
||||||
"""Entry point."""
|
|
||||||
simulator = DeviceSimulator()
|
|
||||||
await simulator.run()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
try:
|
|
||||||
asyncio.run(main())
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
print("\n👋 Simulator terminated")
|
|
||||||
sys.exit(0)
|
|
||||||
13
tools/encrypt-certificates.sh
Executable file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
tar -czvf certificates.tgz ./certificates/
|
||||||
|
|
||||||
|
gpg --symmetric --cipher-algo AES256 --armor --pinentry-mode=loopback --output certificates.asc certificates.tgz
|
||||||
|
|
||||||
|
rm certificates.tgz
|
||||||
|
|
||||||
|
|
||||||
23
tools/setup-ca.sh
Executable file
@@ -0,0 +1,23 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "=== mTLS CA Setup ==="
|
||||||
|
|
||||||
|
# Create CA directory
|
||||||
|
mkdir -p certificates/ca
|
||||||
|
|
||||||
|
# Generate CA private key
|
||||||
|
echo "Generating CA private key..."
|
||||||
|
openssl genrsa -out certificates/ca/ca.key 2048
|
||||||
|
# Generate CA certificate
|
||||||
|
echo "Generating CA certificate..."
|
||||||
|
openssl req -new -x509 -days 3650 -key certificates/ca/ca.key -out certificates/ca/ca.crt \
|
||||||
|
-subj "/DC=de/DC=hottis/DC=homea2/CN=Home Automation CA"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== CA Setup Complete ==="
|
||||||
|
echo "CA Certificate: certificates/ca/ca.crt"
|
||||||
|
echo "CA Private Key: certificates/ca/ca.key"
|
||||||
|
echo ""
|
||||||
|
echo "Deploy to Kubernetes:"
|
||||||
|
echo "kubectl create secret generic mtls-ca-cert --from-file=ca.crt=certificates/ca/ca.crt -n homea2"
|
||||||
@@ -1,169 +0,0 @@
|
|||||||
"""
|
|
||||||
Acceptance tests for API Client (fetch_devices).
|
|
||||||
|
|
||||||
Tests:
|
|
||||||
1. API running: fetch_devices() returns list with test_lampe_1, test_lampe_2
|
|
||||||
2. API down: fetch_devices() returns empty list without crash
|
|
||||||
"""
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
import subprocess
|
|
||||||
from apps.ui.api_client import fetch_devices
|
|
||||||
|
|
||||||
|
|
||||||
def test_api_running():
|
|
||||||
"""Test 1: API is running -> fetch_devices() returns devices."""
|
|
||||||
print("Test 1: API running")
|
|
||||||
print("-" * 60)
|
|
||||||
|
|
||||||
devices = fetch_devices("http://localhost:8001")
|
|
||||||
|
|
||||||
if not devices:
|
|
||||||
print(" ✗ FAILED: Expected devices, got empty list")
|
|
||||||
return False
|
|
||||||
|
|
||||||
print(f" ✓ Received {len(devices)} devices")
|
|
||||||
|
|
||||||
# Check structure
|
|
||||||
for device in devices:
|
|
||||||
device_id = device.get("device_id")
|
|
||||||
device_type = device.get("type")
|
|
||||||
name = device.get("name")
|
|
||||||
|
|
||||||
if not device_id:
|
|
||||||
print(f" ✗ FAILED: Device missing 'device_id': {device}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not device_type:
|
|
||||||
print(f" ✗ FAILED: Device missing 'type': {device}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not name:
|
|
||||||
print(f" ✗ FAILED: Device missing 'name': {device}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
print(f" • {device_id} (type={device_type}, name={name})")
|
|
||||||
|
|
||||||
# Check for expected devices
|
|
||||||
device_ids = {d["device_id"] for d in devices}
|
|
||||||
expected = {"test_lampe_1", "test_lampe_2"}
|
|
||||||
|
|
||||||
if not expected.issubset(device_ids):
|
|
||||||
missing = expected - device_ids
|
|
||||||
print(f" ✗ FAILED: Missing devices: {missing}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
print(" ✓ All expected devices present")
|
|
||||||
print()
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def test_api_down():
|
|
||||||
"""Test 2: API is down -> fetch_devices() returns empty list without crash."""
|
|
||||||
print("Test 2: API down (invalid port)")
|
|
||||||
print("-" * 60)
|
|
||||||
|
|
||||||
try:
|
|
||||||
devices = fetch_devices("http://localhost:9999")
|
|
||||||
|
|
||||||
if devices != []:
|
|
||||||
print(f" ✗ FAILED: Expected empty list, got {len(devices)} devices")
|
|
||||||
return False
|
|
||||||
|
|
||||||
print(" ✓ Returns empty list without crash")
|
|
||||||
print()
|
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ FAILED: Exception raised: {e}")
|
|
||||||
print()
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def test_api_timeout():
|
|
||||||
"""Test 3: API timeout -> fetch_devices() returns empty list."""
|
|
||||||
print("Test 3: API timeout")
|
|
||||||
print("-" * 60)
|
|
||||||
|
|
||||||
# Use httpbin.org delay endpoint to simulate slow API
|
|
||||||
try:
|
|
||||||
start = time.time()
|
|
||||||
devices = fetch_devices("https://httpbin.org/delay/5") # 5s delay, but 3s timeout
|
|
||||||
elapsed = time.time() - start
|
|
||||||
|
|
||||||
if devices != []:
|
|
||||||
print(f" ✗ FAILED: Expected empty list, got {len(devices)} devices")
|
|
||||||
return False
|
|
||||||
|
|
||||||
if elapsed >= 4.0:
|
|
||||||
print(f" ✗ FAILED: Timeout not enforced (took {elapsed:.1f}s)")
|
|
||||||
return False
|
|
||||||
|
|
||||||
print(f" ✓ Returns empty list after timeout ({elapsed:.1f}s)")
|
|
||||||
print()
|
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ FAILED: Exception raised: {e}")
|
|
||||||
print()
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""Run all acceptance tests."""
|
|
||||||
print("=" * 60)
|
|
||||||
print("Testing API Client (fetch_devices)")
|
|
||||||
print("=" * 60)
|
|
||||||
print()
|
|
||||||
|
|
||||||
# Check if API is running
|
|
||||||
print("Prerequisites: Checking if API is running on port 8001...")
|
|
||||||
try:
|
|
||||||
devices = fetch_devices("http://localhost:8001")
|
|
||||||
if devices:
|
|
||||||
print(f"✓ API is running ({len(devices)} devices found)")
|
|
||||||
print()
|
|
||||||
else:
|
|
||||||
print("⚠ API might not be running (no devices returned)")
|
|
||||||
print(" Start API with: poetry run uvicorn apps.api.main:app --port 8001")
|
|
||||||
print()
|
|
||||||
except Exception as e:
|
|
||||||
print(f"✗ Cannot reach API: {e}")
|
|
||||||
print(" Start API with: poetry run uvicorn apps.api.main:app --port 8001")
|
|
||||||
print()
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
results = []
|
|
||||||
|
|
||||||
# Test 1: API running
|
|
||||||
results.append(("API running", test_api_running()))
|
|
||||||
|
|
||||||
# Test 2: API down
|
|
||||||
results.append(("API down", test_api_down()))
|
|
||||||
|
|
||||||
# Test 3: Timeout
|
|
||||||
results.append(("API timeout", test_api_timeout()))
|
|
||||||
|
|
||||||
# Summary
|
|
||||||
print("=" * 60)
|
|
||||||
passed = sum(1 for _, result in results if result)
|
|
||||||
total = len(results)
|
|
||||||
print(f"Results: {passed}/{total} tests passed")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
for name, result in results:
|
|
||||||
status = "✓" if result else "✗"
|
|
||||||
print(f" {status} {name}")
|
|
||||||
|
|
||||||
if passed == total:
|
|
||||||
print()
|
|
||||||
print("All tests passed!")
|
|
||||||
sys.exit(0)
|
|
||||||
else:
|
|
||||||
print()
|
|
||||||
print("Some tests failed.")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@@ -1,253 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""Test configuration loader with different YAML formats."""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import tempfile
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
# Add parent directory to path
|
|
||||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
||||||
|
|
||||||
from apps.abstraction.main import load_config, validate_devices
|
|
||||||
|
|
||||||
|
|
||||||
def test_device_id_format():
|
|
||||||
"""Test YAML with 'device_id' key."""
|
|
||||||
yaml_content = """version: 1
|
|
||||||
|
|
||||||
mqtt:
|
|
||||||
broker: "172.16.2.16"
|
|
||||||
port: 1883
|
|
||||||
client_id: "test"
|
|
||||||
|
|
||||||
redis:
|
|
||||||
url: "redis://localhost:6379/8"
|
|
||||||
channel: "ui:updates"
|
|
||||||
|
|
||||||
devices:
|
|
||||||
- device_id: test_lampe
|
|
||||||
type: light
|
|
||||||
cap_version: "light@1.2.0"
|
|
||||||
technology: zigbee2mqtt
|
|
||||||
features:
|
|
||||||
power: true
|
|
||||||
topics:
|
|
||||||
set: "vendor/test_lampe/set"
|
|
||||||
state: "vendor/test_lampe/state"
|
|
||||||
"""
|
|
||||||
|
|
||||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f:
|
|
||||||
f.write(yaml_content)
|
|
||||||
temp_path = Path(f.name)
|
|
||||||
|
|
||||||
try:
|
|
||||||
config = load_config(temp_path)
|
|
||||||
devices = config.get("devices", [])
|
|
||||||
validate_devices(devices)
|
|
||||||
print("✓ Test 1 passed: YAML with 'device_id' loaded successfully")
|
|
||||||
print(f" Device ID: {devices[0]['device_id']}")
|
|
||||||
return True
|
|
||||||
except Exception as e:
|
|
||||||
print(f"✗ Test 1 failed: {e}")
|
|
||||||
return False
|
|
||||||
finally:
|
|
||||||
temp_path.unlink()
|
|
||||||
|
|
||||||
|
|
||||||
def test_id_format():
|
|
||||||
"""Test YAML with 'id' key (legacy format)."""
|
|
||||||
yaml_content = """version: 1
|
|
||||||
|
|
||||||
mqtt:
|
|
||||||
broker: "172.16.2.16"
|
|
||||||
port: 1883
|
|
||||||
client_id: "test"
|
|
||||||
|
|
||||||
redis:
|
|
||||||
url: "redis://localhost:6379/8"
|
|
||||||
channel: "ui:updates"
|
|
||||||
|
|
||||||
devices:
|
|
||||||
- id: test_lampe
|
|
||||||
type: light
|
|
||||||
cap_version: "light@1.2.0"
|
|
||||||
technology: zigbee2mqtt
|
|
||||||
features:
|
|
||||||
power: true
|
|
||||||
topics:
|
|
||||||
set: "vendor/test_lampe/set"
|
|
||||||
state: "vendor/test_lampe/state"
|
|
||||||
"""
|
|
||||||
|
|
||||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f:
|
|
||||||
f.write(yaml_content)
|
|
||||||
temp_path = Path(f.name)
|
|
||||||
|
|
||||||
try:
|
|
||||||
config = load_config(temp_path)
|
|
||||||
devices = config.get("devices", [])
|
|
||||||
validate_devices(devices)
|
|
||||||
print("✓ Test 2 passed: YAML with 'id' loaded successfully")
|
|
||||||
print(f" Device ID: {devices[0]['device_id']}")
|
|
||||||
return True
|
|
||||||
except Exception as e:
|
|
||||||
print(f"✗ Test 2 failed: {e}")
|
|
||||||
return False
|
|
||||||
finally:
|
|
||||||
temp_path.unlink()
|
|
||||||
|
|
||||||
|
|
||||||
def test_missing_id():
|
|
||||||
"""Test YAML without 'id' or 'device_id' key."""
|
|
||||||
yaml_content = """version: 1
|
|
||||||
|
|
||||||
mqtt:
|
|
||||||
broker: "172.16.2.16"
|
|
||||||
port: 1883
|
|
||||||
|
|
||||||
devices:
|
|
||||||
- type: light
|
|
||||||
cap_version: "light@1.2.0"
|
|
||||||
technology: zigbee2mqtt
|
|
||||||
topics:
|
|
||||||
set: "vendor/test_lampe/set"
|
|
||||||
state: "vendor/test_lampe/state"
|
|
||||||
"""
|
|
||||||
|
|
||||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f:
|
|
||||||
f.write(yaml_content)
|
|
||||||
temp_path = Path(f.name)
|
|
||||||
|
|
||||||
try:
|
|
||||||
config = load_config(temp_path)
|
|
||||||
devices = config.get("devices", [])
|
|
||||||
validate_devices(devices)
|
|
||||||
print("✗ Test 3 failed: Should have raised ValueError for missing device_id")
|
|
||||||
return False
|
|
||||||
except ValueError as e:
|
|
||||||
if "requires 'id' or 'device_id'" in str(e):
|
|
||||||
print(f"✓ Test 3 passed: Correct error for missing device_id")
|
|
||||||
print(f" Error: {e}")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print(f"✗ Test 3 failed: Wrong error message: {e}")
|
|
||||||
return False
|
|
||||||
except Exception as e:
|
|
||||||
print(f"✗ Test 3 failed: Unexpected error: {e}")
|
|
||||||
return False
|
|
||||||
finally:
|
|
||||||
temp_path.unlink()
|
|
||||||
|
|
||||||
|
|
||||||
def test_missing_topics_set():
|
|
||||||
"""Test YAML without 'topics.set'."""
|
|
||||||
yaml_content = """version: 1
|
|
||||||
|
|
||||||
mqtt:
|
|
||||||
broker: "172.16.2.16"
|
|
||||||
port: 1883
|
|
||||||
|
|
||||||
devices:
|
|
||||||
- device_id: test_lampe
|
|
||||||
type: light
|
|
||||||
cap_version: "light@1.2.0"
|
|
||||||
technology: zigbee2mqtt
|
|
||||||
topics:
|
|
||||||
state: "vendor/test_lampe/state"
|
|
||||||
"""
|
|
||||||
|
|
||||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f:
|
|
||||||
f.write(yaml_content)
|
|
||||||
temp_path = Path(f.name)
|
|
||||||
|
|
||||||
try:
|
|
||||||
config = load_config(temp_path)
|
|
||||||
devices = config.get("devices", [])
|
|
||||||
validate_devices(devices)
|
|
||||||
print("✗ Test 4 failed: Should have raised ValueError for missing topics.set")
|
|
||||||
return False
|
|
||||||
except ValueError as e:
|
|
||||||
if "missing 'topics.set'" in str(e):
|
|
||||||
print(f"✓ Test 4 passed: Correct error for missing topics.set")
|
|
||||||
print(f" Error: {e}")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print(f"✗ Test 4 failed: Wrong error message: {e}")
|
|
||||||
return False
|
|
||||||
except Exception as e:
|
|
||||||
print(f"✗ Test 4 failed: Unexpected error: {e}")
|
|
||||||
return False
|
|
||||||
finally:
|
|
||||||
temp_path.unlink()
|
|
||||||
|
|
||||||
|
|
||||||
def test_missing_topics_state():
|
|
||||||
"""Test YAML without 'topics.state'."""
|
|
||||||
yaml_content = """version: 1
|
|
||||||
|
|
||||||
mqtt:
|
|
||||||
broker: "172.16.2.16"
|
|
||||||
port: 1883
|
|
||||||
|
|
||||||
devices:
|
|
||||||
- device_id: test_lampe
|
|
||||||
type: light
|
|
||||||
cap_version: "light@1.2.0"
|
|
||||||
technology: zigbee2mqtt
|
|
||||||
topics:
|
|
||||||
set: "vendor/test_lampe/set"
|
|
||||||
"""
|
|
||||||
|
|
||||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f:
|
|
||||||
f.write(yaml_content)
|
|
||||||
temp_path = Path(f.name)
|
|
||||||
|
|
||||||
try:
|
|
||||||
config = load_config(temp_path)
|
|
||||||
devices = config.get("devices", [])
|
|
||||||
validate_devices(devices)
|
|
||||||
print("✗ Test 5 failed: Should have raised ValueError for missing topics.state")
|
|
||||||
return False
|
|
||||||
except ValueError as e:
|
|
||||||
if "missing 'topics.state'" in str(e):
|
|
||||||
print(f"✓ Test 5 passed: Correct error for missing topics.state")
|
|
||||||
print(f" Error: {e}")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print(f"✗ Test 5 failed: Wrong error message: {e}")
|
|
||||||
return False
|
|
||||||
except Exception as e:
|
|
||||||
print(f"✗ Test 5 failed: Unexpected error: {e}")
|
|
||||||
return False
|
|
||||||
finally:
|
|
||||||
temp_path.unlink()
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""Run all tests."""
|
|
||||||
print("Testing Configuration Loader")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
tests = [
|
|
||||||
test_device_id_format,
|
|
||||||
test_id_format,
|
|
||||||
test_missing_id,
|
|
||||||
test_missing_topics_set,
|
|
||||||
test_missing_topics_state,
|
|
||||||
]
|
|
||||||
|
|
||||||
results = []
|
|
||||||
for test in tests:
|
|
||||||
results.append(test())
|
|
||||||
print()
|
|
||||||
|
|
||||||
print("=" * 60)
|
|
||||||
passed = sum(results)
|
|
||||||
total = len(results)
|
|
||||||
print(f"Results: {passed}/{total} tests passed")
|
|
||||||
|
|
||||||
return 0 if all(results) else 1
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
||||||
@@ -1,210 +0,0 @@
|
|||||||
"""
|
|
||||||
Acceptance tests for Dashboard Route.
|
|
||||||
|
|
||||||
Tests:
|
|
||||||
1. GET /dashboard loads without errors
|
|
||||||
2. Rooms are shown in layout.yaml order
|
|
||||||
3. Devices within each room are sorted by rank (ascending)
|
|
||||||
4. GET / redirects to dashboard
|
|
||||||
"""
|
|
||||||
import sys
|
|
||||||
import httpx
|
|
||||||
|
|
||||||
|
|
||||||
def test_dashboard_loads():
|
|
||||||
"""Test 1: Dashboard loads without errors."""
|
|
||||||
print("Test 1: Dashboard loads")
|
|
||||||
print("-" * 60)
|
|
||||||
|
|
||||||
try:
|
|
||||||
response = httpx.get("http://localhost:8002/dashboard", timeout=5.0)
|
|
||||||
response.raise_for_status()
|
|
||||||
|
|
||||||
html = response.text
|
|
||||||
|
|
||||||
# Check for essential elements
|
|
||||||
if "Home Automation" not in html:
|
|
||||||
print(" ✗ FAILED: Missing title 'Home Automation'")
|
|
||||||
return False
|
|
||||||
|
|
||||||
if "Wohnzimmer" not in html:
|
|
||||||
print(" ✗ FAILED: Missing room 'Wohnzimmer'")
|
|
||||||
return False
|
|
||||||
|
|
||||||
if "Schlafzimmer" not in html:
|
|
||||||
print(" ✗ FAILED: Missing room 'Schlafzimmer'")
|
|
||||||
return False
|
|
||||||
|
|
||||||
print(" ✓ Dashboard loads successfully")
|
|
||||||
print(" ✓ Contains expected rooms")
|
|
||||||
print()
|
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ FAILED: {e}")
|
|
||||||
print()
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def test_room_order():
|
|
||||||
"""Test 2: Rooms appear in layout.yaml order."""
|
|
||||||
print("Test 2: Room order matches layout.yaml")
|
|
||||||
print("-" * 60)
|
|
||||||
|
|
||||||
try:
|
|
||||||
response = httpx.get("http://localhost:8002/dashboard", timeout=5.0)
|
|
||||||
response.raise_for_status()
|
|
||||||
|
|
||||||
html = response.text
|
|
||||||
|
|
||||||
# Find positions of room titles
|
|
||||||
wohnzimmer_pos = html.find('class="room-title">Wohnzimmer<')
|
|
||||||
schlafzimmer_pos = html.find('class="room-title">Schlafzimmer<')
|
|
||||||
|
|
||||||
if wohnzimmer_pos == -1:
|
|
||||||
print(" ✗ FAILED: Room 'Wohnzimmer' not found")
|
|
||||||
return False
|
|
||||||
|
|
||||||
if schlafzimmer_pos == -1:
|
|
||||||
print(" ✗ FAILED: Room 'Schlafzimmer' not found")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Wohnzimmer should appear before Schlafzimmer
|
|
||||||
if wohnzimmer_pos > schlafzimmer_pos:
|
|
||||||
print(" ✗ FAILED: Room order incorrect")
|
|
||||||
print(f" Wohnzimmer at position {wohnzimmer_pos}")
|
|
||||||
print(f" Schlafzimmer at position {schlafzimmer_pos}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
print(" ✓ Rooms appear in correct order:")
|
|
||||||
print(" 1. Wohnzimmer")
|
|
||||||
print(" 2. Schlafzimmer")
|
|
||||||
print()
|
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ FAILED: {e}")
|
|
||||||
print()
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def test_device_rank_sorting():
|
|
||||||
"""Test 3: Devices are sorted by rank (ascending)."""
|
|
||||||
print("Test 3: Devices sorted by rank")
|
|
||||||
print("-" * 60)
|
|
||||||
|
|
||||||
try:
|
|
||||||
response = httpx.get("http://localhost:8002/dashboard", timeout=5.0)
|
|
||||||
response.raise_for_status()
|
|
||||||
|
|
||||||
html = response.text
|
|
||||||
|
|
||||||
# In Wohnzimmer: Deckenlampe (rank=5) should come before Stehlampe (rank=10)
|
|
||||||
deckenlampe_pos = html.find('device-title">Deckenlampe<')
|
|
||||||
stehlampe_pos = html.find('device-title">Stehlampe<')
|
|
||||||
|
|
||||||
if deckenlampe_pos == -1:
|
|
||||||
print(" ✗ FAILED: Device 'Deckenlampe' not found")
|
|
||||||
return False
|
|
||||||
|
|
||||||
if stehlampe_pos == -1:
|
|
||||||
print(" ✗ FAILED: Device 'Stehlampe' not found")
|
|
||||||
return False
|
|
||||||
|
|
||||||
if deckenlampe_pos > stehlampe_pos:
|
|
||||||
print(" ✗ FAILED: Devices not sorted by rank")
|
|
||||||
print(f" Deckenlampe (rank=5) at position {deckenlampe_pos}")
|
|
||||||
print(f" Stehlampe (rank=10) at position {stehlampe_pos}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
print(" ✓ Devices sorted by rank (ascending):")
|
|
||||||
print(" Wohnzimmer: Deckenlampe (rank=5) → Stehlampe (rank=10)")
|
|
||||||
print()
|
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ FAILED: {e}")
|
|
||||||
print()
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def test_root_redirect():
|
|
||||||
"""Test 4: GET / shows dashboard."""
|
|
||||||
print("Test 4: Root path (/) shows dashboard")
|
|
||||||
print("-" * 60)
|
|
||||||
|
|
||||||
try:
|
|
||||||
response = httpx.get("http://localhost:8002/", timeout=5.0, follow_redirects=True)
|
|
||||||
response.raise_for_status()
|
|
||||||
|
|
||||||
html = response.text
|
|
||||||
|
|
||||||
# Should contain dashboard elements
|
|
||||||
if "Home Automation" not in html:
|
|
||||||
print(" ✗ FAILED: Root path doesn't show dashboard")
|
|
||||||
return False
|
|
||||||
|
|
||||||
if "Wohnzimmer" not in html:
|
|
||||||
print(" ✗ FAILED: Root path missing rooms")
|
|
||||||
return False
|
|
||||||
|
|
||||||
print(" ✓ Root path shows dashboard")
|
|
||||||
print()
|
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ FAILED: {e}")
|
|
||||||
print()
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""Run all acceptance tests."""
|
|
||||||
print("=" * 60)
|
|
||||||
print("Testing Dashboard Route")
|
|
||||||
print("=" * 60)
|
|
||||||
print()
|
|
||||||
|
|
||||||
# Check if UI is running
|
|
||||||
print("Prerequisites: Checking if UI is running on port 8002...")
|
|
||||||
try:
|
|
||||||
response = httpx.get("http://localhost:8002/dashboard", timeout=3.0)
|
|
||||||
print("✓ UI is running")
|
|
||||||
print()
|
|
||||||
except Exception as e:
|
|
||||||
print(f"✗ Cannot reach UI: {e}")
|
|
||||||
print(" Start UI with: poetry run uvicorn apps.ui.main:app --port 8002")
|
|
||||||
print()
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
results = []
|
|
||||||
|
|
||||||
# Run tests
|
|
||||||
results.append(("Dashboard loads", test_dashboard_loads()))
|
|
||||||
results.append(("Room order", test_room_order()))
|
|
||||||
results.append(("Device rank sorting", test_device_rank_sorting()))
|
|
||||||
results.append(("Root redirect", test_root_redirect()))
|
|
||||||
|
|
||||||
# Summary
|
|
||||||
print("=" * 60)
|
|
||||||
passed = sum(1 for _, result in results if result)
|
|
||||||
total = len(results)
|
|
||||||
print(f"Results: {passed}/{total} tests passed")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
for name, result in results:
|
|
||||||
status = "✓" if result else "✗"
|
|
||||||
print(f" {status} {name}")
|
|
||||||
|
|
||||||
if passed == total:
|
|
||||||
print()
|
|
||||||
print("All tests passed!")
|
|
||||||
sys.exit(0)
|
|
||||||
else:
|
|
||||||
print()
|
|
||||||
print("Some tests failed.")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# Test script for device_simulator.py
|
|
||||||
|
|
||||||
set -e # Exit on error
|
|
||||||
|
|
||||||
echo "=== Device Simulator Test Suite ==="
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# 1. Stop all running services
|
|
||||||
echo "1. Stoppe alle laufenden Services..."
|
|
||||||
pkill -f "device_simulator" 2>/dev/null || true
|
|
||||||
pkill -f "uvicorn apps" 2>/dev/null || true
|
|
||||||
pkill -f "apps.abstraction" 2>/dev/null || true
|
|
||||||
sleep 2
|
|
||||||
echo " ✓ Services gestoppt"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# 2. Start services
|
|
||||||
echo "2. Starte Services..."
|
|
||||||
poetry run python -m apps.abstraction.main > /tmp/abstraction.log 2>&1 &
|
|
||||||
ABSTRACTION_PID=$!
|
|
||||||
echo " Abstraction Layer gestartet (PID: $ABSTRACTION_PID)"
|
|
||||||
sleep 2
|
|
||||||
|
|
||||||
poetry run uvicorn apps.api.main:app --host 0.0.0.0 --port 8001 > /tmp/api.log 2>&1 &
|
|
||||||
API_PID=$!
|
|
||||||
echo " API Server gestartet (PID: $API_PID)"
|
|
||||||
sleep 2
|
|
||||||
|
|
||||||
poetry run python tools/device_simulator.py > /tmp/simulator.log 2>&1 &
|
|
||||||
SIM_PID=$!
|
|
||||||
echo " Device Simulator gestartet (PID: $SIM_PID)"
|
|
||||||
sleep 2
|
|
||||||
|
|
||||||
echo " ✓ Alle Services laufen"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# 3. Test API reachability
|
|
||||||
echo "3. Teste API Erreichbarkeit..."
|
|
||||||
if timeout 3 curl -s http://localhost:8001/devices > /dev/null; then
|
|
||||||
echo " ✓ API antwortet"
|
|
||||||
else
|
|
||||||
echo " ✗ API antwortet nicht!"
|
|
||||||
echo " API Log:"
|
|
||||||
tail -10 /tmp/api.log
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# 4. Test Light Operations
|
|
||||||
echo "4. Teste Lampen-Operationen..."
|
|
||||||
|
|
||||||
# 4.1 Power On
|
|
||||||
echo " 4.1 Lampe einschalten (test_lampe_1)..."
|
|
||||||
RESPONSE=$(timeout 3 curl -s -X POST http://localhost:8001/devices/test_lampe_1/set \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"type":"light","payload":{"power":"on"}}')
|
|
||||||
echo " Response: $RESPONSE"
|
|
||||||
sleep 1
|
|
||||||
|
|
||||||
# 4.2 Check state via MQTT
|
|
||||||
echo " 4.2 Prüfe State via MQTT..."
|
|
||||||
STATE=$(timeout 2 mosquitto_sub -h 172.16.2.16 -t 'vendor/test_lampe_1/state' -C 1)
|
|
||||||
echo " State: $STATE"
|
|
||||||
if echo "$STATE" | grep -q '"power": "on"'; then
|
|
||||||
echo " ✓ Power ist ON"
|
|
||||||
else
|
|
||||||
echo " ✗ Power nicht ON!"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 4.3 Brightness
|
|
||||||
echo " 4.3 Helligkeit setzen (75%)..."
|
|
||||||
RESPONSE=$(timeout 3 curl -s -X POST http://localhost:8001/devices/test_lampe_1/set \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"type":"light","payload":{"brightness":75}}')
|
|
||||||
echo " Response: $RESPONSE"
|
|
||||||
sleep 1
|
|
||||||
|
|
||||||
STATE=$(timeout 2 mosquitto_sub -h 172.16.2.16 -t 'vendor/test_lampe_1/state' -C 1)
|
|
||||||
echo " State: $STATE"
|
|
||||||
if echo "$STATE" | grep -q '"brightness": 75'; then
|
|
||||||
echo " ✓ Brightness ist 75"
|
|
||||||
else
|
|
||||||
echo " ✗ Brightness nicht 75!"
|
|
||||||
fi
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# 5. Test Thermostat Operations
|
|
||||||
echo "5. Teste Thermostat-Operationen..."
|
|
||||||
|
|
||||||
# 5.1 Set mode and target
|
|
||||||
echo " 5.1 Setze Mode HEAT und Target 22.5°C..."
|
|
||||||
RESPONSE=$(timeout 3 curl -s -X POST http://localhost:8001/devices/test_thermo_1/set \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"type":"thermostat","payload":{"mode":"heat","target":22.5}}')
|
|
||||||
echo " Response: $RESPONSE"
|
|
||||||
sleep 1
|
|
||||||
|
|
||||||
STATE=$(timeout 2 mosquitto_sub -h 172.16.2.16 -t 'vendor/test_thermo_1/state' -C 1)
|
|
||||||
echo " State: $STATE"
|
|
||||||
if echo "$STATE" | grep -q '"mode": "heat"' && echo "$STATE" | grep -q '"target": 22.5'; then
|
|
||||||
echo " ✓ Mode ist HEAT, Target ist 22.5"
|
|
||||||
else
|
|
||||||
echo " ✗ Mode oder Target nicht korrekt!"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 5.2 Wait for temperature drift
|
|
||||||
echo " 5.2 Warte 6 Sekunden auf Temperature Drift..."
|
|
||||||
sleep 6
|
|
||||||
|
|
||||||
STATE=$(timeout 2 mosquitto_sub -h 172.16.2.16 -t 'vendor/test_thermo_1/state' -C 1)
|
|
||||||
echo " State: $STATE"
|
|
||||||
CURRENT=$(echo "$STATE" | grep -o '"current": [0-9.]*' | grep -o '[0-9.]*$')
|
|
||||||
echo " Current Temperature: ${CURRENT}°C"
|
|
||||||
if [ -n "$CURRENT" ]; then
|
|
||||||
echo " ✓ Temperature drift funktioniert"
|
|
||||||
else
|
|
||||||
echo " ✗ Temperature drift nicht sichtbar!"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 5.3 Set mode OFF
|
|
||||||
echo " 5.3 Setze Mode OFF..."
|
|
||||||
RESPONSE=$(timeout 3 curl -s -X POST http://localhost:8001/devices/test_thermo_1/set \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"type":"thermostat","payload":{"mode":"off","target":22.5}}')
|
|
||||||
echo " Response: $RESPONSE"
|
|
||||||
sleep 1
|
|
||||||
|
|
||||||
STATE=$(timeout 2 mosquitto_sub -h 172.16.2.16 -t 'vendor/test_thermo_1/state' -C 1)
|
|
||||||
if echo "$STATE" | grep -q '"mode": "off"'; then
|
|
||||||
echo " ✓ Mode ist OFF"
|
|
||||||
else
|
|
||||||
echo " ✗ Mode nicht OFF!"
|
|
||||||
fi
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# 6. Check simulator log
|
|
||||||
echo "6. Simulator Log (letzte 20 Zeilen)..."
|
|
||||||
tail -20 /tmp/simulator.log
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# 7. Summary
|
|
||||||
echo "=== Test Summary ==="
|
|
||||||
echo "✓ Alle Tests abgeschlossen"
|
|
||||||
echo ""
|
|
||||||
echo "Laufende Prozesse:"
|
|
||||||
echo " Abstraction: PID $ABSTRACTION_PID"
|
|
||||||
echo " API: PID $API_PID"
|
|
||||||
echo " Simulator: PID $SIM_PID"
|
|
||||||
echo ""
|
|
||||||
echo "Logs verfügbar in:"
|
|
||||||
echo " /tmp/abstraction.log"
|
|
||||||
echo " /tmp/api.log"
|
|
||||||
echo " /tmp/simulator.log"
|
|
||||||
@@ -1,288 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""Test layout loader and models."""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import tempfile
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
# Add parent directory to path
|
|
||||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
||||||
|
|
||||||
from packages.home_capabilities.layout import DeviceTile, Room, UiLayout, load_layout
|
|
||||||
|
|
||||||
|
|
||||||
def test_device_tile_creation():
|
|
||||||
"""Test DeviceTile model creation."""
|
|
||||||
print("Test 1: DeviceTile Creation")
|
|
||||||
|
|
||||||
tile = DeviceTile(
|
|
||||||
device_id="test_lamp",
|
|
||||||
title="Test Lamp",
|
|
||||||
icon="💡",
|
|
||||||
rank=10
|
|
||||||
)
|
|
||||||
|
|
||||||
assert tile.device_id == "test_lamp"
|
|
||||||
assert tile.title == "Test Lamp"
|
|
||||||
assert tile.icon == "💡"
|
|
||||||
assert tile.rank == 10
|
|
||||||
|
|
||||||
print(" ✓ DeviceTile created successfully")
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def test_room_creation():
|
|
||||||
"""Test Room model creation."""
|
|
||||||
print("\nTest 2: Room Creation")
|
|
||||||
|
|
||||||
tiles = [
|
|
||||||
DeviceTile(device_id="lamp1", title="Lamp 1", icon="💡", rank=1),
|
|
||||||
DeviceTile(device_id="lamp2", title="Lamp 2", icon="💡", rank=2)
|
|
||||||
]
|
|
||||||
|
|
||||||
room = Room(name="Living Room", devices=tiles)
|
|
||||||
|
|
||||||
assert room.name == "Living Room"
|
|
||||||
assert len(room.devices) == 2
|
|
||||||
assert room.devices[0].rank == 1
|
|
||||||
|
|
||||||
print(" ✓ Room with 2 devices created")
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def test_ui_layout_creation():
|
|
||||||
"""Test UiLayout model creation."""
|
|
||||||
print("\nTest 3: UiLayout Creation")
|
|
||||||
|
|
||||||
rooms = [
|
|
||||||
Room(
|
|
||||||
name="Room 1",
|
|
||||||
devices=[DeviceTile(device_id="d1", title="D1", icon="💡", rank=1)]
|
|
||||||
),
|
|
||||||
Room(
|
|
||||||
name="Room 2",
|
|
||||||
devices=[DeviceTile(device_id="d2", title="D2", icon="💡", rank=1)]
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
layout = UiLayout(rooms=rooms)
|
|
||||||
|
|
||||||
assert len(layout.rooms) == 2
|
|
||||||
assert layout.total_devices() == 2
|
|
||||||
|
|
||||||
print(" ✓ UiLayout with 2 rooms created")
|
|
||||||
print(f" ✓ Total devices: {layout.total_devices()}")
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def test_layout_validation_empty_rooms():
|
|
||||||
"""Test that layout validation rejects empty rooms list."""
|
|
||||||
print("\nTest 4: Validation - Empty Rooms")
|
|
||||||
|
|
||||||
try:
|
|
||||||
UiLayout(rooms=[])
|
|
||||||
print(" ✗ Should have raised ValueError")
|
|
||||||
return False
|
|
||||||
except ValueError as e:
|
|
||||||
if "at least one room" in str(e):
|
|
||||||
print(f" ✓ Correct validation error: {e}")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print(f" ✗ Wrong error message: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def test_load_layout_from_file():
|
|
||||||
"""Test loading layout from YAML file."""
|
|
||||||
print("\nTest 5: Load Layout from YAML")
|
|
||||||
|
|
||||||
yaml_content = """
|
|
||||||
rooms:
|
|
||||||
- name: Wohnzimmer
|
|
||||||
devices:
|
|
||||||
- device_id: test_lampe_1
|
|
||||||
title: Stehlampe
|
|
||||||
icon: "💡"
|
|
||||||
rank: 10
|
|
||||||
- device_id: test_lampe_2
|
|
||||||
title: Tischlampe
|
|
||||||
icon: "💡"
|
|
||||||
rank: 20
|
|
||||||
|
|
||||||
- name: Schlafzimmer
|
|
||||||
devices:
|
|
||||||
- device_id: test_lampe_3
|
|
||||||
title: Nachttischlampe
|
|
||||||
icon: "🛏️"
|
|
||||||
rank: 5
|
|
||||||
"""
|
|
||||||
|
|
||||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f:
|
|
||||||
f.write(yaml_content)
|
|
||||||
temp_path = Path(f.name)
|
|
||||||
|
|
||||||
try:
|
|
||||||
layout = load_layout(str(temp_path))
|
|
||||||
|
|
||||||
assert len(layout.rooms) == 2
|
|
||||||
assert layout.rooms[0].name == "Wohnzimmer"
|
|
||||||
assert len(layout.rooms[0].devices) == 2
|
|
||||||
assert layout.total_devices() == 3
|
|
||||||
|
|
||||||
# Check device sorting by rank
|
|
||||||
wohnzimmer_devices = layout.rooms[0].devices
|
|
||||||
assert wohnzimmer_devices[0].rank == 10
|
|
||||||
assert wohnzimmer_devices[1].rank == 20
|
|
||||||
|
|
||||||
print(" ✓ Layout loaded from YAML")
|
|
||||||
print(f" ✓ Rooms: {len(layout.rooms)}")
|
|
||||||
print(f" ✓ Total devices: {layout.total_devices()}")
|
|
||||||
print(f" ✓ Room order preserved: {[r.name for r in layout.rooms]}")
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
finally:
|
|
||||||
temp_path.unlink()
|
|
||||||
|
|
||||||
|
|
||||||
def test_load_layout_missing_file():
|
|
||||||
"""Test error handling for missing file."""
|
|
||||||
print("\nTest 6: Missing File Error")
|
|
||||||
|
|
||||||
try:
|
|
||||||
load_layout("/nonexistent/path/layout.yaml")
|
|
||||||
print(" ✗ Should have raised FileNotFoundError")
|
|
||||||
return False
|
|
||||||
except FileNotFoundError as e:
|
|
||||||
if "not found" in str(e):
|
|
||||||
print(f" ✓ Correct error for missing file")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print(f" ✗ Wrong error message: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def test_load_layout_invalid_yaml():
|
|
||||||
"""Test error handling for invalid YAML."""
|
|
||||||
print("\nTest 7: Invalid YAML Error")
|
|
||||||
|
|
||||||
yaml_content = """
|
|
||||||
rooms:
|
|
||||||
- name: Room1
|
|
||||||
devices: [invalid yaml structure
|
|
||||||
"""
|
|
||||||
|
|
||||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f:
|
|
||||||
f.write(yaml_content)
|
|
||||||
temp_path = Path(f.name)
|
|
||||||
|
|
||||||
try:
|
|
||||||
load_layout(str(temp_path))
|
|
||||||
print(" ✗ Should have raised YAMLError")
|
|
||||||
temp_path.unlink()
|
|
||||||
return False
|
|
||||||
except Exception as e:
|
|
||||||
temp_path.unlink()
|
|
||||||
if "YAML" in str(type(e).__name__) or "parse" in str(e).lower():
|
|
||||||
print(f" ✓ Correct YAML parsing error")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print(f" ✗ Unexpected error: {type(e).__name__}: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def test_load_layout_missing_required_fields():
|
|
||||||
"""Test validation for missing required fields."""
|
|
||||||
print("\nTest 8: Missing Required Fields")
|
|
||||||
|
|
||||||
yaml_content = """
|
|
||||||
rooms:
|
|
||||||
- name: Room1
|
|
||||||
devices:
|
|
||||||
- device_id: lamp1
|
|
||||||
title: Lamp
|
|
||||||
# Missing: icon, rank
|
|
||||||
"""
|
|
||||||
|
|
||||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f:
|
|
||||||
f.write(yaml_content)
|
|
||||||
temp_path = Path(f.name)
|
|
||||||
|
|
||||||
try:
|
|
||||||
load_layout(str(temp_path))
|
|
||||||
print(" ✗ Should have raised ValueError")
|
|
||||||
temp_path.unlink()
|
|
||||||
return False
|
|
||||||
except ValueError as e:
|
|
||||||
temp_path.unlink()
|
|
||||||
if "icon" in str(e) or "rank" in str(e) or "required" in str(e).lower():
|
|
||||||
print(f" ✓ Validation error for missing fields")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print(f" ✗ Wrong error: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def test_load_default_layout():
|
|
||||||
"""Test loading default layout from workspace."""
|
|
||||||
print("\nTest 9: Load Default Layout (config/layout.yaml)")
|
|
||||||
|
|
||||||
try:
|
|
||||||
layout = load_layout()
|
|
||||||
|
|
||||||
print(f" ✓ Default layout loaded")
|
|
||||||
print(f" ✓ Rooms: {len(layout.rooms)} ({', '.join(r.name for r in layout.rooms)})")
|
|
||||||
print(f" ✓ Total devices: {layout.total_devices()}")
|
|
||||||
|
|
||||||
for room in layout.rooms:
|
|
||||||
print(f" - {room.name}: {len(room.devices)} devices")
|
|
||||||
for device in room.devices:
|
|
||||||
print(f" • {device.title} (id={device.device_id}, rank={device.rank})")
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
except FileNotFoundError:
|
|
||||||
print(" ⚠ Default layout.yaml not found (expected at config/layout.yaml)")
|
|
||||||
return True # Not a failure if file doesn't exist yet
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ Error loading default layout: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""Run all tests."""
|
|
||||||
print("=" * 60)
|
|
||||||
print("Testing Layout Loader and Models")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
tests = [
|
|
||||||
test_device_tile_creation,
|
|
||||||
test_room_creation,
|
|
||||||
test_ui_layout_creation,
|
|
||||||
test_layout_validation_empty_rooms,
|
|
||||||
test_load_layout_from_file,
|
|
||||||
test_load_layout_missing_file,
|
|
||||||
test_load_layout_invalid_yaml,
|
|
||||||
test_load_layout_missing_required_fields,
|
|
||||||
test_load_default_layout,
|
|
||||||
]
|
|
||||||
|
|
||||||
results = []
|
|
||||||
for test in tests:
|
|
||||||
try:
|
|
||||||
results.append(test())
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ Unexpected exception: {e}")
|
|
||||||
results.append(False)
|
|
||||||
|
|
||||||
print("\n" + "=" * 60)
|
|
||||||
passed = sum(results)
|
|
||||||
total = len(results)
|
|
||||||
print(f"Results: {passed}/{total} tests passed")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
return 0 if all(results) else 1
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
||||||
@@ -1,296 +0,0 @@
|
|||||||
"""
|
|
||||||
Acceptance tests for Responsive Dashboard.
|
|
||||||
|
|
||||||
Tests:
|
|
||||||
1. Desktop: 4 columns grid
|
|
||||||
2. Tablet: 2 columns grid
|
|
||||||
3. Mobile: 1 column grid
|
|
||||||
4. POST requests work (via API check)
|
|
||||||
5. No horizontal scrolling on any viewport
|
|
||||||
"""
|
|
||||||
import sys
|
|
||||||
import httpx
|
|
||||||
from bs4 import BeautifulSoup
|
|
||||||
|
|
||||||
|
|
||||||
def test_dashboard_html_structure():
|
|
||||||
"""Test 1: Dashboard has correct HTML structure."""
|
|
||||||
print("Test 1: Dashboard HTML Structure")
|
|
||||||
print("-" * 60)
|
|
||||||
|
|
||||||
try:
|
|
||||||
response = httpx.get("http://localhost:8002/dashboard", timeout=5.0)
|
|
||||||
response.raise_for_status()
|
|
||||||
|
|
||||||
html = response.text
|
|
||||||
soup = BeautifulSoup(html, 'html.parser')
|
|
||||||
|
|
||||||
# Check for grid container
|
|
||||||
grid = soup.find('div', class_='devices-grid')
|
|
||||||
if not grid:
|
|
||||||
print(" ✗ FAILED: Missing .devices-grid container")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Check for device tiles
|
|
||||||
tiles = soup.find_all('div', class_='device-tile')
|
|
||||||
if len(tiles) < 2:
|
|
||||||
print(f" ✗ FAILED: Expected at least 2 device tiles, found {len(tiles)}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Check for state spans
|
|
||||||
state_spans = soup.find_all('span', id=lambda x: x and x.startswith('state-'))
|
|
||||||
if len(state_spans) < 2:
|
|
||||||
print(f" ✗ FAILED: Expected state spans, found {len(state_spans)}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Check for ON/OFF buttons
|
|
||||||
btn_on = soup.find_all('button', class_='btn-on')
|
|
||||||
btn_off = soup.find_all('button', class_='btn-off')
|
|
||||||
|
|
||||||
if not btn_on or not btn_off:
|
|
||||||
print(" ✗ FAILED: Missing ON/OFF buttons")
|
|
||||||
return False
|
|
||||||
|
|
||||||
print(f" ✓ Found {len(tiles)} device tiles")
|
|
||||||
print(f" ✓ Found {len(state_spans)} state indicators")
|
|
||||||
print(f" ✓ Found {len(btn_on)} ON buttons and {len(btn_off)} OFF buttons")
|
|
||||||
print()
|
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ FAILED: {e}")
|
|
||||||
print()
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def test_responsive_css():
|
|
||||||
"""Test 2: CSS has responsive grid rules."""
|
|
||||||
print("Test 2: Responsive CSS")
|
|
||||||
print("-" * 60)
|
|
||||||
|
|
||||||
try:
|
|
||||||
response = httpx.get("http://localhost:8002/static/style.css", timeout=5.0)
|
|
||||||
response.raise_for_status()
|
|
||||||
|
|
||||||
css = response.text
|
|
||||||
|
|
||||||
# Check for desktop 4 columns
|
|
||||||
if 'grid-template-columns: repeat(4, 1fr)' not in css:
|
|
||||||
print(" ✗ FAILED: Missing desktop grid (4 columns)")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Check for tablet media query (2 columns)
|
|
||||||
if 'max-width: 1024px' not in css or 'repeat(2, 1fr)' not in css:
|
|
||||||
print(" ✗ FAILED: Missing tablet media query (2 columns)")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Check for mobile media query (1 column)
|
|
||||||
if 'max-width: 640px' not in css:
|
|
||||||
print(" ✗ FAILED: Missing mobile media query")
|
|
||||||
return False
|
|
||||||
|
|
||||||
print(" ✓ Desktop: 4 columns (grid-template-columns: repeat(4, 1fr))")
|
|
||||||
print(" ✓ Tablet: 2 columns (@media max-width: 1024px)")
|
|
||||||
print(" ✓ Mobile: 1 column (@media max-width: 640px)")
|
|
||||||
print()
|
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ FAILED: {e}")
|
|
||||||
print()
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def test_javascript_functions():
|
|
||||||
"""Test 3: JavaScript POST function exists."""
|
|
||||||
print("Test 3: JavaScript POST Function")
|
|
||||||
print("-" * 60)
|
|
||||||
|
|
||||||
try:
|
|
||||||
response = httpx.get("http://localhost:8002/dashboard", timeout=5.0)
|
|
||||||
response.raise_for_status()
|
|
||||||
|
|
||||||
html = response.text
|
|
||||||
|
|
||||||
# Check for setDeviceState function
|
|
||||||
if 'function setDeviceState' not in html and 'async function setDeviceState' not in html:
|
|
||||||
print(" ✗ FAILED: Missing setDeviceState function")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Check for fetch POST call
|
|
||||||
if 'fetch(' not in html or 'method: \'POST\'' not in html:
|
|
||||||
print(" ✗ FAILED: Missing fetch POST call")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Check for correct API endpoint pattern
|
|
||||||
if '/devices/${deviceId}/set' not in html and '/devices/' not in html:
|
|
||||||
print(" ✗ FAILED: Missing correct API endpoint")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Check for JSON payload
|
|
||||||
if 'type: \'light\'' not in html and '"type":"light"' not in html:
|
|
||||||
print(" ✗ FAILED: Missing correct JSON payload")
|
|
||||||
return False
|
|
||||||
|
|
||||||
print(" ✓ setDeviceState function defined")
|
|
||||||
print(" ✓ Uses fetch with POST method")
|
|
||||||
print(" ✓ Correct endpoint: /devices/{deviceId}/set")
|
|
||||||
print(" ✓ Correct payload: {type:'light', payload:{power:...}}")
|
|
||||||
print()
|
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ FAILED: {e}")
|
|
||||||
print()
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def test_device_controls():
|
|
||||||
"""Test 4: Devices have correct controls."""
|
|
||||||
print("Test 4: Device Controls")
|
|
||||||
print("-" * 60)
|
|
||||||
|
|
||||||
try:
|
|
||||||
response = httpx.get("http://localhost:8002/dashboard", timeout=5.0)
|
|
||||||
response.raise_for_status()
|
|
||||||
|
|
||||||
html = response.text
|
|
||||||
soup = BeautifulSoup(html, 'html.parser')
|
|
||||||
|
|
||||||
# Find device tiles
|
|
||||||
tiles = soup.find_all('div', class_='device-tile')
|
|
||||||
|
|
||||||
for tile in tiles:
|
|
||||||
device_id = tile.get('data-device-id')
|
|
||||||
|
|
||||||
# Check for device header
|
|
||||||
header = tile.find('div', class_='device-header')
|
|
||||||
if not header:
|
|
||||||
print(f" ✗ FAILED: Device {device_id} missing header")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Check for icon and title
|
|
||||||
icon = tile.find('div', class_='device-icon')
|
|
||||||
title = tile.find('h3', class_='device-title')
|
|
||||||
device_id_elem = tile.find('p', class_='device-id')
|
|
||||||
|
|
||||||
if not icon or not title or not device_id_elem:
|
|
||||||
print(f" ✗ FAILED: Device {device_id} missing icon/title/id")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Check for state indicator
|
|
||||||
state_span = tile.find('span', id=f'state-{device_id}')
|
|
||||||
if not state_span:
|
|
||||||
print(f" ✗ FAILED: Device {device_id} missing state indicator")
|
|
||||||
return False
|
|
||||||
|
|
||||||
print(f" ✓ All {len(tiles)} devices have:")
|
|
||||||
print(" • Icon, title, and device_id")
|
|
||||||
print(" • State indicator (span#state-{{device_id}})")
|
|
||||||
print(" • ON/OFF buttons (for lights with power feature)")
|
|
||||||
print()
|
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ FAILED: {e}")
|
|
||||||
print()
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def test_rank_sorting():
|
|
||||||
"""Test 5: Devices sorted by rank."""
|
|
||||||
print("Test 5: Device Rank Sorting")
|
|
||||||
print("-" * 60)
|
|
||||||
|
|
||||||
try:
|
|
||||||
response = httpx.get("http://localhost:8002/dashboard", timeout=5.0)
|
|
||||||
response.raise_for_status()
|
|
||||||
|
|
||||||
html = response.text
|
|
||||||
|
|
||||||
# In Wohnzimmer: Deckenlampe (rank=5) should come before Stehlampe (rank=10)
|
|
||||||
deckenlampe_pos = html.find('device-title">Deckenlampe<')
|
|
||||||
stehlampe_pos = html.find('device-title">Stehlampe<')
|
|
||||||
|
|
||||||
if deckenlampe_pos == -1 or stehlampe_pos == -1:
|
|
||||||
print(" ℹ INFO: Test devices not found (expected for test)")
|
|
||||||
print()
|
|
||||||
return True
|
|
||||||
|
|
||||||
if deckenlampe_pos > stehlampe_pos:
|
|
||||||
print(" ✗ FAILED: Devices not sorted by rank")
|
|
||||||
return False
|
|
||||||
|
|
||||||
print(" ✓ Devices sorted by rank (Deckenlampe before Stehlampe)")
|
|
||||||
print()
|
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ FAILED: {e}")
|
|
||||||
print()
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""Run all acceptance tests."""
|
|
||||||
print("=" * 60)
|
|
||||||
print("Testing Responsive Dashboard")
|
|
||||||
print("=" * 60)
|
|
||||||
print()
|
|
||||||
|
|
||||||
# Check if UI is running
|
|
||||||
print("Prerequisites: Checking if UI is running on port 8002...")
|
|
||||||
try:
|
|
||||||
response = httpx.get("http://localhost:8002/dashboard", timeout=3.0)
|
|
||||||
print("✓ UI is running")
|
|
||||||
print()
|
|
||||||
except Exception as e:
|
|
||||||
print(f"✗ Cannot reach UI: {e}")
|
|
||||||
print(" Start UI with: poetry run uvicorn apps.ui.main:app --port 8002")
|
|
||||||
print()
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
results = []
|
|
||||||
|
|
||||||
# Run tests
|
|
||||||
results.append(("HTML Structure", test_dashboard_html_structure()))
|
|
||||||
results.append(("Responsive CSS", test_responsive_css()))
|
|
||||||
results.append(("JavaScript POST", test_javascript_functions()))
|
|
||||||
results.append(("Device Controls", test_device_controls()))
|
|
||||||
results.append(("Rank Sorting", test_rank_sorting()))
|
|
||||||
|
|
||||||
# Summary
|
|
||||||
print("=" * 60)
|
|
||||||
passed = sum(1 for _, result in results if result)
|
|
||||||
total = len(results)
|
|
||||||
print(f"Results: {passed}/{total} tests passed")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
for name, result in results:
|
|
||||||
status = "✓" if result else "✗"
|
|
||||||
print(f" {status} {name}")
|
|
||||||
|
|
||||||
print()
|
|
||||||
print("Manual Tests Required:")
|
|
||||||
print(" 1. Open http://localhost:8002/dashboard in browser")
|
|
||||||
print(" 2. Resize browser window to test responsive breakpoints:")
|
|
||||||
print(" - Desktop (>1024px): Should show 4 columns")
|
|
||||||
print(" - Tablet (640-1024px): Should show 2 columns")
|
|
||||||
print(" - Mobile (<640px): Should show 1 column")
|
|
||||||
print(" 3. Click ON/OFF buttons and check Network tab in DevTools")
|
|
||||||
print(" - Should see POST to http://localhost:8001/devices/.../set")
|
|
||||||
print(" - No JavaScript errors in Console")
|
|
||||||
print(" 4. Verify no horizontal scrolling at any viewport size")
|
|
||||||
|
|
||||||
if passed == total:
|
|
||||||
print()
|
|
||||||
print("All automated tests passed!")
|
|
||||||
sys.exit(0)
|
|
||||||
else:
|
|
||||||
print()
|
|
||||||
print("Some tests failed.")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||