smart_brain/devices/brennenstuhl.py

115 lines
4.8 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
from devices.base import base
import json
import task
import time
class brennenstuhl_heatingvalve(base):
""" Communication (MQTT)
brennenstuhl_heatingvalve {
| "away_mode": ["ON", "OFF"]
| "battery": [0...100] %
| "child_lock": ["LOCK", "UNLOCK"]
| "current_heating_setpoint": [5...30] °C
| "linkquality": [0...255] lqi
| "local_temperature": [numeric] °C
| "preset": ["manual", ...]
| "system_mode": ["heat", ...]
| "valve_detection": ["ON", "OFF"]
| "window_detection": ["ON", "OFF"]
| }
+- set {
"away_mode": ["ON", "OFF", "TOGGLE"]
"child_lock": ["LOCK", "UNLOCK"]
"current_heating_setpoint": [5...30] °C
"preset": ["manual", ...]
"system_mode": ["heat", ...]
"valve_detection": ["ON", "OFF", "TOGGLE"]
"window_detection": ["ON", "OFF", "TOGGLE"]
}
"""
KEY_LINKQUALITY = "linkquality"
KEY_BATTERY = "battery"
KEY_HEATING_SETPOINT = "current_heating_setpoint"
KEY_TEMPERATURE = "local_temperature"
#
KEY_AWAY_MODE = "away_mode"
KEY_CHILD_LOCK = "child_lock"
KEY_PRESET = "preset"
KEY_SYSTEM_MODE = "system_mode"
KEY_VALVE_DETECTION = "valve_detection"
KEY_WINDOW_DETECTION = "window_detection"
#
RETRY_CYCLE_TIME = 2.5
MAX_TX_RETRIES = 20
RETRY_TIMEOUT = RETRY_CYCLE_TIME * MAX_TX_RETRIES
#
TX_TYPE = base.TX_DICT
#
RX_KEYS = [KEY_LINKQUALITY, KEY_BATTERY, KEY_HEATING_SETPOINT, KEY_TEMPERATURE]
RX_IGNORE_KEYS = [KEY_AWAY_MODE, KEY_CHILD_LOCK, KEY_PRESET, KEY_SYSTEM_MODE, KEY_VALVE_DETECTION, KEY_WINDOW_DETECTION]
def __init__(self, mqtt_client, topic):
super().__init__(mqtt_client, topic)
self.mqtt_client.send(self.topic + '/' + self.TX_TOPIC, json.dumps({self.KEY_WINDOW_DETECTION: "ON",
self.KEY_CHILD_LOCK: "UNLOCK", self.KEY_VALVE_DETECTION: "ON", self.KEY_SYSTEM_MODE: "heat", self.KEY_PRESET: "manual"}))
self.add_callback(self.KEY_HEATING_SETPOINT, None, self.__valave_temp_rx__)
self.__tx_temperature__ = None
self.__rx_temperature__ = None
self.__tx_timestamp__ = 0
#
self.task = task.periodic(self.RETRY_CYCLE_TIME, self.__task__)
self.task.run()
def __state_logging__(self, inst, key, data):
if key in [self.KEY_HEATING_SETPOINT, self.KEY_CHILD_LOCK, self.KEY_WINDOW_DETECTION, self.KEY_VALVE_DETECTION]:
self.logger.info("State change of '%s' to '%s'", key, repr(data))
def send_command(self, key, data):
if key == self.KEY_HEATING_SETPOINT:
self.__tx_temperature__ = data
self.__tx_timestamp__ = time.time()
base.send_command(self, key, data)
def __valave_temp_rx__(self, inst, key, data):
if key == self.KEY_HEATING_SETPOINT:
self.__rx_temperature__ = data
def __task__(self, rt):
if self.__tx_temperature__ is not None and self.__tx_timestamp__ is not None: # Already send a setpoint
if self.__tx_temperature__ != self.__rx_temperature__: # Setpoint and valve feedback are unequal
if time.time() - self.__tx_timestamp__ < self.RETRY_TIMEOUT: # Timeout condition allows resend of setpoint
self.logger.warning("Setpoint not yet acknoledged by device. Sending setpoint again")
self.set_heating_setpoint(self.__tx_temperature__)
return
else:
self.__tx_timestamp__ = None # Disable resend logic, if setpoint and valve setpoint are equal
#
# RX
#
@property
def linkqulity(self):
return self.get(self.KEY_LINKQUALITY)
@property
def heating_setpoint(self):
return self.get(self.KEY_HEATING_SETPOINT)
@property
def temperature(self):
return self.get(self.KEY_TEMPERATURE)
#
# TX
#
def set_heating_setpoint(self, setpoint):
self.send_command(self.KEY_HEATING_SETPOINT, setpoint)
def set_heating_setpoint_mcb(self, device, key, data):
self.set_heating_setpoint(data)