Compare commits

..

22 Commits

Author SHA1 Message Date
34ba0f0d01 Slight rework of heating function 2023-02-06 12:42:44 +01:00
dd178d81a6 test and simulation extracted to own project space 2023-02-06 11:30:46 +01:00
98f1acd2eb all_off functionality added 2023-01-31 19:17:10 +01:00
a33f9b23da Bug-Fix all-off functionality removed due to missing device 2023-01-30 12:26:33 +01:00
0365b5de4a simulation update 2023-01-30 12:22:59 +01:00
ba7a8b4750 device: brightness and color temperature calculations corrected 2023-01-30 12:22:03 +01:00
5fc096c9c7 old gui interface removed and clean up 2023-01-30 12:20:02 +01:00
3a4d43c0c4 videv loopback prevention ignore rx after tx 2023-01-29 12:41:35 +01:00
6b95bda1a4 Fix: display current_temperature videv on every rx 2023-01-28 21:25:15 +01:00
ed13a96a4d rework of videv 2023-01-28 14:32:52 +01:00
d33901aef3 all off improved 2023-01-26 10:40:56 +01:00
95bfa4937f User feedback (flash) implemented in new structure 2023-01-26 08:25:40 +01:00
51e14b7b1b Revert "send setting always for heating valve device"
This reverts commit 54d72802c253a7f137b58eb3712ea0ecb2929978.
2023-01-25 15:41:37 +01:00
54d72802c2 send setting always for heating valve device 2023-01-25 15:13:08 +01:00
d98541b818 Bug-Fix for videv_multistate 2023-01-25 13:15:33 +01:00
be3d70f942 videv audio_player implemented 2023-01-25 12:49:36 +01:00
40570998d0 Bug-Fix: timer motion_sensor_light 2023-01-25 11:53:38 +01:00
036cddeb8e 2nd Bug-Fix: modules.brightness_choose_n_action 2023-01-25 11:33:46 +01:00
e755b75106 Bug-Fix: modules.brightness_choose_n_action 2023-01-25 11:30:51 +01:00
9d550f1b04 Bug-Fix: current cemperature heating + brightness device selection in videv 2023-01-25 11:17:29 +01:00
4ddc871aa1 Restructured: Splitting function from gui + videv implementation 2023-01-25 07:40:33 +01:00
1ef0f52d37 videv implementation for lights 2023-01-20 08:03:06 +01:00
33 changed files with 1674 additions and 145773 deletions

6
.gitmodules vendored
View File

@ -10,9 +10,3 @@
[submodule "geo"] [submodule "geo"]
path = geo path = geo
url = https://git.mount-mockery.de/pylib/geo.git url = https://git.mount-mockery.de/pylib/geo.git
[submodule "devdi"]
path = devdi
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

View File

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",

21
.vscode/settings.json vendored
View File

@ -1,14 +1,11 @@
{ {
"python.defaultInterpreterPath": "./venv/bin/python", "python.defaultInterpreterPath": "./venv/bin/python",
"autopep8.args": ["--max-line-length=150"], "editor.formatOnSave": true,
"[python]": { "autopep8.args": [
"python.formatting.provider": "none", "--max-line-length=150"
"editor.defaultFormatter": "ms-python.autopep8", ],
"editor.formatOnSave": true "editor.fontSize": 14,
}, "emmet.includeLanguages": {
"editor.fontSize": 14, "django-html": "html"
"emmet.includeLanguages": { "django-html": "html" }, }
"python.testing.pytestArgs": ["-v", "--cov", "--cov-report=xml", "__test__"],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true
} }

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"

41
__install__.py Normal file
View File

@ -0,0 +1,41 @@
#!/bin/python
#
import os
import sys
SERVICE_FILE = """
[Unit]
Description=Smarthome Ambient Information Service
After=network-online.target
Wants=network-online.target
[Service]
User=%(UID)d
Group=%(GID)d
ExecStart=%(MY_PATH)s/smart_brain.sh
Type=simple
[Install]
WantedBy=default.target
"""
def help():
print("Usage: prog <UID> <GID> <TARGET_PATH>")
if __name__ == "__main__":
if len(sys.argv) == 4:
try:
uid = int(sys.argv[1])
gid = int(sys.argv[2])
except ValueError:
help()
else:
if os.path.isdir(sys.argv[3]):
with open(os.path.join(sys.argv[3], 'smart_brain.service'), "w") as fh:
fh.write(SERVICE_FILE % {
"MY_PATH": os.path.dirname(os.path.abspath(__file__)),
"UID": uid,
"GID": gid})
else:
help()
else:
help()

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"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

46
base.py Normal file
View File

@ -0,0 +1,46 @@
import logging
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['__type__'] = self.__class__.__name__
#
self.__callback_list__ = []
self.logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
def add_callback(self, key, data, callback, on_change_only=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)
def set(self, key, data, block_callback=[]):
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])
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)

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

1
devdi

@ -1 +0,0 @@
Subproject commit 5a5679b0baa9ba978f75d8581cb0dc7c13158e34

View File

@ -1,44 +1,50 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
"""
devices (DEVICES)
=================
**Author:**
* Dirk Alders <sudo-dirk@mount-mockery.de>
**Description:**
This Module supports smarthome devices
**Submodules:**
* :mod:`shelly`
* :mod:`silvercrest_powerplug`
**Unittest:**
See also the :download:`unittest <devices/_testresults_/unittest.pdf>` documentation.
**Module Documentation:**
"""
import json
import logging import logging
import task
from smart_devices.shelly import shelly as shelly_sw1
from smart_devices.shelly import shelly_rpc as shelly_pro3
from smart_devices.hue import hue_light as hue_sw_br_ct
from smart_devices.tradfri import tradfri_light as tradfri_sw
from smart_devices.tradfri import tradfri_light as tradfri_sw_br
from smart_devices.tradfri import tradfri_light as tradfri_sw_br_ct
from smart_devices.tradfri import tradfri_button as tradfri_button
from smart_devices.tradfri import tradfri_light as livarno_sw_br_ct
from smart_devices.brennenstuhl import brennenstuhl_heatingvalve
from smart_devices.silvercrest import silvercrest_button
from smart_devices.silvercrest import silvercrest_powerplug
from smart_devices.silvercrest import silvercrest_motion_sensor
from smart_devices.mydevices import powerplug as my_powerplug
from smart_devices.mydevices import audio_status
from smart_devices.mydevices import remote
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__)
BATTERY_WARN_LEVEL = 5
def my_ambient(mqtt_client, topic): def is_json(data):
logger.warning("Device type my_ambient is not yet implemented. Topic %s will not be supported", topic) try:
return None json.loads(data)
except json.decoder.JSONDecodeError:
return False
else:
return True
class group(object): class group(object):
@ -48,23 +54,15 @@ class group(object):
self._iter_counter = 0 self._iter_counter = 0
# #
self.methods = [] self.methods = []
self.variables = [] for method in [m for m in args[0].__class__.__dict__.keys()]:
for name in [m for m in args[0].__class__.__dict__.keys()]: if not method.startswith('_') and callable(getattr(args[0], method)): # add all public callable attributes to the list
if not name.startswith('_') and callable(getattr(args[0], name)): # add all public callable attributes to the list self.methods.append(method)
self.methods.append(name)
if not name.startswith('_') and not callable(getattr(args[0], name)): # add all public callable attributes to the list
self.variables.append(name)
# #
for member in self: for member in self:
methods = [m for m in member.__class__.__dict__.keys() if not m.startswith( methods = [m for m in member.__class__.__dict__.keys() if not m.startswith(
'_') if not m.startswith('_') and callable(getattr(args[0], m))] '_') if not m.startswith('_') and callable(getattr(args[0], m))]
if self.methods != methods: if self.methods != methods:
raise ValueError("All given instances needs to have same methods:", self.methods, methods) raise ValueError("All given instances needs to have same attributes:", self.methods, methods)
#
variables = [v for v in member.__class__.__dict__.keys() if not v.startswith(
'_') if not v.startswith('_') and not callable(getattr(args[0], v))]
if self.variables != variables:
raise ValueError("All given instances needs to have same variables:", self.variables, variables)
def __iter__(self): def __iter__(self):
return self return self
@ -90,9 +88,745 @@ class group(object):
try: try:
rv = super().__getattribute__(name) rv = super().__getattribute__(name)
except AttributeError: except AttributeError:
if callable(getattr(self[0], name)): return group_execution
return group_execution
else:
return getattr(self[0], name)
else: else:
return rv return rv
class base(dict):
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):
# data storage
self.mqtt_client = mqtt_client
self.topic = topic
self.logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
for entry in self.topic.split('/'):
self.logger = self.logger.getChild(entry)
# initialisations
dict.__init__(self)
mqtt_client.add_callback(
topic=self.topic, callback=self.receive_callback)
mqtt_client.add_callback(
topic=self.topic+"/#", callback=self.receive_callback)
#
self.callback_list = []
self.warning_callback = None
#
self.__previous__ = {}
def receive_callback(self, client, userdata, message):
self.unpack(message)
def unpack_filter(self, key):
if key in self.RX_FILTER_DATA_KEYS:
if self.get(key) == 1 or self.get(key) == 'on' or self.get(key) == 'ON':
self[key] = True
elif self.get(key) == 0 or self.get(key) == 'off' or self.get(key) == 'OFF':
self[key] = False
def unpack_single_value(self, key, data):
prev_value = self.get(key)
if key in self.RX_KEYS:
self[key] = data
self.__previous__[key] = prev_value
# Filter, if needed
self.unpack_filter(key)
self.logger.debug("Received data %s - %s", key, str(self.get(key)))
self.callback_caller(key, self[key], self.get(key) != self.__previous__.get(key))
elif key not in self.RX_IGNORE_KEYS:
self.logger.warning('Got a message with unparsed content: "%s - %s"', key, str(data))
else:
self.logger.debug("Ignoring key %s", key)
def unpack(self, message):
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.unpack_single_value(key, data[key])
else:
self.unpack_single_value(content_key, data)
# String
else:
self.unpack_single_value(
content_key, message.payload.decode('utf-8'))
self.warning_caller()
else:
self.logger.debug("Ignoring topic %s", content_key)
def pack_filter(self, key, data):
if key in self.TX_FILTER_DATA_KEYS:
if data is True:
return "on"
elif data is False:
return "off"
else:
return data
return data
def set(self, key, data):
self.pack(key, data)
def pack(self, key, data):
data = self.pack_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:
self.mqtt_client.send('/'.join([self.topic, self.TX_TOPIC]), json.dumps({key: data}))
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")
def add_callback(self, key, data, callback, on_change_only=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)
def add_warning_callback(self, callback):
self.warning_callback = callback
def warning_call_condition(self):
return False
def callback_caller(self, key, data, value_changed):
for cb_key, cb_data, callback, on_change_only in self.callback_list:
if (cb_key == key or cb_key is None) and (cb_data == data or cb_data is None) and callback is not None:
if not on_change_only or value_changed:
callback(self, key, data)
def warning_caller(self):
if self.warning_call_condition():
warn_txt = self.warning_text()
self.logger.warning(warn_txt)
if self.warning_callback is not None:
self.warning_callback(self, warn_txt)
def warning_text(self):
return "default warning text - replace parent warning_text function"
def previous_value(self, key):
return self.__previous__.get(key)
class shelly(base):
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 flash_task(self, *args):
if self.flash_active:
self.pack(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_call_condition(self):
return self.get(self.KEY_OVERTEMPERATURE)
def warning_text(self):
if self.overtemperature:
if self.temperature is not None:
return "Overtemperature detected for %s. Temperature was %.1f°C." % (self.topic, self.temperature)
else:
return "Overtemperature detected for %s." % self.topic
#
# 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, 'toggle']"""
self.pack(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('toggle')
def set_output_1(self, state):
"""state: [True, False, 'toggle']"""
self.pack(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('toggle')
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:
self.set_output_0(False)
self.set_output_1(False)
class silvercrest_powerplug(base):
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 __init__(self, mqtt_client, topic):
super().__init__(mqtt_client, topic)
#
# 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, 'toggle']"""
self.pack(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('toggle')
def all_off(self):
self.set_output_0(False)
class silvercrest_motion_sensor(base):
KEY_BATTERY = "battery"
KEY_BATTERY_LOW = "battery_low"
KEY_LINKQUALITY = "linkquality"
KEY_OCCUPANCY = "occupancy"
KEY_UNMOUNTED = "tamper"
KEY_VOLTAGE = "voltage"
#
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 warning_call_condition(self):
return self.get(self.KEY_BATTERY_LOW)
def warning_text(self, data):
return "Battery low: level=%d" % self.get(self.KEY_BATTERY)
#
# RX
#
@property
def linkquality(self):
"""rv: numeric value"""
return self.get(self.KEY_LINKQUALITY)
class my_powerplug(base):
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 __init__(self, mqtt_client, topic):
super().__init__(mqtt_client, topic)
#
# 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.pack(key, state)
else:
logging.error("Unknown key to set the output!")
def set_output_0(self, state):
"""state: [True, False, 'toggle']"""
self.pack(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('toggle')
def set_output_1(self, state):
"""state: [True, False, 'toggle']"""
self.pack(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('toggle')
def set_output_2(self, state):
"""state: [True, False, 'toggle']"""
self.pack(self.KEY_OUTPUT_2, state)
def set_output_2_mcb(self, device, key, data):
self.logger.log(logging.INFO if data != self.output_2 else logging.DEBUG, "Changing output 2 to %s", str(data))
self.set_output_2(data)
def toggle_output_2_mcb(self, device, key, data):
self.logger.info("Toggeling output 2")
self.set_output_2('toggle')
def set_output_3(self, state):
"""state: [True, False, 'toggle']"""
self.pack(self.KEY_OUTPUT_3, state)
def set_output_3_mcb(self, device, key, data):
self.logger.log(logging.INFO if data != self.output_3 else logging.DEBUG, "Changing output 3 to %s", str(data))
self.set_output_3(data)
def toggle_output_3_mcb(self, device, key, data):
self.logger.info("Toggeling output 3")
self.set_output_3('toggle')
def set_output_all(self, state):
"""state: [True, False, 'toggle']"""
self.pack(self.KEY_OUTPUT_ALL, state)
def set_output_all_mcb(self, device, key, data):
self.logger.info("Changing all outputs to %s", str(data))
self.set_output_all(data)
def toggle_output_all_mcb(self, device, key, data):
self.logger.info("Toggeling all outputs")
self.set_output_0('toggle')
def all_off(self):
self.set_output_all(False)
class tradfri_light(base):
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 __init__(self, mqtt_client, topic):
super().__init__(mqtt_client, topic)
def unpack_filter(self, key):
if key == self.KEY_BRIGHTNESS:
self[key] = round((self[key] - 1) * 100 / 253, 0)
elif key == self.KEY_COLOR_TEMP:
self[key] = round((self[key] - 250) * 10 / 204, 0)
else:
super().unpack_filter(key)
def pack_filter(self, key, data):
if key == self.KEY_BRIGHTNESS:
return round(data * 253 / 100 + 1, 0)
elif key == self.KEY_COLOR_TEMP:
return round(data * 204 / 10 + 250, 0)
else:
return super().pack_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, 'toggle']"""
self.pack(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('toggle')
def set_brightness(self, brightness):
"""brightness: [0, ..., 100]"""
self.pack(self.KEY_BRIGHTNESS, brightness)
def set_brightness_mcb(self, device, key, data):
self.logger.log(logging.INFO if data != self.brightness else logging.DEBUG, "Changing brightness to %s", str(data))
self.set_brightness(data)
def default_inc(self, speed=40):
self.pack(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.pack(self.KEY_COLOR_TEMP, color_temp)
def set_color_temp_mcb(self, device, key, data):
self.logger.log(logging.INFO if data != self.color_temp else logging.DEBUG, "Changing color temperature to %s", str(data))
self.set_color_temp(data)
def all_off(self):
self.set_output_0(False)
class tradfri_button(base):
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)
#
# RX
#
@property
def action(self):
"""rv: action_txt"""
return self.get(self.KEY_ACTION)
#
# WARNING CALL
#
def warning_call_condition(self):
return self.get(self.KEY_BATTERY) is not None and self.get(self.KEY_BATTERY) <= BATTERY_WARN_LEVEL
def warning_text(self):
return "Low battery level detected for %s. Battery level was %.0f%%." % (self.topic, self.get(self.KEY_BATTERY))
class brennenstuhl_heatingvalve(base):
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"
#
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"}))
def warning_call_condition(self):
return self.get(self.KEY_BATTERY, 100) <= BATTERY_WARN_LEVEL
def warning_text(self):
return "Low battery level detected for %s. Battery level was %.0f%%." % (self.topic, self.get(self.KEY_BATTERY))
#
# 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.pack(self.KEY_HEATING_SETPOINT, setpoint)
def set_heating_setpoint_mcb(self, device, key, data):
self.logger.info("Changing heating setpoint to %s", str(data))
self.set_heating_setpoint(data)
class remote(base):
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 set_cd(self, device=None, key=None, data=None):
self.pack(self.KEY_CD, None)
def set_line1(self, device=None, key=None, data=None):
self.pack(self.KEY_LINE1, None)
def set_line3(self, device=None, key=None, data=None):
self.pack(self.KEY_LINE3, None)
def set_mute(self, device=None, key=None, data=None):
self.pack(self.KEY_MUTE, None)
def set_power(self, device=None, key=None, data=None):
self.pack(self.KEY_POWER, None)
def set_volume_up(self, data=False):
"""data: [True, False]"""
self.pack(self.KEY_VOLUP, data)
def set_volume_down(self, data=False):
"""data: [True, False]"""
self.pack(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 status(base):
KEY_STATE = "state"
#
TX_TYPE = base.TX_VALUE
#
RX_KEYS = [KEY_STATE]
def set_state(self, num, data):
"""data: [True, False]"""
self.pack(self.KEY_STATE + "/" + str(num), data)
def set_state_mcb(self, device, key, data):
self.logger.info("Changing state to %s", str(data))
self.set_state(data)
class audio_status(status):
KEY_TITLE = "title"
#
RX_KEYS = [status.KEY_STATE, KEY_TITLE]

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.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 inspect
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,19 +19,12 @@ 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):
super().__init__(mqtt_client) super().__init__(mqtt_client)
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
self.gar = garden(self.mqtt_client)
# stairway # stairway
self.stw = stairway(self.mqtt_client) self.stw = stairway(self.mqtt_client)
# ground floor west # ground floor west
@ -49,57 +40,32 @@ 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(devices.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(devices.tradfri_button.KEY_ACTION,
self.ffe.sleep.input_device.ACTION_RIGHT, self.ffe.floor.switch_main_light.toggle_output_0_mcb) devices.tradfri_button.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(devices.shelly.KEY_LONGPUSH_0, True, self.stw.stairway.main_light_shelly.flash_0_mcb)
True, self.stw.stairway.switch_main_light.flash_0_mcb) self.stw.stairway.main_light_shelly.add_callback(devices.shelly.KEY_LONGPUSH_0, True, self.all_off)
self.stw.stairway.switch_main_light.add_callback(self.stw.stairway.switch_main_light.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(devices.shelly.KEY_LONGPUSH_0, True, self.ffe.floor.main_light_shelly.flash_0_mcb)
True, self.ffe.floor.switch_main_light.flash_0_mcb) self.ffe.floor.main_light_shelly.add_callback(devices.shelly.KEY_LONGPUSH_0, True, self.ffe.all_off)
self.ffe.floor.switch_main_light.add_callback(self.ffe.floor.switch_main_light.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

@ -1,31 +1,15 @@
from function.modules import heating_function
import os import os
import sqlite3 import sqlite3
db_file = os.path.join(os.path.dirname(__file__), '..', 'database.db') db_file = os.path.join(os.path.dirname(__file__), '..', 'database.db')
db_mapping_radiator = {
0: heating_function.KEY_AWAY_MODE,
1: heating_function.KEY_SUMMER_MODE,
2: heating_function.KEY_USER_TEMPERATURE_SETPOINT,
3: heating_function.KEY_TEMPERATURE_SETPOINT
}
def get_radiator_data(topic): def get_radiator_data(topic):
db_data = __storage__().get_radiator_data(topic) return __storage__().get_radiator_data(topic)
rv = {}
for index in db_mapping_radiator:
rv[db_mapping_radiator[index]] = db_data[index]
return rv
def set_radiator_data(device, key, data): def set_radiator_data(topic, away_mode, summer_mode, user_temperatur_setpoint, temperatur_setpoint):
if key in db_mapping_radiator.values(): return __storage__().store_radiator_data(topic, away_mode, summer_mode, user_temperatur_setpoint, temperatur_setpoint)
db_data = []
for index in range(0, len(db_mapping_radiator)):
db_data.append(device.get(db_mapping_radiator[index]))
return __storage__().store_radiator_data(device.heating_valve.topic, db_data)
class __storage__(object): class __storage__(object):
@ -41,17 +25,19 @@ class __storage__(object):
temperatur_setpoint real temperatur_setpoint real
)""") )""")
def store_radiator_data(self, topic, target_data): def store_radiator_data(self, topic, away_mode, summer_mode, user_temperatur_setpoint, temperatur_setpoint):
data = [topic, away_mode, summer_mode, user_temperatur_setpoint, temperatur_setpoint]
try: try:
with self.conn: with self.conn:
self.c.execute( self.c.execute(
'INSERT INTO radiator VALUES (?, ?, ?, ?, ?)', [topic] + target_data) 'INSERT INTO radiator VALUES (?, ?, ?, ?, ?)', data)
except sqlite3.IntegrityError: except sqlite3.IntegrityError:
data = [away_mode, summer_mode, user_temperatur_setpoint, temperatur_setpoint]
db_data = self.get_radiator_data(topic) db_data = self.get_radiator_data(topic)
if db_data != target_data: if db_data != data:
with self.conn: with self.conn:
self.c.execute( self.c.execute(
'UPDATE radiator SET away_mode = ?, summer_mode = ?, user_temperatur_setpoint = ?, temperatur_setpoint = ? WHERE topic = ?', target_data + [topic]) 'UPDATE radiator SET away_mode = ?, summer_mode = ?, user_temperatur_setpoint = ?, temperatur_setpoint = ? WHERE topic = ?', data + [topic])
def get_radiator_data(self, topic): def get_radiator_data(self, topic):
""" returns a list [away_mode, summer_mode, user_temperatur_setpoint, temperatur_setpoint] or [None, None, None, None]""" """ returns a list [away_mode, summer_mode, user_temperatur_setpoint, temperatur_setpoint] or [None, None, None, None]"""

View File

@ -3,11 +3,10 @@
# #
import config import config
from devdi import rooms import devices
from function.db import get_radiator_data, set_radiator_data from function.modules import brightness_choose_n_action, timer_on_activation, heating_function
from function.helpers import day_event
from function.modules import brightness_choose_n_action, timer_on_activation, 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, videv_switching_timer, videv_switch_brightness_color_temp, videv_heating, videv_multistate
import logging import logging
try: try:
@ -18,7 +17,7 @@ logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
class first_floor_east(room_collection): class first_floor_east(room_collection):
def __init__(self, mqtt_client,): def __init__(self, mqtt_client):
super().__init__(mqtt_client) super().__init__(mqtt_client)
self.dining = first_floor_east_dining(mqtt_client) self.dining = first_floor_east_dining(mqtt_client)
self.floor = first_floor_east_floor(mqtt_client) self.floor = first_floor_east_floor(mqtt_client)
@ -27,147 +26,193 @@ class first_floor_east(room_collection):
self.sleep = first_floor_east_sleep(mqtt_client) self.sleep = first_floor_east_sleep(mqtt_client)
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):
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)
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) self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFE_FLOOR_MAIN_LIGHT_SHELLY)
self.videv_main_light.connect_br_device(self.light_main_light, self.light_main_light.KEY_BRIGHTNESS) super().__init__(mqtt_client)
self.videv_main_light.connect_ct_device(self.light_main_light, self.light_main_light.KEY_COLOR_TEMP)
# 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
#
# circulation pump self.main_light = videv_switching(
self.circulation_pump = timer_on_activation(self.switch_circulation_pump, self.switch_circulation_pump.KEY_OUTPUT_0, 10*60) mqtt_client, config.TOPIC_FFE_FLOOR_MAIN_LIGHT_VIDEV,
self.switch_circulation_pump.add_callback(self.switch_circulation_pump.KEY_OUTPUT_0, True, self.switch_main_light.flash_0_mcb, True) self.main_light_shelly, devices.shelly.KEY_OUTPUT_0
self.videv_circulation_pump.connect_sw_device(self.switch_circulation_pump, self.switch_circulation_pump.KEY_OUTPUT_0)
self.videv_circulation_pump.connect_tm_device(self.circulation_pump, timer_on_activation.KEY_TIMER)
# 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)
self.videv_heating.connect_heating_function(self.heating_function)
class first_floor_east_dining(rooms.ffe_diningroom, room): class first_floor_east_kitchen(room):
def __init__(self, mqtt_client): def __init__(self, mqtt_client):
super().__init__(mqtt_client)
room.__init__(self, mqtt_client)
# #
self.day_events = day_event((6, 0), (22, 0), 30, -30) # Device initialisation
self.day_events.add_callback(None, True, self.__day_events__, True) #
# http://shelly1l-8CAAB5616C01
self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFE_KITCHEN_MAIN_LIGHT_SHELLY)
# http://shelly1-e89f6d85a466/
self.circulation_pump_shelly = devices.shelly(mqtt_client, config.TOPIC_FFE_KITCHEN_CIRCULATION_PUMP_SHELLY)
# light <-> videv super().__init__(mqtt_client)
self.videv_main_light.connect_sw_device(self.switch_main_light, self.switch_main_light.KEY_OUTPUT_0)
self.videv_floor_light.connect_sw_device(self.switch_floor_light, self.switch_floor_light.KEY_OUTPUT_0) #
# Functionality initialisation
#
self.circulation_pump = timer_on_activation(self.circulation_pump_shelly, devices.shelly.KEY_OUTPUT_0, 10*60)
self.circulation_pump_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, True, self.main_light_shelly.flash_0_mcb, True)
#
# Virtual Device Interface
#
self.main_light_videv = videv_switching(
mqtt_client, config.TOPIC_FFE_KITCHEN_MAIN_LIGHT_VIDEV,
self.main_light_shelly, devices.shelly.KEY_OUTPUT_0
)
self.circulation_pump_videv = videv_switching_timer(
mqtt_client, config.TOPIC_FFE_KITCHEN_CIRCULATION_PUMP_VIDEV,
self.circulation_pump_shelly, devices.shelly.KEY_OUTPUT_0,
self.circulation_pump, timer_on_activation.KEY_TIMER
)
class first_floor_east_dining(room):
def __init__(self, mqtt_client):
#
# Device initialisation
#
# http://shelly1l-84CCA8ADD055
self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFE_DININGROOM_MAIN_LIGHT_SHELLY)
self.floorlamp_powerplug = devices.silvercrest_powerplug(mqtt_client, config.TOPIC_FFE_DININGROOM_FLOOR_LAMP_POWERPLUG)
if config.CHRISTMAS: if config.CHRISTMAS:
self.videv_garland_light.connect_sw_device(self.switch_garland_light, self.switch_garland_light.KEY_OUTPUT_0) self.garland_powerplug = devices.silvercrest_powerplug(mqtt_client, config.TOPIC_FFE_DININGROOM_GARLAND_POWERPLUG)
# 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
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)
def __day_events__(self, device, key, data):
if key in (self.day_events.KEY_SUNSET, self.day_events.KEY_START_OF_DAY):
if config.CHRISTMAS:
self.switch_garland_light.set_output_0(True)
elif key in (self.day_events.KEY_START_OF_NIGHT, self.day_events.KEY_SUNRISE):
if config.CHRISTMAS:
self.switch_garland_light.set_output_0(False)
class first_floor_east_sleep(rooms.ffe_sleep, room):
def __init__(self, mqtt_client):
super().__init__(mqtt_client) super().__init__(mqtt_client)
room.__init__(self, mqtt_client)
#
self.light_wardrobe_light.disable_all_off() # Always on - Off by light sensor
# 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)
self.videv_bed_dirk_light.connect_br_device(self.light_bed_dirk_light, self.light_bed_dirk_light.KEY_BRIGHTNESS)
#
self.videv_bed_marion_light.connect_sw_device(self.switch_bed_marion_light, self.switch_bed_marion_light.KEY_OUTPUT_0)
#
self.videv_wardrobe_light.connect_sw_device(self.light_wardrobe_light, self.light_wardrobe_light.KEY_OUTPUT_0)
self.videv_wardrobe_light.connect_br_device(self.light_wardrobe_light, self.light_wardrobe_light.KEY_BRIGHTNESS)
#
# Functionality initialisation
#
self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.floorlamp_powerplug.set_output_0_mcb, True)
#
# Virtual Device Interface
#
self.main_light_videv = videv_switching(
mqtt_client, config.TOPIC_FFE_DININGROOM_MAIN_LIGHT_VIDEV,
self.main_light_shelly, devices.shelly.KEY_OUTPUT_0
)
self.floorlamp_videv = videv_switching(
mqtt_client, config.TOPIC_FFE_DININGROOM_FLOOR_LAMP_VIDEV,
self.floorlamp_powerplug, devices.silvercrest_powerplug.KEY_OUTPUT_0
)
if config.CHRISTMAS:
self.garland_videv = videv_switching(
mqtt_client, config.TOPIC_FFE_DININGROOM_GARLAND_VIDEV,
self.garland_powerplug, devices.silvercrest_powerplug.KEY_OUTPUT_0
)
class first_floor_east_sleep(room):
def __init__(self, mqtt_client):
#
# Device initialisation
#
# http://shelly1l-E8DB84A254C7
self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFE_SLEEP_MAIN_LIGHT_SHELLY)
self.main_light_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_FFE_SLEEP_MAIN_LIGHT_ZIGBEE)
self.bed_light_di_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_FFE_SLEEP_BED_LIGHT_DI_ZIGBEE)
self.bed_light_ma_powerplug = devices.silvercrest_powerplug(mqtt_client, config.TOPIC_FFE_SLEEP_BED_LIGHT_MA_POWERPLUG)
self.heating_valve = devices.brennenstuhl_heatingvalve(mqtt_client, config.TOPIC_FFE_SLEEP_HEATING_VALVE_ZIGBEE)
self.button_tradfri = devices.tradfri_button(mqtt_client, config.TOPIC_FFE_SLEEP_INPUT_DEVICE)
super().__init__(mqtt_client)
#
# Functionality initialisation
#
# button / brightness function # button / brightness function
self.brightness_functions = brightness_choose_n_action(self.input_device) self.brightness_functions = brightness_choose_n_action(self.button_tradfri)
self.brightness_functions.add(self.light_main_light, self.switch_main_light, self.switch_main_light.KEY_OUTPUT_0) self.brightness_functions.add(self.main_light_tradfri, self.main_light_shelly, devices.shelly.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) self.brightness_functions.add(self.bed_light_di_tradfri, self.bed_light_di_tradfri, devices.tradfri_light.KEY_OUTPUT_0)
# button / main light # 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) self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_TOGGLE,
self.main_light_shelly.toggle_output_0_mcb)
# button / bed light # 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.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_LEFT,
self.input_device.add_callback(self.input_device.KEY_ACTION, self.input_device.ACTION_LEFT_LONG, self.bed_light_di_tradfri.toggle_output_0_mcb)
self.switch_bed_marion_light.toggle_output_0_mcb) self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_LEFT_LONG,
# button self.bed_light_ma_powerplug.toggle_output_0_mcb)
self.videv_multistate.connect_br_function(self.brightness_functions, brightness_choose_n_action.KEY_ACTIVE_DEVICE, 2)
# heating function # heating function
self.heating_function = heating_function( self.heating_function = heating_function(self.heating_valve)
self.valve_heating,
config.DEFAULT_TEMPERATURE, #
**get_radiator_data(self.valve_heating.topic) # Virtual Device Interface
#
self.main_light_videv = videv_switch_brightness_color_temp(
mqtt_client, config.TOPIC_FFE_SLEEP_MAIN_LIGHT_VIDEV,
self.main_light_shelly, devices.shelly.KEY_OUTPUT_0,
self.main_light_tradfri, devices.tradfri_light.KEY_BRIGHTNESS,
self.main_light_tradfri, devices.tradfri_light.KEY_COLOR_TEMP
)
self.bed_light_di_videv = videv_switch_brightness(
mqtt_client, config.TOPIC_FFE_SLEEP_BED_LIGHT_DI_VIDEV,
self.bed_light_di_tradfri, devices.tradfri_light.KEY_OUTPUT_0,
self.bed_light_di_tradfri, devices.tradfri_light.KEY_BRIGHTNESS,
)
self.bed_light_ma_videv = videv_switching(
mqtt_client, config.TOPIC_FFE_SLEEP_BED_LIGHT_MA_VIDEV,
self.bed_light_ma_powerplug, devices.silvercrest_powerplug.KEY_OUTPUT_0
)
self.heating_function_videv = videv_heating(
mqtt_client, config.TOPIC_FFE_SLEEP_HEATING_VALVE_VIDEV,
self.heating_function
)
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
) )
self.heating_function.add_callback(None, None, set_radiator_data, True)
self.videv_heating.connect_heating_function(self.heating_function)
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):
super().__init__(mqtt_client)
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
self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFE_LIVINGROOM_MAIN_LIGHT_SHELLY)
self.main_light_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_FFE_LIVINGROOM_MAIN_LIGHT_ZIGBEE)
self.floorlamp_tradfri = devices.group(
*[devices.tradfri_light(mqtt_client, config.TOPIC_FFE_LIVINGROOM_FLOOR_LAMP_ZIGBEE % i) for i in range(1, 7)])
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 = devices.silvercrest_powerplug(mqtt_client, config.TOPIC_FFE_LIVINGROOM_XMAS_TREE_POWERPLUG)
self.powerplug_xmas_star = devices.silvercrest_powerplug(mqtt_client, config.TOPIC_FFE_LIVINGROOM_XMAS_STAR_POWERPLUG)
# main light -> floor_light super().__init__(mqtt_client)
self.switch_main_light.add_callback(self.switch_main_light.KEY_OUTPUT_0, None, self.light_floor_light.set_output_0_mcb, True)
# heating function #
self.heating_function = heating_function( # Functionality initialisation
self.valve_heating, #
config.DEFAULT_TEMPERATURE, # floor lamp synchronisation with main_light
**get_radiator_data(self.valve_heating.topic) self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.floorlamp_tradfri.set_output_0_mcb, True)
#
# Virtual Device Interface
#
self.main_light_videv = videv_switch_brightness_color_temp(
mqtt_client, config.TOPIC_FFE_LIVINGROOM_MAIN_LIGHT_VIDEV,
self.main_light_shelly, devices.shelly.KEY_OUTPUT_0,
self.main_light_tradfri, devices.tradfri_light.KEY_BRIGHTNESS,
self.main_light_tradfri, devices.tradfri_light.KEY_COLOR_TEMP
) )
self.heating_function.add_callback(None, None, set_radiator_data, True) self.floorlamp_videv = videv_switch_brightness_color_temp(
self.videv_heating.connect_heating_function(self.heating_function) mqtt_client, config.TOPIC_FFE_LIVINGROOM_FLOOR_LAMP_VIDEV,
self.floorlamp_tradfri, devices.tradfri_light.KEY_OUTPUT_0,
self.floorlamp_tradfri, devices.tradfri_light.KEY_BRIGHTNESS,
self.floorlamp_tradfri, devices.tradfri_light.KEY_COLOR_TEMP
)
if config.CHRISTMAS:
self.xmas_tree_videv = videv_switching(
mqtt_client, config.TOPIC_FFE_LIVINGROOM_XMAS_TREE_VIDEV,
self.powerplug_xmas_tree, devices.silvercrest_powerplug.KEY_OUTPUT_0
)

View File

@ -3,10 +3,10 @@
# #
import config import config
from devdi import rooms import devices
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
@ -20,101 +20,91 @@ logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
class first_floor_west(room_collection): class first_floor_west(room_collection):
def __init__(self, mqtt_client): def __init__(self, mqtt_client):
super().__init__(mqtt_client) super().__init__(mqtt_client)
self.floor = first_floor_west_floor(mqtt_client)
self.bath = first_floor_west_bath(mqtt_client) self.bath = first_floor_west_bath(mqtt_client)
self.julian = first_floor_west_julian(mqtt_client) self.julian = first_floor_west_julian(mqtt_client)
self.livingroom = first_floor_west_living(mqtt_client) self.livingroom = first_floor_west_living(mqtt_client)
self.sleep = first_floor_west_sleep(mqtt_client) 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):
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)
class first_floor_west_julian(rooms.ffw_julian, room):
def __init__(self, mqtt_client):
super().__init__(mqtt_client)
room.__init__(self, mqtt_client)
# #
# light <-> videv # http://shelly1l-3C6105E43452
self.videv_main_light.connect_sw_device(self.switch_main_light, self.switch_main_light.KEY_OUTPUT_0) self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFW_JULIAN_MAIN_LIGHT_SHELLY)
self.videv_main_light.connect_br_device(self.light_main_light, self.light_main_light.KEY_BRIGHTNESS) self.main_light_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_FFW_JULIAN_MAIN_LIGHT_ZIGBEE)
self.videv_main_light.connect_ct_device(self.light_main_light, self.light_main_light.KEY_COLOR_TEMP) super().__init__(mqtt_client)
# heating function #
self.heating_function = heating_function( # Virtual Device Interface
self.valve_heating, #
config.DEFAULT_TEMPERATURE, self.main_light_videv = videv_switch_brightness_color_temp(
**get_radiator_data(self.valve_heating.topic) mqtt_client, config.TOPIC_FFW_JULIAN_MAIN_LIGHT_VIDEV,
self.main_light_shelly, devices.shelly.KEY_OUTPUT_0,
self.main_light_tradfri, devices.tradfri_light.KEY_BRIGHTNESS,
self.main_light_tradfri, devices.tradfri_light.KEY_COLOR_TEMP
) )
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): class first_floor_west_bath(room):
def __init__(self, mqtt_client): def __init__(self, mqtt_client):
super().__init__(mqtt_client)
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.heating_valve = devices.brennenstuhl_heatingvalve(mqtt_client, config.TOPIC_FFW_BATH_HEATING_VALVE_ZIGBEE)
super().__init__(mqtt_client)
#
# Functionality initialisation
#
# heating function # heating function
self.heating_function = heating_function( self.heating_function = heating_function(self.heating_valve)
self.valve_heating,
config.DEFAULT_TEMPERATURE, #
**get_radiator_data(self.valve_heating.topic) # Virtual Device Interface
#
self.heating_function_videv = videv_heating(
mqtt_client, config.TOPIC_FFW_BATH_HEATING_VALVE_VIDEV,
self.heating_function
) )
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): class first_floor_west_living(room):
def __init__(self, mqtt_client): def __init__(self, mqtt_client):
super().__init__(mqtt_client)
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) # http://shelly1l-84CCA8ACE6A1
self.videv_main_light.connect_ct_device(self.light_main_light, self.light_main_light.KEY_COLOR_TEMP) self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFW_LIVINGROOM_MAIN_LIGHT_SHELLY)
self.main_light_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_FFW_LIVINGROOM_MAIN_LIGHT_ZIGBEE)
super().__init__(mqtt_client)
# heating function #
self.heating_function = heating_function( # Virtual Device Interface
self.valve_heating, #
config.DEFAULT_TEMPERATURE, self.main_light_videv = videv_switch_brightness_color_temp(
**get_radiator_data(self.valve_heating.topic) mqtt_client, config.TOPIC_FFW_LIVINGROOM_MAIN_LIGHT_VIDEV,
self.main_light_shelly, devices.shelly.KEY_OUTPUT_0,
self.main_light_tradfri, devices.tradfri_light.KEY_BRIGHTNESS,
self.main_light_tradfri, devices.tradfri_light.KEY_COLOR_TEMP
) )
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): class first_floor_west_sleep(room):
def __init__(self, mqtt_client): def __init__(self, mqtt_client):
#
# Device initialisation
#
# http://shelly1-3494546A51F2
self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFW_SLEEP_MAIN_LIGHT_SHELLY)
self.main_light_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_FFW_SLEEP_MAIN_LIGHT_ZIGBEE)
super().__init__(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_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)
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) # Virtual Device Interface
#
# heating function self.main_light_videv = videv_switch_brightness(
self.heating_function = heating_function( mqtt_client, config.TOPIC_FFW_SLEEP_MAIN_LIGHT_VIDEV,
self.valve_heating, self.main_light_shelly, devices.shelly.KEY_OUTPUT_0,
config.DEFAULT_TEMPERATURE, self.main_light_tradfri, devices.tradfri_light.KEY_BRIGHTNESS
**get_radiator_data(self.valve_heating.topic)
) )
self.heating_function.add_callback(None, None, set_radiator_data, True)
self.videv_heating.connect_heating_function(self.heating_function)

View File

@ -1,40 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
from devdi import rooms
from function.helpers import day_event
from function.rooms import room, room_collection
import logging
try:
from config import APP_NAME as ROOT_LOGGER_NAME
except ImportError:
ROOT_LOGGER_NAME = 'root'
logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
class garden(room_collection):
def __init__(self, mqtt_client):
super().__init__(mqtt_client)
self.garden = garden_garden(mqtt_client)
class garden_garden(rooms.gar_garden, room):
def __init__(self, mqtt_client):
super().__init__(mqtt_client)
room.__init__(self, mqtt_client)
#
self.day_events = day_event((6, 0), (22, 0), 30, -30)
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)
self.videv_repeater.connect_sw_device(self.switch_repeater, self.switch_repeater.KEY_OUTPUT_0)
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):
self.switch_garland_light.set_output_0(True)
elif key in (self.day_events.KEY_START_OF_NIGHT, self.day_events.KEY_SUNRISE):
self.switch_garland_light.set_output_0(False)

View File

@ -3,10 +3,10 @@
# #
import config import config
from devdi import rooms import devices
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
@ -25,167 +25,196 @@ class ground_floor_west(room_collection):
self.marion = ground_floor_west_marion(mqtt_client) self.marion = ground_floor_west_marion(mqtt_client)
class ground_floor_west_dirk(rooms.gfw_dirk, room): class ground_floor_west_floor(room):
def __init__(self, mqtt_client):
#
# Device initialisation
#
# http://shelly1l-84CCA8AD1148
self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_SHELLY)
self.main_light_tradfri = devices.group(
devices.tradfri_light(mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_ZIGBEE % 1),
devices.tradfri_light(mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_ZIGBEE % 2)
)
super().__init__(mqtt_client)
#
# Functionality initialisation
#
# Request silvercrest data of lead light after power on
switched_light(self.main_light_shelly, devices.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, devices.shelly.KEY_OUTPUT_0,
self.main_light_tradfri, devices.tradfri_light.KEY_BRIGHTNESS,
self.main_light_tradfri, devices.tradfri_light.KEY_COLOR_TEMP
)
class ground_floor_west_marion(room):
def __init__(self, mqtt_client):
#
# Device initialisation
#
# http://shelly1l-E8DB84A1E067
self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_GFW_MARION_MAIN_LIGHT_SHELLY)
self.heating_valve = devices.brennenstuhl_heatingvalve(mqtt_client, config.TOPIC_GFW_MARION_HEATING_VALVE_ZIGBEE)
super().__init__(mqtt_client)
#
# Functionality initialisation
#
# heating function
self.heating_function = heating_function(self.heating_valve)
#
# Virtual Device Interface
#
self.main_light_videv = videv_switching(
mqtt_client, config.TOPIC_GFW_MARION_MAIN_LIGHT_VIDEV,
self.main_light_shelly, devices.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
STATE_ACTIVE_DEVICE_MAX_VALUE = STATE_ACTIVE_DEVICE_AMPLIFIER STATE_ACTIVE_DEVICE_MAX_VALUE = STATE_ACTIVE_DEVICE_AMPLIFIER
# #
KEY_POWERPLUG_AMPLIFIER = devices.my_powerplug.KEY_OUTPUT_0
KEY_POWERPLUG_CD_PLAYER = devices.my_powerplug.KEY_OUTPUT_2
KEY_POWERPLUG_DESK_LIGHT = devices.my_powerplug.KEY_OUTPUT_1
KEY_POWERPLUG_PC_DOCK = devices.my_powerplug.KEY_OUTPUT_3
#
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):
#
# Device initialisation
#
# http://shelly1l-3C6105E44F27
self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_GFW_DIRK_MAIN_LIGHT_SHELLY)
self.main_light_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_GFW_DIRK_MAIN_LIGHT_ZIGBEE)
self.powerplug_common = devices.my_powerplug(mqtt_client, config.TOPIC_GFW_DIRK_POWERPLUG)
self.desk_light_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_GFW_DIRK_DESK_LIGHT_ZIGBEE)
self.button_tradfri = devices.tradfri_button(mqtt_client, config.TOPIC_GFW_DIRK_INPUT_DEVICE)
self.remote_amplifier = devices.remote(mqtt_client, config.TOPIC_GFW_DIRK_AMPLIFIER_REMOTE)
self.spotify_state = devices.audio_status(mqtt_client, config.TOPIC_GFW_DIRK_SPOTIFY)
self.mpd_state = devices.audio_status(mqtt_client, config.TOPIC_GFW_DIRK_MPD)
self.heating_valve = devices.brennenstuhl_heatingvalve(mqtt_client, config.TOPIC_GFW_DIRK_HEATING_VALVE_ZIGBEE)
super().__init__(mqtt_client) super().__init__(mqtt_client)
room.__init__(self, mqtt_client)
# #
# light <-> videv # Functionality 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) # Button - Brightness functionality
self.videv_desk_light.connect_br_device(self.light_desk_light, self.light_desk_light.KEY_BRIGHTNESS) self.brightness_functions = brightness_choose_n_action(self.button_tradfri)
self.videv_desk_light.connect_ct_device(self.light_desk_light, self.light_desk_light.KEY_COLOR_TEMP) self.brightness_functions.add(self.main_light_tradfri, self.main_light_shelly, devices.shelly.KEY_OUTPUT_0)
# self.brightness_functions.add(self.desk_light_tradfri, self.powerplug_common, self.KEY_POWERPLUG_DESK_LIGHT)
self.videv_amplifier.connect_sw_device(self.switch_powerplug_4, self.KEY_POWERPLUG_AMPLIFIER) self.brightness_functions.add(self.remote_amplifier, self.powerplug_common, self.KEY_POWERPLUG_AMPLIFIER)
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)
# amplifier on, if playing device on
self.switch_powerplug_4.add_callback(self.KEY_POWERPLUG_PHONO, None, self.switch_powerplug_4.set_output_0_mcb, True)
self.switch_powerplug_4.add_callback(self.KEY_POWERPLUG_CD_PLAYER, None, self.switch_powerplug_4.set_output_0_mcb, True)
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(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.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(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.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(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.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(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.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(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.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(devices.status.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(devices.status.KEY_STATE, None, self.powerplug_common.set_output_0_mcb, True)
self.videv_audio_player.connect_audio_device(self.audio_status_mpd) # Mediaplayer - Audio source selection
self.videv_audio_player.connect_audio_device(self.audio_status_spotify) 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(devices.status.KEY_STATE, True, self.audio_source_selector, True)
self.mpd_state.add_callback(devices.status.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.heating_valve)
self.valve_heating,
config.DEFAULT_TEMPERATURE, #
**get_radiator_data(self.valve_heating.topic) # 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, devices.shelly.KEY_OUTPUT_0,
self.main_light_tradfri, devices.tradfri_light.KEY_BRIGHTNESS,
self.main_light_tradfri, devices.tradfri_light.KEY_COLOR_TEMP
) )
self.heating_function.add_callback(None, None, set_radiator_data, True) self.desk_light_videv = videv_switch_brightness_color_temp(
# heating function <-> videv mqtt_client, config.TOPIC_GFW_DIRK_DESK_LIGHT_VIDEV,
self.videv_heating.connect_heating_function(self.heating_function) self.powerplug_common, self.KEY_POWERPLUG_DESK_LIGHT,
self.desk_light_tradfri, devices.tradfri_light.KEY_BRIGHTNESS,
self.desk_light_tradfri, devices.tradfri_light.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
)
#
# 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]:
# 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,10 +1,9 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from mqtt.smarthome import common_base
import config import config
import geo import geo
import task import inspect
import time import time
@ -12,104 +11,9 @@ def now():
return time.mktime(time.localtime()) return time.mktime(time.localtime())
def next_sunrise_time(time_offs_min=30): def sunrise_time(time_offs_min=30):
tm = now() return time.mktime(geo.sun.sunrise(config.GEO_POSITION)) + time_offs_min * 60
rv = time.mktime(geo.sun.sunrise(config.GEO_POSITION)) + time_offs_min * 60
if tm > rv:
rv = time.mktime(geo.sun.sunrise(config.GEO_POSITION, date=time.localtime(tm + 24 * 60 * 60))) + time_offs_min * 60
return rv
def next_sunset_time(time_offs_min=-30): def sunset_time(time_offs_min=-30):
tm = now() return time.mktime(geo.sun.sunset(config.GEO_POSITION)) + time_offs_min * 60
rv = time.mktime(geo.sun.sunset(config.GEO_POSITION)) + time_offs_min * 60
if tm > rv:
rv = time.mktime(geo.sun.sunset(config.GEO_POSITION, date=time.localtime(tm + 24 * 60 * 60))) + time_offs_min * 60
return rv
def next_user_time(hh, mm):
ts = time.localtime()
tm = time.mktime(ts)
ut_ts = list(ts)
ut_ts[3] = hh
ut_ts[4] = mm
ut = time.mktime(time.struct_time(list(ts[:3]) + [hh, mm, 0] + list(ts[6:])))
if ts[3] > hh or (ts[3] == hh and ts[4] >= mm):
ut += 24 * 60 * 60
#
return ut
class day_state(common_base):
"""
Class to subscribe day events as a callback (see add_callback)
:param time_start_of_day: Time of a day (tuple including hour and minute) for start of day or None for no start of day state.
:type time_start_of_day: tuple
:param time_start_of_night: Time of a day (tuple including hour and minute) for start of night or None for no end of day state.
:type time_start_of_night: tuple
:param time_offset_sunrise: time offset for sunrise in minutes (negative values lead to earlier sunrise state) or None for no sunrise state.
:type time_start_of_day: int
:param time_offset_sunset: time offset for sunset in minutes (negative values lead to earlier sunset state) or None for no sunrise state.
:type time_start_of_day: int
"""
KEY_SUNRISE = 'sunrise'
KEY_SUNSET = 'sunset'
KEY_START_OF_NIGHT = 'start_of_night'
KEY_START_OF_DAY = 'start_of_day'
#
STATES = (KEY_START_OF_DAY, KEY_SUNRISE, KEY_SUNSET, KEY_START_OF_NIGHT)
def __init__(self, time_start_of_day, time_start_of_night, time_offset_sunrise, time_offset_sunset):
self.__time_start_of_day__ = time_start_of_day
self.__time_start_of_night__ = time_start_of_night
self.__time_offset_sunrise__ = time_offset_sunrise
self.__time_offset_sunset__ = time_offset_sunset
super().__init__()
#
def get_state(self):
tm = {}
if self.__time_offset_sunrise__ is not None:
tm[next_sunrise_time(self.__time_offset_sunrise__)] = self.KEY_SUNRISE
if self.__time_start_of_day__ is not None:
tm[next_user_time(*(self.__time_start_of_day__))] = self.KEY_START_OF_DAY
if self.__time_offset_sunset__ is not None:
tm[next_sunset_time(self.__time_offset_sunset__)] = self.KEY_SUNSET
if self.__time_start_of_night__ is not None:
tm[next_user_time(*(self.__time_start_of_night__))] = self.KEY_START_OF_NIGHT
#
tms = list(tm.keys())
tms.sort()
return tm[tms[-1]]
class day_event(day_state):
"""
Class to subscribe day events as a callback (see add_callback)
:param time_start_of_day: Time of a day (tuple including hour and minute) for start of day or None for no start of day state.
:type time_start_of_day: tuple
:param time_start_of_night: Time of a day (tuple including hour and minute) for start of night or None for no end of day state.
:type time_start_of_night: tuple
:param time_offset_sunrise: time offset for sunrise in seconds (negative values lead to earlier sunrise state) or None for no sunrise state.
:type time_start_of_day: int
:param time_offset_sunset: time offset for sunset in seconds (negative values lead to earlier sunset state) or None for no sunrise state.
:type time_start_of_day: int
"""
def __init__(self, time_start_of_day=(5, 0), time_start_of_night=(22, 0), time_offset_sunrise=30, time_offset_sunset=-30):
super().__init__(time_start_of_day, time_start_of_night, time_offset_sunrise, time_offset_sunset)
#
current_day_state = self.get_state()
for key in self.STATES:
self[key] = current_day_state == key
#
cyclic = task.periodic(30, self.__cyclic__)
cyclic.run()
def __cyclic__(self, a):
current_day_state = self.get_state()
for key in self.STATES:
self.set(key, current_day_state == key)

View File

@ -11,12 +11,13 @@ 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.db import get_radiator_data, set_radiator_data
from function.helpers import now, sunset_time, sunrise_time
import logging import logging
import task import task
import time
try: try:
from config import APP_NAME as ROOT_LOGGER_NAME from config import APP_NAME as ROOT_LOGGER_NAME
@ -27,7 +28,7 @@ logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
class switched_light(object): class switched_light(object):
def __init__(self, sw_device, sw_key, li_device): def __init__(self, sw_device, sw_key, li_device):
sw_device.add_callback(sw_device.KEY_OUTPUT_0, True, li_device.request_data, True) sw_device.add_callback(devices.shelly.KEY_OUTPUT_0, True, li_device.request_data, True)
class brightness_choose_n_action(common_base): class brightness_choose_n_action(common_base):
@ -152,44 +153,19 @@ class heating_function(common_base):
AWAY_REDUCTION = 5 AWAY_REDUCTION = 5
SUMMER_TEMPERATURE = 5 SUMMER_TEMPERATURE = 5
class value_timeout_list(object): def __init__(self, heating_valve):
MAX_DELAY = 10
def __init__(self):
self.__data__ = []
self.__time__ = []
def __cleanup__(self):
now = time.time()
for i, tm in enumerate(self.__time__):
if tm + self.MAX_DELAY < now:
del (self.__data__[i])
del (self.__time__[i])
def new(self, item):
self.__cleanup__()
self.__data__.append(item)
self.__time__.append(time.time())
def is_valid_value(self, data):
self.__cleanup__()
return data not in self.__data__
def __init__(self, heating_valve, default_temperature, **kwargs):
self.heating_valve = heating_valve self.heating_valve = heating_valve
self.default_temperature = default_temperature self.default_temperature = config.DEFAULT_TEMPERATURE[heating_valve.topic]
# db_data = get_radiator_data(heating_valve.topic)
self.valve_value = self.value_timeout_list()
#
super().__init__({ super().__init__({
self.KEY_USER_TEMPERATURE_SETPOINT: kwargs.get(self.KEY_USER_TEMPERATURE_SETPOINT, self.default_temperature), self.KEY_USER_TEMPERATURE_SETPOINT: db_data[2] or self.default_temperature,
self.KEY_TEMPERATURE_SETPOINT: kwargs.get(self.KEY_TEMPERATURE_SETPOINT, self.default_temperature), self.KEY_TEMPERATURE_SETPOINT: db_data[3] or self.default_temperature,
self.KEY_TEMPERATURE_CURRENT: kwargs.get(self.KEY_TEMPERATURE_CURRENT, None), self.KEY_TEMPERATURE_CURRENT: None,
self.KEY_AWAY_MODE: kwargs.get(self.KEY_AWAY_MODE, False), self.KEY_AWAY_MODE: db_data[0] or False,
self.KEY_SUMMER_MODE: kwargs.get(self.KEY_SUMMER_MODE, False), self.KEY_SUMMER_MODE: db_data[1] or False,
self.KEY_START_BOOST: kwargs.get(self.KEY_START_BOOST, True), self.KEY_START_BOOST: True,
self.KEY_SET_DEFAULT_TEMPERATURE: kwargs.get(self.KEY_SET_DEFAULT_TEMPERATURE, False), self.KEY_SET_DEFAULT_TEMPERATURE: False,
self.KEY_BOOST_TIMER: kwargs.get(self.KEY_BOOST_TIMER, 0) self.KEY_BOOST_TIMER: 0
}) })
# #
self.heating_valve.set_heating_setpoint(self[self.KEY_TEMPERATURE_SETPOINT]) self.heating_valve.set_heating_setpoint(self[self.KEY_TEMPERATURE_SETPOINT])
@ -223,8 +199,11 @@ class heating_function(common_base):
def cancel_boost(self): def cancel_boost(self):
self.set(self.KEY_BOOST_TIMER, 0, block_callback=[self.timer_expired]) self.set(self.KEY_BOOST_TIMER, 0, block_callback=[self.timer_expired])
def send_command(self, key, data, block_callback=[]): def set(self, key, data, block_callback=[]):
return super().set(key, data, block_callback) rv = super().set(key, data, block_callback)
set_radiator_data(self.heating_valve.topic, self[self.KEY_AWAY_MODE], self[self.KEY_SUMMER_MODE],
self[self.KEY_USER_TEMPERATURE_SETPOINT], self[self.KEY_TEMPERATURE_SETPOINT])
return rv
def away_mode(self, device, key, value): def away_mode(self, device, key, value):
if value is True: if value is True:
@ -266,13 +245,11 @@ class heating_function(common_base):
self.set(self.KEY_TEMPERATURE_SETPOINT, data) self.set(self.KEY_TEMPERATURE_SETPOINT, data)
def set_heating_setpoint(self, device, key, data): def set_heating_setpoint(self, device, key, data):
self.valve_value.new(data)
self.heating_valve.set_heating_setpoint(data) self.heating_valve.set_heating_setpoint(data)
def get_radiator_setpoint(self, device, key, data): def get_radiator_setpoint(self, device, key, data):
if self.valve_value.is_valid_value(data): if self[self.KEY_BOOST_TIMER] == 0 and not self[self.KEY_AWAY_MODE] and not self[self.KEY_SUMMER_MODE]:
if self[self.KEY_BOOST_TIMER] == 0 and not self[self.KEY_AWAY_MODE] and not self[self.KEY_SUMMER_MODE]: self.set(self.KEY_USER_TEMPERATURE_SETPOINT, data, block_callback=[self.set_heating_setpoint])
self.set(self.KEY_USER_TEMPERATURE_SETPOINT, data, block_callback=[self.set_heating_setpoint])
def get_radiator_temperature(self, device, key, data): def get_radiator_temperature(self, device, key, data):
self.set(self.KEY_TEMPERATURE_CURRENT, data) self.set(self.KEY_TEMPERATURE_CURRENT, data)
@ -302,8 +279,8 @@ class motion_sensor_light(common_base):
self.motion_sensors = args self.motion_sensors = args
self.timer_reload_value = timer_value self.timer_reload_value = timer_value
# #
sw_device.add_callback(devices.shelly_sw1.KEY_OUTPUT_0, True, self.reload_timer, True) sw_device.add_callback(devices.shelly.KEY_OUTPUT_0, True, self.reload_timer, True)
sw_device.add_callback(devices.shelly_sw1.KEY_OUTPUT_0, False, self.reset_timer, True) sw_device.add_callback(devices.shelly.KEY_OUTPUT_0, False, self.reset_timer, True)
for motion_sensor in args: for motion_sensor in args:
motion_sensor.add_callback(motion_sensor.KEY_OCCUPANCY, None, self.set_motion_detected, True) motion_sensor.add_callback(motion_sensor.KEY_OCCUPANCY, None, self.set_motion_detected, True)
# #
@ -323,8 +300,7 @@ class motion_sensor_light(common_base):
if arg_device.topic == device.topic: if arg_device.topic == device.topic:
break break
self.set(self.KEY_MOTION_SENSOR % sensor_index, data) self.set(self.KEY_MOTION_SENSOR % sensor_index, data)
# auto light on with state sunset -> time_offset_sunrise=60 (longer sunset) and time_offset_sunset=-60 (longer sunset) if now() < sunrise_time(60) or now() > sunset_time(-60):
if day_state(None, None, 60, -60).get_state() == day_state.KEY_SUNSET:
if data is True: if data is True:
logger.info("%s: Motion detected - Switching on main light %s", device.topic, self.sw_device.topic) logger.info("%s: Motion detected - Switching on main light %s", device.topic, self.sw_device.topic)
self.sw_method(True) self.sw_method(True)

View File

@ -13,8 +13,6 @@ 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): def __init__(self, mqtt_client):
self.mqtt_client = mqtt_client self.mqtt_client = mqtt_client
@ -22,20 +20,14 @@ class room(object):
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__ == '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):
self.mqtt_client = mqtt_client self.mqtt_client = mqtt_client
@ -46,34 +38,5 @@ 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):
target = object_to_analyse or self
#
devices = []
for name, obj in inspect.getmembers(target):
if not callable(obj): # sort out methods
try:
if obj.__module__.startswith('function.') and not obj.__module__.endswith('.videv'):
devices.extend(self.all_devices(obj, depth+1)) # rekurse in function instances
elif obj.__module__ == "devices":
devices.append(obj)
except AttributeError:
pass # sort out non modules
return devices

View File

@ -3,10 +3,11 @@
# #
import config import config
from devdi import rooms import devices
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
@ -21,17 +22,31 @@ class stairway(room_collection):
self.stairway = stairway_stairway(mqtt_client) self.stairway = stairway_stairway(mqtt_client)
class stairway_stairway(rooms.stairway, room): class stairway_stairway(room):
def __init__(self, mqtt_client): def __init__(self, mqtt_client):
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 = devices.shelly(mqtt_client, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_SHELLY)
self.motion_sensor_gf = devices.silvercrest_motion_sensor(mqtt_client, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_MOTION_SENSOR_GF)
self.motion_sensor_ff = devices.silvercrest_motion_sensor(mqtt_client, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_MOTION_SENSOR_FF)
super().__init__(mqtt_client)
#
# 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, devices.shelly.KEY_OUTPUT_0,
self.motion_sensor_light
)

317
function/videv.py Normal file
View File

@ -0,0 +1,317 @@
#!/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 mqtt_base
from function.rooms import room, room_collection
import json
import time
try:
from config import APP_NAME as ROOT_LOGGER_NAME
except ImportError:
ROOT_LOGGER_NAME = 'root'
class base(mqtt_base):
KEY_INFO = '__info__'
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.__capabilities__ = None
self.__active_tx__ = {}
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 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)
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)
# send default data to videv interface
def __rx_ext_device_data__(self, ext_device, ext_key, data):
my_key = self.__display_dict__[(id(ext_device), ext_key)]
self[my_key] = data
self.__tx__(my_key, data)
def __tx__(self, key, data):
if key in self.__control_dict__:
self.__active_tx__[key] = (time.time(), data)
if type(data) not in (str, ):
data = json.dumps(data)
self.mqtt_client.send(self.topic + '/' + key, data)
self.__tx_capabilities__()
def __tx_capabilities__(self):
self.mqtt_client.send(self.topic + '/' + self.KEY_INFO, json.dumps(self.capabilities))
def add_control(self, my_key, ext_device, ext_key, on_change_only=True):
"""
listen to videv information and pass data to ext_device
"""
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(self.topic + '/' + my_key, self.__rx_videv_data__)
def __rx_videv_data__(self, client, userdata, message):
my_key = message.topic.split('/')[-1]
try:
data = json.loads(message.payload)
except json.decoder.JSONDecodeError:
data = message.payload
if my_key in self.__active_tx__:
tm, tx_data = self.__active_tx__.pop(my_key)
do_ex = data != tx_data and time.time() - tm < 2
else:
do_ex = True
if do_ex:
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.set(ext_key, data)
self.set(my_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)
@property
def capabilities(self):
if self.__capabilities__ is None:
self.__capabilities__ = {}
self.__capabilities__['__type__'] = self.__class__.__name__
for key in self.__control_dict__:
if not key in self.__capabilities__:
self.__capabilities__[key] = {}
self.__capabilities__[key]['control'] = True
for key in self.__display_dict__.values():
if not key in self.__capabilities__:
self.__capabilities__[key] = {}
self.__capabilities__[key]['display'] = True
return self.__capabilities__
class videv_switching(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)
#
self.__tx_capabilities__()
class videv_switching_timer(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)
#
self.__tx_capabilities__()
class videv_switching_motion(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)
#
self.__tx_capabilities__()
class videv_switch_brightness(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)
#
self.__tx_capabilities__()
class videv_switch_brightness_color_temp(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)
#
self.__tx_capabilities__()
class videv_heating(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)
#
self.__tx_capabilities__()
class videv_multistate(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)
#
self.__tx_capabilities__()
def __index_rx__(self, device, key, data):
for i in range(0, self.num_states):
self.__tx__(self.KEY_STATE % i, i == data)
#
self.__tx_capabilities__()
class videv_audio_player(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)
#
self.__tx_capabilities__()
def __title_rx__(self, device, key, data):
self.__tx__(self.KEY_TITLE, data or self.NO_TITLE)
@property
def capabilities(self):
super().capabilities
self.__capabilities__[self.KEY_TITLE] = {'display': True}
return self.__capabilities__
class all_off(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)
#
self.__tx_capabilities__()
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()
self.__tx_capabilities__()
@property
def capabilities(self):
if self.__capabilities__ is None:
self.__capabilities__ = {}
self.__capabilities__['__type__'] = self.__class__.__name__
for key in self.__inst_dict__:
self.__capabilities__[key] = {}
self.__capabilities__[key]['control'] = True
return self.__capabilities__

2
geo

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

2
mqtt

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

2
report

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

View File

@ -1,53 +1,28 @@
import config import config
import function import function
import json import logging
import mqtt import mqtt
import os
import report import report
import subprocess
import time import time
logger = report.default_logging_config() logger = logging.getLogger(config.APP_NAME)
# TODO: Restructure nodered gui (own heating page - with circulation pump)
VERS_MAJOR = 1 # TODO: Rework devices to base.mqtt (pack -> set, ...)
VERS_MINOR = 4 # TODO: Implement handling of warnings (videv element to show in webapp?)
VERS_PATCH = 2 # TODO: implement garland (incl. day events like sunset, sunrise, ...)
INFO_TOPIC = "__info__"
INFO_DATA = {
"app_name": os.path.splitext(os.path.basename(__file__))[0],
"version": {
"readable": "%d.%d.%d" % (VERS_MAJOR, VERS_MINOR, VERS_PATCH),
"major": VERS_MAJOR,
"minor": VERS_MINOR,
"patch": VERS_PATCH
},
"git": {
"url": subprocess.check_output(["git", "config", "--get", "remote.origin.url"])[:-1].decode("utf-8"),
"ref": subprocess.check_output(["git", "log", "-1", '--format="%H"'])[1:-2].decode("utf-8")
}
}
def __info_publisher__(client, userdata, message):
data = json.loads(message.payload)
if data != INFO_DATA:
client.publish(INFO_TOPIC, json.dumps(INFO_DATA))
if __name__ == "__main__": if __name__ == "__main__":
# if config.DEBUG:
# MQTT Client report.appLoggingConfigure(None, None, ((config.APP_NAME, logging.DEBUG), ), fmt=report.SHORT_FMT, host='localhost', port=19996)
else:
report.stdoutLoggingConfigure(((config.APP_NAME, logging.INFO), ), report.SHORT_FMT)
# #
mc = mqtt.mqtt_client(host=config.MQTT_SERVER, port=config.MQTT_PORT, username=config.MQTT_USER, mc = mqtt.mqtt_client(host=config.MQTT_SERVER, port=config.MQTT_PORT, username=config.MQTT_USER,
password=config.MQTT_PASSWORD, name=config.APP_NAME) password=config.MQTT_PASSWORD, name=config.APP_NAME)
mc.add_callback(INFO_TOPIC, __info_publisher__)
#
# Smart Home Functionality
#
func = function.all_functions(mc) func = function.all_functions(mc)
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