window contact first try
This commit is contained in:
@@ -15,7 +15,7 @@ import uuid
|
||||
from aiomqtt import Client
|
||||
from pydantic import ValidationError
|
||||
|
||||
from packages.home_capabilities import LightState, ThermostatState
|
||||
from packages.home_capabilities import LightState, ThermostatState, ContactState
|
||||
from apps.abstraction.transformation import (
|
||||
transform_abstract_to_vendor,
|
||||
transform_vendor_to_abstract
|
||||
@@ -89,11 +89,12 @@ def validate_devices(devices: list[dict[str, Any]]) -> None:
|
||||
if "topics" not in device:
|
||||
raise ValueError(f"Device {device_id} missing 'topics'")
|
||||
|
||||
if "set" not in device["topics"]:
|
||||
raise ValueError(f"Device {device_id} missing 'topics.set'")
|
||||
|
||||
# 'state' topic is required for all devices
|
||||
if "state" not in device["topics"]:
|
||||
raise ValueError(f"Device {device_id} missing 'topics.state'")
|
||||
|
||||
# 'set' topic is optional (read-only devices like contact sensors don't have it)
|
||||
# No validation needed for topics.set
|
||||
|
||||
# Log loaded devices
|
||||
device_ids = [d["device_id"] for d in devices]
|
||||
@@ -166,6 +167,10 @@ async def handle_abstract_set(
|
||||
|
||||
# Validate against ThermostatState (current/battery/window_open are optional)
|
||||
ThermostatState.model_validate(abstract_payload)
|
||||
elif device_type in {"contact", "contact_sensor"}:
|
||||
# Contact sensors are read-only - SET commands should not occur
|
||||
logger.warning(f"Contact sensor {device_id} received SET command - ignoring (read-only device)")
|
||||
return
|
||||
except ValidationError as e:
|
||||
logger.error(f"Validation failed for {device_type} SET {device_id}: {e}")
|
||||
return
|
||||
@@ -214,12 +219,18 @@ async def handle_vendor_state(
|
||||
elif device_type == "thermostat":
|
||||
# Validate thermostat state: mode, target, current (required), battery, window_open
|
||||
ThermostatState.model_validate(abstract_payload)
|
||||
elif device_type in {"contact", "contact_sensor"}:
|
||||
# Validate contact sensor state
|
||||
ContactState.model_validate(abstract_payload)
|
||||
except ValidationError as e:
|
||||
logger.error(f"Validation failed for {device_type} STATE {device_id}: {e}")
|
||||
return
|
||||
|
||||
# Normalize device type for topic (use 'contact' for both 'contact' and 'contact_sensor')
|
||||
topic_type = "contact" if device_type in {"contact", "contact_sensor"} else device_type
|
||||
|
||||
# Publish to abstract state topic (retained)
|
||||
abstract_topic = f"home/{device_type}/{device_id}/state"
|
||||
abstract_topic = f"home/{topic_type}/{device_id}/state"
|
||||
abstract_message = json.dumps(abstract_payload)
|
||||
|
||||
logger.info(f"← abstract STATE {device_id}: {abstract_topic} → {abstract_message}")
|
||||
@@ -273,15 +284,22 @@ async def mqtt_worker(config: dict[str, Any], redis_client: aioredis.Redis) -> N
|
||||
keepalive=keepalive,
|
||||
timeout=10.0 # Add explicit timeout for operations
|
||||
) as client:
|
||||
logger.info(f"Connected to MQTT broker as {client_id}")
|
||||
logger.info(f"Connected to MQTT broker as {unique_client_id}")
|
||||
|
||||
# Subscribe to abstract SET topics for all devices
|
||||
# Subscribe to topics for all devices
|
||||
for device in devices.values():
|
||||
abstract_set_topic = f"home/{device['type']}/{device['device_id']}/set"
|
||||
await client.subscribe(abstract_set_topic)
|
||||
logger.info(f"Subscribed to abstract SET: {abstract_set_topic}")
|
||||
device_id = device['device_id']
|
||||
device_type = device['type']
|
||||
|
||||
# Subscribe to vendor STATE topics
|
||||
# Subscribe to abstract SET topic only if device has a SET topic (not read-only)
|
||||
if "set" in device["topics"]:
|
||||
abstract_set_topic = f"home/{device_type}/{device_id}/set"
|
||||
await client.subscribe(abstract_set_topic)
|
||||
logger.info(f"Subscribed to abstract SET: {abstract_set_topic}")
|
||||
else:
|
||||
logger.info(f"Skipping SET subscription for read-only device: {device_id}")
|
||||
|
||||
# Subscribe to vendor STATE topics (all devices have state)
|
||||
vendor_state_topic = device["topics"]["state"]
|
||||
await client.subscribe(vendor_state_topic)
|
||||
logger.info(f"Subscribed to vendor STATE: {vendor_state_topic}")
|
||||
|
||||
Reference in New Issue
Block a user