Compare commits

..

No commits in common. "master" and "heat_valve_setpoint_retries" have entirely different histories.

46 changed files with 2561 additions and 145486 deletions

3
.gitmodules vendored
View File

@ -13,6 +13,3 @@
[submodule "devdi"] [submodule "devdi"]
path = devdi path = devdi
url = https://git.mount-mockery.de/smarthome/smart_devdi.git url = https://git.mount-mockery.de/smarthome/smart_devdi.git
[submodule "smart_devices"]
path = smart_devices
url = https://git.mount-mockery.de/smarthome/smart_devices.git

2
.vscode/launch.json vendored
View File

@ -6,7 +6,7 @@
"configurations": [ "configurations": [
{ {
"name": "Python: Main File execution", "name": "Python: Main File execution",
"type": "debugpy", "type": "python",
"request": "launch", "request": "launch",
"program": "${workspaceFolder}/smart_brain.py", "program": "${workspaceFolder}/smart_brain.py",
"console": "integratedTerminal", "console": "integratedTerminal",

133
Makefile
View File

@ -1,133 +0,0 @@
# git helper Makefile: Version 2.5 (2025-08-11)
default: help
.ONESHELL:
SHELL = /usr/bin/bash
MAKEFLAGS += --no-print-directory
.SILENT:
-include __make.d__/*.mk
GIT_FLAG = ./.git
VENV_FLAG = ./.venv_required
VENV_FOLDER = ./venv
localhelp:
help:
echo "Possible common options are:"
echo " - init - Initialise the repository and all folders below with a Makefile"
echo " - giti - Get the git status of all submodules including their submodules"
echo " - update_submodules - Set all submodules to remote master"
echo " - venv_flag - Set the venev flag for the base folder. A venv will be generated"
echo " - clean - clean up"
echo " - deepclean - clean up this and all Makefiles below"
$(MAKE) localhelp
echo "You are able to create files make.d/*.mk and add local rules there. "
echo " - localinit: print_head - Will be executed as last step in the init process."
echo " - localhelp: print_head - Will be executed in th middle of the help text generation"
echo " - localclean:print_head - Will be executed before the clean rule"
localinit:
init: print_head
# Init git repo
if [[ -e $(GIT_FLAG) ]]; then
echo -e "\033[1;33mInitialising git submodules...\e[0m"
git submodule init
git submodule update
echo
fi
# Init submodules
SUBDIRS=$$(find . -maxdepth 2 -mindepth 2 -name Makefile | sort)
for subdir in $$SUBDIRS; do
$(MAKE) --no-print-directory -C $$(dirname $$subdir) init
done
if [[ $$SUBDIRS = *[![:space:]]* ]]; then
$(MAKE) print_head
fi
# Create venv if needed
if [[ -e $(VENV_FLAG) ]]; then
BASEPATH=$$(pwd -P)
#
# Create venv
#
if [ ! -e venv ];then
echo -e "\033[1;33mCreating venv in $$BASEPATH...\e[0m"
python3 -m venv $$BASEPATH/venv > /dev/null 2>&1
else
echo -e "\033[1;33mVirtualenv already exists in $$BASEPATH...\e[0m"
fi
echo
#
# Install modules
#
echo -e "\033[1;33mInstalling modules to venv in $$BASEPATH...\e[0m"
for req_file in $$(find -L $$BASEPATH -name requirements.txt 2> /dev/null); do
# echo " $$req_file"
while read req_mod; do
if [[ $$req_mod = *[![:space:]]* ]]; then
# req_mod is not empty
OUT=$$($$BASEPATH/venv/bin/pip install -U $$req_mod 2>&1 )
if [[ $$OUT =~ "Successfully installed" ]]; then
echo -e " * \033[1;32m$$req_mod installed.\e[0m"
elif [[ $$OUT =~ "already satisfied" ]]; then
echo -e " * \033[1;36m$$req_mod already installed.\e[0m"
else
echo -e " * \033[1;31m$$req_mod installation FAILED!\e[0m"
#echo $$OUT
fi
fi
done < $$req_file
done
echo
fi
# Start local init
echo -e "\033[1;33mDoing localinit...\e[0m"
$(MAKE) localinit
update_submodules:
git submodule foreach "git fetch && git checkout master && git pull && git submodule init && git submodule update"
giti_this: print_head
giti
echo " Submodules:"
git submodule --quiet foreach "echo -n ' ' && giti"
giti:
git submodule --quiet foreach "[ -e Makefile ] && make --no-print-directory giti_this || :"
localclean:
clean: localclean
if [[ ! -e $(VENV_FLAG) ]]; then
if [[ -d $(VENV_FOLDER) ]]; then
rm -rf $(VENV_FOLDER)
fi
fi
cleanall: clean
for subdir in $$(find . -maxdepth 2 -mindepth 2 -name Makefile | sort); do
$(MAKE) --no-print-directory -C $$(dirname $$subdir) cleanall
done
venv_flag:
if [[ ! -e $(VENV_FLAG) ]]; then
touch $(VENV_FLAG)
if [[ -e $(GIT_FLAG) ]]; then
git add $(VENV_FLAG)
fi
fi
print_head:
DIRNAME=$$(basename $$(pwd))
DIRLENGTH=$${#DIRNAME}
echo -ne "\n\n\033[1;34m╔═"
for i in $$(seq 1 $$DIRLENGTH); do echo -n "═"; done
echo -e "═╗"
echo -e "$$DIRNAME ║"
echo -ne "╚═"
for i in $$(seq 1 $$DIRLENGTH); do echo -n "═"; done
echo -e "═╝\033[00m"

View File

@ -1,17 +0,0 @@
.ONESHELL:
SHELL = /usr/bin/bash
MAKEFLAGS += --no-print-directory
.SILENT:
devinit:
if [[ ! -e config.py ]]; then
cp config_example/config.j2 config.py
chmod 600 config.py
sed -i "/^DEBUG.*=.*/c\DEBUG = True" config.py
sed -i "/^LOG_HOSTNAME.*=.*/c\LOG_HOSTNAME = \"localhost\"" config.py
sed -i "/^LOG_LEVEL.*=.*/c\LOG_LEVEL = logging.WARNING" config.py
sed -i 's/{{ smart_srv_brain_hostname }}/localhost/' config.py
sed -i 's/"{{ smart_srv_brain_username }}"/None/' config.py
sed -i 's/"{{ smart_srv_brain_password }}"/None/' config.py
fi

View File

@ -1,12 +0,0 @@
.ONESHELL:
SHELL = /usr/bin/bash
MAKEFLAGS += --no-print-directory
.SILENT:
COV3_CMD=venv/bin/coverage
coverage:
$(COV3_CMD) erase
$(COV3_CMD) run -a --branch --source=devdi,smart_devices,devices,function smart_brain.py
$(COV3_CMD) xml -o ../smart_brain_test/testresults/coverage.xml

View File

@ -1,13 +0,0 @@
.ONESHELL:
SHELL = /usr/bin/bash
MAKEFLAGS += --no-print-directory
.SILENT:
GIT_FLAG = ./.git
VENV_FLAG = ./.venv_required
VENV_FOLDER = ./venv
localhelp:
echo "Possible local options are:"
echo " - devinit - Initialise the application for development usage"
echo " - coverage - Run smarthome in coverage mode"

View File

@ -0,0 +1,35 @@
import json
from mqtt import mqtt_client
import time
TEST_CLIENT_ID = "__test_device_tester__"
mqtt_test_client = mqtt_client(TEST_CLIENT_ID, "localhost")
def init_state(all_state_keys, device):
for state_topic in all_state_keys:
assert device.get(state_topic, 0) == None
def state_change_by_mqtt(all_state_keys, num_states, mqtt_test_client, base_topic, device, mqtt_data, state_data, warning_condition, mqtt_signal_time):
tm_warning = None
for i in range(num_states):
for state_topic in all_state_keys:
if device.TX_TYPE == device.TX_VALUE:
data = json.dumps(mqtt_data(state_topic)[i])
mqtt_test_client.send(base_topic + '/' + state_topic, data)
elif device.TX_TYPE == device.TX_DICT:
mqtt_test_client.send(base_topic, json.dumps({state_topic: mqtt_data(state_topic)[i]}))
else:
raise TypeError("Unknown TX_TYPE for device.")
if callable(warning_condition):
if warning_condition(state_topic, mqtt_data(state_topic)[i]):
tm_warning = int(time.time())
time.sleep(mqtt_signal_time)
for state_topic in all_state_keys:
assert device.get(state_topic) == state_data(state_topic)[i]
return tm_warning

View File

@ -0,0 +1,45 @@
from module import mqtt_test_client, init_state, state_change_by_mqtt
from devices import my_powerplug as test_device
from devices.base import warning
from mqtt import mqtt_client
import pytest
import time
DUT_CLIENT_ID = "__%s__" % __name__
TOPIC = "__test__/%s" % __name__
#
MQTT_SIGNAL_TIME = 0.2
ALL_STATE_KEYS = ["output/1", "output/2", "output/3", "output/4", ]
BOOL_KEYS = ALL_STATE_KEYS
@pytest.fixture
def this_device():
mc = mqtt_client(DUT_CLIENT_ID, 'localhost')
return test_device(mc, TOPIC)
def test_initial_states(this_device: test_device):
# test all initial values
init_state(ALL_STATE_KEYS, this_device)
def test_state_change_by_mqtt(this_device: test_device):
def state_data(key):
return (True, False)
def mqtt_data(key):
return state_data(key)
# test state changes
tm_warning = state_change_by_mqtt(ALL_STATE_KEYS, 2, mqtt_test_client, TOPIC, this_device,
mqtt_data, state_data, None, MQTT_SIGNAL_TIME)
def test_specific_get_functions(this_device: test_device):
assert this_device.output_0 == this_device.get(this_device.KEY_OUTPUT_0)
assert this_device.output_1 == this_device.get(this_device.KEY_OUTPUT_1)
assert this_device.output_2 == this_device.get(this_device.KEY_OUTPUT_2)
assert this_device.output_3 == this_device.get(this_device.KEY_OUTPUT_3)

View File

@ -0,0 +1,250 @@
from module import mqtt_test_client, init_state, state_change_by_mqtt
from devices import shelly_sw1 as test_device
from devices.base import warning
from mqtt import mqtt_client
import pytest
import time
DUT_CLIENT_ID = "__%s__" % __name__
TOPIC = "__test__/%s" % __name__
#
MQTT_SIGNAL_TIME = 0.2
ALL_STATE_KEYS = ["relay/0", "relay/1", "input/0", "input/1", "longpush/0", "longpush/1", "temperature", "overtemperature"]
BOOL_KEYS = ["relay/0", "relay/1", "input/0", "input/1", "longpush/0", "longpush/1", "overtemperature"]
@pytest.fixture
def this_device():
mc = mqtt_client(DUT_CLIENT_ID, 'localhost')
return test_device(mc, TOPIC)
def test_initial_states(this_device: test_device):
# test all initial values
init_state(ALL_STATE_KEYS, this_device)
def test_state_change_by_mqtt(this_device: test_device):
def state_data(key):
if key in BOOL_KEYS:
return (True, False)
elif key == "temperature":
return (85.3, 20.1)
else:
raise IndexError("No return value defined for key %s" % key)
def mqtt_data(key):
if key in ["relay/0", "relay/1"]:
return ('on', 'off')
elif key in ["input/0", "input/1", "longpush/0", "longpush/1", "overtemperature"]:
return (1, 0)
else:
return state_data(key)
def warning_condition(state_topic, value):
return state_topic == "overtemperature" and value == 1
# test state changes
tm_warning = state_change_by_mqtt(ALL_STATE_KEYS, 2, mqtt_test_client, TOPIC, this_device,
mqtt_data, state_data, warning_condition, MQTT_SIGNAL_TIME)
# test warning
w: warning = this_device.get(this_device.KEY_WARNING)
assert w.get(w.KEY_ID) == TOPIC
assert w.get(w.KEY_TYPE) == w.TYPE_OVERTEMPERATURE
wt = time.mktime(w.get(w.KEY_TM))
wt_min = tm_warning
wt_max = tm_warning + 2
assert wt >= wt_min and wt <= wt_max
def test_specific_get_functions(this_device: test_device):
assert this_device.output_0 == this_device.get(this_device.KEY_OUTPUT_0)
assert this_device.output_1 == this_device.get(this_device.KEY_OUTPUT_1)
assert this_device.input_0 == this_device.get(this_device.KEY_INPUT_0)
assert this_device.input_1 == this_device.get(this_device.KEY_INPUT_1)
assert this_device.longpush_0 == this_device.get(this_device.KEY_LONGPUSH_0)
assert this_device.longpush_1 == this_device.get(this_device.KEY_LONGPUSH_1)
assert this_device.temperature == this_device.get(this_device.KEY_TEMPERATURE)
def test_send_command(this_device: test_device):
this_device.set_output_0(True)
this_device.set_output_0(False)
'''
class shelly(base):
""" Communication (MQTT)
shelly
+- relay
| +- 0 ["on" / "off"] <- status
| | +- command ["on"/ "off"] <- command
| | +- energy [numeric] <- status
| +- 1 ["on" / "off"] <- status
| +- command ["on"/ "off"] <- command
| +- energy [numeric] <- status
+- input
| +- 0 [0 / 1] <- status
| +- 1 [0 / 1] <- status
+- input_event
| +- 0 <- status
| +- 1 <- status
+- logpush
| +- 0 [0 / 1] <- status
| +- 1 [0 / 1] <- status
+- temperature [numeric] °C <- status
+- temperature_f [numeric] F <- status
+- overtemperature [0 / 1] <- status
+- id <- status
+- model <- status
+- mac <- status
+- ip <- status
+- new_fw <- status
+- fw_ver <- status
"""
KEY_OUTPUT_0 = "relay/0"
KEY_OUTPUT_1 = "relay/1"
KEY_INPUT_0 = "input/0"
KEY_INPUT_1 = "input/1"
KEY_LONGPUSH_0 = "longpush/0"
KEY_LONGPUSH_1 = "longpush/1"
KEY_TEMPERATURE = "temperature"
KEY_OVERTEMPERATURE = "overtemperature"
KEY_ID = "id"
KEY_MODEL = "model"
KEY_MAC = "mac"
KEY_IP = "ip"
KEY_NEW_FIRMWARE = "new_fw"
KEY_FIRMWARE_VERSION = "fw_ver"
#
TX_TOPIC = "command"
TX_TYPE = base.TX_VALUE
TX_FILTER_DATA_KEYS = [KEY_OUTPUT_0, KEY_OUTPUT_1]
#
RX_KEYS = [KEY_OUTPUT_0, KEY_OUTPUT_1, KEY_INPUT_0, KEY_INPUT_1, KEY_LONGPUSH_0, KEY_LONGPUSH_1, KEY_OVERTEMPERATURE, KEY_TEMPERATURE,
KEY_ID, KEY_MODEL, KEY_MAC, KEY_IP, KEY_NEW_FIRMWARE, KEY_FIRMWARE_VERSION]
RX_IGNORE_TOPICS = [KEY_OUTPUT_0 + '/' + "energy", KEY_OUTPUT_1 + '/' + "energy", 'input_event/0', 'input_event/1']
RX_IGNORE_KEYS = ['temperature_f']
RX_FILTER_DATA_KEYS = [KEY_INPUT_0, KEY_INPUT_1, KEY_LONGPUSH_0, KEY_LONGPUSH_1, KEY_OUTPUT_0, KEY_OUTPUT_1, KEY_OVERTEMPERATURE]
def __init__(self, mqtt_client, topic):
super().__init__(mqtt_client, topic)
#
self.output_key_delayed = None
self.delayed_flash_task = task.delayed(0.3, self.flash_task)
self.delayed_off_task = task.delayed(0.3, self.off_task)
#
self.add_callback(self.KEY_OVERTEMPERATURE, True, self.__warning__, True)
#
self.all_off_requested = False
def flash_task(self, *args):
if self.flash_active:
self.send_command(self.output_key_delayed, not self.get(self.output_key_delayed))
self.output_key_delayed = None
if self.all_off_requested:
self.delayed_off_task.run()
def off_task(self, *args):
self.all_off()
@property
def flash_active(self):
return self.output_key_delayed is not None
#
# WARNING CALL
#
def __warning__(self, client, key, data):
w = warning(self.topic, warning.TYPE_OVERTEMPERATURE, "Temperature to high (%.1f°C)", self.get(self.KEY_TEMPERATURE) or math.nan)
self.logger.warning(w)
self.set(self.KEY_WARNING, w)
#
# RX
#
@property
def output_0(self):
"""rv: [True, False]"""
return self.get(self.KEY_OUTPUT_0)
@property
def output_1(self):
"""rv: [True, False]"""
return self.get(self.KEY_OUTPUT_1)
@property
def input_0(self):
"""rv: [True, False]"""
return self.get(self.KEY_INPUT_0)
@property
def input_1(self):
"""rv: [True, False]"""
return self.get(self.KEY_INPUT_1)
@property
def longpush_0(self):
"""rv: [True, False]"""
return self.get(self.KEY_LONGPUSH_0)
@property
def longpush_1(self):
"""rv: [True, False]"""
return self.get(self.KEY_LONGPUSH_1)
@property
def temperature(self):
"""rv: numeric value"""
return self.get(self.KEY_TEMPERATURE)
#
# TX
#
def set_output_0(self, state):
"""state: [True, False]"""
self.send_command(self.KEY_OUTPUT_0, state)
def set_output_0_mcb(self, device, key, data):
self.logger.log(logging.INFO if data != self.output_0 else logging.DEBUG, "Changing output 0 to %s", str(data))
self.set_output_0(data)
def toggle_output_0_mcb(self, device, key, data):
self.logger.info("Toggeling output 0")
self.set_output_0(not self.output_0)
def set_output_1(self, state):
"""state: [True, False]"""
self.send_command(self.KEY_OUTPUT_1, state)
def set_output_1_mcb(self, device, key, data):
self.logger.log(logging.INFO if data != self.output_1 else logging.DEBUG, "Changing output 1 to %s", str(data))
self.set_output_1(data)
def toggle_output_1_mcb(self, device, key, data):
self.logger.info("Toggeling output 1")
self.set_output_1(not self.output_1)
def flash_0_mcb(self, device, key, data):
self.output_key_delayed = self.KEY_OUTPUT_0
self.toggle_output_0_mcb(device, key, data)
self.delayed_flash_task.run()
def flash_1_mcb(self, device, key, data):
self.output_key_delayed = self.KEY_OUTPUT_1
self.toggle_output_1_mcb(device, key, data)
self.delayed_flash_task.run()
def all_off(self):
if self.flash_active:
self.all_off_requested = True
else:
if self.output_0:
self.set_output_0(False)
if self.output_1:
self.set_output_1(False)
'''

View File

@ -0,0 +1,55 @@
from module import mqtt_test_client, init_state, state_change_by_mqtt
from devices import silvercrest_motion_sensor as test_device
from devices.base import warning
from mqtt import mqtt_client
import pytest
import time
DUT_CLIENT_ID = "__%s__" % __name__
TOPIC = "__test__/%s" % __name__
#
MQTT_SIGNAL_TIME = 0.2
ALL_STATE_KEYS = ["battery", "battery_low", "linkquality", "occupancy", "tamper", "voltage"]
BOOL_KEYS = ["battery_low", "occupancy", "tamper"]
@pytest.fixture
def this_device():
mc = mqtt_client(DUT_CLIENT_ID, 'localhost')
return test_device(mc, TOPIC)
def test_initial_states(this_device: test_device):
# test all initial values
init_state(ALL_STATE_KEYS, this_device)
def test_state_change_by_mqtt(this_device: test_device):
def state_data(key):
if key in BOOL_KEYS:
return (True, False)
elif key == "battery":
return (2, 87)
elif key == "linkquality":
return (1, 217)
elif key == "voltage":
return (1.17, 2.53)
else:
raise IndexError("No return value defined for key %s" % key)
def mqtt_data(key):
return state_data(key)
def warning_condition(state_topic, value):
return state_topic == "battery_low" and value is True
# test state changes
tm_warning = state_change_by_mqtt(ALL_STATE_KEYS, 2, mqtt_test_client, TOPIC, this_device,
mqtt_data, state_data, None, MQTT_SIGNAL_TIME)
def test_specific_get_functions(this_device: test_device):
assert this_device.linkquality == this_device.get(this_device.KEY_LINKQUALITY)
assert this_device.battery == this_device.get(this_device.KEY_BATTERY)

View File

@ -0,0 +1,49 @@
from module import mqtt_test_client, init_state, state_change_by_mqtt
from devices import silvercrest_powerplug as test_device
from devices.base import warning
from mqtt import mqtt_client
import pytest
import time
DUT_CLIENT_ID = "__%s__" % __name__
TOPIC = "__test__/%s" % __name__
#
MQTT_SIGNAL_TIME = 0.2
ALL_STATE_KEYS = ["state"]
BOOL_KEYS = ["state"]
@pytest.fixture
def this_device():
mc = mqtt_client(DUT_CLIENT_ID, 'localhost')
return test_device(mc, TOPIC)
def test_initial_states(this_device: test_device):
# test all initial values
init_state(ALL_STATE_KEYS, this_device)
def test_state_change_by_mqtt(this_device: test_device):
def state_data(key):
if key in BOOL_KEYS:
return (True, False)
else:
raise IndexError("No return value defined for key %s" % key)
def mqtt_data(key):
if key in BOOL_KEYS:
return ('ON', 'OFF')
else:
return state_data(key)
# test state changes
tm_warning = state_change_by_mqtt(ALL_STATE_KEYS, 2, mqtt_test_client, TOPIC, this_device,
mqtt_data, state_data, None, MQTT_SIGNAL_TIME)
def test_specific_get_functions(this_device: test_device):
assert this_device.linkquality == this_device.get(this_device.KEY_LINKQUALITY)
assert this_device.output_0 == this_device.get(this_device.KEY_OUTPUT_0)

View File

@ -0,0 +1,40 @@
from function.modules import heating_function as test_class
"""
config.DEFAULT_TEMPERATURE[heating_valve.topic],
db_data = get_radiator_data(heating_valve.topic)
**{
test_class.KEY_USER_TEMPERATURE_SETPOINT: db_data[2],
test_class.KEY_TEMPERATURE_SETPOINT: db_data[3],
test_class.KEY_AWAY_MODE: db_data[0],
test_class.KEY_SUMMER_MODE: db_data[1],
})
"""
def test_initial_states():
class heating_valve(object):
KEY_HEATING_SETPOINT = 'hsp'
KEY_TEMPERATURE = 'temp'
def set_heating_setpoint(self, value):
pass
def add_callback(self, key, value, callback):
pass
#
#
#
tc = test_class(
heating_valve(),
21, **{
test_class.KEY_USER_TEMPERATURE_SETPOINT: 22,
test_class.KEY_TEMPERATURE_SETPOINT: 17,
test_class.KEY_AWAY_MODE: True,
test_class.KEY_SUMMER_MODE: False,
})
assert tc.get(test_class.KEY_USER_TEMPERATURE_SETPOINT) == 22
assert tc.get(test_class.KEY_TEMPERATURE_SETPOINT) == 17
assert tc.get(test_class.KEY_AWAY_MODE) == True
assert tc.get(test_class.KEY_SUMMER_MODE) == False

View File

@ -0,0 +1,2 @@
pytest
pytest-cov

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

138
base.py Normal file
View File

@ -0,0 +1,138 @@
import json
import logging
import task
try:
from config import APP_NAME as ROOT_LOGGER_NAME
except ImportError:
ROOT_LOGGER_NAME = 'root'
class common_base(dict):
DEFAULT_VALUES = {}
def __init__(self, default_values=None):
super().__init__(default_values or self.DEFAULT_VALUES)
#
self.__callback_list__ = []
self.logger = logging.getLogger(ROOT_LOGGER_NAME).getChild("devices")
def add_callback(self, key, data, callback, on_change_only=False, init_now=False):
"""
key: key or None for all keys
data: data or None for all data
"""
cb_tup = (key, data, callback, on_change_only)
if cb_tup not in self.__callback_list__:
self.__callback_list__.append(cb_tup)
if init_now and self.get(key) is not None:
callback(self, key, self[key])
def set(self, key, data, block_callback=[]):
if key in self.keys():
value_changed = self[key] != data
self[key] = data
for cb_key, cb_data, callback, on_change_only in self.__callback_list__:
if cb_key is None or key == cb_key: # key fits to callback definition
if cb_data is None or cb_data == self[key]: # data fits to callback definition
if value_changed or not on_change_only: # change status fits to callback definition
if not callback in block_callback: # block given callbacks
callback(self, key, self[key])
else:
self.logger.warning("Unexpected key %s", key)
class mqtt_base(common_base):
def __init__(self, mqtt_client, topic, default_values=None):
super().__init__(default_values)
#
self.mqtt_client = mqtt_client
self.topic = topic
for entry in self.topic.split('/'):
self.logger = self.logger.getChild(entry)
class videv_base(mqtt_base):
KEY_INFO = '__info__'
#
SET_TOPIC = "set"
def __init__(self, mqtt_client, topic, default_values=None):
super().__init__(mqtt_client, topic, default_values=default_values)
self.__display_dict__ = {}
self.__control_dict__ = {}
self.__periodic__ = task.periodic(300, self.send_all)
self.__periodic__.run()
def send_all(self, rt):
try:
for key in self:
if self[key] is not None:
self.__tx__(key, self[key])
except RuntimeError:
self.logger.warning("Runtimeerror while sending cyclic videv information. This may happen on startup.")
def add_display(self, my_key, ext_device, ext_key, on_change_only=True):
"""
listen to data changes of ext_device and update videv information
"""
if my_key not in self.keys():
self[my_key] = None
if ext_device.__class__.__name__ == "group":
# store information to identify callback from ext_device
self.__display_dict__[(id(ext_device[0]), ext_key)] = my_key
# register a callback to listen for data from external device
ext_device[0].add_callback(ext_key, None, self.__rx_ext_device_data__, on_change_only, init_now=True)
else:
# store information to identify callback from ext_device
self.__display_dict__[(id(ext_device), ext_key)] = my_key
# register a callback to listen for data from external device
ext_device.add_callback(ext_key, None, self.__rx_ext_device_data__, on_change_only, init_now=True)
# send initial display data to videv interface
data = ext_device.get(ext_key)
if data is not None:
self.__tx__(my_key, data)
def __rx_ext_device_data__(self, ext_device, ext_key, data):
my_key = self.__display_dict__[(id(ext_device), ext_key)]
self.set(my_key, data)
self.__tx__(my_key, data)
def __tx__(self, key, data):
if type(data) not in (str, ):
data = json.dumps(data)
self.mqtt_client.send('/'.join([self.topic, key]), data)
def add_control(self, my_key, ext_device, ext_key, on_change_only=True):
"""
listen to videv information and pass data to ext_device
"""
if my_key not in self.keys():
self[my_key] = None
# store information to identify callback from videv
self.__control_dict__[my_key] = (ext_device, ext_key, on_change_only)
# add callback for videv changes
self.mqtt_client.add_callback('/'.join([self.topic, my_key, self.SET_TOPIC]), self.__rx_videv_data__)
def __rx_videv_data__(self, client, userdata, message):
my_key = message.topic.split('/')[-2]
try:
data = json.loads(message.payload)
except json.decoder.JSONDecodeError:
data = message.payload
ext_device, ext_key, on_change_only = self.__control_dict__[my_key]
if my_key in self.keys():
if data != self[my_key] or not on_change_only:
ext_device.send_command(ext_key, data)
else:
self.logger.info("Ignoring rx message with topic %s", message.topic)
def add_routing(self, my_key, ext_device, ext_key, on_change_only_disp=True, on_change_only_videv=True):
"""
listen to data changes of ext_device and update videv information
and
listen to videv information and pass data to ext_device
"""
# add display
self.add_display(my_key, ext_device, ext_key, on_change_only_disp)
self.add_control(my_key, ext_device, ext_key, on_change_only_videv)

View File

@ -1,32 +0,0 @@
import geo
import logging
DEBUG = False
#
# Logging
#
APP_NAME = "smart_brain"
LOG_HOSTNAME = "loggy" # When DEBUG is True
LOG_LEVEL = logging.INFO # STDOUT logging
#
# Application
#
GEO_POSITION = geo.gps.coordinate(lat=49.519167, lon=9.3672222)
MQTT_SERVER = "{{ smart_srv_brain_hostname }}"
MQTT_PORT = 1883
MQTT_USER = "{{ smart_srv_brain_username }}"
MQTT_PASSWORD = "{{ smart_srv_brain_password }}"
CHRISTMAS = True
#
# PARAMETERS
#
USER_ON_TIME_STAIRWAYS = 100
DEFAULT_TEMPERATURE = 21.5

26
config_example/config.py Normal file
View File

@ -0,0 +1,26 @@
import geo
import logging
import report
from topics import *
DEBUG = False # False: logging to stdout with given LOGLEVEL - True: logging all to localhost:19996 and warnings or higher to stdout
LOGLEVEL = logging.INFO
GEO_POSITION = geo.gps.coordinate(lat=49.519167, lon=9.3672222)
APP_NAME = "smart_brain"
MQTT_SERVER = "<hostname>"
MQTT_PORT = 1883
MQTT_USER = "<username>"
MQTT_PASSWORD = "<password>"
CHRISTMAS = True
#
# PARAMETERS
#
USER_ON_TIME_STAIRWAYS = 100
DEFAULT_TEMPERATURE = 21.5

2
devdi

@ -1 +1 @@
Subproject commit 5a5679b0baa9ba978f75d8581cb0dc7c13158e34 Subproject commit 2e66d0e27416739a8e5c1fa096490882f1b9643c

View File

@ -1,44 +1,48 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
import logging """
devices (DEVICES)
=================
from smart_devices.shelly import shelly as shelly_sw1 **Author:**
from smart_devices.shelly import shelly_rpc as shelly_pro3
from smart_devices.hue import hue_light as hue_sw_br_ct * Dirk Alders <sudo-dirk@mount-mockery.de>
from smart_devices.tradfri import tradfri_light as tradfri_sw
from smart_devices.tradfri import tradfri_light as tradfri_sw_br **Description:**
from smart_devices.tradfri import tradfri_light as tradfri_sw_br_ct
from smart_devices.tradfri import tradfri_button as tradfri_button This Module supports smarthome devices
from smart_devices.tradfri import tradfri_light as livarno_sw_br_ct
from smart_devices.brennenstuhl import brennenstuhl_heatingvalve **Submodules:**
from smart_devices.silvercrest import silvercrest_button
from smart_devices.silvercrest import silvercrest_powerplug * :mod:`shelly`
from smart_devices.silvercrest import silvercrest_motion_sensor * :mod:`silvercrest_powerplug`
from smart_devices.mydevices import powerplug as my_powerplug
from smart_devices.mydevices import audio_status **Unittest:**
from smart_devices.mydevices import remote
See also the :download:`unittest <devices/_testresults_/unittest.pdf>` documentation.
**Module Documentation:**
"""
from smart_devices.videv import videv_switching as videv_sw
from smart_devices.videv import videv_switch_brightness as videv_sw_br
from smart_devices.videv import videv_switch_brightness_color_temp as videv_sw_br_ct
from smart_devices.videv import videv_switching_timer as videv_sw_tm
from smart_devices.videv import videv_switching_motion as videv_sw_mo
from smart_devices.videv import videv_heating as videv_hea
from smart_devices.videv import videv_pure_switch
from smart_devices.videv import videv_multistate
from smart_devices.videv import videv_audio_player
from smart_devices.videv import videv_all_off
try: try:
from config import APP_NAME as ROOT_LOGGER_NAME from config import APP_NAME as ROOT_LOGGER_NAME
except ImportError: except ImportError:
ROOT_LOGGER_NAME = 'root' ROOT_LOGGER_NAME = 'root'
logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
from devices.shelly import shelly as shelly_sw1
def my_ambient(mqtt_client, topic): from devices.tradfri import tradfri_light as tradfri_sw
logger.warning("Device type my_ambient is not yet implemented. Topic %s will not be supported", topic) from devices.tradfri import tradfri_light as tradfri_sw_br
return None from devices.tradfri import tradfri_light as tradfri_sw_br_ct
from devices.tradfri import tradfri_button as tradfri_button
from devices.tradfri import tradfri_light as livarno_sw_br_ct
from devices.brennenstuhl import brennenstuhl_heatingvalve
from devices.silvercrest import silvercrest_powerplug
from devices.silvercrest import silvercrest_motion_sensor
from devices.mydevices import powerplug as my_powerplug
from devices.mydevices import audio_status
from devices.mydevices import remote
class group(object): class group(object):

103
devices/base.py Normal file
View File

@ -0,0 +1,103 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
from base import mqtt_base
from base import videv_base
import json
import time
def is_json(data):
try:
json.loads(data)
except json.decoder.JSONDecodeError:
return False
else:
return True
class base(mqtt_base):
TX_TOPIC = "set"
TX_VALUE = 0
TX_DICT = 1
TX_TYPE = -1
TX_FILTER_DATA_KEYS = []
#
RX_KEYS = []
RX_IGNORE_TOPICS = []
RX_IGNORE_KEYS = []
RX_FILTER_DATA_KEYS = []
def __init__(self, mqtt_client, topic):
super().__init__(mqtt_client, topic, default_values=dict.fromkeys(self.RX_KEYS))
# data storage
# initialisations
mqtt_client.add_callback(topic=self.topic, callback=self.receive_callback)
mqtt_client.add_callback(topic=self.topic+"/#", callback=self.receive_callback)
#
self.add_callback(None, None, self.__state_logging__, on_change_only=True)
def set(self, key, data, block_callback=[]):
if key in self.RX_IGNORE_KEYS:
pass # ignore these keys
elif key in self.RX_KEYS:
return super().set(key, data, block_callback)
else:
self.logger.warning("Unexpected key %s", key)
def receive_callback(self, client, userdata, message):
if message.topic != self.topic + '/' + videv_base.KEY_INFO:
content_key = message.topic[len(self.topic) + 1:]
if content_key not in self.RX_IGNORE_TOPICS and (not message.topic.endswith(self.TX_TOPIC) or len(self.TX_TOPIC) == 0):
self.logger.debug("Unpacking content_key \"%s\" from message.", content_key)
if is_json(message.payload):
data = json.loads(message.payload)
if type(data) is dict:
for key in data:
self.set(key, self.__device_to_instance_filter__(key, data[key]))
else:
self.set(content_key, self.__device_to_instance_filter__(content_key, data))
# String
else:
self.set(content_key, self.__device_to_instance_filter__(content_key, message.payload.decode('utf-8')))
else:
self.logger.debug("Ignoring topic %s", content_key)
def __device_to_instance_filter__(self, key, data):
if key in self.RX_FILTER_DATA_KEYS:
if data in [1, 'on', 'ON']:
return True
elif data in [0, 'off', 'OFF']:
return False
return data
def __instance_to_device_filter__(self, key, data):
if key in self.TX_FILTER_DATA_KEYS:
if data is True:
return "on"
elif data is False:
return "off"
return data
def send_command(self, key, data):
data = self.__instance_to_device_filter__(key, data)
if self.TX_TOPIC is not None:
if self.TX_TYPE < 0:
self.logger.error("Unknown tx type. Set TX_TYPE of class to a known value")
else:
self.logger.debug("Sending data for %s - %s", key, str(data))
if self.TX_TYPE == self.TX_DICT:
try:
self.mqtt_client.send('/'.join([self.topic, self.TX_TOPIC]), json.dumps({key: data}))
except TypeError:
print(self.topic)
print(key.__dict__)
print(key)
print(data)
raise TypeError
else:
if type(data) not in [str, bytes]:
data = json.dumps(data)
self.mqtt_client.send('/'.join([self.topic, key, self.TX_TOPIC] if len(self.TX_TOPIC) > 0 else [self.topic, key]), data)
else:
self.logger.error("Unknown tx toptic. Set TX_TOPIC of class to a known value")

114
devices/brennenstuhl.py Normal file
View File

@ -0,0 +1,114 @@
#!/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)

252
devices/mydevices.py Normal file
View File

@ -0,0 +1,252 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
from devices.base import base
import logging
class powerplug(base):
""" Communication (MQTT)
my_powerplug
+- output
+- 1 [True, False] <- status
| +- set [True, False, "toggle"] <- command
+- 2 [True, False] <- status
| +- set [True, False, "toggle"] <- command
+- 3 [True, False] <- status
| +- set [True, False, "toggle"] <- command
+- 4 [True, False] <- status
| +- set [True, False, "toggle"] <- command
+- all
+- set [True, False, "toggle"] <- command
"""
KEY_OUTPUT_0 = "output/1"
KEY_OUTPUT_1 = "output/2"
KEY_OUTPUT_2 = "output/3"
KEY_OUTPUT_3 = "output/4"
KEY_OUTPUT_ALL = "output/all"
KEY_OUTPUT_LIST = [KEY_OUTPUT_0, KEY_OUTPUT_1, KEY_OUTPUT_2, KEY_OUTPUT_3]
#
TX_TYPE = base.TX_VALUE
#
RX_KEYS = [KEY_OUTPUT_0, KEY_OUTPUT_1, KEY_OUTPUT_2, KEY_OUTPUT_3]
def __state_logging__(self, inst, key, data):
if key in self.KEY_OUTPUT_LIST:
self.logger.info("State change of '%s' to '%s'", key, repr(data))
#
# RX
#
@property
def output_0(self):
"""rv: [True, False]"""
return self.get(self.KEY_OUTPUT_0)
@property
def output_1(self):
"""rv: [True, False]"""
return self.get(self.KEY_OUTPUT_1)
@property
def output_2(self):
"""rv: [True, False]"""
return self.get(self.KEY_OUTPUT_2)
@property
def output_3(self):
"""rv: [True, False]"""
return self.get(self.KEY_OUTPUT_3)
#
# TX
#
def set_output(self, key, state):
if key in self.KEY_OUTPUT_LIST:
self.send_command(key, state)
else:
logging.error("Unknown key to set the output!")
def set_output_0(self, state):
"""state: [True, False]"""
self.send_command(self.KEY_OUTPUT_0, state)
def set_output_0_mcb(self, device, key, data):
self.set_output_0(data)
def toggle_output_0_mcb(self, device, key, data):
self.set_output_0(not self.output_0)
def set_output_1(self, state):
"""state: [True, False]"""
self.send_command(self.KEY_OUTPUT_1, state)
def set_output_1_mcb(self, device, key, data):
self.set_output_1(data)
def toggle_output_1_mcb(self, device, key, data):
self.set_output_1(not self.output_1)
def set_output_2(self, state):
"""state: [True, False]"""
self.send_command(self.KEY_OUTPUT_2, state)
def set_output_2_mcb(self, device, key, data):
self.set_output_2(data)
def toggle_output_2_mcb(self, device, key, data):
self.set_output_2(not self.output_2)
def set_output_3(self, state):
"""state: [True, False]"""
self.send_command(self.KEY_OUTPUT_3, state)
def set_output_3_mcb(self, device, key, data):
self.set_output_3(data)
def toggle_output_3_mcb(self, device, key, data):
self.set_output_3(not self.output_3)
def set_output_all(self, state):
"""state: [True, False, 'toggle']"""
self.send_command(self.KEY_OUTPUT_ALL, state)
def set_output_all_mcb(self, device, key, data):
self.set_output_all(data)
def all_off(self):
self.set_output_all(False)
class remote(base):
""" Communication (MQTT)
remote (RAS5) <- command
+- CD [dc]
+- LINE1 [dc]
+- LINE2 [dc]
+- LINE3 [dc]
+- MUTE [dc]
+- POWER [dc]
+- VOLDOWN [dc]
+- VOLUP [dc]
+- PHONO [dc]
+- DOCK [dc]
remote (EUR642100) <- command
+- OPEN_CLOSE [dc]
+- VOLDOWN [dc]
+- VOLUP [dc]
+- ONE [dc]
+- TWO [dc]
+- THREE [dc]
+- FOUR [dc]
+- FIVE [dc]
+- SIX [dc]
+- SEVEN [dc]
+- EIGHT [dc]
+- NINE [dc]
+- ZERO [dc]
+- TEN [dc]
+- TEN_PLUS [dc]
+- PROGRAM [dc]
+- CLEAR [dc]
+- RECALL [dc]
+- TIME_MODE [dc]
+- A_B_REPEAT [dc]
+- REPEAT [dc]
+- RANDOM [dc]
+- AUTO_CUE [dc]
+- TAPE_LENGTH [dc]
+- SIDE_A_B [dc]
+- TIME_FADE [dc]
+- PEAK_SEARCH [dc]
+- SEARCH_BACK [dc]
+- SEARCH_FOR [dc]
+- TRACK_NEXT [dc]
+- TRACK_PREV [dc]
+- STOP [dc]
+- PAUSE [dc]
+- PLAY [dc]
"""
KEY_CD = "CD"
KEY_LINE1 = "LINE1"
KEY_LINE3 = "LINE3"
KEY_MUTE = "MUTE"
KEY_POWER = "POWER"
KEY_VOLDOWN = "VOLDOWN"
KEY_VOLUP = "VOLUP"
#
TX_TOPIC = ''
TX_TYPE = base.TX_VALUE
#
RX_IGNORE_TOPICS = [KEY_CD, KEY_LINE1, KEY_LINE3, KEY_MUTE, KEY_POWER, KEY_VOLUP, KEY_VOLDOWN]
def __state_logging__(self, inst, key, data):
pass # This is just a TX device using self.set_*
def set_cd(self, device=None, key=None, data=None):
self.logger.info("Changing amplifier source to CD")
self.send_command(self.KEY_CD, None)
def set_line1(self, device=None, key=None, data=None):
self.logger.info("Changing amplifier source to LINE1")
self.send_command(self.KEY_LINE1, None)
def set_line3(self, device=None, key=None, data=None):
self.logger.info("Changing amplifier source to LINE3")
self.send_command(self.KEY_LINE3, None)
def set_mute(self, device=None, key=None, data=None):
self.logger.info("Muting / Unmuting amplifier")
self.send_command(self.KEY_MUTE, None)
def set_power(self, device=None, key=None, data=None):
self.logger.info("Power on/off amplifier")
self.send_command(self.KEY_POWER, None)
def set_volume_up(self, data=False):
"""data: [True, False]"""
self.logger.info("Increasing amplifier volume")
self.send_command(self.KEY_VOLUP, data)
def set_volume_down(self, data=False):
"""data: [True, False]"""
self.logger.info("Decreasing amplifier volume")
self.send_command(self.KEY_VOLDOWN, data)
def default_inc(self, device=None, key=None, data=None):
self.set_volume_up(True)
def default_dec(self, device=None, key=None, data=None):
self.set_volume_down(True)
def default_stop(self, device=None, key=None, data=None):
self.set_volume_up(False)
class audio_status(base):
""" Communication (MQTT)
audio_status
+- state [True, False] <- status
+- title [text] <- status
"""
KEY_STATE = "state"
KEY_TITLE = "title"
#
TX_TYPE = base.TX_VALUE
#
RX_KEYS = [KEY_STATE, KEY_TITLE]
def __state_logging__(self, inst, key, data):
if key in [self.KEY_STATE, self.KEY_TITLE]:
self.logger.info("State change of '%s' to '%s'", key, repr(data))
def set_state(self, num, data):
"""data: [True, False]"""
self.send_command(self.KEY_STATE + "/" + str(num), data)
def set_state_mcb(self, device, key, data):
self.set_state(data)

171
devices/shelly.py Normal file
View File

@ -0,0 +1,171 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
from devices.base import base
import logging
import task
class shelly(base):
""" Communication (MQTT)
shelly
+- relay
| +- 0 ["on" / "off"] <- status
| | +- command ["on"/ "off"] <- command
| | +- energy [numeric] <- status
| +- 1 ["on" / "off"] <- status
| +- command ["on"/ "off"] <- command
| +- energy [numeric] <- status
+- input
| +- 0 [0 / 1] <- status
| +- 1 [0 / 1] <- status
+- input_event
| +- 0 <- status
| +- 1 <- status
+- logpush
| +- 0 [0 / 1] <- status
| +- 1 [0 / 1] <- status
+- temperature [numeric] °C <- status
+- temperature_f [numeric] F <- status
+- overtemperature [0 / 1] <- status
+- id <- status
+- model <- status
+- mac <- status
+- ip <- status
+- new_fw <- status
+- fw_ver <- status
"""
KEY_OUTPUT_0 = "relay/0"
KEY_OUTPUT_1 = "relay/1"
KEY_INPUT_0 = "input/0"
KEY_INPUT_1 = "input/1"
KEY_LONGPUSH_0 = "longpush/0"
KEY_LONGPUSH_1 = "longpush/1"
KEY_TEMPERATURE = "temperature"
KEY_OVERTEMPERATURE = "overtemperature"
KEY_ID = "id"
KEY_MODEL = "model"
KEY_MAC = "mac"
KEY_IP = "ip"
KEY_NEW_FIRMWARE = "new_fw"
KEY_FIRMWARE_VERSION = "fw_ver"
#
TX_TOPIC = "command"
TX_TYPE = base.TX_VALUE
TX_FILTER_DATA_KEYS = [KEY_OUTPUT_0, KEY_OUTPUT_1]
#
RX_KEYS = [KEY_OUTPUT_0, KEY_OUTPUT_1, KEY_INPUT_0, KEY_INPUT_1, KEY_LONGPUSH_0, KEY_LONGPUSH_1, KEY_OVERTEMPERATURE, KEY_TEMPERATURE,
KEY_ID, KEY_MODEL, KEY_MAC, KEY_IP, KEY_NEW_FIRMWARE, KEY_FIRMWARE_VERSION]
RX_IGNORE_TOPICS = [KEY_OUTPUT_0 + '/' + "energy", KEY_OUTPUT_1 + '/' + "energy", 'input_event/0', 'input_event/1']
RX_IGNORE_KEYS = ['temperature_f']
RX_FILTER_DATA_KEYS = [KEY_INPUT_0, KEY_INPUT_1, KEY_LONGPUSH_0, KEY_LONGPUSH_1, KEY_OUTPUT_0, KEY_OUTPUT_1, KEY_OVERTEMPERATURE]
def __init__(self, mqtt_client, topic):
super().__init__(mqtt_client, topic)
#
self.output_key_delayed = None
self.delayed_flash_task = task.delayed(0.3, self.flash_task)
self.delayed_off_task = task.delayed(0.3, self.off_task)
#
self.all_off_requested = False
def __state_logging__(self, inst, key, data):
if key in [self.KEY_OUTPUT_0, self.KEY_OUTPUT_1]:
self.logger.info("State change of '%s' to '%s'", key, repr(data))
elif key in [self.KEY_INPUT_0, self.KEY_INPUT_1, self.KEY_LONGPUSH_0, self.KEY_LONGPUSH_1]:
self.logger.info("Input action '%s' with '%s'", key, repr(data))
def flash_task(self, *args):
if self.flash_active:
self.send_command(self.output_key_delayed, not self.get(self.output_key_delayed))
self.output_key_delayed = None
if self.all_off_requested:
self.delayed_off_task.run()
def off_task(self, *args):
self.all_off()
@property
def flash_active(self):
return self.output_key_delayed is not None
#
# RX
#
@property
def output_0(self):
"""rv: [True, False]"""
return self.get(self.KEY_OUTPUT_0)
@property
def output_1(self):
"""rv: [True, False]"""
return self.get(self.KEY_OUTPUT_1)
@property
def input_0(self):
"""rv: [True, False]"""
return self.get(self.KEY_INPUT_0)
@property
def input_1(self):
"""rv: [True, False]"""
return self.get(self.KEY_INPUT_1)
@property
def longpush_0(self):
"""rv: [True, False]"""
return self.get(self.KEY_LONGPUSH_0)
@property
def longpush_1(self):
"""rv: [True, False]"""
return self.get(self.KEY_LONGPUSH_1)
@property
def temperature(self):
"""rv: numeric value"""
return self.get(self.KEY_TEMPERATURE)
#
# TX
#
def set_output_0(self, state):
"""state: [True, False]"""
self.send_command(self.KEY_OUTPUT_0, state)
def set_output_0_mcb(self, device, key, data):
self.set_output_0(data)
def toggle_output_0_mcb(self, device, key, data):
self.set_output_0(not self.output_0)
def set_output_1(self, state):
"""state: [True, False]"""
self.send_command(self.KEY_OUTPUT_1, state)
def set_output_1_mcb(self, device, key, data):
self.set_output_1(data)
def toggle_output_1_mcb(self, device, key, data):
self.set_output_1(not self.output_1)
def flash_0_mcb(self, device, key, data):
self.output_key_delayed = self.KEY_OUTPUT_0
self.toggle_output_0_mcb(device, key, data)
self.delayed_flash_task.run()
def flash_1_mcb(self, device, key, data):
self.output_key_delayed = self.KEY_OUTPUT_1
self.toggle_output_1_mcb(device, key, data)
self.delayed_flash_task.run()
def all_off(self):
if self.flash_active:
self.all_off_requested = True
else:
if self.output_0:
self.set_output_0(False)
if self.output_1:
self.set_output_1(False)

107
devices/silvercrest.py Normal file
View File

@ -0,0 +1,107 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
from devices.base import base
import logging
class silvercrest_powerplug(base):
""" Communication (MQTT)
silvercrest_powerplug {
| "state": ["ON" / "OFF"]
| "linkquality": [0...255] lqi
| }
+- get {
| "state": ""
| }
+- set {
"state": ["ON" / "OFF"]
}
"""
KEY_LINKQUALITY = "linkquality"
KEY_OUTPUT_0 = "state"
#
TX_TYPE = base.TX_DICT
TX_FILTER_DATA_KEYS = [KEY_OUTPUT_0]
#
RX_KEYS = [KEY_LINKQUALITY, KEY_OUTPUT_0]
RX_FILTER_DATA_KEYS = [KEY_OUTPUT_0]
def __state_logging__(self, inst, key, data):
if key in [self.KEY_OUTPUT_0]:
self.logger.info("State change of '%s' to '%s'", key, repr(data))
#
# RX
#
@property
def output_0(self):
"""rv: [True, False]"""
return self.get(self.KEY_OUTPUT_0)
@property
def linkquality(self):
"""rv: numeric value"""
return self.get(self.KEY_LINKQUALITY)
#
# TX
#
def set_output_0(self, state):
"""state: [True, False]"""
self.send_command(self.KEY_OUTPUT_0, state)
def set_output_0_mcb(self, device, key, data):
self.set_output_0(data)
def toggle_output_0_mcb(self, device, key, data):
self.set_output_0(not self.output_0)
def all_off(self):
if self.output_0:
self.set_output_0(False)
class silvercrest_motion_sensor(base):
""" Communication (MQTT)
silvercrest_motion_sensor {
battery: [0...100] %
battery_low: [True, False]
linkquality: [0...255] lqi
occupancy: [True, False]
tamper: [True, False]
voltage: [0...] mV
}
"""
KEY_BATTERY = "battery"
KEY_BATTERY_LOW = "battery_low"
KEY_LINKQUALITY = "linkquality"
KEY_OCCUPANCY = "occupancy"
KEY_UNMOUNTED = "tamper"
KEY_VOLTAGE = "voltage"
#
TX_TYPE = base.TX_DICT
#
RX_KEYS = [KEY_BATTERY, KEY_BATTERY_LOW, KEY_LINKQUALITY, KEY_OCCUPANCY, KEY_UNMOUNTED, KEY_VOLTAGE]
def __init__(self, mqtt_client, topic):
super().__init__(mqtt_client, topic)
def __state_logging__(self, inst, key, data):
if key in [self.KEY_OCCUPANCY, self.KEY_UNMOUNTED]:
self.logger.info("State change of '%s' to '%s'", key, repr(data))
#
# RX
#
@property
def linkquality(self):
"""rv: numeric value"""
return self.get(self.KEY_LINKQUALITY)
@property
def battery(self):
"""rv: numeric value"""
return self.get(self.KEY_BATTERY)

192
devices/tradfri.py Normal file
View File

@ -0,0 +1,192 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
from devices.base import base
import logging
class tradfri_light(base):
""" Communication (MQTT)
tradfri_light {
| "state": ["ON" / "OFF" / "TOGGLE"]
| "linkquality": [0...255] lqi
| "brightness": [0...254]
| "color_mode": ["color_temp"]
| "color_temp": ["coolest", "cool", "neutral", "warm", "warmest", 250...454]
| "color_temp_startup": ["coolest", "cool", "neutral", "warm", "warmest", "previous", 250...454]
| "update": []
| }
+- get {
| "state": ""
| }
+- set {
"state": ["ON" / "OFF"]
"brightness": [0...256]
"color_temp": [250...454]
"transition": [0...] seconds
"brightness_move": [-X...0...X] X/s
"brightness_step": [-X...0...X]
"color_temp_move": [-X...0...X] X/s
"color_temp_step": [-X...0...X]
}
"""
KEY_LINKQUALITY = "linkquality"
KEY_OUTPUT_0 = "state"
KEY_BRIGHTNESS = "brightness"
KEY_COLOR_TEMP = "color_temp"
KEY_BRIGHTNESS_FADE = "brightness_move"
#
TX_TYPE = base.TX_DICT
TX_FILTER_DATA_KEYS = [KEY_OUTPUT_0, KEY_BRIGHTNESS, KEY_COLOR_TEMP, KEY_BRIGHTNESS_FADE]
#
RX_KEYS = [KEY_LINKQUALITY, KEY_OUTPUT_0, KEY_BRIGHTNESS, KEY_COLOR_TEMP]
RX_IGNORE_KEYS = ['update', 'color_mode', 'color_temp_startup']
RX_FILTER_DATA_KEYS = [KEY_OUTPUT_0, KEY_BRIGHTNESS, KEY_COLOR_TEMP]
def __state_logging__(self, inst, key, data):
if key in [self.KEY_OUTPUT_0, self.KEY_BRIGHTNESS, self.KEY_COLOR_TEMP, self.KEY_BRIGHTNESS_FADE]:
self.logger.info("State change of '%s' to '%s'", key, repr(data))
def __device_to_instance_filter__(self, key, data):
if key == self.KEY_BRIGHTNESS:
return int(round((data - 1) * 100 / 253, 0))
elif key == self.KEY_COLOR_TEMP:
return int(round((data - 250) * 10 / 204, 0))
return super().__device_to_instance_filter__(key, data)
def __instance_to_device_filter__(self, key, data):
if key == self.KEY_BRIGHTNESS:
return int(round(data * 253 / 100 + 1, 0))
elif key == self.KEY_COLOR_TEMP:
return int(round(data * 204 / 10 + 250, 0))
return super().__instance_to_device_filter__(key, data)
#
# RX
#
@property
def output_0(self):
"""rv: [True, False]"""
return self.get(self.KEY_OUTPUT_0, False)
@property
def linkquality(self):
"""rv: numeric value"""
return self.get(self.KEY_LINKQUALITY, 0)
@property
def brightness(self):
"""rv: numeric value [0%, ..., 100%]"""
return self.get(self.KEY_BRIGHTNESS, 0)
@property
def color_temp(self):
"""rv: numeric value [0, ..., 10]"""
return self.get(self.KEY_COLOR_TEMP, 0)
#
# TX
#
def request_data(self, device=None, key=None, data=None):
self.mqtt_client.send(self.topic + "/get", '{"%s": ""}' % self.KEY_OUTPUT_0)
def set_output_0(self, state):
"""state: [True, False]"""
self.send_command(self.KEY_OUTPUT_0, state)
def set_output_0_mcb(self, device, key, data):
self.set_output_0(data)
def toggle_output_0_mcb(self, device, key, data):
self.set_output_0(not self.output_0)
def set_brightness(self, brightness):
"""brightness: [0, ..., 100]"""
self.send_command(self.KEY_BRIGHTNESS, brightness)
def set_brightness_mcb(self, device, key, data):
self.set_brightness(data)
def default_inc(self, speed=40):
self.send_command(self.KEY_BRIGHTNESS_FADE, speed)
def default_dec(self, speed=-40):
self.default_inc(speed)
def default_stop(self):
self.default_inc(0)
def set_color_temp(self, color_temp):
"""color_temp: [0, ..., 10]"""
self.send_command(self.KEY_COLOR_TEMP, color_temp)
def set_color_temp_mcb(self, device, key, data):
self.set_color_temp(data)
def all_off(self):
if self.output_0:
self.set_output_0(False)
class tradfri_button(base):
""" Communication (MQTT)
tradfri_button {
"action": [
"arrow_left_click",
"arrow_left_hold",
"arrow_left_release",
"arrow_right_click",
"arrow_right_hold",
"arrow_right_release",
"brightness_down_click",
"brightness_down_hold",
"brightness_down_release",
"brightness_up_click",
"brightness_up_hold",
"brightness_up_release",
"toggle"
]
"action_duration": [0...] s
"battery": [0...100] %
"linkquality": [0...255] lqi
"update": []
}
"""
ACTION_TOGGLE = "toggle"
ACTION_BRIGHTNESS_UP = "brightness_up_click"
ACTION_BRIGHTNESS_DOWN = "brightness_down_click"
ACTION_RIGHT = "arrow_right_click"
ACTION_LEFT = "arrow_left_click"
ACTION_BRIGHTNESS_UP_LONG = "brightness_up_hold"
ACTION_BRIGHTNESS_UP_RELEASE = "brightness_up_release"
ACTION_BRIGHTNESS_DOWN_LONG = "brightness_down_hold"
ACTION_BRIGHTNESS_DOWN_RELEASE = "brightness_down_release"
ACTION_RIGHT_LONG = "arrow_right_hold"
ACTION_RIGHT_RELEASE = "arrow_right_release"
ACTION_LEFT_LONG = "arrow_left_hold"
ACTION_LEFT_RELEASE = "arrow_left_release"
#
KEY_LINKQUALITY = "linkquality"
KEY_BATTERY = "battery"
KEY_ACTION = "action"
KEY_ACTION_DURATION = "action_duration"
#
RX_KEYS = [KEY_LINKQUALITY, KEY_BATTERY, KEY_ACTION]
RX_IGNORE_KEYS = ['update', KEY_ACTION_DURATION]
def __init__(self, mqtt_client, topic):
super().__init__(mqtt_client, topic)
def __state_logging__(self, inst, key, data):
if key in [self.KEY_ACTION]:
self.logger.info("Input '%s' with '%s'", key, repr(data))
#
# RX
#
@property
def action(self):
"""rv: action_txt"""
return self.get(self.KEY_ACTION)

View File

@ -2,17 +2,15 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
import config import config
from devdi import rooms as devdi_rooms import devices
from devdi.topic import STOP_EXECUTION_TOPIC
from function.garden import garden from function.garden import garden
from function.stairway import stairway from function.stairway import stairway
from function.ground_floor_west import ground_floor_west from function.ground_floor_west import ground_floor_west
from function.first_floor_west import first_floor_west from function.first_floor_west import first_floor_west
from function.first_floor_east import first_floor_east from function.first_floor_east import first_floor_east
from function.rooms import room_collection from function.rooms import room_collection
import json from function.videv import all_off
import logging import logging
import mqtt
try: try:
from config import APP_NAME as ROOT_LOGGER_NAME from config import APP_NAME as ROOT_LOGGER_NAME
@ -21,27 +19,22 @@ except ImportError:
logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
class all_functions(room_collection, devdi_rooms.collection): class all_functions(room_collection):
def __init__(self, mqtt_client: mqtt.mqtt_client): def __init__(self, mqtt_client, pd, vd):
super().__init__(mqtt_client) super().__init__(mqtt_client, pd, vd)
devdi_rooms.collection.__init__(self, mqtt_client)
#
self.run = True
if config.DEBUG:
mqtt_client.add_callback(STOP_EXECUTION_TOPIC, self.__stop_execution__)
# #
# Rooms # Rooms
# #
# garden # garden
self.gar = garden(self.mqtt_client) self.gar = garden(self.mqtt_client, pd, vd)
# stairway # stairway
self.stw = stairway(self.mqtt_client) self.stw = stairway(self.mqtt_client, pd, vd)
# ground floor west # ground floor west
self.gfw = ground_floor_west(self.mqtt_client) self.gfw = ground_floor_west(self.mqtt_client, pd, vd)
# first floor west # first floor west
self.ffw = first_floor_west(self.mqtt_client) self.ffw = first_floor_west(self.mqtt_client, pd, vd)
# first floor east # first floor east
self.ffe = first_floor_east(self.mqtt_client) self.ffe = first_floor_east(self.mqtt_client, pd, vd)
# #
# Interactions # Interactions
# #
@ -49,57 +42,35 @@ class all_functions(room_collection, devdi_rooms.collection):
self.init_cross_room_interactions() self.init_cross_room_interactions()
# Off Buttons # Off Buttons
self.init_off_functionality() self.init_off_functionality()
# Summer / Winter mode
self.init_sumer_winter_mode()
def __stop_execution__(self, client, userdata, message):
if config.DEBUG:
try:
data = json.loads(message.payload)
except:
logger.error("Error while receiving mqtt message: topic=%s - payload=%s", repr(message.topic), repr(message.payload))
else:
if data is True:
self.run = False
def init_cross_room_interactions(self): def init_cross_room_interactions(self):
# shelly dirk input 1 # shelly dirk input 1
self.last_gfw_dirk_input_1 = None self.last_gfw_dirk_input_1 = None
self.gfw.dirk.switch_main_light.add_callback(self.gfw.dirk.switch_main_light.KEY_INPUT_1, None, self.gfw_dirk_input_1) self.gfw.dirk.main_light_shelly.add_callback(self.gfw.dirk.main_light_shelly.KEY_INPUT_1, None, self.gfw_dirk_input_1)
# tradfri button ffe_sleep right click # tradfri button ffe_sleep right click
self.ffe.sleep.input_device.add_callback(self.ffe.sleep.input_device.KEY_ACTION, self.ffe.sleep.button_tradfri.add_callback(self.ffe.sleep.button_tradfri.KEY_ACTION,
self.ffe.sleep.input_device.ACTION_RIGHT, self.ffe.floor.switch_main_light.toggle_output_0_mcb) self.ffe.sleep.button_tradfri.ACTION_RIGHT,
self.ffe.floor.main_light_shelly.toggle_output_0_mcb)
def init_off_functionality(self): def init_off_functionality(self):
# ALL OFF - Virtual device # ALL OFF - Virtual device
self.videv_all_off.connect_room_collection(self) self.videv_all_off = all_off(self.mqtt_client, config.TOPIC_ALL_OFF_VIDEV, self)
# ALL OFF - Long push stairway # ALL OFF - Long push stairway
self.stw.stairway.switch_main_light.add_callback(self.stw.stairway.switch_main_light.KEY_LONGPUSH_0, self.stw.stairway.main_light_shelly.add_callback(self.stw.stairway.main_light_shelly.KEY_LONGPUSH_0,
True, self.stw.stairway.switch_main_light.flash_0_mcb) True, self.stw.stairway.main_light_shelly.flash_0_mcb)
self.stw.stairway.switch_main_light.add_callback(self.stw.stairway.switch_main_light.KEY_LONGPUSH_0, True, self.all_off) self.stw.stairway.main_light_shelly.add_callback(self.stw.stairway.main_light_shelly.KEY_LONGPUSH_0, True, self.all_off)
# FFE ALL OFF - Long push ffe_floor # FFE ALL OFF - Long push ffe_floor
self.ffe.floor.switch_main_light.add_callback(self.ffe.floor.switch_main_light.KEY_LONGPUSH_0, self.ffe.floor.main_light_shelly.add_callback(self.ffe.floor.main_light_shelly.KEY_LONGPUSH_0,
True, self.ffe.floor.switch_main_light.flash_0_mcb) True, self.ffe.floor.main_light_shelly.flash_0_mcb)
self.ffe.floor.switch_main_light.add_callback(self.ffe.floor.switch_main_light.KEY_LONGPUSH_0, True, self.ffe.all_off) self.ffe.floor.main_light_shelly.add_callback(self.ffe.floor.main_light_shelly.KEY_LONGPUSH_0, True, self.ffe.all_off)
# FFE ALL OFF - Long push input device # FFE ALL OFF - Long push input device
self.ffe.sleep.input_device.add_callback(self.ffe.sleep.input_device.KEY_ACTION, self.ffe.sleep.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_RIGHT_LONG, self.ffe.all_off)
self.ffe.sleep.input_device.ACTION_RIGHT_LONG, self.ffe.all_off)
# FFW ALL OFF - Long push ffw_floor
self.ffw.floor.switch_main_light.add_callback(self.ffw.floor.switch_main_light.KEY_LONGPUSH_0,
True, self.ffw.floor.switch_main_light.flash_0_mcb)
self.ffw.floor.switch_main_light.add_callback(self.ffw.floor.switch_main_light.KEY_LONGPUSH_0, True, self.ffw.all_off)
def init_sumer_winter_mode(self):
self.videv_summer_mode.add_callback(self.videv_summer_mode.KEY_STATE, None, self.gfw.summer_mode)
self.videv_summer_mode.add_callback(self.videv_summer_mode.KEY_STATE, None, self.ffw.summer_mode)
self.videv_summer_mode.add_callback(self.videv_summer_mode.KEY_STATE, None, self.ffe.summer_mode)
def gfw_dirk_input_1(self, device, key, data): def gfw_dirk_input_1(self, device, key, data):
if self.last_gfw_dirk_input_1 is not None: if self.last_gfw_dirk_input_1 is not None:
if self.last_gfw_dirk_input_1 != data: if self.last_gfw_dirk_input_1 != data:
self.gfw.floor.switch_main_light.toggle_output_0_mcb(device, key, data) self.gfw.floor.main_light_shelly.toggle_output_0_mcb(device, key, data)
self.last_gfw_dirk_input_1 = data self.last_gfw_dirk_input_1 = data

View File

@ -3,11 +3,13 @@
# #
import config import config
from devdi import rooms from devdi import topic as props
from devices import group
from function.db import get_radiator_data, set_radiator_data from function.db import get_radiator_data, set_radiator_data
from function.helpers import day_event from function.helpers import day_event
from function.modules import brightness_choose_n_action, timer_on_activation, heating_function, switched_light from function.modules import brightness_choose_n_action, timer_on_activation, heating_function
from function.rooms import room, room_collection from function.rooms import room, room_collection
from function.videv import videv_switching, videv_switch_brightness, videv_switching_timer, videv_switch_brightness_color_temp, videv_heating, videv_multistate
import logging import logging
try: try:
@ -16,158 +18,297 @@ except ImportError:
ROOT_LOGGER_NAME = 'root' ROOT_LOGGER_NAME = 'root'
logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
loc = props.LOC_FFE
class first_floor_east(room_collection): class first_floor_east(room_collection):
def __init__(self, mqtt_client,): def __init__(self, mqtt_client, pd, vd):
super().__init__(mqtt_client) super().__init__(mqtt_client, pd, vd)
self.dining = first_floor_east_dining(mqtt_client) self.dining = first_floor_east_dining(mqtt_client, pd, vd)
self.floor = first_floor_east_floor(mqtt_client) self.floor = first_floor_east_floor(mqtt_client, pd, vd)
self.kitchen = first_floor_east_kitchen(mqtt_client) self.kitchen = first_floor_east_kitchen(mqtt_client, pd, vd)
self.livingroom = first_floor_east_living(mqtt_client) self.livingroom = first_floor_east_living(mqtt_client, pd, vd)
self.sleep = first_floor_east_sleep(mqtt_client) self.sleep = first_floor_east_sleep(mqtt_client, pd, vd)
class first_floor_east_floor(rooms.ffe_floor, room): class first_floor_east_floor(room):
def __init__(self, mqtt_client): def __init__(self, mqtt_client, pd, vd):
super().__init__(mqtt_client) roo = props.ROO_FLO
room.__init__(self, mqtt_client)
# #
# connect videv and switch # Device initialisation
self.videv_main_light.connect_sw_device(self.switch_main_light, self.switch_main_light.KEY_OUTPUT_0)
class first_floor_east_kitchen(rooms.ffe_kitchen, room):
def __init__(self, mqtt_client):
super().__init__(mqtt_client)
room.__init__(self, mqtt_client)
# #
# light <-> videv # http://shelly1l-3C6105E4E629
self.videv_main_light.connect_sw_device(self.switch_main_light, self.switch_main_light.KEY_OUTPUT_0) # main light
self.videv_main_light.connect_br_device(self.light_main_light, self.light_main_light.KEY_BRIGHTNESS) self.main_light_shelly = pd.get(props.STG_SHE, loc, roo, props.FUN_MAL)
self.videv_main_light.connect_ct_device(self.light_main_light, self.light_main_light.KEY_COLOR_TEMP) super().__init__(mqtt_client, pd, vd)
# Request hue data of lead light after power on #
switched_light(self.switch_main_light, self.switch_main_light.KEY_OUTPUT_0, self.light_main_light) # Virtual Device Interface
#
# main light
self.main_light = videv_switching(
mqtt_client, config.TOPIC_FFE_FLOOR_MAIN_LIGHT_VIDEV,
self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0
)
class first_floor_east_kitchen(room):
def __init__(self, mqtt_client, pd, vd):
roo = props.ROO_KIT
#
# Device initialisation
#
# http://shelly1l-8CAAB5616C01
# main light
self.main_light_shelly = pd.get(props.STG_SHE, loc, roo, props.FUN_MAL)
# http://shelly1-e89f6d85a466
# circulation pump # circulation pump
self.circulation_pump = timer_on_activation(self.switch_circulation_pump, self.switch_circulation_pump.KEY_OUTPUT_0, 10*60) self.circulation_pump_shelly = pd.get(props.STG_SHE, loc, roo, props.FUN_CIR)
self.switch_circulation_pump.add_callback(self.switch_circulation_pump.KEY_OUTPUT_0, True, self.switch_main_light.flash_0_mcb, True) # heating function
self.videv_circulation_pump.connect_sw_device(self.switch_circulation_pump, self.switch_circulation_pump.KEY_OUTPUT_0) self.heating_valve = pd.get(props.STG_ZFE, loc, roo, props.FUN_HEA)
self.videv_circulation_pump.connect_tm_device(self.circulation_pump, timer_on_activation.KEY_TIMER)
super().__init__(mqtt_client, pd, vd)
#
# Functionality initialisation
#
# circulation pump
self.circulation_pump = timer_on_activation(self.circulation_pump_shelly, self.circulation_pump_shelly.KEY_OUTPUT_0, 10*60)
self.circulation_pump_shelly.add_callback(self.circulation_pump_shelly.KEY_OUTPUT_0, True, self.main_light_shelly.flash_0_mcb, True)
# heating function # heating function
self.heating_function = heating_function( self.heating_function = heating_function(
self.valve_heating, self.heating_valve,
config.DEFAULT_TEMPERATURE, config.DEFAULT_TEMPERATURE,
**get_radiator_data(self.valve_heating.topic) **get_radiator_data(self.heating_valve.topic)
) )
self.heating_function.add_callback(None, None, set_radiator_data, True) self.heating_function.add_callback(None, None, set_radiator_data, True)
self.videv_heating.connect_heating_function(self.heating_function)
#
# Virtual Device Interface
#
# main light
self.main_light_videv = videv_switching(
mqtt_client, config.TOPIC_FFE_KITCHEN_MAIN_LIGHT_VIDEV,
self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0
)
# circulation pump
self.circulation_pump_videv = videv_switching_timer(
mqtt_client, config.TOPIC_FFE_KITCHEN_CIRCULATION_PUMP_VIDEV,
self.circulation_pump_shelly, self.circulation_pump_shelly.KEY_OUTPUT_0,
self.circulation_pump, timer_on_activation.KEY_TIMER
)
# heating function
self.heating_function_videv = videv_heating(
mqtt_client, config.TOPIC_FFE_KITCHEN_HEATING_VALVE_VIDEV,
self.heating_function
)
class first_floor_east_dining(rooms.ffe_diningroom, room): class first_floor_east_dining(room):
def __init__(self, mqtt_client): def __init__(self, mqtt_client, pd, vd):
super().__init__(mqtt_client) roo = props.ROO_DIN
room.__init__(self, mqtt_client) #
# Device initialisation
# #
self.day_events = day_event((6, 0), (22, 0), 30, -30) self.day_events = day_event((6, 0), (22, 0), 30, -30)
# http://shelly1l-84CCA8ADD055
# main light
self.main_light_shelly = pd.get(props.STG_SHE, loc, roo, props.FUN_MAL)
# floor lamp
self.floorlamp_powerplug = pd.get(props.STG_ZFE, loc, roo, props.FUN_FLL)
# heating function
self.heating_valve = pd.get(props.STG_ZFE, loc, roo, props.FUN_HEA)
# garland
if config.CHRISTMAS:
self.garland_powerplug = pd.get(props.STG_ZFE, loc, roo, props.FUN_GAR)
super().__init__(mqtt_client, pd, vd)
#
# Functionality initialisation
#
self.day_events.add_callback(None, True, self.__day_events__, True) self.day_events.add_callback(None, True, self.__day_events__, True)
# light <-> videv # main light
self.videv_main_light.connect_sw_device(self.switch_main_light, self.switch_main_light.KEY_OUTPUT_0) self.main_light_shelly.add_callback(self.main_light_shelly.KEY_OUTPUT_0, None, self.floorlamp_powerplug.set_output_0_mcb, True)
self.videv_floor_light.connect_sw_device(self.switch_floor_light, self.switch_floor_light.KEY_OUTPUT_0)
if config.CHRISTMAS:
self.videv_garland_light.connect_sw_device(self.switch_garland_light, self.switch_garland_light.KEY_OUTPUT_0)
# main light -> floor_light
self.switch_main_light.add_callback(self.switch_main_light.KEY_OUTPUT_0, None, self.switch_floor_light.set_output_0_mcb, True)
# heating function # heating function
self.heating_function = heating_function( self.heating_function = heating_function(
self.valve_heating, self.heating_valve,
config.DEFAULT_TEMPERATURE, config.DEFAULT_TEMPERATURE,
**get_radiator_data(self.valve_heating.topic) **get_radiator_data(self.heating_valve.topic)
) )
self.heating_function.add_callback(None, None, set_radiator_data, True) self.heating_function.add_callback(None, None, set_radiator_data, True)
# heating function <-> videv
self.videv_heating.connect_heating_function(self.heating_function) #
# Virtual Device Interface
#
# main light
self.main_light_videv = videv_switching(
mqtt_client, config.TOPIC_FFE_DININGROOM_MAIN_LIGHT_VIDEV,
self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0
)
# floor lamp
self.floorlamp_videv = videv_switching(
mqtt_client, config.TOPIC_FFE_DININGROOM_FLOOR_LAMP_VIDEV,
self.floorlamp_powerplug, self.floorlamp_powerplug.KEY_OUTPUT_0
)
# heating function
self.heating_function_videv = videv_heating(
mqtt_client, config.TOPIC_FFE_DININGROOM_HEATING_VALVE_VIDEV,
self.heating_function
)
# garland
if config.CHRISTMAS:
self.garland_videv = videv_switching(
mqtt_client, config.TOPIC_FFE_DININGROOM_GARLAND_VIDEV,
self.garland_powerplug, self.garland_powerplug.KEY_OUTPUT_0
)
def __day_events__(self, device, key, data): def __day_events__(self, device, key, data):
if key in (self.day_events.KEY_SUNSET, self.day_events.KEY_START_OF_DAY): if key in (self.day_events.KEY_SUNSET, self.day_events.KEY_START_OF_DAY):
if config.CHRISTMAS: if config.CHRISTMAS:
self.switch_garland_light.set_output_0(True) self.garland_powerplug.set_output_0(True)
elif key in (self.day_events.KEY_START_OF_NIGHT, self.day_events.KEY_SUNRISE): elif key in (self.day_events.KEY_START_OF_NIGHT, self.day_events.KEY_SUNRISE):
if config.CHRISTMAS: if config.CHRISTMAS:
self.switch_garland_light.set_output_0(False) self.garland_powerplug.set_output_0(False)
class first_floor_east_sleep(rooms.ffe_sleep, room): class first_floor_east_sleep(room):
def __init__(self, mqtt_client): def __init__(self, mqtt_client, pd, vd):
super().__init__(mqtt_client) roo = props.ROO_SLP
room.__init__(self, mqtt_client)
# #
self.light_wardrobe_light.disable_all_off() # Always on - Off by light sensor # Device initialisation
# light <-> videv
self.videv_main_light.connect_sw_device(self.switch_main_light, self.switch_main_light.KEY_OUTPUT_0)
self.videv_main_light.connect_br_device(self.light_main_light, self.light_main_light.KEY_BRIGHTNESS)
self.videv_main_light.connect_ct_device(self.light_main_light, self.light_main_light.KEY_COLOR_TEMP)
# #
self.videv_bed_dirk_light.connect_sw_device(self.light_bed_dirk_light, self.light_bed_dirk_light.KEY_OUTPUT_0) # http://shelly1l-E8DB84A254C7
self.videv_bed_dirk_light.connect_br_device(self.light_bed_dirk_light, self.light_bed_dirk_light.KEY_BRIGHTNESS) # main light
# self.main_light_shelly = pd.get(props.STG_SHE, loc, roo, props.FUN_MAL)
self.videv_bed_marion_light.connect_sw_device(self.switch_bed_marion_light, self.switch_bed_marion_light.KEY_OUTPUT_0) self.main_light_tradfri = pd.get(props.STG_ZFE, loc, roo, props.FUN_MAL)
# # bed light
self.videv_wardrobe_light.connect_sw_device(self.light_wardrobe_light, self.light_wardrobe_light.KEY_OUTPUT_0) self.bed_light_di_tradfri = pd.get(props.STG_ZFE, loc, roo, props.FUN_BLD)
self.videv_wardrobe_light.connect_br_device(self.light_wardrobe_light, self.light_wardrobe_light.KEY_BRIGHTNESS) self.bed_light_ma_powerplug = pd.get(props.STG_ZFE, loc, roo, props.FUN_BLM)
# heating function
# button / brightness function self.heating_valve = pd.get(props.STG_ZFE, loc, roo, props.FUN_HEA)
self.brightness_functions = brightness_choose_n_action(self.input_device)
self.brightness_functions.add(self.light_main_light, self.switch_main_light, self.switch_main_light.KEY_OUTPUT_0)
self.brightness_functions.add(self.light_bed_dirk_light, self.light_bed_dirk_light, self.light_bed_dirk_light.KEY_OUTPUT_0)
# button / main light
self.input_device.add_callback(self.input_device.KEY_ACTION, self.input_device.ACTION_TOGGLE, self.switch_main_light.toggle_output_0_mcb)
# button / bed light
self.input_device.add_callback(self.input_device.KEY_ACTION, self.input_device.ACTION_LEFT, self.light_bed_dirk_light.toggle_output_0_mcb)
self.input_device.add_callback(self.input_device.KEY_ACTION, self.input_device.ACTION_LEFT_LONG,
self.switch_bed_marion_light.toggle_output_0_mcb)
# button # button
self.videv_multistate.connect_br_function(self.brightness_functions, brightness_choose_n_action.KEY_ACTIVE_DEVICE, 2) self.button_tradfri = pd.get(props.STG_ZFE, loc, roo, props.FUN_INP)
super().__init__(mqtt_client, pd, vd)
#
# Functionality initialisation
#
# button / brightness function
self.brightness_functions = brightness_choose_n_action(self.button_tradfri)
self.brightness_functions.add(self.main_light_tradfri, self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0)
self.brightness_functions.add(self.bed_light_di_tradfri, self.bed_light_di_tradfri, self.bed_light_di_tradfri.KEY_OUTPUT_0)
# button / main light
self.button_tradfri.add_callback(self.button_tradfri.KEY_ACTION, self.button_tradfri.ACTION_TOGGLE,
self.main_light_shelly.toggle_output_0_mcb)
# button / bed light
self.button_tradfri.add_callback(self.button_tradfri.KEY_ACTION, self.button_tradfri.ACTION_LEFT,
self.bed_light_di_tradfri.toggle_output_0_mcb)
self.button_tradfri.add_callback(self.button_tradfri.KEY_ACTION, self.button_tradfri.ACTION_LEFT_LONG,
self.bed_light_ma_powerplug.toggle_output_0_mcb)
# heating function # heating function
self.heating_function = heating_function( self.heating_function = heating_function(
self.valve_heating, self.heating_valve,
config.DEFAULT_TEMPERATURE, config.DEFAULT_TEMPERATURE,
**get_radiator_data(self.valve_heating.topic) **get_radiator_data(self.heating_valve.topic)
) )
self.heating_function.add_callback(None, None, set_radiator_data, True) self.heating_function.add_callback(None, None, set_radiator_data, True)
self.videv_heating.connect_heating_function(self.heating_function)
#
# Virtual Device Interface
#
# main light
self.main_light_videv = videv_switch_brightness_color_temp(
mqtt_client, config.TOPIC_FFE_SLEEP_MAIN_LIGHT_VIDEV,
self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0,
self.main_light_tradfri, self.main_light_tradfri.KEY_BRIGHTNESS,
self.main_light_tradfri, self.main_light_tradfri.KEY_COLOR_TEMP
)
# bed light
self.bed_light_di_videv = videv_switch_brightness(
mqtt_client, config.TOPIC_FFE_SLEEP_BED_LIGHT_DI_VIDEV,
self.bed_light_di_tradfri, self.bed_light_di_tradfri.KEY_OUTPUT_0,
self.bed_light_di_tradfri, self.bed_light_di_tradfri.KEY_BRIGHTNESS,
)
self.bed_light_ma_videv = videv_switching(
mqtt_client, config.TOPIC_FFE_SLEEP_BED_LIGHT_MA_VIDEV,
self.bed_light_ma_powerplug, self.bed_light_ma_powerplug.KEY_OUTPUT_0
)
# heating function
self.heating_function_videv = videv_heating(
mqtt_client, config.TOPIC_FFE_SLEEP_HEATING_VALVE_VIDEV,
self.heating_function
)
# button
self.brightness_functions_device_videv = videv_multistate(
mqtt_client, config.TOPIC_FFE_SLEEP_ACTIVE_BRIGHTNESS_DEVICE_VIDEV,
brightness_choose_n_action.KEY_ACTIVE_DEVICE, self.brightness_functions, 2
)
class first_floor_east_living(rooms.ffe_livingroom, room): class first_floor_east_living(room):
def __init__(self, mqtt_client): def __init__(self, mqtt_client, pd, vd):
super().__init__(mqtt_client) roo = props.ROO_LIV
room.__init__(self, mqtt_client)
# #
# light <-> videv # Device initialisation
self.videv_main_light.connect_sw_device(self.switch_main_light, self.switch_main_light.KEY_OUTPUT_0)
self.videv_main_light.connect_br_device(self.light_main_light, self.light_main_light.KEY_BRIGHTNESS)
self.videv_main_light.connect_ct_device(self.light_main_light, self.light_main_light.KEY_COLOR_TEMP)
#
self.videv_floor_light.connect_sw_device(self.light_floor_light, self.light_floor_light.KEY_OUTPUT_0)
self.videv_floor_light.connect_br_device(self.light_floor_light, self.light_floor_light.KEY_BRIGHTNESS)
self.videv_floor_light.connect_ct_device(self.light_floor_light, self.light_floor_light.KEY_COLOR_TEMP)
# #
# http://shelly1l-3C6105E3F910
# main light
self.main_light_shelly = pd.get(props.STG_SHE, loc, roo, props.FUN_MAL)
self.main_light_tradfri = pd.get(props.STG_ZFE, loc, roo, props.FUN_MAL)
# floor lamp
self.floorlamp_tradfri = pd.get(props.STG_ZFE, loc, roo, props.FUN_FLL)
# heating function
self.heating_valve = pd.get(props.STG_ZFE, loc, roo, props.FUN_HEA)
# xmas tree
if config.CHRISTMAS: if config.CHRISTMAS:
self.videv_xmas_tree_light.connect_sw_device(self.switch_xmas_tree_light, self.switch_xmas_tree_light.KEY_OUTPUT_0) self.powerplug_xmas_tree = pd.get(props.STG_ZFE, loc, roo, props.FUN_XTR)
self.powerplug_xmas_star = pd.get(props.STG_ZFE, loc, roo, props.FUN_XST)
# main light -> floor_light super().__init__(mqtt_client, pd, vd)
self.switch_main_light.add_callback(self.switch_main_light.KEY_OUTPUT_0, None, self.light_floor_light.set_output_0_mcb, True)
#
# Functionality initialisation
#
# floor lamp synchronisation with main_light
self.main_light_shelly.add_callback(self.main_light_shelly.KEY_OUTPUT_0, None, self.floorlamp_tradfri.set_output_0_mcb, True)
# heating function # heating function
self.heating_function = heating_function( self.heating_function = heating_function(
self.valve_heating, self.heating_valve,
config.DEFAULT_TEMPERATURE, config.DEFAULT_TEMPERATURE,
**get_radiator_data(self.valve_heating.topic) **get_radiator_data(self.heating_valve.topic)
) )
self.heating_function.add_callback(None, None, set_radiator_data, True) self.heating_function.add_callback(None, None, set_radiator_data, True)
self.videv_heating.connect_heating_function(self.heating_function)
#
# Virtual Device Interface
#
# main light
self.main_light_videv = videv_switch_brightness_color_temp(
mqtt_client, config.TOPIC_FFE_LIVINGROOM_MAIN_LIGHT_VIDEV,
self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0,
self.main_light_tradfri, self.main_light_tradfri.KEY_BRIGHTNESS,
self.main_light_tradfri, self.main_light_tradfri.KEY_COLOR_TEMP
)
# floor lamp
self.floorlamp_videv = videv_switch_brightness_color_temp(
mqtt_client, config.TOPIC_FFE_LIVINGROOM_FLOOR_LAMP_VIDEV,
self.floorlamp_tradfri, self.floorlamp_tradfri.KEY_OUTPUT_0,
self.floorlamp_tradfri, self.floorlamp_tradfri.KEY_BRIGHTNESS,
self.floorlamp_tradfri, self.floorlamp_tradfri.KEY_COLOR_TEMP
)
# heating function
self.heating_function_videv = videv_heating(
mqtt_client, config.TOPIC_FFE_LIVINGROOM_HEATING_VALVE_VIDEV,
self.heating_function
)
# xmas tree
if config.CHRISTMAS:
self.xmas_tree_videv = videv_switching(
mqtt_client, config.TOPIC_FFE_LIVINGROOM_XMAS_TREE_VIDEV,
self.powerplug_xmas_tree, self.powerplug_xmas_tree.KEY_OUTPUT_0
)

View File

@ -3,10 +3,11 @@
# #
import config import config
from devdi import rooms from devdi import topic as props
from function.db import get_radiator_data, set_radiator_data from function.db import get_radiator_data, set_radiator_data
from function.modules import heating_function from function.modules import heating_function
from function.rooms import room, room_collection from function.rooms import room, room_collection
from function.videv import videv_switch_brightness, videv_switch_brightness_color_temp, videv_heating
import logging import logging
@ -16,105 +17,168 @@ except ImportError:
ROOT_LOGGER_NAME = 'root' ROOT_LOGGER_NAME = 'root'
logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
loc = props.LOC_FFW
class first_floor_west(room_collection): class first_floor_west(room_collection):
def __init__(self, mqtt_client): def __init__(self, mqtt_client, pd, vd):
super().__init__(mqtt_client) super().__init__(mqtt_client, pd, vd)
self.floor = first_floor_west_floor(mqtt_client) self.bath = first_floor_west_bath(mqtt_client, pd, vd)
self.bath = first_floor_west_bath(mqtt_client) self.julian = first_floor_west_julian(mqtt_client, pd, vd)
self.julian = first_floor_west_julian(mqtt_client) self.livingroom = first_floor_west_living(mqtt_client, pd, vd)
self.livingroom = first_floor_west_living(mqtt_client) self.sleep = first_floor_west_sleep(mqtt_client, pd, vd)
self.sleep = first_floor_west_sleep(mqtt_client)
class first_floor_west_floor(rooms.ffw_floor, room): class first_floor_west_julian(room):
def __init__(self, mqtt_client): def __init__(self, mqtt_client, pd, vd):
super().__init__(mqtt_client) roo = props.ROO_JUL
room.__init__(self, mqtt_client)
# #
# connect videv and switch # Device initialisation
self.videv_main_light.connect_sw_device(self.switch_main_light, self.switch_main_light.KEY_OUTPUT_0) #
# http://shelly1l-3C6105E43452
# main light
class first_floor_west_julian(rooms.ffw_julian, room): self.main_light_shelly = pd.get(props.STG_SHE, loc, roo, props.FUN_MAL)
def __init__(self, mqtt_client): self.main_light_tradfri = pd.get(props.STG_ZFW, loc, roo, props.FUN_MAL)
super().__init__(mqtt_client) # heating function
room.__init__(self, mqtt_client) self.heating_valve = pd.get(props.STG_ZFW, loc, roo, props.FUN_HEA)
super().__init__(mqtt_client, pd, vd)
#
# Functionality initialisation
# #
# light <-> videv
self.videv_main_light.connect_sw_device(self.switch_main_light, self.switch_main_light.KEY_OUTPUT_0)
self.videv_main_light.connect_br_device(self.light_main_light, self.light_main_light.KEY_BRIGHTNESS)
self.videv_main_light.connect_ct_device(self.light_main_light, self.light_main_light.KEY_COLOR_TEMP)
# heating function # heating function
self.heating_function = heating_function( self.heating_function = heating_function(
self.valve_heating, self.heating_valve,
config.DEFAULT_TEMPERATURE, config.DEFAULT_TEMPERATURE,
**get_radiator_data(self.valve_heating.topic) **get_radiator_data(self.heating_valve.topic)
) )
self.heating_function.add_callback(None, None, set_radiator_data, True) self.heating_function.add_callback(None, None, set_radiator_data, True)
self.videv_heating.connect_heating_function(self.heating_function)
class first_floor_west_bath(rooms.ffw_bath, room):
def __init__(self, mqtt_client):
super().__init__(mqtt_client)
room.__init__(self, mqtt_client)
# #
# light <-> videv # Virtual Device Interface
self.videv_main_light.connect_sw_device(self.switch_main_light, self.switch_main_light.KEY_OUTPUT_0) #
# main light
self.main_light_videv = videv_switch_brightness_color_temp(
mqtt_client, config.TOPIC_FFW_JULIAN_MAIN_LIGHT_VIDEV,
self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0,
self.main_light_tradfri, self.main_light_tradfri.KEY_BRIGHTNESS,
self.main_light_tradfri, self.main_light_tradfri.KEY_COLOR_TEMP
)
# heating function
self.heating_function_videv = videv_heating(
mqtt_client, config.TOPIC_FFW_JULIAN_HEATING_VALVE_VIDEV,
self.heating_function
)
class first_floor_west_bath(room):
def __init__(self, mqtt_client, pd, vd):
roo = props.ROO_BAT
#
# Device initialisation
#
# heating function
self.heating_valve = pd.get(props.STG_ZFW, loc, roo, props.FUN_HEA)
super().__init__(mqtt_client, pd, vd)
#
# Functionality initialisation
#
# heating function # heating function
self.heating_function = heating_function( self.heating_function = heating_function(
self.valve_heating, self.heating_valve,
config.DEFAULT_TEMPERATURE, config.DEFAULT_TEMPERATURE,
**get_radiator_data(self.valve_heating.topic) **get_radiator_data(self.heating_valve.topic)
) )
self.heating_function.add_callback(None, None, set_radiator_data, True) self.heating_function.add_callback(None, None, set_radiator_data, True)
self.videv_heating.connect_heating_function(self.heating_function)
class first_floor_west_living(rooms.ffw_livingroom, room):
def __init__(self, mqtt_client):
super().__init__(mqtt_client)
room.__init__(self, mqtt_client)
# #
# light <-> videv # Virtual Device Interface
self.videv_main_light.connect_sw_device(self.switch_main_light, self.switch_main_light.KEY_OUTPUT_0) #
self.videv_main_light.connect_br_device(self.light_main_light, self.light_main_light.KEY_BRIGHTNESS) # heating function
self.videv_main_light.connect_ct_device(self.light_main_light, self.light_main_light.KEY_COLOR_TEMP) self.heating_function_videv = videv_heating(
mqtt_client, config.TOPIC_FFW_BATH_HEATING_VALVE_VIDEV,
self.heating_function
)
class first_floor_west_living(room):
def __init__(self, mqtt_client, pd, vd):
roo = props.ROO_LIV
#
# Device initialisation
#
# http://shelly1l-84CCA8ACE6A1
# main light
self.main_light_shelly = pd.get(props.STG_SHE, loc, roo, props.FUN_MAL)
self.main_light_tradfri = pd.get(props.STG_ZFW, loc, roo, props.FUN_MAL)
# heating function
self.heating_valve = pd.get(props.STG_ZFW, loc, roo, props.FUN_HEA)
super().__init__(mqtt_client, pd, vd)
#
# Functionality initialisation
#
# heating function # heating function
self.heating_function = heating_function( self.heating_function = heating_function(
self.valve_heating, self.heating_valve,
config.DEFAULT_TEMPERATURE, config.DEFAULT_TEMPERATURE,
**get_radiator_data(self.valve_heating.topic) **get_radiator_data(self.heating_valve.topic)
) )
self.heating_function.add_callback(None, None, set_radiator_data, True) self.heating_function.add_callback(None, None, set_radiator_data, True)
self.videv_heating.connect_heating_function(self.heating_function)
class first_floor_west_sleep(rooms.ffw_sleep, room):
def __init__(self, mqtt_client):
super().__init__(mqtt_client)
room.__init__(self, mqtt_client)
# #
# light <-> videv # Virtual Device Interface
self.videv_main_light.connect_sw_device(self.switch_main_light, self.switch_main_light.KEY_OUTPUT_0)
self.videv_main_light.connect_br_device(self.light_main_light, self.light_main_light.KEY_BRIGHTNESS)
# #
self.videv_window_light.connect_sw_device(self.light_window_light, self.light_window_light.KEY_OUTPUT_0) # main light
self.videv_window_light.connect_br_device(self.light_window_light, self.light_window_light.KEY_BRIGHTNESS) self.main_light_videv = videv_switch_brightness_color_temp(
self.videv_window_light.connect_ct_device(self.light_window_light, self.light_window_light.KEY_COLOR_TEMP) mqtt_client, config.TOPIC_FFW_LIVINGROOM_MAIN_LIGHT_VIDEV,
self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0,
self.main_light_tradfri, self.main_light_tradfri.KEY_BRIGHTNESS,
self.main_light_tradfri, self.main_light_tradfri.KEY_COLOR_TEMP
)
# heating function
self.heating_function_videv = videv_heating(
mqtt_client, config.TOPIC_FFW_LIVINGROOM_HEATING_VALVE_VIDEV,
self.heating_function
)
# main light -> window light
self.switch_main_light.add_callback(self.switch_main_light.KEY_OUTPUT_0, None, self.light_window_light.set_output_0_mcb, True)
class first_floor_west_sleep(room):
def __init__(self, mqtt_client, pd, vd):
roo = props.ROO_SLP
#
# Device initialisation
#
# http://shelly1-3494546A51F2
# main light
self.main_light_shelly = pd.get(props.STG_SHE, loc, roo, props.FUN_MAL)
self.main_light_tradfri = pd.get(props.STG_ZFW, loc, roo, props.FUN_MAL)
# heating function
self.heating_valve = pd.get(props.STG_ZFW, loc, roo, props.FUN_HEA)
super().__init__(mqtt_client, pd, vd)
#
# Functionality initialisation
#
# heating function # heating function
self.heating_function = heating_function( self.heating_function = heating_function(
self.valve_heating, self.heating_valve,
config.DEFAULT_TEMPERATURE, config.DEFAULT_TEMPERATURE,
**get_radiator_data(self.valve_heating.topic) **get_radiator_data(self.heating_valve.topic)
) )
self.heating_function.add_callback(None, None, set_radiator_data, True) self.heating_function.add_callback(None, None, set_radiator_data, True)
self.videv_heating.connect_heating_function(self.heating_function)
#
# Virtual Device Interface
#
# main light
self.main_light_videv = videv_switch_brightness(
mqtt_client, config.TOPIC_FFW_SLEEP_MAIN_LIGHT_VIDEV,
self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0,
self.main_light_tradfri, self.main_light_tradfri.KEY_BRIGHTNESS
)
# heating function
self.heating_function_videv = videv_heating(
mqtt_client, config.TOPIC_FFW_SLEEP_HEATING_VALVE_VIDEV,
self.heating_function
)

View File

@ -2,9 +2,14 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from devdi import rooms import config
import devdi.topic as props
from devices import group
from function.db import get_radiator_data, set_radiator_data
from function.helpers import day_event from function.helpers import day_event
from function.modules import brightness_choose_n_action, timer_on_activation, heating_function
from function.rooms import room, room_collection from function.rooms import room, room_collection
from function.videv import videv_switching, videv_switch_brightness, videv_switching_timer, videv_switch_brightness_color_temp, videv_heating, videv_multistate
import logging import logging
try: try:
@ -13,28 +18,42 @@ except ImportError:
ROOT_LOGGER_NAME = 'root' ROOT_LOGGER_NAME = 'root'
logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
loc = props.LOC_GAR
class garden(room_collection): class garden(room_collection):
def __init__(self, mqtt_client): def __init__(self, mqtt_client, pd, vd):
super().__init__(mqtt_client) super().__init__(mqtt_client, pd, vd)
self.garden = garden_garden(mqtt_client) self.garden = garden_garden(mqtt_client, pd, vd)
class garden_garden(rooms.gar_garden, room): class garden_garden(room):
def __init__(self, mqtt_client): def __init__(self, mqtt_client, pd, vd):
super().__init__(mqtt_client) roo = props.ROO_GAR
room.__init__(self, mqtt_client) #
# Device initialisation
# #
self.day_events = day_event((6, 0), (22, 0), 30, -30) self.day_events = day_event((6, 0), (22, 0), 30, -30)
self.garland_powerplug = pd.get(props.STG_ZGW, loc, roo, props.FUN_GAR)
super().__init__(mqtt_client, pd, vd)
#
# Functionality initialisation
#
self.day_events.add_callback(None, True, self.__day_events__, True) self.day_events.add_callback(None, True, self.__day_events__, True)
# xxx <-> videv #
self.videv_garland_light.connect_sw_device(self.switch_garland_light, self.switch_garland_light.KEY_OUTPUT_0) # Virtual Device Interface
self.videv_repeater.connect_sw_device(self.switch_repeater, self.switch_repeater.KEY_OUTPUT_0) #
# garland
self.garland_videv = videv_switching(
mqtt_client, config.TOPIC_GAR_GARDEN_GARLAND_VIDEV,
self.garland_powerplug, self.garland_powerplug.KEY_OUTPUT_0
)
def __day_events__(self, device, key, data): def __day_events__(self, device, key, data):
if self.videv_mode.get(self.videv_mode.KEY_STATE):
if key in (self.day_events.KEY_SUNSET, self.day_events.KEY_START_OF_DAY): if key in (self.day_events.KEY_SUNSET, self.day_events.KEY_START_OF_DAY):
self.switch_garland_light.set_output_0(True) self.garland_powerplug.set_output_0(True)
elif key in (self.day_events.KEY_START_OF_NIGHT, self.day_events.KEY_SUNRISE): elif key in (self.day_events.KEY_START_OF_NIGHT, self.day_events.KEY_SUNRISE):
self.switch_garland_light.set_output_0(False) self.garland_powerplug.set_output_0(False)

View File

@ -3,10 +3,12 @@
# #
import config import config
from devdi import rooms from devdi import topic as props
from devices import group
from function.db import get_radiator_data, set_radiator_data from function.db import get_radiator_data, set_radiator_data
from function.modules import brightness_choose_n_action, heating_function, switched_light from function.modules import brightness_choose_n_action, heating_function, switched_light
from function.rooms import room, room_collection from function.rooms import room, room_collection
from function.videv import videv_switching, videv_switch_brightness_color_temp, videv_heating, videv_multistate, videv_audio_player
import logging import logging
import task import task
@ -16,16 +18,83 @@ except ImportError:
ROOT_LOGGER_NAME = 'root' ROOT_LOGGER_NAME = 'root'
logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
loc = props.LOC_GFW
class ground_floor_west(room_collection): class ground_floor_west(room_collection):
def __init__(self, mqtt_client): def __init__(self, mqtt_client, pd, vd):
super().__init__(mqtt_client) super().__init__(mqtt_client, pd, vd)
self.dirk = ground_floor_west_dirk(mqtt_client) self.dirk = ground_floor_west_dirk(mqtt_client, pd, vd)
self.floor = ground_floor_west_floor(mqtt_client) self.floor = ground_floor_west_floor(mqtt_client, pd, vd)
self.marion = ground_floor_west_marion(mqtt_client) self.marion = ground_floor_west_marion(mqtt_client, pd, vd)
class ground_floor_west_dirk(rooms.gfw_dirk, room): class ground_floor_west_floor(room):
def __init__(self, mqtt_client, pd, vd):
roo = props.ROO_FLO
#
# Device initialisation
#
# http://shelly1l-84CCA8AD1148
self.main_light_shelly = pd.get(props.STG_SHE, loc, roo, props.FUN_MAL)
self.main_light_tradfri = pd.get(props.STG_ZGW, loc, roo, props.FUN_MAL)
super().__init__(mqtt_client, pd, vd)
#
# Functionality initialisation
#
# Request silvercrest data of lead light after power on
switched_light(self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0, self.main_light_tradfri)
#
# Virtual Device Interface
#
self.main_light_videv = videv_switch_brightness_color_temp(
mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_VIDEV,
self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0,
self.main_light_tradfri, self.main_light_tradfri.KEY_BRIGHTNESS,
self.main_light_tradfri, self.main_light_tradfri.KEY_COLOR_TEMP
)
class ground_floor_west_marion(room):
def __init__(self, mqtt_client, pd, vd):
roo = props.ROO_MAR
#
# Device initialisation
#
# http://shelly1l-E8DB84A1E067
# main light
self.main_light_shelly = pd.get(props.STG_SHE, loc, roo, props.FUN_MAL)
# heating function
self.heating_valve = pd.get(props.STG_ZGW, loc, roo, props.FUN_HEA)
super().__init__(mqtt_client, pd, vd)
#
# Functionality initialisation
#
# heating function
self.heating_function = heating_function(
self.heating_valve,
config.DEFAULT_TEMPERATURE,
**get_radiator_data(self.heating_valve.topic)
)
self.heating_function.add_callback(None, None, set_radiator_data, True)
#
# Virtual Device Interface
#
self.main_light_videv = videv_switching(
mqtt_client, config.TOPIC_GFW_MARION_MAIN_LIGHT_VIDEV,
self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0
)
self.heating_function_videv = videv_heating(
mqtt_client, config.TOPIC_GFW_MARION_HEATING_VALVE_VIDEV,
self.heating_function
)
class ground_floor_west_dirk(room):
STATE_ACTIVE_DEVICE_MAIN_LIGHT = 0 STATE_ACTIVE_DEVICE_MAIN_LIGHT = 0
STATE_ACTIVE_DEVICE_DESK_LIGHT = 1 STATE_ACTIVE_DEVICE_DESK_LIGHT = 1
STATE_ACTIVE_DEVICE_AMPLIFIER = 2 STATE_ACTIVE_DEVICE_AMPLIFIER = 2
@ -34,158 +103,142 @@ class ground_floor_west_dirk(rooms.gfw_dirk, room):
AUDIO_SOURCE_PC = 0 AUDIO_SOURCE_PC = 0
AUDIO_SOURCE_CD = 1 AUDIO_SOURCE_CD = 1
AUDIO_SOURCE_RASPI = 2 AUDIO_SOURCE_RASPI = 2
AUDIO_SOURCE_BT = 3
AUDIO_SOURCE_PHONO = 4
def __init__(self, mqtt_client): def __init__(self, mqtt_client, pd, vd):
super().__init__(mqtt_client) roo = props.ROO_DIR
room.__init__(self, mqtt_client)
# #
# light <-> videv # Device initialisation
self.videv_main_light.connect_sw_device(self.switch_main_light, self.switch_main_light.KEY_OUTPUT_0)
self.videv_main_light.connect_br_device(self.light_main_light, self.light_main_light.KEY_BRIGHTNESS)
self.videv_main_light.connect_ct_device(self.light_main_light, self.light_main_light.KEY_COLOR_TEMP)
# #
self.videv_desk_light.connect_sw_device(self.light_desk_light, self.light_desk_light.KEY_OUTPUT_0) # http://shelly1l-3C6105E44F27
self.videv_desk_light.connect_br_device(self.light_desk_light, self.light_desk_light.KEY_BRIGHTNESS) # main light
self.videv_desk_light.connect_ct_device(self.light_desk_light, self.light_desk_light.KEY_COLOR_TEMP) self.main_light_shelly = pd.get(props.STG_SHE, loc, roo, props.FUN_MAL)
self.main_light_tradfri = pd.get(props.STG_ZGW, loc, roo, props.FUN_MAL)
# powerplug
self.powerplug_common = pd.get(props.STG_MYA, loc, roo, props.FUN_MPP)
self.KEY_POWERPLUG_AMPLIFIER = self.powerplug_common.KEY_OUTPUT_0
self.KEY_POWERPLUG_DESK_LIGHT = self.powerplug_common.KEY_OUTPUT_1
self.KEY_POWERPLUG_CD_PLAYER = self.powerplug_common.KEY_OUTPUT_2
self.KEY_POWERPLUG_PC_DOCK = self.powerplug_common.KEY_OUTPUT_3
# desk light
self.desk_light_tradfri = pd.get(props.STG_ZGW, loc, roo, props.FUN_DEL)
# button
self.button_tradfri = pd.get(props.STG_ZGW, loc, roo, props.FUN_INP)
# hifi
self.remote_amplifier = pd.get(props.STG_MYA, loc, roo, props.FUN_RCA)
self.spotify_state = pd.get(props.STG_MYA, loc, roo, props.FUN_ASS)
self.mpd_state = pd.get(props.STG_MYA, loc, roo, props.FUN_ASM)
self.bt_state = pd.get(props.STG_MYA, loc, roo, props.FUN_ASB)
# heating function
self.heating_valve = pd.get(props.STG_ZGW, loc, roo, props.FUN_HEA)
super().__init__(mqtt_client, pd, vd)
# #
self.videv_amplifier.connect_sw_device(self.switch_powerplug_4, self.KEY_POWERPLUG_AMPLIFIER) # Functionality initialisation
self.videv_bluetooth.connect_sw_device(self.switch_powerplug_4, self.KEY_POWERPLUG_BT)
self.videv_cd_player.connect_sw_device(self.switch_powerplug_4, self.KEY_POWERPLUG_CD_PLAYER)
self.videv_phono.connect_sw_device(self.switch_powerplug_4, self.KEY_POWERPLUG_PHONO)
# #
self.videv_pc_dock.connect_sw_device(self.switch_pc_dock, self.switch_pc_dock.KEY_OUTPUT_0) # Button - Brightness functionality
self.brightness_functions = brightness_choose_n_action(self.button_tradfri)
# amplifier on, if playing device on self.brightness_functions.add(self.main_light_tradfri, self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0)
self.switch_powerplug_4.add_callback(self.KEY_POWERPLUG_PHONO, None, self.switch_powerplug_4.set_output_0_mcb, True) self.brightness_functions.add(self.desk_light_tradfri, self.powerplug_common, self.KEY_POWERPLUG_DESK_LIGHT)
self.switch_powerplug_4.add_callback(self.KEY_POWERPLUG_CD_PLAYER, None, self.switch_powerplug_4.set_output_0_mcb, True) self.brightness_functions.add(self.remote_amplifier, self.powerplug_common, self.KEY_POWERPLUG_AMPLIFIER)
self.switch_powerplug_4.add_callback(self.KEY_POWERPLUG_BT, None, self.switch_powerplug_4.set_output_0_mcb, True)
# amplifier on, if player on
self.audio_status_bluetooth.add_callback(self.audio_status_bluetooth.KEY_STATE, None, self.switch_powerplug_4.set_output_0_mcb, True)
self.audio_status_mpd.add_callback(self.audio_status_mpd.KEY_STATE, None, self.switch_powerplug_4.set_output_0_mcb, True)
self.audio_status_spotify.add_callback(self.audio_status_spotify.KEY_STATE, None, self.switch_powerplug_4.set_output_0_mcb, True)
# Audio source selection
self.switch_powerplug_4.add_callback(self.KEY_POWERPLUG_AMPLIFIER, True, self.audio_source_selector, True)
self.switch_powerplug_4.add_callback(self.KEY_POWERPLUG_CD_PLAYER, True, self.audio_source_selector, True)
self.switch_powerplug_4.add_callback(self.KEY_POWERPLUG_BT, True, self.audio_source_selector, True)
self.switch_powerplug_4.add_callback(self.KEY_POWERPLUG_PHONO, True, self.audio_source_selector, True)
self.audio_status_bluetooth.add_callback(self.audio_status_bluetooth.KEY_STATE, True, self.audio_source_selector, True)
self.audio_status_mpd.add_callback(self.audio_status_mpd.KEY_STATE, True, self.audio_source_selector, True)
self.audio_status_spotify.add_callback(self.audio_status_spotify.KEY_STATE, True, self.audio_source_selector, True)
self.audio_source = self.AUDIO_SOURCE_PC
self.delayed_task_remote = task.delayed(1.0, self.send_audio_source)
# input device functions
# Brightness functionality
self.brightness_functions = brightness_choose_n_action(self.input_device)
self.brightness_functions.add(self.light_main_light, self.switch_main_light, self.switch_main_light.KEY_OUTPUT_0)
self.brightness_functions.add(self.light_desk_light, self.light_desk_light, self.light_desk_light.KEY_OUTPUT_0)
self.brightness_functions.add(self.remote_ctrl, self.switch_powerplug_4, self.KEY_POWERPLUG_AMPLIFIER)
# Button - Main light # Button - Main light
self.input_device.add_callback(self.input_device.KEY_ACTION, self.input_device.ACTION_TOGGLE, self.button_tradfri.add_callback(self.button_tradfri.KEY_ACTION, self.button_tradfri.ACTION_TOGGLE,
self.switch_main_light.toggle_output_0_mcb) self.main_light_shelly.toggle_output_0_mcb)
# Button - Desk light # Button - Desk light
self.input_device.add_callback(self.input_device.KEY_ACTION, self.input_device.ACTION_RIGHT, self.button_tradfri.add_callback(self.button_tradfri.KEY_ACTION, self.button_tradfri.ACTION_RIGHT,
self.light_desk_light.toggle_output_0_mcb) self.powerplug_common.toggle_output_1_mcb)
# Button - Amplifier # Button - Amplifier
self.input_device.add_callback(self.input_device.KEY_ACTION, self.input_device.ACTION_LEFT_LONG, self.button_tradfri.add_callback(self.button_tradfri.KEY_ACTION, self.button_tradfri.ACTION_LEFT_LONG,
self.switch_powerplug_4.toggle_output_0_mcb) self.powerplug_common.toggle_output_0_mcb)
# Button - CD player # Button - CD player
self.input_device.add_callback(self.input_device.KEY_ACTION, self.input_device.ACTION_RIGHT_LONG, self.button_tradfri.add_callback(self.button_tradfri.KEY_ACTION, self.button_tradfri.ACTION_RIGHT_LONG,
self.switch_powerplug_4.toggle_output_2_mcb) self.powerplug_common.toggle_output_2_mcb)
# Button - PC dock # Button - PC dock
self.input_device.add_callback(self.input_device.KEY_ACTION, self.input_device.ACTION_LEFT, self.button_tradfri.add_callback(self.button_tradfri.KEY_ACTION, self.button_tradfri.ACTION_LEFT,
self.switch_pc_dock.toggle_output_0_mcb) self.powerplug_common.toggle_output_3_mcb)
# additional videv connections # Mediaplayer - Amplifier auto on
self.videv_multistate.connect_br_function(self.brightness_functions, brightness_choose_n_action.KEY_ACTIVE_DEVICE, 3) self.powerplug_common.add_callback(self.KEY_POWERPLUG_CD_PLAYER, None, self.powerplug_common.set_output_0_mcb, True)
# self.spotify_state.add_callback(self.spotify_state.KEY_STATE, None, self.powerplug_common.set_output_0_mcb, True)
self.videv_audio_player.connect_audio_device(self.audio_status_bluetooth) self.mpd_state.add_callback(self.mpd_state.KEY_STATE, None, self.powerplug_common.set_output_0_mcb, True)
self.videv_audio_player.connect_audio_device(self.audio_status_mpd) self.bt_state.add_callback(self.bt_state.KEY_STATE, None, self.powerplug_common.set_output_0_mcb, True)
self.videv_audio_player.connect_audio_device(self.audio_status_spotify) # Mediaplayer - Audio source selection
self.powerplug_common.add_callback(self.KEY_POWERPLUG_AMPLIFIER, True, self.audio_source_selector, True)
self.powerplug_common.add_callback(self.KEY_POWERPLUG_CD_PLAYER, True, self.audio_source_selector, True)
self.spotify_state.add_callback(self.spotify_state.KEY_STATE, True, self.audio_source_selector, True)
self.mpd_state.add_callback(self.mpd_state.KEY_STATE, True, self.audio_source_selector, True)
self.bt_state.add_callback(self.bt_state.KEY_STATE, True, self.audio_source_selector, True)
self.audio_source = self.AUDIO_SOURCE_PC
# heating function # heating function
self.heating_function = heating_function( self.heating_function = heating_function(
self.valve_heating, self.heating_valve,
config.DEFAULT_TEMPERATURE, config.DEFAULT_TEMPERATURE,
**get_radiator_data(self.valve_heating.topic) **get_radiator_data(self.heating_valve.topic)
) )
self.heating_function.add_callback(None, None, set_radiator_data, True) self.heating_function.add_callback(None, None, set_radiator_data, True)
# heating function <-> videv
self.videv_heating.connect_heating_function(self.heating_function) #
# Virtual Device Interface
#
self.main_light_videv = videv_switch_brightness_color_temp(
mqtt_client, config.TOPIC_GFW_DIRK_MAIN_LIGHT_VIDEV,
self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0,
self.main_light_tradfri, self.main_light_tradfri.KEY_BRIGHTNESS,
self.main_light_tradfri, self.main_light_tradfri.KEY_COLOR_TEMP
)
self.desk_light_videv = videv_switch_brightness_color_temp(
mqtt_client, config.TOPIC_GFW_DIRK_DESK_LIGHT_VIDEV,
self.powerplug_common, self.KEY_POWERPLUG_DESK_LIGHT,
self.desk_light_tradfri, self.desk_light_tradfri.KEY_BRIGHTNESS,
self.desk_light_tradfri, self.desk_light_tradfri.KEY_COLOR_TEMP
)
self.amplifier_videv = videv_switching(
mqtt_client, config.TOPIC_GFW_DIRK_AMPLIFIER_VIDEV,
self.powerplug_common, self.KEY_POWERPLUG_AMPLIFIER
)
self.cd_player_videv = videv_switching(
mqtt_client, config.TOPIC_GFW_DIRK_CD_PLAYER_VIDEV,
self.powerplug_common, self.KEY_POWERPLUG_CD_PLAYER
)
self.pc_dock_videv = videv_switching(
mqtt_client, config.TOPIC_GFW_DIRK_PC_DOCK_VIDEV,
self.powerplug_common, self.KEY_POWERPLUG_PC_DOCK
)
self.heating_function_videv = videv_heating(
mqtt_client, config.TOPIC_GFW_DIRK_HEATING_VALVE_VIDEV,
self.heating_function
)
self.brightness_functions_device_videv = videv_multistate(
mqtt_client, config.TOPIC_GFW_DIRK_ACTIVE_BRIGHTNESS_DEVICE_VIDEV,
brightness_choose_n_action.KEY_ACTIVE_DEVICE, self.brightness_functions, 3
)
self.audio_player_videv = videv_audio_player(
mqtt_client, config.TOPIC_GFW_DIRK_AUDIO_PLAYER_VIDEV,
self.spotify_state, self.mpd_state, self.bt_state
)
#
# Other stuff
#
self.delayed_task_remote = task.delayed(1.0, self.send_audio_source)
def audio_source_selector(self, device, key, data): def audio_source_selector(self, device, key, data):
if device == self.switch_powerplug_4 and key == self.KEY_POWERPLUG_CD_PLAYER: if device == self.powerplug_common and key == self.KEY_POWERPLUG_CD_PLAYER:
# switch on of cd player # switch on of cd player
self.audio_source = self.AUDIO_SOURCE_CD self.audio_source = self.AUDIO_SOURCE_CD
elif device == self.switch_powerplug_4 and key == self.KEY_POWERPLUG_BT: elif device in [self.spotify_state, self.mpd_state, self.bt_state]:
# switch on of bluetooth
self.audio_source = self.AUDIO_SOURCE_BT
elif device == self.switch_powerplug_4 and key == self.KEY_POWERPLUG_PHONO:
# switch on of bluetooth
self.audio_source = self.AUDIO_SOURCE_PHONO
elif device in [self.audio_status_spotify, self.audio_status_mpd, self.audio_status_bluetooth]:
# switch on raspi-source # switch on raspi-source
self.audio_source = self.AUDIO_SOURCE_RASPI self.audio_source = self.AUDIO_SOURCE_RASPI
elif device == self.switch_powerplug_4 and key == self.KEY_POWERPLUG_AMPLIFIER: elif device == self.powerplug_common and key == self.KEY_POWERPLUG_AMPLIFIER:
# switch on of amplifier -> select source and reset stored source value # switch on of amplifier -> select source and reset stored source value
self.delayed_task_remote.run() self.delayed_task_remote.run()
def send_audio_source(self): def send_audio_source(self):
if self.audio_source == self.AUDIO_SOURCE_PC: if self.audio_source == self.AUDIO_SOURCE_PC:
logger.info("Sending IR command to change audio source to pc") logger.info("Sending IR command to change audio source to pc")
self.remote_ctrl.set_line3() self.remote_amplifier.set_line3()
elif self.audio_source == self.AUDIO_SOURCE_CD: elif self.audio_source == self.AUDIO_SOURCE_CD:
logger.info("Sending IR command to change audio source to cd") logger.info("Sending IR command to change audio source to cd")
self.remote_ctrl.set_cd() self.remote_amplifier.set_cd()
elif self.audio_source == self.AUDIO_SOURCE_BT:
logger.info("Sending IR command to change audio source to bluetooth")
self.remote_ctrl.set_line2()
elif self.audio_source == self.AUDIO_SOURCE_PHONO:
logger.info("Sending IR command to change audio source to phono")
self.remote_ctrl.set_phono()
elif self.audio_source == self.AUDIO_SOURCE_RASPI: elif self.audio_source == self.AUDIO_SOURCE_RASPI:
logger.info("Sending IR command to change audio source to raspi") logger.info("Sending IR command to change audio source to raspi")
self.remote_ctrl.set_line1() self.remote_amplifier.set_line1()
self.audio_source = self.AUDIO_SOURCE_PC self.audio_source = self.AUDIO_SOURCE_PC
class ground_floor_west_floor(rooms.gfw_floor, room):
def __init__(self, mqtt_client):
super().__init__(mqtt_client)
room.__init__(self, mqtt_client)
#
# Request silvercrest data of lead light after power on
switched_light(self.switch_main_light, self.switch_main_light.KEY_OUTPUT_0, self.light_main_light)
# light <-> videv
self.videv_main_light.connect_sw_device(self.switch_main_light, self.switch_main_light.KEY_OUTPUT_0)
self.videv_main_light.connect_br_device(self.light_main_light, self.light_main_light.KEY_BRIGHTNESS)
self.videv_main_light.connect_ct_device(self.light_main_light, self.light_main_light.KEY_COLOR_TEMP)
class ground_floor_west_marion(rooms.gfw_marion, room):
def __init__(self, mqtt_client):
super().__init__(mqtt_client)
room.__init__(self, mqtt_client)
#
# light <-> videv
self.videv_main_light.connect_sw_device(self.switch_main_light, self.switch_main_light.KEY_OUTPUT_0)
#
self.videv_window_light.connect_sw_device(self.light_window_light, self.light_window_light.KEY_OUTPUT_0)
self.videv_window_light.connect_br_device(self.light_window_light, self.light_window_light.KEY_BRIGHTNESS)
self.videv_window_light.connect_ct_device(self.light_window_light, self.light_window_light.KEY_COLOR_TEMP)
# main light -> window_light
self.switch_main_light.add_callback(self.switch_main_light.KEY_OUTPUT_0, None, self.light_window_light.set_output_0_mcb, True)
# heating function
self.heating_function = heating_function(
self.valve_heating,
config.DEFAULT_TEMPERATURE,
**get_radiator_data(self.valve_heating.topic)
)
self.heating_function.add_callback(None, None, set_radiator_data, True)
# heating function <-> videv
self.videv_heating.connect_heating_function(self.heating_function)

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from mqtt.smarthome import common_base from base import common_base
import config import config
import geo import geo
import task import task

View File

@ -11,7 +11,8 @@ Targets:
- Method .add_calback(key, data, callback, on_change_only=False) to register videv actualisation on changes - Method .add_calback(key, data, callback, on_change_only=False) to register videv actualisation on changes
""" """
from mqtt.smarthome import common_base from base import common_base
import config
import devices import devices
from function.helpers import day_state from function.helpers import day_state
import logging import logging

View File

@ -13,32 +13,28 @@ logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
class room(object): class room(object):
ADD_TO_VIDEV_ALL_OFF = None def __init__(self, mqtt_client, pd, vd):
def __init__(self, mqtt_client):
self.mqtt_client = mqtt_client self.mqtt_client = mqtt_client
self.pd = pd
self.vd = vd
def all_off(self, device=None, key=None, data=None): def all_off(self, device=None, key=None, data=None):
logger.info("Switching all off \"%s\"", type(self).__name__) logger.info("Switching all off \"%s\"", type(self).__name__)
for name, obj in inspect.getmembers(self): for name, obj in inspect.getmembers(self):
try: try:
if obj.__module__.startswith('smart_devices'): if obj.__module__.startswith('devices'):
obj.all_off() obj.all_off()
except AttributeError: except AttributeError:
pass # not a module or has no method all_off pass # not a module or has no method all_off
def summer_mode(self, enable):
for name, obj in inspect.getmembers(self):
if obj.__class__.__name__ == 'heating_function':
if obj.__module__ == 'function.modules':
obj.set(obj.KEY_SUMMER_MODE, enable)
class room_collection(object): class room_collection(object):
ADD_TO_VIDEV_ALL_OFF = None ALLOWED_CLASSES = ("room", "room_collection")
def __init__(self, mqtt_client): def __init__(self, mqtt_client, pd, vd):
self.mqtt_client = mqtt_client self.mqtt_client = mqtt_client
self.pd = pd
self.vd = vd
def all_off(self, device=None, key=None, data=None): def all_off(self, device=None, key=None, data=None):
logger.info("Switching all off \"%s\"", type(self).__name__) logger.info("Switching all off \"%s\"", type(self).__name__)
@ -46,22 +42,8 @@ class room_collection(object):
# attribute name is not private # attribute name is not private
if not sub_name.startswith("__"): if not sub_name.startswith("__"):
sub = getattr(self, sub_name) sub = getattr(self, sub_name)
# try to call all_off if sub.__class__.__bases__[0].__name__ in self.ALLOWED_CLASSES:
try:
sub.all_off() sub.all_off()
except AttributeError:
pass # don't mind, if sub has no method all_off
except:
logger.error("Failed to switch off %s (%s)", repr(sub_name), type(sub).__name__)
def summer_mode(self, device=None, key=None, data=None):
logger.info("Changing to %s \"%s\"", "summer mode" if data else "winter_mode", type(self).__name__)
for sub_name in dir(self):
# attribute name is not private
if not sub_name.startswith("__"):
sub = getattr(self, sub_name)
if isinstance(sub, (room, room_collection)):
sub.summer_mode(data)
def all_devices(self, object_to_analyse=None, depth=0): def all_devices(self, object_to_analyse=None, depth=0):
target = object_to_analyse or self target = object_to_analyse or self

View File

@ -3,10 +3,11 @@
# #
import config import config
from devdi import rooms from devdi import topic as props
import logging import logging
from function.modules import motion_sensor_light from function.modules import motion_sensor_light
from function.rooms import room, room_collection from function.rooms import room, room_collection
from function.videv import videv_switching_motion
try: try:
from config import APP_NAME as ROOT_LOGGER_NAME from config import APP_NAME as ROOT_LOGGER_NAME
@ -14,24 +15,40 @@ except ImportError:
ROOT_LOGGER_NAME = 'root' ROOT_LOGGER_NAME = 'root'
logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
loc = props.LOC_STW
class stairway(room_collection): class stairway(room_collection):
def __init__(self, mqtt_client): def __init__(self, mqtt_client, pd, vd):
super().__init__(mqtt_client) super().__init__(mqtt_client, pd, vd)
self.stairway = stairway_stairway(mqtt_client) self.stairway = stairway_stairway(mqtt_client, pd, vd)
class stairway_stairway(rooms.stairway, room): class stairway_stairway(room):
def __init__(self, mqtt_client): def __init__(self, mqtt_client, pd, vd):
super().__init__(mqtt_client)
room.__init__(self, mqtt_client)
# #
# connect videv and switch # Device initialisation
self.videv_main_light.connect_sw_device(self.switch_main_light, self.switch_main_light.KEY_OUTPUT_0) #
# http://shelly1-3494546A9364
self.main_light_shelly = pd.get(props.STG_SHE, loc, props.ROO_STF, props.FUN_MAL)
self.motion_sensor_ff = pd.get(props.STG_ZFE, loc, props.ROO_STF, props.FUN_MSE)
self.motion_sensor_gf = pd.get(props.STG_ZGW, loc, props.ROO_STG, props.FUN_MSE)
super().__init__(mqtt_client, pd, vd)
#
# Functionality initialisation
#
self.motion_sensor_light = motion_sensor_light( self.motion_sensor_light = motion_sensor_light(
self.switch_main_light, self.switch_main_light.set_output_0, self.main_light_shelly, self.main_light_shelly.set_output_0,
self.motion_main_light_gf, self.motion_main_light_ff, self.motion_sensor_gf, self.motion_sensor_ff,
timer_value=config.USER_ON_TIME_STAIRWAYS timer_value=config.USER_ON_TIME_STAIRWAYS
) )
self.videv_main_light.connect_mo_function(self.motion_sensor_light)
#
# Virtual Device Interface
#
self.main_light_videv = videv_switching_motion(
mqtt_client, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_VIDEV,
self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0,
self.motion_sensor_light
)

181
function/videv.py Normal file
View File

@ -0,0 +1,181 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
"""
Virtual Device(s)
Targets:
* MQTT-Interface to control joined devices as one virtual device
* Primary signal routing
* No functionality should be implemented here
"""
from base import videv_base
from function.rooms import room, room_collection
import time
try:
from config import APP_NAME as ROOT_LOGGER_NAME
except ImportError:
ROOT_LOGGER_NAME = 'root'
class videv_switching(videv_base):
KEY_STATE = 'state'
def __init__(self, mqtt_client, topic, sw_device, sw_key):
super().__init__(mqtt_client, topic)
self.add_routing(self.KEY_STATE, sw_device, sw_key)
class videv_switching_timer(videv_base):
KEY_STATE = 'state'
KEY_TIMER = 'timer'
def __init__(self, mqtt_client, topic, sw_device, sw_key, tm_device, tm_key):
super().__init__(mqtt_client, topic)
self.add_routing(self.KEY_STATE, sw_device, sw_key)
self.add_display(self.KEY_TIMER, tm_device, tm_key)
class videv_switching_motion(videv_base):
KEY_STATE = 'state'
#
KEY_TIMER = 'timer'
KEY_MOTION_SENSOR = 'motion_%d'
def __init__(self, mqtt_client, topic, sw_device, sw_key, motion_function):
self.motion_sensors = motion_function.motion_sensors
#
super().__init__(mqtt_client, topic)
self.add_routing(self.KEY_STATE, sw_device, sw_key)
self.add_display(self.KEY_TIMER, motion_function, motion_function.KEY_TIMER)
# motion sensor state
for index, motion_sensor in enumerate(self.motion_sensors):
self.add_display(self.KEY_MOTION_SENSOR % index, motion_sensor, motion_sensor.KEY_OCCUPANCY)
class videv_switch_brightness(videv_base):
KEY_STATE = 'state'
KEY_BRIGHTNESS = 'brightness'
def __init__(self, mqtt_client, topic, sw_device, sw_key, br_device, br_key):
super().__init__(mqtt_client, topic)
self.add_routing(self.KEY_STATE, sw_device, sw_key)
self.add_routing(self.KEY_BRIGHTNESS, br_device, br_key)
class videv_switch_brightness_color_temp(videv_base):
KEY_STATE = 'state'
KEY_BRIGHTNESS = 'brightness'
KEY_COLOR_TEMP = 'color_temp'
def __init__(self, mqtt_client, topic, sw_device, sw_key, br_device, br_key, ct_device, ct_key):
super().__init__(mqtt_client, topic)
self.add_routing(self.KEY_STATE, sw_device, sw_key)
self.add_routing(self.KEY_BRIGHTNESS, br_device, br_key)
self.add_routing(self.KEY_COLOR_TEMP, ct_device, ct_key)
class videv_heating(videv_base):
KEY_USER_TEMPERATURE_SETPOINT = 'user_temperature_setpoint'
KEY_VALVE_TEMPERATURE_SETPOINT = 'valve_temperature_setpoint'
KEY_AWAY_MODE = 'away_mode'
KEY_SUMMER_MODE = 'summer_mode'
KEY_START_BOOST = 'start_boost'
KEY_SET_DEFAULT_TEMPERATURE = 'set_default_temperature'
KEY_BOOST_TIMER = 'boost_timer'
#
KEY_TEMPERATURE = 'temperature'
def __init__(self, mqtt_client, topic, heating_function):
super().__init__(mqtt_client, topic)
#
self.add_routing(self.KEY_USER_TEMPERATURE_SETPOINT, heating_function, heating_function.KEY_USER_TEMPERATURE_SETPOINT)
self.add_routing(self.KEY_AWAY_MODE, heating_function, heating_function.KEY_AWAY_MODE)
self.add_routing(self.KEY_SUMMER_MODE, heating_function, heating_function.KEY_SUMMER_MODE)
#
self.add_control(self.KEY_START_BOOST, heating_function, heating_function.KEY_START_BOOST, False)
self.add_control(self.KEY_SET_DEFAULT_TEMPERATURE, heating_function, heating_function.KEY_SET_DEFAULT_TEMPERATURE, False)
#
self.add_display(self.KEY_VALVE_TEMPERATURE_SETPOINT, heating_function, heating_function.KEY_TEMPERATURE_SETPOINT)
self.add_display(self.KEY_BOOST_TIMER, heating_function, heating_function.KEY_BOOST_TIMER)
self.add_display(self.KEY_TEMPERATURE, heating_function, heating_function.KEY_TEMPERATURE_CURRENT, False)
class videv_multistate(videv_base):
KEY_STATE = 'state_%d'
def __init__(self, mqtt_client, topic, key_for_device, device, num_states, default_values=None):
super().__init__(mqtt_client, topic)
self.num_states = num_states
# send default values
for i in range(0, num_states):
self.__tx__(self.KEY_STATE % i, False)
#
device.add_callback(key_for_device, None, self.__index_rx__, True)
def __index_rx__(self, device, key, data):
for i in range(0, self.num_states):
self.__tx__(self.KEY_STATE % i, i == data)
class videv_audio_player(videv_base):
KEY_ACTIVE_PLAYER = 'player_%d'
KEY_TITLE = 'title'
NO_TITLE = '---'
def __init__(self, mqtt_client, topic, *args):
super().__init__(mqtt_client, topic)
for i, device in enumerate(args):
self.add_display(self.KEY_ACTIVE_PLAYER % i, device, device.KEY_STATE)
#
for audio_device in args:
audio_device.add_callback(audio_device.KEY_TITLE, None, self.__title_rx__, True)
def __title_rx__(self, device, key, data):
self.__tx__(self.KEY_TITLE, data or self.NO_TITLE)
class all_off(videv_base):
ALLOWED_CLASSES = (room, room_collection, )
def __init__(self, mqtt_client, topic, room_collection):
super().__init__(mqtt_client, topic)
self.__room_collection__ = room_collection
# init __inst_dict__
self.__inst_dict__ = {}
self.__add_instances__("all", self.__room_collection__)
# register mqtt callbacks for all my keys
for key in self.__inst_dict__:
mqtt_client.add_callback(topic + "/" + key, self.all_off)
def __check_inst_capabilities__(self, name, inst):
# fits to specified classes
if isinstance(inst, self.ALLOWED_CLASSES):
try:
# all_off method is callable
return callable(inst.all_off)
except AttributeError:
# all_off method does not exist
return False
return False
def __add_instances__(self, name, inst, level=0):
if self.__check_inst_capabilities__(name, inst):
# add given instance to my __inst_dict__
self.__inst_dict__[name] = inst
# iterate over all attribute names of instance
for sub_name in dir(inst):
# attribute name is not private
if not sub_name.startswith("__"):
sub = getattr(inst, sub_name)
# recurse with this object
if level == 0:
self.__add_instances__(sub_name, sub, level=level+1)
else:
self.__add_instances__(name + "/" + sub_name, sub, level=level+1)
def all_off(self, client, userdata, message):
key = message.topic[len(self.topic) + 1:]
self.__inst_dict__[key].all_off()

2
geo

@ -1 +1 @@
Subproject commit edc3c9975ec723abf81e9ba52b4012f4674f3ac5 Subproject commit 11166bb27ad2335f7812fcb88c788397f5106751

8
init_venv Executable file
View File

@ -0,0 +1,8 @@
#!/bin/sh
#
BASEPATH=`realpath $(dirname $0)`
python3 -m venv $BASEPATH/venv
$BASEPATH/venv/bin/pip install --upgrade pip
find $BASEPATH -name requirements.txt | xargs -L 1 $BASEPATH/venv/bin/pip install -r
$BASEPATH/venv/bin/pip list --outdated --format=json | jq -r '.[] | .name'|xargs -n1 $BASEPATH/venv/bin/pip install -U

2
mqtt

@ -1 +1 @@
Subproject commit c2b32852127bc1a3aca078a2dad3993f46cb81c2 Subproject commit 1adfb0626e7777c6d29be65d4ad4ce2d57541301

2
report

@ -1 +1 @@
Subproject commit 152690007a3b87ee0047bcad78b2673111ff1928 Subproject commit b53dd30eae1d679b7eec4999bec50aed55bc105b

View File

@ -1,18 +1,20 @@
import config import config
import devdi.devices
import function import function
import json import json
import logging
import mqtt import mqtt
import os import os
import report import report
import subprocess import subprocess
import time import time
logger = report.default_logging_config() logger = logging.getLogger(config.APP_NAME)
VERS_MAJOR = 1 VERS_MAJOR = 1
VERS_MINOR = 4 VERS_MINOR = 2
VERS_PATCH = 2 VERS_PATCH = 6
INFO_TOPIC = "__info__" INFO_TOPIC = "__info__"
INFO_DATA = { INFO_DATA = {
@ -37,6 +39,15 @@ def __info_publisher__(client, userdata, message):
if __name__ == "__main__": if __name__ == "__main__":
#
# Logging
#
if config.DEBUG:
report.appLoggingConfigure(None, 'stdout', ((config.APP_NAME, logging.DEBUG), ),
target_level=logging.WARNING, fmt=report.SHORT_FMT, host='localhost', port=19996)
else:
report.stdoutLoggingConfigure(((config.APP_NAME, config.LOGLEVEL), ), report.SHORT_FMT)
# #
# MQTT Client # MQTT Client
# #
@ -44,10 +55,20 @@ if __name__ == "__main__":
password=config.MQTT_PASSWORD, name=config.APP_NAME) password=config.MQTT_PASSWORD, name=config.APP_NAME)
mc.add_callback(INFO_TOPIC, __info_publisher__) mc.add_callback(INFO_TOPIC, __info_publisher__)
#
# Smarthome physical Devices
#
pd = devdi.devices.physical_devices(mc)
#
# Smarthome physical Devices
#
vd = devdi.devices.videv_devices(mc)
# #
# Smart Home Functionality # Smart Home Functionality
# #
func = function.all_functions(mc) func = function.all_functions(mc, pd, vd)
while (func.run): while (True):
time.sleep(1) time.sleep(1)

@ -1 +0,0 @@
Subproject commit c75c0cfacd48e9d51817f128539f96487cef2f98

2
task

@ -1 +1 @@
Subproject commit bb6f7ea26f24481cba7218256e47572fa84db478 Subproject commit af35e83d1f07fd4cb9070bdb77cf1f3bdda3a463

73
topics.py Normal file
View File

@ -0,0 +1,73 @@
#
# TOPICS
#
TOPIC_WARNINGS = "videv/warnings"
TOPIC_ALL_OFF_VIDEV = "videv/off"
# ground floor west
# floor
TOPIC_GFW_FLOOR_MAIN_LIGHT_VIDEV = "videv/gfw/floor/main_light"
# marion
TOPIC_GFW_MARION_MAIN_LIGHT_VIDEV = "videv/gfw/marion/main_light"
TOPIC_GFW_MARION_HEATING_VALVE_VIDEV = "videv/gfw/marion/heating_valve"
# dirk
TOPIC_GFW_DIRK_MAIN_LIGHT_VIDEV = "videv/gfw/dirk/main_light"
TOPIC_GFW_DIRK_DESK_LIGHT_VIDEV = "videv/gfw/dirk/desk_light"
TOPIC_GFW_DIRK_AMPLIFIER_VIDEV = "videv/gfw/dirk/amplifier"
TOPIC_GFW_DIRK_CD_PLAYER_VIDEV = "videv/gfw/dirk/cd_player"
TOPIC_GFW_DIRK_PC_DOCK_VIDEV = "videv/gfw/dirk/pc_dock"
TOPIC_GFW_DIRK_ACTIVE_BRIGHTNESS_DEVICE_VIDEV = "videv/gfw/dirk/active_brightness_device"
TOPIC_GFW_DIRK_AUDIO_PLAYER_VIDEV = "videv/gfw/dirk/audio_player"
TOPIC_GFW_DIRK_HEATING_VALVE_VIDEV = "videv/gfw/dirk/heating_valve"
TOPIC_GAR_GARDEN_GARLAND_VIDEV = 'videv/gar/garden/garland'
# first floor west
# julian
TOPIC_FFW_JULIAN_MAIN_LIGHT_VIDEV = "videv/ffw/julian/main_light"
TOPIC_FFW_JULIAN_HEATING_VALVE_VIDEV = "videv/ffw/julian/heating_valve"
# bath
TOPIC_FFW_BATH_HEATING_VALVE_VIDEV = "videv/ffw/bath/heating_valve"
# livingroom
TOPIC_FFW_LIVINGROOM_MAIN_LIGHT_VIDEV = "videv/ffw/livingroom/main_light"
TOPIC_FFW_LIVINGROOM_HEATING_VALVE_VIDEV = "videv/ffw/livingroom/heating_valve"
# sleep
TOPIC_FFW_SLEEP_MAIN_LIGHT_VIDEV = "videv/ffw/sleep/main_light"
TOPIC_FFW_SLEEP_HEATING_VALVE_VIDEV = "videv/ffw/sleep/heating_valve"
# first floor east
# floor
TOPIC_FFE_FLOOR_MAIN_LIGHT_VIDEV = "videv/ffe/floor/main_light"
# kitchen
TOPIC_FFE_KITCHEN_MAIN_LIGHT_VIDEV = "videv/ffe/kitchen/main_light"
TOPIC_FFE_KITCHEN_CIRCULATION_PUMP_VIDEV = "videv/ffe/kitchen/circulation_pump"
TOPIC_FFE_KITCHEN_HEATING_VALVE_VIDEV = "videv/ffe/kitchen/heating_valve"
# diningroom
TOPIC_FFE_DININGROOM_MAIN_LIGHT_VIDEV = "videv/ffe/diningroom/main_light"
TOPIC_FFE_DININGROOM_FLOOR_LAMP_VIDEV = "videv/ffe/diningroom/floorlamp"
TOPIC_FFE_DININGROOM_GARLAND_VIDEV = "videv/ffe/diningroom/garland"
TOPIC_FFE_DININGROOM_HEATING_VALVE_VIDEV = "videv/ffe/diningroom/heating_valve"
# sleep
TOPIC_FFE_SLEEP_MAIN_LIGHT_VIDEV = "videv/ffe/sleep/main_light"
TOPIC_FFE_SLEEP_BED_LIGHT_DI_VIDEV = "videv/ffe/sleep/bed_light_di"
TOPIC_FFE_SLEEP_BED_LIGHT_MA_VIDEV = "videv/ffe/sleep/bed_light_ma"
TOPIC_FFE_SLEEP_ACTIVE_BRIGHTNESS_DEVICE_VIDEV = "videv/ffe/sleep/active_brightness_device"
TOPIC_FFE_SLEEP_HEATING_VALVE_VIDEV = "videv/ffe/sleep/heating_valve"
# livingroom
TOPIC_FFE_LIVINGROOM_MAIN_LIGHT_VIDEV = "videv/ffe/livingroom/main_light"
TOPIC_FFE_LIVINGROOM_FLOOR_LAMP_VIDEV = "videv/ffe/livingroom/floorlamp"
TOPIC_FFE_LIVINGROOM_XMAS_TREE_VIDEV = "videv/ffe/livingroom/xmas_tree"
TOPIC_FFE_LIVINGROOM_HEATING_VALVE_VIDEV = "videv/ffe/livingroom/heating_valve"
# first floor east
# floor
TOPIC_STW_STAIRWAY_MAIN_LIGHT_VIDEV = "videv/stw/stairway/main_light"