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
40 changed files with 1200 additions and 3037 deletions

3
.gitmodules vendored
View File

@ -10,6 +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

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

17
.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, "editor.fontSize": 14,
"emmet.includeLanguages": { "django-html": "html" }, "emmet.includeLanguages": {
"python.testing.pytestArgs": ["-v", "--cov", "--cov-report=xml", "__test__"], "django-html": "html"
"python.testing.unittestEnabled": false, }
"python.testing.pytestEnabled": true
} }

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

98
base.py
View File

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

View File

@ -1,26 +0,0 @@
import geo
import logging
import report
from topics import *
DEBUG = False # False: logging to stdout with given LOGLEVEL - True: logging all to localhost:19996 and warnings or higher to stdout
LOGLEVEL = logging.INFO
GEO_POSITION = geo.gps.coordinate(lat=49.519167, lon=9.3672222)
APP_NAME = "smart_brain"
MQTT_SERVER = "{{ 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

View File

1
devdi

@ -1 +0,0 @@
Subproject commit 619d7f926f13ce03950db9c5dab3461e5b7da23a

View File

@ -1,114 +0,0 @@
import config
import mqtt
import readline
import sys
import report
import logging
import devices
import json
if __name__ == "__main__":
report.stdoutLoggingConfigure([[config.APP_NAME, logging.INFO], ], fmt=report.SHORT_FMT)
mc = mqtt.mqtt_client(
host=config.MQTT_SERVER,
port=config.MQTT_PORT,
username=config.MQTT_USER,
password=config.MQTT_PASSWORD,
name=config.APP_NAME + '_devicetest'
)
#
devicedict = {}
for device in [
# devices.shelly_pro3(mc, "shellies/gfw/pro3"),
# devices.brennenstuhl_heatingvalve(mc, "zigbee_raspi/heatvlv"),
# devices.silvercrest_button(mc, "zigbee_raspi/button"),
devices.hue_sw_br_ct(mc, "zigbee_ffe/kitchen/main_light_1"),
]:
devicedict[device.topic.replace("/", "_")] = device
#
COMMANDS = ['quit', 'help', 'action']
for key in devicedict:
device = devicedict[key]
for cmd in device.__class__.__dict__:
obj = getattr(device, cmd)
if callable(obj) and not cmd.startswith("_"):
COMMANDS.append(key + "." + cmd)
#
def reduced_list(text):
"""
Create reduced completation list
"""
reduced_list = {}
for cmd in COMMANDS:
next_dot = cmd[len(text):].find('.')
if next_dot >= 0:
reduced_list[cmd[:len(text) + next_dot + 1]] = None
else:
reduced_list[cmd] = None
return reduced_list.keys()
def completer(text, state):
"""
Our custom completer function
"""
options = [x for x in reduced_list(text) if x.startswith(text)]
return options[state]
def complete(text, state):
for cmd in COMMANDS:
if cmd.startswith(text):
if not state:
hit = ""
index = 0
sub_list = cmd.split('.')
while len(text) >= len(hit):
hit += sub_list[index] + '.'
return hit # cmd
else:
state -= 1
if len(sys.argv) == 1:
readline.parse_and_bind("tab: complete")
readline.set_completer(completer)
print("\nEnter command: ")
while True:
userfeedback = input('')
command = userfeedback.split(' ')[0]
if userfeedback == 'quit':
break
elif userfeedback == 'help':
print("Help is not yet implemented!")
else:
try:
key, command = userfeedback.split(".", 1)
except ValueError:
print("Unknown device.")
else:
device = devicedict[key]
try:
command, params = command.split(" ", 1)
except ValueError:
params = None
try:
obj = getattr(device, command)
except AttributeError:
print("Unknown command.")
else:
if params is not None:
params = params.replace("True", "true")
params = params.replace("False", "false")
params = params.replace("None", "null")
params = params.replace(",", " ")
params = params.split(" ")
params = " ".join([p for p in params if p])
try:
params = json.loads("[" + params.replace(" ", ", ") + "]")
except json.decoder.JSONDecodeError:
print("You need to give python like parameters (e.g. 'test' for a string containing test).")
params = None
try:
obj(*params)
except TypeError:
print("Give the correct parameters to execute.")

View File

@ -26,27 +26,25 @@ devices (DEVICES)
""" """
import json
import logging
import task
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'
from devices.shelly import shelly as shelly_sw1 BATTERY_WARN_LEVEL = 5
from devices.shelly import shelly_rpc as shelly_pro3
from devices.hue import hue_light as hue_sw_br_ct
from devices.tradfri import tradfri_light as tradfri_sw def is_json(data):
from devices.tradfri import tradfri_light as tradfri_sw_br try:
from devices.tradfri import tradfri_light as tradfri_sw_br_ct json.loads(data)
from devices.tradfri import tradfri_button as tradfri_button except json.decoder.JSONDecodeError:
from devices.tradfri import tradfri_light as livarno_sw_br_ct return False
from devices.brennenstuhl import brennenstuhl_heatingvalve else:
from devices.silvercrest import silvercrest_button return True
from devices.silvercrest import silvercrest_powerplug
from devices.silvercrest import silvercrest_motion_sensor
from devices.mydevices import powerplug as my_powerplug
from devices.mydevices import audio_status
from devices.mydevices import remote
my_ambient = None
class group(object): class group(object):
@ -56,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
@ -98,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

@ -1,235 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
from base import mqtt_base
from base import videv_base
import json
def is_json(data):
try:
json.loads(data)
except json.decoder.JSONDecodeError:
return False
else:
return True
class base(mqtt_base):
TX_TOPIC = "set"
TX_VALUE = 0
TX_DICT = 1
TX_TYPE = -1
TX_FILTER_DATA_KEYS = []
#
RX_KEYS = []
RX_IGNORE_TOPICS = []
RX_IGNORE_KEYS = []
RX_FILTER_DATA_KEYS = []
#
CFG_DATA = {}
def __init__(self, mqtt_client, topic):
super().__init__(mqtt_client, topic, default_values=dict.fromkeys(self.RX_KEYS))
# data storage
self.__cfg_by_mid__ = None
# initialisations
mqtt_client.add_callback(topic=self.topic, callback=self.receive_callback)
mqtt_client.add_callback(topic=self.topic+"/#", callback=self.receive_callback)
#
self.add_callback(None, None, self.__state_logging__, on_change_only=True)
def __cfg_callback__(self, key, value, mid):
if self.CFG_DATA.get(key) != value and self.__cfg_by_mid__ != mid and mid is not None:
self.__cfg_by_mid__ = mid
self.logger.warning("Differing configuration identified: Sending default configuration to defice: %s", repr(self.CFG_DATA))
if self.TX_TYPE == self.TX_DICT:
self.mqtt_client.send(self.topic + '/' + self.TX_TOPIC, json.dumps(self.CFG_DATA))
else:
for key in self.CFG_DATA:
self.send_command(key, self.CFG_DATA.get(key))
def set(self, key, data, mid=None, block_callback=[]):
if key in self.CFG_DATA:
self.__cfg_callback__(key, data, mid)
if key in self.RX_IGNORE_KEYS:
pass # ignore these keys
elif key in self.RX_KEYS:
return super().set(key, data, block_callback)
else:
self.logger.warning("Unexpected key %s", key)
def receive_callback(self, client, userdata, message):
if message.topic != self.topic + '/' + videv_base.KEY_INFO:
content_key = message.topic[len(self.topic) + 1:]
if content_key not in self.RX_IGNORE_TOPICS and (not message.topic.endswith(self.TX_TOPIC) or len(self.TX_TOPIC) == 0):
self.logger.debug("Unpacking content_key \"%s\" from message.", content_key)
if is_json(message.payload):
data = json.loads(message.payload)
if type(data) is dict:
for key in data:
self.set(key, self.__device_to_instance_filter__(key, data[key]), message.mid)
else:
self.set(content_key, self.__device_to_instance_filter__(content_key, data), message.mid)
# String
else:
self.set(content_key, self.__device_to_instance_filter__(content_key, message.payload.decode('utf-8')), message.mid)
else:
self.logger.debug("Ignoring topic %s", content_key)
def __device_to_instance_filter__(self, key, data):
if key in self.RX_FILTER_DATA_KEYS:
if data in [1, 'on', 'ON']:
return True
elif data in [0, 'off', 'OFF']:
return False
return data
def __instance_to_device_filter__(self, key, data):
if key in self.TX_FILTER_DATA_KEYS:
if data is True:
return "on"
elif data is False:
return "off"
return data
def send_command(self, key, data):
data = self.__instance_to_device_filter__(key, data)
if self.TX_TOPIC is not None:
if self.TX_TYPE < 0:
self.logger.error("Unknown tx type. Set TX_TYPE of class to a known value")
else:
self.logger.debug("Sending data for %s - %s", key, str(data))
if self.TX_TYPE == self.TX_DICT:
try:
self.mqtt_client.send('/'.join([self.topic, self.TX_TOPIC]), json.dumps({key: data}))
except TypeError:
print(self.topic)
print(key.__dict__)
print(key)
print(data)
raise TypeError
else:
if type(data) not in [str, bytes]:
data = json.dumps(data)
self.mqtt_client.send('/'.join([self.topic, key, self.TX_TOPIC] if len(self.TX_TOPIC) > 0 else [self.topic, key]), data)
else:
self.logger.error("Unknown tx toptic. Set TX_TOPIC of class to a known value")
class base_rpc(mqtt_base):
SRC_RESPONSE = "/response"
SRC_NULL = "/null"
#
EVENTS_TOPIC = "/events/rpc"
TX_TOPIC = "/rpc"
RESPONSE_TOPIC = SRC_RESPONSE + "/rpc"
NULL_TOPIC = SRC_NULL + "/rpc"
#
RPC_ID_GET_STATUS = 1
RPC_ID_SET = 1734
#
def __init__(self, mqtt_client, topic):
super().__init__(mqtt_client, topic, default_values=dict.fromkeys(self.RX_KEYS))
# data storage
self.__cfg_by_mid__ = None
# initialisations
mqtt_client.add_callback(topic=self.topic, callback=self.receive_callback)
mqtt_client.add_callback(topic=self.topic+"/#", callback=self.receive_callback)
#
self.add_callback(None, None, self.__state_logging__, on_change_only=False)
#
self.rpc_get_status()
def receive_callback(self, client, userdata, message):
data = json.loads(message.payload)
#
if message.topic == self.topic + self.EVENTS_TOPIC:
self.events(data)
elif message.topic == self.topic + self.RESPONSE_TOPIC:
self.response(data)
elif message.topic == self.topic + self.NULL_TOPIC or message.topic == self.topic + self.TX_TOPIC or message.topic == self.topic + "/online":
pass # Ignore response
else:
self.logger.warning("Unexpected message received: %s::%s", message.topic, json.dumps(data, sort_keys=True, indent=4))
def events(self, data):
for rx_key in data["params"]:
if rx_key == "events":
for evt in data["params"]["events"]:
key = evt["component"]
event = evt["event"]
if key in self.RX_KEYS:
if event == "btn_down":
self.set(key, True)
elif event == "btn_up":
self.set(key, False)
else:
key = key + ":" + event
if key in self.RX_KEYS:
self.set(key, True)
else:
self.logger.warning("Unexpected event with data=%s", json.dumps(data, sort_keys=True, indent=4))
elif rx_key in self.RX_KEYS:
state = data["params"][rx_key].get("output")
if state is not None:
self.set(rx_key, state)
def response(self, data):
try:
rpc_id = data.get("id")
except AttributeError:
rpc_id = None
try:
rpc_method = data.get("method")
except AttributeError:
rpc_method = None
if rpc_id == self.RPC_ID_GET_STATUS:
#
# Shelly.GetStatus
#
for rx_key in data.get("result", []):
if rx_key in self.RX_KEYS:
key_data = data["result"][rx_key]
state = key_data.get("output", key_data.get("state"))
if state is not None:
self.set(rx_key, state)
else:
self.logger.warning("Unexpected response with data=%s", json.dumps(data, sort_keys=True, indent=4))
def rpc_tx(self, **kwargs):
if not "id" in kwargs:
raise AttributeError("'id' is missing in keyword arguments")
self.mqtt_client.send(self.topic + self.TX_TOPIC, json.dumps(kwargs))
def rpc_get_status(self):
self.rpc_tx(
id=self.RPC_ID_GET_STATUS,
src=self.topic + self.SRC_RESPONSE,
method="Shelly.GetStatus"
)
def rpc_switch_set(self, key, state: bool):
self.rpc_tx(
id=self.RPC_ID_SET,
src=self.topic + self.SRC_NULL,
method="Switch.Set",
params={"id": int(key[-1]), "on": state}
)
class base_output(base):
def __init__(self, mqtt_client, topic):
super().__init__(mqtt_client, topic)
self.__all_off_enabled__ = True
def disable_all_off(self, state=True):
self.__all_off_enabled__ = not state
def all_off(self):
if self.__all_off_enabled__:
try:
self.__all_off__()
except (AttributeError, TypeError) as e:
self.logger.warning("Method all_off was used, but __all_off__ method wasn't callable: %s", repr(e))

View File

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

View File

@ -1,118 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
from devices.base import base, base_output
import logging
class hue_light(base_output):
""" Communication (MQTT)
hue_light {
| "state": ["ON" / "OFF" / "TOGGLE"]
| "linkquality": [0...255] lqi
| "brightness": [0...254]
| "color_mode": ["color_temp"]
| "color_temp": ["coolest", "cool", "neutral", "warm", "warmest", 250...454]
| }
+- get {
| "state": ""
| }
+- set {
"state": ["ON" / "OFF"]
"brightness": [0...256]
"color_temp": [250...454]
"transition": [0...] seconds
"brightness_move": [-X...0...X] X/s
"brightness_step": [-X...0...X]
"color_temp_move": [-X...0...X] X/s
"color_temp_step": [-X...0...X]
}
"""
KEY_LINKQUALITY = "linkquality"
KEY_OUTPUT_0 = "state"
KEY_BRIGHTNESS = "brightness"
KEY_COLOR_TEMP = "color_temp"
#
TX_TYPE = base.TX_DICT
TX_FILTER_DATA_KEYS = [KEY_OUTPUT_0, KEY_BRIGHTNESS, KEY_COLOR_TEMP]
STATE_KEYS = TX_FILTER_DATA_KEYS
#
RX_KEYS = [KEY_LINKQUALITY, KEY_OUTPUT_0, KEY_BRIGHTNESS, KEY_COLOR_TEMP]
RX_IGNORE_KEYS = ['update', 'color_mode']
RX_FILTER_DATA_KEYS = [KEY_OUTPUT_0, KEY_BRIGHTNESS, KEY_COLOR_TEMP]
def __state_logging__(self, inst, key, data):
if key in [self.KEY_OUTPUT_0, self.KEY_BRIGHTNESS, self.KEY_COLOR_TEMP]:
self.logger.info("State change of '%s' to '%s'", key, repr(data))
def __device_to_instance_filter__(self, key, data):
if key == self.KEY_BRIGHTNESS:
return int(round((data - 1) * 100 / 253, 0))
elif key == self.KEY_COLOR_TEMP:
return int(round((data - 250) * 10 / 204, 0))
return super().__device_to_instance_filter__(key, data)
def __instance_to_device_filter__(self, key, data):
if key == self.KEY_BRIGHTNESS:
return int(round(data * 253 / 100 + 1, 0))
elif key == self.KEY_COLOR_TEMP:
return int(round(data * 204 / 10 + 250, 0))
return super().__instance_to_device_filter__(key, data)
#
# RX
#
@property
def output_0(self):
"""rv: [True, False]"""
return self.get(self.KEY_OUTPUT_0, False)
@property
def linkquality(self):
"""rv: numeric value"""
return self.get(self.KEY_LINKQUALITY, 0)
@property
def brightness(self):
"""rv: numeric value [0%, ..., 100%]"""
return self.get(self.KEY_BRIGHTNESS, 0)
@property
def color_temp(self):
"""rv: numeric value [0, ..., 10]"""
return self.get(self.KEY_COLOR_TEMP, 0)
#
# TX
#
def request_data(self, device=None, key=None, data=None):
self.mqtt_client.send(self.topic + "/set", '{"hue_power_on_behavior": "recover"}')
def set_output_0(self, state):
"""state: [True, False]"""
self.send_command(self.KEY_OUTPUT_0, state)
def set_output_0_mcb(self, device, key, data):
self.set_output_0(data)
def toggle_output_0_mcb(self, device, key, data):
self.set_output_0(not self.output_0)
def set_brightness(self, brightness):
"""brightness: [0, ..., 100]"""
self.send_command(self.KEY_BRIGHTNESS, brightness)
def set_brightness_mcb(self, device, key, data):
self.set_brightness(data)
def set_color_temp(self, color_temp):
"""color_temp: [0, ..., 10]"""
self.send_command(self.KEY_COLOR_TEMP, color_temp)
def set_color_temp_mcb(self, device, key, data):
self.set_color_temp(data)
def __all_off__(self):
if self.output_0:
self.set_output_0(False)

View File

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

View File

@ -1,238 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
from devices.base import base_output
from devices.base import base_rpc
import task
class shelly(base_output):
""" Communication (MQTT)
shelly
+- relay
| +- 0 ["on" / "off"] <- status
| | +- command ["on"/ "off"] <- command
| | +- energy [numeric] <- status
| +- 1 ["on" / "off"] <- status
| +- command ["on"/ "off"] <- command
| +- energy [numeric] <- status
+- input
| +- 0 [0 / 1] <- status
| +- 1 [0 / 1] <- status
+- input_event
| +- 0 <- status
| +- 1 <- status
+- logpush
| +- 0 [0 / 1] <- status
| +- 1 [0 / 1] <- status
+- temperature [numeric] °C <- status
+- temperature_f [numeric] F <- status
+- overtemperature [0 / 1] <- status
+- id <- status
+- model <- status
+- mac <- status
+- ip <- status
+- new_fw <- status
+- fw_ver <- status
"""
KEY_OUTPUT_0 = "relay/0"
KEY_OUTPUT_1 = "relay/1"
KEY_INPUT_0 = "input/0"
KEY_INPUT_1 = "input/1"
KEY_LONGPUSH_0 = "longpush/0"
KEY_LONGPUSH_1 = "longpush/1"
KEY_TEMPERATURE = "temperature"
KEY_OVERTEMPERATURE = "overtemperature"
KEY_ID = "id"
KEY_MODEL = "model"
KEY_MAC = "mac"
KEY_IP = "ip"
KEY_NEW_FIRMWARE = "new_fw"
KEY_FIRMWARE_VERSION = "fw_ver"
#
TX_TOPIC = "command"
TX_TYPE = base_output.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.75, self.flash_task)
self.delayed_off_task = task.delayed(0.75, self.off_task)
#
self.all_off_requested = False
def __state_logging__(self, inst, key, data):
if key in [self.KEY_OUTPUT_0, self.KEY_OUTPUT_1]:
self.logger.info("State change of '%s' to '%s'", key, repr(data))
elif key in [self.KEY_INPUT_0, self.KEY_INPUT_1, self.KEY_LONGPUSH_0, self.KEY_LONGPUSH_1]:
self.logger.info("Input action '%s' with '%s'", key, repr(data))
def flash_task(self, *args):
if self.flash_active:
self.send_command(self.output_key_delayed, not self.get(self.output_key_delayed))
self.output_key_delayed = None
if self.all_off_requested:
self.delayed_off_task.run()
def off_task(self, *args):
self.all_off()
@property
def flash_active(self):
return self.output_key_delayed is not None
#
# RX
#
@property
def output_0(self):
"""rv: [True, False]"""
return self.get(self.KEY_OUTPUT_0)
@property
def output_1(self):
"""rv: [True, False]"""
return self.get(self.KEY_OUTPUT_1)
@property
def input_0(self):
"""rv: [True, False]"""
return self.get(self.KEY_INPUT_0)
@property
def input_1(self):
"""rv: [True, False]"""
return self.get(self.KEY_INPUT_1)
@property
def longpush_0(self):
"""rv: [True, False]"""
return self.get(self.KEY_LONGPUSH_0)
@property
def longpush_1(self):
"""rv: [True, False]"""
return self.get(self.KEY_LONGPUSH_1)
@property
def temperature(self):
"""rv: numeric value"""
return self.get(self.KEY_TEMPERATURE)
#
# TX
#
def set_output_0(self, state):
"""state: [True, False]"""
self.send_command(self.KEY_OUTPUT_0, state)
def set_output_0_mcb(self, device, key, data):
self.set_output_0(data)
def toggle_output_0_mcb(self, device, key, data):
self.set_output_0(not self.output_0)
def set_output_1(self, state):
"""state: [True, False]"""
self.send_command(self.KEY_OUTPUT_1, state)
def set_output_1_mcb(self, device, key, data):
self.set_output_1(data)
def toggle_output_1_mcb(self, device, key, data):
self.set_output_1(not self.output_1)
def flash_0_mcb(self, device, key, data):
self.output_key_delayed = self.KEY_OUTPUT_0
self.toggle_output_0_mcb(device, key, data)
self.delayed_flash_task.run()
def flash_1_mcb(self, device, key, data):
self.output_key_delayed = self.KEY_OUTPUT_1
self.toggle_output_1_mcb(device, key, data)
self.delayed_flash_task.run()
def __all_off__(self):
if self.flash_active:
self.all_off_requested = True
else:
if self.output_0:
self.set_output_0(False)
if self.output_1:
self.set_output_1(False)
class shelly_rpc(base_rpc):
KEY_OUTPUT_0 = "switch:0"
KEY_OUTPUT_1 = "switch:1"
KEY_OUTPUT_2 = "switch:2"
KEY_INPUT_0 = "input:0"
KEY_INPUT_1 = "input:1"
KEY_INPUT_2 = "input:2"
KEY_LONGPUSH_0 = "input:0:long_push"
KEY_LONGPUSH_1 = "input:1:long_push"
KEY_LONGPUSH_2 = "input:2:long_push"
KEY_SINGLEPUSH_0 = "input:0:single_push"
KEY_SINGLEPUSH_1 = "input:1:single_push"
KEY_SINGLEPUSH_2 = "input:2:single_push"
KEY_DOUBLEPUSH_0 = "input:0:double_push"
KEY_DOUBLEPUSH_1 = "input:1:double_push"
KEY_DOUBLEPUSH_2 = "input:2:double_push"
KEY_TRIPLEPUSH_0 = "input:0:triple_push"
KEY_TRIPLEPUSH_1 = "input:1:triple_push"
KEY_TRIPLEPUSH_2 = "input:2:triple_push"
RX_KEYS = [KEY_OUTPUT_0, KEY_OUTPUT_1, KEY_OUTPUT_2, KEY_INPUT_0, KEY_INPUT_1, KEY_INPUT_2,
KEY_LONGPUSH_0, KEY_LONGPUSH_1, KEY_LONGPUSH_2, KEY_SINGLEPUSH_0, KEY_SINGLEPUSH_1, KEY_SINGLEPUSH_2,
KEY_DOUBLEPUSH_0, KEY_DOUBLEPUSH_1, KEY_DOUBLEPUSH_2, KEY_TRIPLEPUSH_0, KEY_TRIPLEPUSH_1, KEY_TRIPLEPUSH_2]
def __state_logging__(self, inst, key, data):
if key in [self.KEY_OUTPUT_0, self.KEY_OUTPUT_1, self.KEY_OUTPUT_2]:
self.logger.info("State change of '%s' to '%s'", key, repr(data))
elif key in [self.KEY_INPUT_0, self.KEY_INPUT_1, self.KEY_INPUT_2]:
self.logger.info("Input action '%s' with '%s'", key, repr(data))
elif key in [self.KEY_LONGPUSH_0, self.KEY_LONGPUSH_1, self.KEY_LONGPUSH_2,
self.KEY_SINGLEPUSH_0, self.KEY_SINGLEPUSH_1, self.KEY_SINGLEPUSH_2,
self.KEY_DOUBLEPUSH_0, self.KEY_DOUBLEPUSH_1, self.KEY_DOUBLEPUSH_2,
self.KEY_TRIPLEPUSH_0, self.KEY_TRIPLEPUSH_1, self.KEY_TRIPLEPUSH_2]:
self.logger.info("Input action '%s'", key)
def set_output_0(self, state):
"""state: [True, False]"""
self.rpc_switch_set(self.KEY_OUTPUT_0, state)
def set_output_0_mcb(self, device, key, data):
self.set_output_0(data)
def toggle_output_0_mcb(self, device, key, data):
self.set_output_0(not self.get(self.KEY_OUTPUT_0))
def set_output_1(self, state):
"""state: [True, False]"""
self.rpc_switch_set(self.KEY_OUTPUT_1, state)
def set_output_1_mcb(self, device, key, data):
self.set_output_1(data)
def toggle_output_1_mcb(self, device, key, data):
print(self.get(self.KEY_OUTPUT_1))
self.set_output_1(not self.get(self.KEY_OUTPUT_1))
def set_output_2(self, state):
"""state: [True, False]"""
self.rpc_switch_set(self.KEY_OUTPUT_2, state)
def set_output_2_mcb(self, device, key, data):
self.set_output_2(data)
def toggle_output_2_mcb(self, device, key, data):
self.set_output_2(not self.get(self.KEY_OUTPUT_2))

View File

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

View File

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

View File

@ -3,13 +3,13 @@
# #
import config import config
import devices import devices
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
from function.videv import all_off, videv_pure_switch from function.videv import all_off
import inspect
import logging import logging
try: try:
@ -20,21 +20,19 @@ logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
class all_functions(room_collection): class all_functions(room_collection):
def __init__(self, mqtt_client, pd, vd): def __init__(self, mqtt_client):
super().__init__(mqtt_client, pd, vd) super().__init__(mqtt_client)
# #
# Rooms # Rooms
# #
# garden
self.gar = garden(self.mqtt_client, pd, vd)
# stairway # stairway
self.stw = stairway(self.mqtt_client, pd, vd) self.stw = stairway(self.mqtt_client)
# ground floor west # ground floor west
self.gfw = ground_floor_west(self.mqtt_client, pd, vd) self.gfw = ground_floor_west(self.mqtt_client)
# first floor west # first floor west
self.ffw = first_floor_west(self.mqtt_client, pd, vd) self.ffw = first_floor_west(self.mqtt_client)
# first floor east # first floor east
self.ffe = first_floor_east(self.mqtt_client, pd, vd) self.ffe = first_floor_east(self.mqtt_client)
# #
# Interactions # Interactions
# #
@ -42,48 +40,30 @@ class all_functions(room_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 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.main_light_shelly.add_callback(self.gfw.dirk.main_light_shelly.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.button_tradfri.add_callback(self.ffe.sleep.button_tradfri.KEY_ACTION, self.ffe.sleep.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION,
self.ffe.sleep.button_tradfri.ACTION_RIGHT, devices.tradfri_button.ACTION_RIGHT, self.ffe.floor.main_light_shelly.toggle_output_0_mcb)
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 = all_off(self.mqtt_client, config.TOPIC_ALL_OFF_VIDEV, 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.main_light_shelly.add_callback(self.stw.stairway.main_light_shelly.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.main_light_shelly.flash_0_mcb) self.stw.stairway.main_light_shelly.add_callback(devices.shelly.KEY_LONGPUSH_0, True, self.all_off)
self.stw.stairway.main_light_shelly.add_callback(self.stw.stairway.main_light_shelly.KEY_LONGPUSH_0, True, self.all_off)
# FFE ALL OFF - Long push ffe_floor # FFE ALL OFF - Long push ffe_floor
self.ffe.floor.main_light_shelly.add_callback(self.ffe.floor.main_light_shelly.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.main_light_shelly.flash_0_mcb) self.ffe.floor.main_light_shelly.add_callback(devices.shelly.KEY_LONGPUSH_0, True, self.ffe.all_off)
self.ffe.floor.main_light_shelly.add_callback(self.ffe.floor.main_light_shelly.KEY_LONGPUSH_0, True, self.ffe.all_off)
# FFE ALL OFF - Long push input device # FFE ALL OFF - Long push input device
self.ffe.sleep.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_RIGHT_LONG, self.ffe.all_off) self.ffe.sleep.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_RIGHT_LONG, self.ffe.all_off)
# FFW ALL OFF - Long push ffw_floor
self.ffw.floor.main_light_shelly.add_callback(self.ffw.floor.main_light_shelly.KEY_LONGPUSH_0,
True, self.ffw.floor.main_light_shelly.flash_0_mcb)
self.ffw.floor.main_light_shelly.add_callback(self.ffw.floor.main_light_shelly.KEY_LONGPUSH_0, True, self.ffw.all_off)
def init_sumer_winter_mode(self):
# ALL summer/winter mode
self.videv_summer_mode = videv_pure_switch(self.mqtt_client, config.TOPIC_ALL_SUMMER_WINTER_MODE)
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:

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,8 @@
# #
import config import config
from devdi import topic as props import devices
from devices import group from function.modules import brightness_choose_n_action, timer_on_activation, heating_function
from function.db import get_radiator_data, set_radiator_data
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 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
@ -18,311 +15,204 @@ except ImportError:
ROOT_LOGGER_NAME = 'root' ROOT_LOGGER_NAME = 'root'
logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
loc = props.LOC_FFE
class first_floor_east(room_collection): class first_floor_east(room_collection):
def __init__(self, mqtt_client, pd, vd): def __init__(self, mqtt_client):
super().__init__(mqtt_client, pd, vd) super().__init__(mqtt_client)
self.dining = first_floor_east_dining(mqtt_client, pd, vd) self.dining = first_floor_east_dining(mqtt_client)
self.floor = first_floor_east_floor(mqtt_client, pd, vd) self.floor = first_floor_east_floor(mqtt_client)
self.kitchen = first_floor_east_kitchen(mqtt_client, pd, vd) self.kitchen = first_floor_east_kitchen(mqtt_client)
self.livingroom = first_floor_east_living(mqtt_client, pd, vd) self.livingroom = first_floor_east_living(mqtt_client)
self.sleep = first_floor_east_sleep(mqtt_client, pd, vd) self.sleep = first_floor_east_sleep(mqtt_client)
class first_floor_east_floor(room): class first_floor_east_floor(room):
def __init__(self, mqtt_client, pd, vd): def __init__(self, mqtt_client):
roo = props.ROO_FLO
# #
# Device initialisation # Device initialisation
# #
# http://shelly1l-3C6105E4E629 # http://shelly1l-3C6105E4E629
# main light self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFE_FLOOR_MAIN_LIGHT_SHELLY)
self.main_light_shelly = pd.get(props.STG_SHE, loc, roo, props.FUN_MAL) super().__init__(mqtt_client)
super().__init__(mqtt_client, pd, vd)
# #
# Virtual Device Interface # Virtual Device Interface
# #
# main light
self.main_light = videv_switching( self.main_light = videv_switching(
mqtt_client, config.TOPIC_FFE_FLOOR_MAIN_LIGHT_VIDEV, mqtt_client, config.TOPIC_FFE_FLOOR_MAIN_LIGHT_VIDEV,
self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0 self.main_light_shelly, devices.shelly.KEY_OUTPUT_0
) )
class first_floor_east_kitchen(room): class first_floor_east_kitchen(room):
def __init__(self, mqtt_client, pd, vd): def __init__(self, mqtt_client):
roo = props.ROO_KIT
# #
# Device initialisation # Device initialisation
# #
# http://shelly1l-8CAAB5616C01 # http://shelly1l-8CAAB5616C01
# main light self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFE_KITCHEN_MAIN_LIGHT_SHELLY)
self.main_light_shelly = pd.get(props.STG_SHE, loc, roo, props.FUN_MAL) # http://shelly1-e89f6d85a466/
self.main_light_hue = pd.get(props.STG_ZFE, loc, roo, props.FUN_MAL) self.circulation_pump_shelly = devices.shelly(mqtt_client, config.TOPIC_FFE_KITCHEN_CIRCULATION_PUMP_SHELLY)
# http://shelly1-e89f6d85a466 super().__init__(mqtt_client)
# circulation pump
self.circulation_pump_shelly = pd.get(props.STG_SHE, loc, roo, props.FUN_CIR)
# heating function
self.heating_valve = pd.get(props.STG_ZFE, loc, roo, props.FUN_HEA)
super().__init__(mqtt_client, pd, vd)
# #
# Functionality initialisation # Functionality initialisation
# #
# circulation pump self.circulation_pump = timer_on_activation(self.circulation_pump_shelly, devices.shelly.KEY_OUTPUT_0, 10*60)
self.circulation_pump = timer_on_activation(self.circulation_pump_shelly, self.circulation_pump_shelly.KEY_OUTPUT_0, 10*60) self.circulation_pump_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, True, self.main_light_shelly.flash_0_mcb, True)
self.circulation_pump_shelly.add_callback(self.circulation_pump_shelly.KEY_OUTPUT_0, True, self.main_light_shelly.flash_0_mcb, True)
# Request hue data of lead light after power on
switched_light(self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0, self.main_light_hue)
# heating function
self.heating_function = heating_function(
self.heating_valve,
config.DEFAULT_TEMPERATURE,
**get_radiator_data(self.heating_valve.topic)
)
self.heating_function.add_callback(None, None, set_radiator_data, True)
# #
# Virtual Device Interface # Virtual Device Interface
# #
# main light self.main_light_videv = videv_switching(
self.main_light_videv = videv_switch_brightness_color_temp(
mqtt_client, config.TOPIC_FFE_KITCHEN_MAIN_LIGHT_VIDEV, mqtt_client, config.TOPIC_FFE_KITCHEN_MAIN_LIGHT_VIDEV,
self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0, self.main_light_shelly, devices.shelly.KEY_OUTPUT_0
self.main_light_hue, self.main_light_hue.KEY_BRIGHTNESS,
self.main_light_hue, self.main_light_hue.KEY_COLOR_TEMP
) )
# circulation pump
self.circulation_pump_videv = videv_switching_timer( self.circulation_pump_videv = videv_switching_timer(
mqtt_client, config.TOPIC_FFE_KITCHEN_CIRCULATION_PUMP_VIDEV, mqtt_client, config.TOPIC_FFE_KITCHEN_CIRCULATION_PUMP_VIDEV,
self.circulation_pump_shelly, self.circulation_pump_shelly.KEY_OUTPUT_0, self.circulation_pump_shelly, devices.shelly.KEY_OUTPUT_0,
self.circulation_pump, timer_on_activation.KEY_TIMER self.circulation_pump, timer_on_activation.KEY_TIMER
) )
# heating function
self.heating_function_videv = videv_heating(
mqtt_client, config.TOPIC_FFE_KITCHEN_HEATING_VALVE_VIDEV,
self.heating_function
)
class first_floor_east_dining(room): class first_floor_east_dining(room):
def __init__(self, mqtt_client, pd, vd): def __init__(self, mqtt_client):
roo = props.ROO_DIN
# #
# Device initialisation # Device initialisation
# #
self.day_events = day_event((6, 0), (22, 0), 30, -30)
# http://shelly1l-84CCA8ADD055 # http://shelly1l-84CCA8ADD055
# main light self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFE_DININGROOM_MAIN_LIGHT_SHELLY)
self.main_light_shelly = pd.get(props.STG_SHE, loc, roo, props.FUN_MAL) self.floorlamp_powerplug = devices.silvercrest_powerplug(mqtt_client, config.TOPIC_FFE_DININGROOM_FLOOR_LAMP_POWERPLUG)
# floor lamp
self.floorlamp_powerplug = pd.get(props.STG_ZFE, loc, roo, props.FUN_FLL)
# heating function
self.heating_valve = pd.get(props.STG_ZFE, loc, roo, props.FUN_HEA)
# garland
if config.CHRISTMAS: if config.CHRISTMAS:
self.garland_powerplug = pd.get(props.STG_ZFE, loc, roo, props.FUN_GAR) self.garland_powerplug = devices.silvercrest_powerplug(mqtt_client, config.TOPIC_FFE_DININGROOM_GARLAND_POWERPLUG)
super().__init__(mqtt_client, pd, vd) super().__init__(mqtt_client)
# #
# Functionality initialisation # Functionality initialisation
# #
self.day_events.add_callback(None, True, self.__day_events__, True) self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.floorlamp_powerplug.set_output_0_mcb, True)
# main light
self.main_light_shelly.add_callback(self.main_light_shelly.KEY_OUTPUT_0, None, self.floorlamp_powerplug.set_output_0_mcb, True)
# heating function
self.heating_function = heating_function(
self.heating_valve,
config.DEFAULT_TEMPERATURE,
**get_radiator_data(self.heating_valve.topic)
)
self.heating_function.add_callback(None, None, set_radiator_data, True)
# #
# Virtual Device Interface # Virtual Device Interface
# #
# main light
self.main_light_videv = videv_switching( self.main_light_videv = videv_switching(
mqtt_client, config.TOPIC_FFE_DININGROOM_MAIN_LIGHT_VIDEV, mqtt_client, config.TOPIC_FFE_DININGROOM_MAIN_LIGHT_VIDEV,
self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0 self.main_light_shelly, devices.shelly.KEY_OUTPUT_0
) )
# floor lamp
self.floorlamp_videv = videv_switching( self.floorlamp_videv = videv_switching(
mqtt_client, config.TOPIC_FFE_DININGROOM_FLOOR_LAMP_VIDEV, mqtt_client, config.TOPIC_FFE_DININGROOM_FLOOR_LAMP_VIDEV,
self.floorlamp_powerplug, self.floorlamp_powerplug.KEY_OUTPUT_0 self.floorlamp_powerplug, devices.silvercrest_powerplug.KEY_OUTPUT_0
) )
# heating function
self.heating_function_videv = videv_heating(
mqtt_client, config.TOPIC_FFE_DININGROOM_HEATING_VALVE_VIDEV,
self.heating_function
)
# garland
if config.CHRISTMAS: if config.CHRISTMAS:
self.garland_videv = videv_switching( self.garland_videv = videv_switching(
mqtt_client, config.TOPIC_FFE_DININGROOM_GARLAND_VIDEV, mqtt_client, config.TOPIC_FFE_DININGROOM_GARLAND_VIDEV,
self.garland_powerplug, self.garland_powerplug.KEY_OUTPUT_0 self.garland_powerplug, devices.silvercrest_powerplug.KEY_OUTPUT_0
) )
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.garland_powerplug.set_output_0(True)
elif key in (self.day_events.KEY_START_OF_NIGHT, self.day_events.KEY_SUNRISE):
if config.CHRISTMAS:
self.garland_powerplug.set_output_0(False)
class first_floor_east_sleep(room): class first_floor_east_sleep(room):
def __init__(self, mqtt_client, pd, vd): def __init__(self, mqtt_client):
roo = props.ROO_SLP
# #
# Device initialisation # Device initialisation
# #
# http://shelly1l-E8DB84A254C7 # http://shelly1l-E8DB84A254C7
# main light self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFE_SLEEP_MAIN_LIGHT_SHELLY)
self.main_light_shelly = pd.get(props.STG_SHE, loc, roo, props.FUN_MAL) self.main_light_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_FFE_SLEEP_MAIN_LIGHT_ZIGBEE)
self.main_light_tradfri = pd.get(props.STG_ZFE, loc, roo, props.FUN_MAL) self.bed_light_di_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_FFE_SLEEP_BED_LIGHT_DI_ZIGBEE)
# bed light self.bed_light_ma_powerplug = devices.silvercrest_powerplug(mqtt_client, config.TOPIC_FFE_SLEEP_BED_LIGHT_MA_POWERPLUG)
self.bed_light_di_tradfri = pd.get(props.STG_ZFE, loc, roo, props.FUN_BLD) self.heating_valve = devices.brennenstuhl_heatingvalve(mqtt_client, config.TOPIC_FFE_SLEEP_HEATING_VALVE_ZIGBEE)
self.bed_light_ma_powerplug = pd.get(props.STG_ZFE, loc, roo, props.FUN_BLM) self.button_tradfri = devices.tradfri_button(mqtt_client, config.TOPIC_FFE_SLEEP_INPUT_DEVICE)
# heating function
self.heating_valve = pd.get(props.STG_ZFE, loc, roo, props.FUN_HEA)
# button
self.button_tradfri = pd.get(props.STG_ZFE, loc, roo, props.FUN_INP)
# wardrobe light
self.wardrobe_light = pd.get(props.STG_ZFE, loc, roo, props.FUN_WLI)
self.wardrobe_light.disable_all_off() # Always on - Off by light sensor
super().__init__(mqtt_client, pd, vd) super().__init__(mqtt_client)
# #
# Functionality initialisation # Functionality initialisation
# #
# button / brightness function # button / brightness function
self.brightness_functions = brightness_choose_n_action(self.button_tradfri) self.brightness_functions = brightness_choose_n_action(self.button_tradfri)
self.brightness_functions.add(self.main_light_tradfri, self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0) self.brightness_functions.add(self.main_light_tradfri, self.main_light_shelly, devices.shelly.KEY_OUTPUT_0)
self.brightness_functions.add(self.bed_light_di_tradfri, self.bed_light_di_tradfri, self.bed_light_di_tradfri.KEY_OUTPUT_0) 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.button_tradfri.add_callback(self.button_tradfri.KEY_ACTION, self.button_tradfri.ACTION_TOGGLE, self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_TOGGLE,
self.main_light_shelly.toggle_output_0_mcb) self.main_light_shelly.toggle_output_0_mcb)
# button / bed light # button / bed light
self.button_tradfri.add_callback(self.button_tradfri.KEY_ACTION, self.button_tradfri.ACTION_LEFT, self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_LEFT,
self.bed_light_di_tradfri.toggle_output_0_mcb) self.bed_light_di_tradfri.toggle_output_0_mcb)
self.button_tradfri.add_callback(self.button_tradfri.KEY_ACTION, self.button_tradfri.ACTION_LEFT_LONG, self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_LEFT_LONG,
self.bed_light_ma_powerplug.toggle_output_0_mcb) self.bed_light_ma_powerplug.toggle_output_0_mcb)
# heating function # heating function
self.heating_function = heating_function( self.heating_function = heating_function(self.heating_valve)
self.heating_valve,
config.DEFAULT_TEMPERATURE,
**get_radiator_data(self.heating_valve.topic)
)
self.heating_function.add_callback(None, None, set_radiator_data, True)
# #
# Virtual Device Interface # Virtual Device Interface
# #
# main light
self.main_light_videv = videv_switch_brightness_color_temp( self.main_light_videv = videv_switch_brightness_color_temp(
mqtt_client, config.TOPIC_FFE_SLEEP_MAIN_LIGHT_VIDEV, mqtt_client, config.TOPIC_FFE_SLEEP_MAIN_LIGHT_VIDEV,
self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0, self.main_light_shelly, devices.shelly.KEY_OUTPUT_0,
self.main_light_tradfri, self.main_light_tradfri.KEY_BRIGHTNESS, self.main_light_tradfri, devices.tradfri_light.KEY_BRIGHTNESS,
self.main_light_tradfri, self.main_light_tradfri.KEY_COLOR_TEMP self.main_light_tradfri, devices.tradfri_light.KEY_COLOR_TEMP
) )
# bed light
self.bed_light_di_videv = videv_switch_brightness( self.bed_light_di_videv = videv_switch_brightness(
mqtt_client, config.TOPIC_FFE_SLEEP_BED_LIGHT_DI_VIDEV, mqtt_client, config.TOPIC_FFE_SLEEP_BED_LIGHT_DI_VIDEV,
self.bed_light_di_tradfri, self.bed_light_di_tradfri.KEY_OUTPUT_0, self.bed_light_di_tradfri, devices.tradfri_light.KEY_OUTPUT_0,
self.bed_light_di_tradfri, self.bed_light_di_tradfri.KEY_BRIGHTNESS, self.bed_light_di_tradfri, devices.tradfri_light.KEY_BRIGHTNESS,
) )
self.bed_light_ma_videv = videv_switching( self.bed_light_ma_videv = videv_switching(
mqtt_client, config.TOPIC_FFE_SLEEP_BED_LIGHT_MA_VIDEV, mqtt_client, config.TOPIC_FFE_SLEEP_BED_LIGHT_MA_VIDEV,
self.bed_light_ma_powerplug, self.bed_light_ma_powerplug.KEY_OUTPUT_0 self.bed_light_ma_powerplug, devices.silvercrest_powerplug.KEY_OUTPUT_0
) )
# heating function
self.heating_function_videv = videv_heating( self.heating_function_videv = videv_heating(
mqtt_client, config.TOPIC_FFE_SLEEP_HEATING_VALVE_VIDEV, mqtt_client, config.TOPIC_FFE_SLEEP_HEATING_VALVE_VIDEV,
self.heating_function self.heating_function
) )
# button
self.brightness_functions_device_videv = videv_multistate( self.brightness_functions_device_videv = videv_multistate(
mqtt_client, config.TOPIC_FFE_SLEEP_ACTIVE_BRIGHTNESS_DEVICE_VIDEV, mqtt_client, config.TOPIC_FFE_SLEEP_ACTIVE_BRIGHTNESS_DEVICE_VIDEV,
brightness_choose_n_action.KEY_ACTIVE_DEVICE, self.brightness_functions, 2 brightness_choose_n_action.KEY_ACTIVE_DEVICE, self.brightness_functions, 2
) )
self.wardrobe_light_videv = videv_switch_brightness(
mqtt_client, config.TOPIC_FFE_SLEEP_WARDROBE_LIGHT_VIDEV,
self.wardrobe_light, self.wardrobe_light.KEY_OUTPUT_0,
self.wardrobe_light, self.wardrobe_light.KEY_BRIGHTNESS,
)
class first_floor_east_living(room): class first_floor_east_living(room):
def __init__(self, mqtt_client, pd, vd): def __init__(self, mqtt_client):
roo = props.ROO_LIV
# #
# Device initialisation # Device initialisation
# #
# http://shelly1l-3C6105E3F910 # http://shelly1l-3C6105E3F910
# main light self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFE_LIVINGROOM_MAIN_LIGHT_SHELLY)
self.main_light_shelly = pd.get(props.STG_SHE, loc, roo, props.FUN_MAL) self.main_light_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_FFE_LIVINGROOM_MAIN_LIGHT_ZIGBEE)
self.main_light_tradfri = pd.get(props.STG_ZFE, loc, roo, props.FUN_MAL) self.floorlamp_tradfri = devices.group(
# floor lamp *[devices.tradfri_light(mqtt_client, config.TOPIC_FFE_LIVINGROOM_FLOOR_LAMP_ZIGBEE % i) for i in range(1, 7)])
self.floorlamp_tradfri = pd.get(props.STG_ZFE, loc, roo, props.FUN_FLL)
# heating function
self.heating_valve = pd.get(props.STG_ZFE, loc, roo, props.FUN_HEA)
# xmas tree
if config.CHRISTMAS: if config.CHRISTMAS:
self.powerplug_xmas_tree = pd.get(props.STG_ZFE, loc, roo, props.FUN_XTR) self.powerplug_xmas_tree = devices.silvercrest_powerplug(mqtt_client, config.TOPIC_FFE_LIVINGROOM_XMAS_TREE_POWERPLUG)
self.powerplug_xmas_star = pd.get(props.STG_ZFE, loc, roo, props.FUN_XST) self.powerplug_xmas_star = devices.silvercrest_powerplug(mqtt_client, config.TOPIC_FFE_LIVINGROOM_XMAS_STAR_POWERPLUG)
super().__init__(mqtt_client, pd, vd) super().__init__(mqtt_client)
# #
# Functionality initialisation # Functionality initialisation
# #
# floor lamp synchronisation with main_light # floor lamp synchronisation with main_light
self.main_light_shelly.add_callback(self.main_light_shelly.KEY_OUTPUT_0, None, self.floorlamp_tradfri.set_output_0_mcb, True) self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.floorlamp_tradfri.set_output_0_mcb, True)
# heating function
self.heating_function = heating_function(
self.heating_valve,
config.DEFAULT_TEMPERATURE,
**get_radiator_data(self.heating_valve.topic)
)
self.heating_function.add_callback(None, None, set_radiator_data, True)
# #
# Virtual Device Interface # Virtual Device Interface
# #
# main light
self.main_light_videv = videv_switch_brightness_color_temp( self.main_light_videv = videv_switch_brightness_color_temp(
mqtt_client, config.TOPIC_FFE_LIVINGROOM_MAIN_LIGHT_VIDEV, mqtt_client, config.TOPIC_FFE_LIVINGROOM_MAIN_LIGHT_VIDEV,
self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0, self.main_light_shelly, devices.shelly.KEY_OUTPUT_0,
self.main_light_tradfri, self.main_light_tradfri.KEY_BRIGHTNESS, self.main_light_tradfri, devices.tradfri_light.KEY_BRIGHTNESS,
self.main_light_tradfri, self.main_light_tradfri.KEY_COLOR_TEMP self.main_light_tradfri, devices.tradfri_light.KEY_COLOR_TEMP
) )
# floor lamp
self.floorlamp_videv = videv_switch_brightness_color_temp( self.floorlamp_videv = videv_switch_brightness_color_temp(
mqtt_client, config.TOPIC_FFE_LIVINGROOM_FLOOR_LAMP_VIDEV, mqtt_client, config.TOPIC_FFE_LIVINGROOM_FLOOR_LAMP_VIDEV,
self.floorlamp_tradfri, self.floorlamp_tradfri.KEY_OUTPUT_0, self.floorlamp_tradfri, devices.tradfri_light.KEY_OUTPUT_0,
self.floorlamp_tradfri, self.floorlamp_tradfri.KEY_BRIGHTNESS, self.floorlamp_tradfri, devices.tradfri_light.KEY_BRIGHTNESS,
self.floorlamp_tradfri, self.floorlamp_tradfri.KEY_COLOR_TEMP self.floorlamp_tradfri, devices.tradfri_light.KEY_COLOR_TEMP
) )
# heating function
self.heating_function_videv = videv_heating(
mqtt_client, config.TOPIC_FFE_LIVINGROOM_HEATING_VALVE_VIDEV,
self.heating_function
)
# xmas tree
if config.CHRISTMAS: if config.CHRISTMAS:
self.xmas_tree_videv = videv_switching( self.xmas_tree_videv = videv_switching(
mqtt_client, config.TOPIC_FFE_LIVINGROOM_XMAS_TREE_VIDEV, mqtt_client, config.TOPIC_FFE_LIVINGROOM_XMAS_TREE_VIDEV,
self.powerplug_xmas_tree, self.powerplug_xmas_tree.KEY_OUTPUT_0 self.powerplug_xmas_tree, devices.silvercrest_powerplug.KEY_OUTPUT_0
) )

View File

@ -3,11 +3,10 @@
# #
import config import config
from devdi import topic as props 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, videv_switching from function.videv import videv_switch_brightness, videv_switch_brightness_color_temp, videv_heating
import logging import logging
@ -17,114 +16,53 @@ except ImportError:
ROOT_LOGGER_NAME = 'root' ROOT_LOGGER_NAME = 'root'
logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
loc = props.LOC_FFW
class first_floor_west(room_collection): class first_floor_west(room_collection):
def __init__(self, mqtt_client, pd, vd): def __init__(self, mqtt_client):
super().__init__(mqtt_client, pd, vd) super().__init__(mqtt_client)
self.floor = first_floor_west_floor(mqtt_client, pd, vd) self.bath = first_floor_west_bath(mqtt_client)
self.bath = first_floor_west_bath(mqtt_client, pd, vd) self.julian = first_floor_west_julian(mqtt_client)
self.julian = first_floor_west_julian(mqtt_client, pd, vd) self.livingroom = first_floor_west_living(mqtt_client)
self.livingroom = first_floor_west_living(mqtt_client, pd, vd) self.sleep = first_floor_west_sleep(mqtt_client)
self.sleep = first_floor_west_sleep(mqtt_client, pd, vd)
class first_floor_west_floor(room):
def __init__(self, mqtt_client, pd, vd):
roo = props.ROO_FLO
#
# Device initialisation
#
# http://shelly1-58BF25D848EA
# main light
self.main_light_shelly = pd.get(props.STG_SHE, loc, roo, props.FUN_MAL)
super().__init__(mqtt_client, pd, vd)
#
# Virtual Device Interface
#
# main light
self.main_light = videv_switching(
mqtt_client, config.TOPIC_FFW_FLOOR_MAIN_LIGHT_VIDEV,
self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0
)
class first_floor_west_julian(room): class first_floor_west_julian(room):
def __init__(self, mqtt_client, pd, vd): def __init__(self, mqtt_client):
roo = props.ROO_JUL
# #
# Device initialisation # Device initialisation
# #
# http://shelly1l-3C6105E43452 # http://shelly1l-3C6105E43452
# main light self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFW_JULIAN_MAIN_LIGHT_SHELLY)
self.main_light_shelly = pd.get(props.STG_SHE, loc, roo, props.FUN_MAL) self.main_light_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_FFW_JULIAN_MAIN_LIGHT_ZIGBEE)
self.main_light_tradfri = pd.get(props.STG_ZFW, loc, roo, props.FUN_MAL) super().__init__(mqtt_client)
# heating function
self.heating_valve = pd.get(props.STG_ZFW, loc, roo, props.FUN_HEA)
super().__init__(mqtt_client, pd, vd)
#
# Functionality initialisation
#
# heating function
self.heating_function = heating_function(
self.heating_valve,
config.DEFAULT_TEMPERATURE,
**get_radiator_data(self.heating_valve.topic)
)
self.heating_function.add_callback(None, None, set_radiator_data, True)
# #
# Virtual Device Interface # Virtual Device Interface
# #
# main light
self.main_light_videv = videv_switch_brightness_color_temp( self.main_light_videv = videv_switch_brightness_color_temp(
mqtt_client, config.TOPIC_FFW_JULIAN_MAIN_LIGHT_VIDEV, mqtt_client, config.TOPIC_FFW_JULIAN_MAIN_LIGHT_VIDEV,
self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0, self.main_light_shelly, devices.shelly.KEY_OUTPUT_0,
self.main_light_tradfri, self.main_light_tradfri.KEY_BRIGHTNESS, self.main_light_tradfri, devices.tradfri_light.KEY_BRIGHTNESS,
self.main_light_tradfri, self.main_light_tradfri.KEY_COLOR_TEMP self.main_light_tradfri, devices.tradfri_light.KEY_COLOR_TEMP
)
# heating function
self.heating_function_videv = videv_heating(
mqtt_client, config.TOPIC_FFW_JULIAN_HEATING_VALVE_VIDEV,
self.heating_function
) )
class first_floor_west_bath(room): class first_floor_west_bath(room):
def __init__(self, mqtt_client, pd, vd): def __init__(self, mqtt_client):
roo = props.ROO_BAT
# #
# Device initialisation # Device initialisation
# #
# http://shelly1-58BF25D84219 self.heating_valve = devices.brennenstuhl_heatingvalve(mqtt_client, config.TOPIC_FFW_BATH_HEATING_VALVE_ZIGBEE)
# main light super().__init__(mqtt_client)
self.main_light_shelly = pd.get(props.STG_SHE, loc, roo, props.FUN_MAL)
# heating function
self.heating_valve = pd.get(props.STG_ZFW, loc, roo, props.FUN_HEA)
super().__init__(mqtt_client, pd, vd)
# #
# Functionality initialisation # Functionality initialisation
# #
# heating function # heating function
self.heating_function = heating_function( self.heating_function = heating_function(self.heating_valve)
self.heating_valve,
config.DEFAULT_TEMPERATURE,
**get_radiator_data(self.heating_valve.topic)
)
self.heating_function.add_callback(None, None, set_radiator_data, True)
# #
# Virtual Device Interface # Virtual Device Interface
# #
# main light
self.main_light = videv_switching(
mqtt_client, config.TOPIC_FFW_BATH_MAIN_LIGHT_VIDEV,
self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0
)
# heating function
self.heating_function_videv = videv_heating( self.heating_function_videv = videv_heating(
mqtt_client, config.TOPIC_FFW_BATH_HEATING_VALVE_VIDEV, mqtt_client, config.TOPIC_FFW_BATH_HEATING_VALVE_VIDEV,
self.heating_function self.heating_function
@ -132,95 +70,41 @@ class first_floor_west_bath(room):
class first_floor_west_living(room): class first_floor_west_living(room):
def __init__(self, mqtt_client, pd, vd): def __init__(self, mqtt_client):
roo = props.ROO_LIV
# #
# Device initialisation # Device initialisation
# #
# http://shelly1l-84CCA8ACE6A1 # http://shelly1l-84CCA8ACE6A1
# main light self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFW_LIVINGROOM_MAIN_LIGHT_SHELLY)
self.main_light_shelly = pd.get(props.STG_SHE, loc, roo, props.FUN_MAL) self.main_light_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_FFW_LIVINGROOM_MAIN_LIGHT_ZIGBEE)
self.main_light_tradfri = pd.get(props.STG_ZFW, loc, roo, props.FUN_MAL) super().__init__(mqtt_client)
# heating function
self.heating_valve = pd.get(props.STG_ZFW, loc, roo, props.FUN_HEA)
super().__init__(mqtt_client, pd, vd)
#
# Functionality initialisation
#
# heating function
self.heating_function = heating_function(
self.heating_valve,
config.DEFAULT_TEMPERATURE,
**get_radiator_data(self.heating_valve.topic)
)
self.heating_function.add_callback(None, None, set_radiator_data, True)
# #
# Virtual Device Interface # Virtual Device Interface
# #
# main light
self.main_light_videv = videv_switch_brightness_color_temp( self.main_light_videv = videv_switch_brightness_color_temp(
mqtt_client, config.TOPIC_FFW_LIVINGROOM_MAIN_LIGHT_VIDEV, mqtt_client, config.TOPIC_FFW_LIVINGROOM_MAIN_LIGHT_VIDEV,
self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0, self.main_light_shelly, devices.shelly.KEY_OUTPUT_0,
self.main_light_tradfri, self.main_light_tradfri.KEY_BRIGHTNESS, self.main_light_tradfri, devices.tradfri_light.KEY_BRIGHTNESS,
self.main_light_tradfri, self.main_light_tradfri.KEY_COLOR_TEMP self.main_light_tradfri, devices.tradfri_light.KEY_COLOR_TEMP
)
# heating function
self.heating_function_videv = videv_heating(
mqtt_client, config.TOPIC_FFW_LIVINGROOM_HEATING_VALVE_VIDEV,
self.heating_function
) )
class first_floor_west_sleep(room): class first_floor_west_sleep(room):
def __init__(self, mqtt_client, pd, vd): def __init__(self, mqtt_client):
roo = props.ROO_SLP
# #
# Device initialisation # Device initialisation
# #
# http://shelly1-3494546A51F2 # http://shelly1-3494546A51F2
# main light self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_FFW_SLEEP_MAIN_LIGHT_SHELLY)
self.main_light_shelly = pd.get(props.STG_SHE, loc, roo, props.FUN_MAL) self.main_light_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_FFW_SLEEP_MAIN_LIGHT_ZIGBEE)
self.main_light_tradfri = pd.get(props.STG_ZFW, loc, roo, props.FUN_MAL) super().__init__(mqtt_client)
# heating function
self.heating_valve = pd.get(props.STG_ZFW, loc, roo, props.FUN_HEA)
# window light
self.window_light = pd.get(props.STG_ZFW, loc, roo, props.FUN_WIL)
super().__init__(mqtt_client, pd, vd)
#
# Functionality initialisation
#
# heating function
self.heating_function = heating_function(
self.heating_valve,
config.DEFAULT_TEMPERATURE,
**get_radiator_data(self.heating_valve.topic)
)
self.heating_function.add_callback(None, None, set_radiator_data, True)
# main light
self.main_light_shelly.add_callback(self.main_light_shelly.KEY_OUTPUT_0, None, self.window_light.set_output_0_mcb, True)
# #
# Virtual Device Interface # Virtual Device Interface
# #
# main light
self.main_light_videv = videv_switch_brightness( self.main_light_videv = videv_switch_brightness(
mqtt_client, config.TOPIC_FFW_SLEEP_MAIN_LIGHT_VIDEV, mqtt_client, config.TOPIC_FFW_SLEEP_MAIN_LIGHT_VIDEV,
self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0, self.main_light_shelly, devices.shelly.KEY_OUTPUT_0,
self.main_light_tradfri, self.main_light_tradfri.KEY_BRIGHTNESS self.main_light_tradfri, devices.tradfri_light.KEY_BRIGHTNESS
)
# heating function
self.heating_function_videv = videv_heating(
mqtt_client, config.TOPIC_FFW_SLEEP_HEATING_VALVE_VIDEV,
self.heating_function
)
# window lamp
self.windowlamp_videv = videv_switch_brightness_color_temp(
mqtt_client, config.TOPIC_FFW_SLEEP_WINDOW_LAMP_VIDEV,
self.window_light, self.window_light.KEY_OUTPUT_0,
self.window_light, self.window_light.KEY_BRIGHTNESS,
self.window_light, self.window_light.KEY_COLOR_TEMP
) )

View File

@ -1,70 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
import config
import devdi.topic as props
from function.helpers import day_event
from function.rooms import room, room_collection
from function.videv import videv_switching, videv_pure_switch
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__)
loc = props.LOC_GAR
class garden(room_collection):
def __init__(self, mqtt_client, pd, vd):
super().__init__(mqtt_client, pd, vd)
self.garden = garden_garden(mqtt_client, pd, vd)
class garden_garden(room):
def __init__(self, mqtt_client, pd, vd):
roo = props.ROO_GAR
#
# Device initialisation
#
self.day_events = day_event((6, 0), (22, 0), 30, -30)
# garden powerplugs
self.garland_powerplug = pd.get(props.STG_ZGW, loc, roo, props.FUN_GAR)
# repeater
self.repeater = pd.get(props.STG_ZGW, loc, roo, props.FUN_REP)
super().__init__(mqtt_client, pd, vd)
#
# Functionality initialisation
#
self.day_events.add_callback(None, True, self.__day_events__, True)
#
# Virtual Device Interface
#
# mode
self.mode_videv = videv_pure_switch(
mqtt_client, config.TOPIC_GAR_GARDEN_MODE_VIDEV
)
# garland
self.garland_videv = videv_switching(
mqtt_client, config.TOPIC_GAR_GARDEN_GARLAND_VIDEV,
self.garland_powerplug, self.garland_powerplug.KEY_OUTPUT_0
)
# repeater
self.repeater_videv = videv_switching(
mqtt_client, config.TOPIC_GAR_GARDEN_REPEATER_VIDEV,
self.repeater, self.repeater.KEY_OUTPUT_0
)
def __day_events__(self, device, key, data):
if self.mode_videv.get(self.mode_videv.KEY_STATE):
if key in (self.day_events.KEY_SUNSET, self.day_events.KEY_START_OF_DAY):
self.garland_powerplug.set_output_0(True)
elif key in (self.day_events.KEY_START_OF_NIGHT, self.day_events.KEY_SUNRISE):
self.garland_powerplug.set_output_0(False)

View File

@ -3,9 +3,7 @@
# #
import config import config
from devdi import topic as props import devices
from devices import group
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 from function.videv import videv_switching, videv_switch_brightness_color_temp, videv_heating, videv_multistate, videv_audio_player
@ -18,92 +16,72 @@ except ImportError:
ROOT_LOGGER_NAME = 'root' ROOT_LOGGER_NAME = 'root'
logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
loc = props.LOC_GFW
class ground_floor_west(room_collection): class ground_floor_west(room_collection):
def __init__(self, mqtt_client, pd, vd): def __init__(self, mqtt_client):
super().__init__(mqtt_client, pd, vd) super().__init__(mqtt_client)
self.dirk = ground_floor_west_dirk(mqtt_client, pd, vd) self.dirk = ground_floor_west_dirk(mqtt_client)
self.floor = ground_floor_west_floor(mqtt_client, pd, vd) self.floor = ground_floor_west_floor(mqtt_client)
self.marion = ground_floor_west_marion(mqtt_client, pd, vd) self.marion = ground_floor_west_marion(mqtt_client)
class ground_floor_west_floor(room): class ground_floor_west_floor(room):
def __init__(self, mqtt_client, pd, vd): def __init__(self, mqtt_client):
roo = props.ROO_FLO
# #
# Device initialisation # Device initialisation
# #
# http://shelly1l-84CCA8AD1148 # http://shelly1l-84CCA8AD1148
self.main_light_shelly = pd.get(props.STG_SHE, loc, roo, props.FUN_MAL) self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_SHELLY)
self.main_light_tradfri = pd.get(props.STG_ZGW, loc, roo, props.FUN_MAL) self.main_light_tradfri = devices.group(
super().__init__(mqtt_client, pd, vd) 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 # Functionality initialisation
# #
# Request silvercrest data of lead light after power on # Request silvercrest data of lead light after power on
switched_light(self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0, self.main_light_tradfri) switched_light(self.main_light_shelly, devices.shelly.KEY_OUTPUT_0, self.main_light_tradfri)
# #
# Virtual Device Interface # Virtual Device Interface
# #
self.main_light_videv = videv_switch_brightness_color_temp( self.main_light_videv = videv_switch_brightness_color_temp(
mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_VIDEV, mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_VIDEV,
self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0, self.main_light_shelly, devices.shelly.KEY_OUTPUT_0,
self.main_light_tradfri, self.main_light_tradfri.KEY_BRIGHTNESS, self.main_light_tradfri, devices.tradfri_light.KEY_BRIGHTNESS,
self.main_light_tradfri, self.main_light_tradfri.KEY_COLOR_TEMP self.main_light_tradfri, devices.tradfri_light.KEY_COLOR_TEMP
) )
class ground_floor_west_marion(room): class ground_floor_west_marion(room):
def __init__(self, mqtt_client, pd, vd): def __init__(self, mqtt_client):
roo = props.ROO_MAR
# #
# Device initialisation # Device initialisation
# #
# http://shelly1l-E8DB84A1E067 # http://shelly1l-E8DB84A1E067
# main light self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_GFW_MARION_MAIN_LIGHT_SHELLY)
self.main_light_shelly = pd.get(props.STG_SHE, loc, roo, props.FUN_MAL) self.heating_valve = devices.brennenstuhl_heatingvalve(mqtt_client, config.TOPIC_GFW_MARION_HEATING_VALVE_ZIGBEE)
# heating function super().__init__(mqtt_client)
self.heating_valve = pd.get(props.STG_ZGW, loc, roo, props.FUN_HEA)
# window light
self.window_light = pd.get(props.STG_ZGW, loc, roo, props.FUN_WIL)
super().__init__(mqtt_client, pd, vd)
# #
# Functionality initialisation # Functionality initialisation
# #
# heating function # heating function
self.heating_function = heating_function( self.heating_function = heating_function(self.heating_valve)
self.heating_valve,
config.DEFAULT_TEMPERATURE,
**get_radiator_data(self.heating_valve.topic)
)
self.heating_function.add_callback(None, None, set_radiator_data, True)
# main light
self.main_light_shelly.add_callback(self.main_light_shelly.KEY_OUTPUT_0, None, self.window_light.set_output_0_mcb, True)
# #
# Virtual Device Interface # Virtual Device Interface
# #
self.main_light_videv = videv_switching( self.main_light_videv = videv_switching(
mqtt_client, config.TOPIC_GFW_MARION_MAIN_LIGHT_VIDEV, mqtt_client, config.TOPIC_GFW_MARION_MAIN_LIGHT_VIDEV,
self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0 self.main_light_shelly, devices.shelly.KEY_OUTPUT_0
) )
self.heating_function_videv = videv_heating( self.heating_function_videv = videv_heating(
mqtt_client, config.TOPIC_GFW_MARION_HEATING_VALVE_VIDEV, mqtt_client, config.TOPIC_GFW_MARION_HEATING_VALVE_VIDEV,
self.heating_function self.heating_function
) )
# window lamp
self.windowlamp_videv = videv_switch_brightness_color_temp(
mqtt_client, config.TOPIC_GFW_MARION_WINDOW_LAMP_VIDEV,
self.window_light, self.window_light.KEY_OUTPUT_0,
self.window_light, self.window_light.KEY_BRIGHTNESS,
self.window_light, self.window_light.KEY_COLOR_TEMP
)
class ground_floor_west_dirk(room): class ground_floor_west_dirk(room):
@ -112,122 +90,94 @@ class ground_floor_west_dirk(room):
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
def __init__(self, mqtt_client, pd, vd): def __init__(self, mqtt_client):
roo = props.ROO_DIR
# #
# Device initialisation # Device initialisation
# #
# http://shelly1l-3C6105E44F27 # http://shelly1l-3C6105E44F27
# main light self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_GFW_DIRK_MAIN_LIGHT_SHELLY)
self.main_light_shelly = pd.get(props.STG_SHE, loc, roo, props.FUN_MAL) self.main_light_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_GFW_DIRK_MAIN_LIGHT_ZIGBEE)
self.main_light_tradfri = pd.get(props.STG_ZGW, loc, roo, props.FUN_MAL) self.powerplug_common = devices.my_powerplug(mqtt_client, config.TOPIC_GFW_DIRK_POWERPLUG)
# powerplug self.desk_light_tradfri = devices.tradfri_light(mqtt_client, config.TOPIC_GFW_DIRK_DESK_LIGHT_ZIGBEE)
self.powerplug_common = pd.get(props.STG_MYA, loc, roo, props.FUN_MPP) self.button_tradfri = devices.tradfri_button(mqtt_client, config.TOPIC_GFW_DIRK_INPUT_DEVICE)
self.KEY_POWERPLUG_AMPLIFIER = self.powerplug_common.KEY_OUTPUT_0 self.remote_amplifier = devices.remote(mqtt_client, config.TOPIC_GFW_DIRK_AMPLIFIER_REMOTE)
self.KEY_POWERPLUG_PHONO = self.powerplug_common.KEY_OUTPUT_1 self.spotify_state = devices.audio_status(mqtt_client, config.TOPIC_GFW_DIRK_SPOTIFY)
self.KEY_POWERPLUG_CD_PLAYER = self.powerplug_common.KEY_OUTPUT_2 self.mpd_state = devices.audio_status(mqtt_client, config.TOPIC_GFW_DIRK_MPD)
self.KEY_POWERPLUG_BT = self.powerplug_common.KEY_OUTPUT_3 self.heating_valve = devices.brennenstuhl_heatingvalve(mqtt_client, config.TOPIC_GFW_DIRK_HEATING_VALVE_ZIGBEE)
# dock super().__init__(mqtt_client)
self.dock_tradfri = pd.get(props.STG_ZGW, loc, roo, props.FUN_DCK)
# desk light
self.desk_light_tradfri = pd.get(props.STG_ZGW, loc, roo, props.FUN_DEL)
# button
self.button_tradfri = pd.get(props.STG_ZGW, loc, roo, props.FUN_INP)
# hifi
self.remote_amplifier = pd.get(props.STG_MYA, loc, roo, props.FUN_RCA)
self.spotify_state = pd.get(props.STG_MYA, loc, roo, props.FUN_ASS)
self.mpd_state = pd.get(props.STG_MYA, loc, roo, props.FUN_ASM)
self.bt_state = pd.get(props.STG_MYA, loc, roo, props.FUN_ASB)
# heating function
self.heating_valve = pd.get(props.STG_ZGW, loc, roo, props.FUN_HEA)
super().__init__(mqtt_client, pd, vd)
# #
# Functionality initialisation # Functionality initialisation
# #
# Button - Brightness functionality # Button - Brightness functionality
self.brightness_functions = brightness_choose_n_action(self.button_tradfri) self.brightness_functions = brightness_choose_n_action(self.button_tradfri)
self.brightness_functions.add(self.main_light_tradfri, self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0) self.brightness_functions.add(self.main_light_tradfri, self.main_light_shelly, devices.shelly.KEY_OUTPUT_0)
self.brightness_functions.add(self.desk_light_tradfri, self.desk_light_tradfri, self.desk_light_tradfri.KEY_OUTPUT_0) self.brightness_functions.add(self.desk_light_tradfri, self.powerplug_common, self.KEY_POWERPLUG_DESK_LIGHT)
self.brightness_functions.add(self.remote_amplifier, self.powerplug_common, self.KEY_POWERPLUG_AMPLIFIER) self.brightness_functions.add(self.remote_amplifier, self.powerplug_common, self.KEY_POWERPLUG_AMPLIFIER)
# Button - Main light # Button - Main light
self.button_tradfri.add_callback(self.button_tradfri.KEY_ACTION, self.button_tradfri.ACTION_TOGGLE, self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_TOGGLE,
self.main_light_shelly.toggle_output_0_mcb) self.main_light_shelly.toggle_output_0_mcb)
# Button - Desk light # Button - Desk light
self.button_tradfri.add_callback(self.button_tradfri.KEY_ACTION, self.button_tradfri.ACTION_RIGHT, self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_RIGHT,
self.desk_light_tradfri.toggle_output_0_mcb) self.powerplug_common.toggle_output_1_mcb)
# Button - Amplifier # Button - Amplifier
self.button_tradfri.add_callback(self.button_tradfri.KEY_ACTION, self.button_tradfri.ACTION_LEFT_LONG, self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_LEFT_LONG,
self.powerplug_common.toggle_output_0_mcb) self.powerplug_common.toggle_output_0_mcb)
# Button - CD player # Button - CD player
self.button_tradfri.add_callback(self.button_tradfri.KEY_ACTION, self.button_tradfri.ACTION_RIGHT_LONG, self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_RIGHT_LONG,
self.powerplug_common.toggle_output_2_mcb) self.powerplug_common.toggle_output_2_mcb)
# Button - PC dock # Button - PC dock
self.button_tradfri.add_callback(self.button_tradfri.KEY_ACTION, self.button_tradfri.ACTION_LEFT, self.button_tradfri.add_callback(devices.tradfri_button.KEY_ACTION, devices.tradfri_button.ACTION_LEFT,
self.dock_tradfri.toggle_output_0_mcb) self.powerplug_common.toggle_output_3_mcb)
# Mediaplayer - Amplifier auto on # Mediaplayer - Amplifier auto on
self.powerplug_common.add_callback(self.KEY_POWERPLUG_PHONO, None, self.powerplug_common.set_output_0_mcb, True)
self.powerplug_common.add_callback(self.KEY_POWERPLUG_CD_PLAYER, None, self.powerplug_common.set_output_0_mcb, True) self.powerplug_common.add_callback(self.KEY_POWERPLUG_CD_PLAYER, None, self.powerplug_common.set_output_0_mcb, True)
self.powerplug_common.add_callback(self.KEY_POWERPLUG_BT, 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.spotify_state.add_callback(self.spotify_state.KEY_STATE, None, self.powerplug_common.set_output_0_mcb, True) self.mpd_state.add_callback(devices.status.KEY_STATE, None, self.powerplug_common.set_output_0_mcb, True)
self.mpd_state.add_callback(self.mpd_state.KEY_STATE, None, self.powerplug_common.set_output_0_mcb, True)
self.bt_state.add_callback(self.bt_state.KEY_STATE, None, self.powerplug_common.set_output_0_mcb, True)
# Mediaplayer - Audio source selection # Mediaplayer - Audio source selection
self.powerplug_common.add_callback(self.KEY_POWERPLUG_AMPLIFIER, True, self.audio_source_selector, True) self.powerplug_common.add_callback(self.KEY_POWERPLUG_AMPLIFIER, True, self.audio_source_selector, True)
self.powerplug_common.add_callback(self.KEY_POWERPLUG_CD_PLAYER, True, self.audio_source_selector, True) self.powerplug_common.add_callback(self.KEY_POWERPLUG_CD_PLAYER, True, self.audio_source_selector, True)
self.powerplug_common.add_callback(self.KEY_POWERPLUG_BT, True, self.audio_source_selector, True) self.spotify_state.add_callback(devices.status.KEY_STATE, True, self.audio_source_selector, True)
self.spotify_state.add_callback(self.spotify_state.KEY_STATE, True, self.audio_source_selector, True) self.mpd_state.add_callback(devices.status.KEY_STATE, True, self.audio_source_selector, True)
self.mpd_state.add_callback(self.mpd_state.KEY_STATE, True, self.audio_source_selector, True)
self.bt_state.add_callback(self.bt_state.KEY_STATE, True, self.audio_source_selector, True)
self.audio_source = self.AUDIO_SOURCE_PC 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.heating_valve,
config.DEFAULT_TEMPERATURE,
**get_radiator_data(self.heating_valve.topic)
)
self.heating_function.add_callback(None, None, set_radiator_data, True)
# #
# Virtual Device Interface # Virtual Device Interface
# #
self.main_light_videv = videv_switch_brightness_color_temp( self.main_light_videv = videv_switch_brightness_color_temp(
mqtt_client, config.TOPIC_GFW_DIRK_MAIN_LIGHT_VIDEV, mqtt_client, config.TOPIC_GFW_DIRK_MAIN_LIGHT_VIDEV,
self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0, self.main_light_shelly, devices.shelly.KEY_OUTPUT_0,
self.main_light_tradfri, self.main_light_tradfri.KEY_BRIGHTNESS, self.main_light_tradfri, devices.tradfri_light.KEY_BRIGHTNESS,
self.main_light_tradfri, self.main_light_tradfri.KEY_COLOR_TEMP self.main_light_tradfri, devices.tradfri_light.KEY_COLOR_TEMP
) )
self.desk_light_videv = videv_switch_brightness_color_temp( self.desk_light_videv = videv_switch_brightness_color_temp(
mqtt_client, config.TOPIC_GFW_DIRK_DESK_LIGHT_VIDEV, mqtt_client, config.TOPIC_GFW_DIRK_DESK_LIGHT_VIDEV,
self.desk_light_tradfri, self.desk_light_tradfri.KEY_OUTPUT_0, self.powerplug_common, self.KEY_POWERPLUG_DESK_LIGHT,
self.desk_light_tradfri, self.desk_light_tradfri.KEY_BRIGHTNESS, self.desk_light_tradfri, devices.tradfri_light.KEY_BRIGHTNESS,
self.desk_light_tradfri, self.desk_light_tradfri.KEY_COLOR_TEMP self.desk_light_tradfri, devices.tradfri_light.KEY_COLOR_TEMP
) )
self.amplifier_videv = videv_switching( self.amplifier_videv = videv_switching(
mqtt_client, config.TOPIC_GFW_DIRK_AMPLIFIER_VIDEV, mqtt_client, config.TOPIC_GFW_DIRK_AMPLIFIER_VIDEV,
self.powerplug_common, self.KEY_POWERPLUG_AMPLIFIER self.powerplug_common, self.KEY_POWERPLUG_AMPLIFIER
) )
self.phono_videv = videv_switching(
mqtt_client, config.TOPIC_GFW_DIRK_PHONO_VIDEV,
self.powerplug_common, self.KEY_POWERPLUG_PHONO
)
self.cd_player_videv = videv_switching( self.cd_player_videv = videv_switching(
mqtt_client, config.TOPIC_GFW_DIRK_CD_PLAYER_VIDEV, mqtt_client, config.TOPIC_GFW_DIRK_CD_PLAYER_VIDEV,
self.powerplug_common, self.KEY_POWERPLUG_CD_PLAYER self.powerplug_common, self.KEY_POWERPLUG_CD_PLAYER
) )
self.bt_videv = videv_switching(
mqtt_client, config.TOPIC_GFW_DIRK_BT_VIDEV,
self.powerplug_common, self.KEY_POWERPLUG_BT
)
self.pc_dock_videv = videv_switching( self.pc_dock_videv = videv_switching(
mqtt_client, config.TOPIC_GFW_DIRK_PC_DOCK_VIDEV, mqtt_client, config.TOPIC_GFW_DIRK_PC_DOCK_VIDEV,
self.dock_tradfri, self.dock_tradfri.KEY_OUTPUT_0 self.powerplug_common, self.KEY_POWERPLUG_PC_DOCK
) )
self.heating_function_videv = videv_heating( self.heating_function_videv = videv_heating(
mqtt_client, config.TOPIC_GFW_DIRK_HEATING_VALVE_VIDEV, mqtt_client, config.TOPIC_GFW_DIRK_HEATING_VALVE_VIDEV,
@ -239,7 +189,7 @@ class ground_floor_west_dirk(room):
) )
self.audio_player_videv = videv_audio_player( self.audio_player_videv = videv_audio_player(
mqtt_client, config.TOPIC_GFW_DIRK_AUDIO_PLAYER_VIDEV, mqtt_client, config.TOPIC_GFW_DIRK_AUDIO_PLAYER_VIDEV,
self.spotify_state, self.mpd_state, self.bt_state self.spotify_state, self.mpd_state
) )
# #
# Other stuff # Other stuff
@ -250,10 +200,7 @@ class ground_floor_west_dirk(room):
if device == self.powerplug_common 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.powerplug_common 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 in [self.spotify_state, self.mpd_state, self.bt_state]:
# 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.powerplug_common and key == self.KEY_POWERPLUG_AMPLIFIER: elif device == self.powerplug_common and key == self.KEY_POWERPLUG_AMPLIFIER:
@ -267,9 +214,6 @@ class ground_floor_west_dirk(room):
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_amplifier.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 tuner")
self.remote_amplifier.set_line2()
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_amplifier.set_line1() self.remote_amplifier.set_line1()

View File

@ -1,10 +1,9 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from base 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

@ -14,10 +14,10 @@ Targets:
from base import common_base from base import common_base
import config 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
@ -28,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):
@ -153,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])
@ -224,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:
@ -267,11 +245,9 @@ 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])
@ -303,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)
# #
@ -324,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,34 +13,24 @@ logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
class room(object): class room(object):
def __init__(self, mqtt_client, pd, vd): def __init__(self, mqtt_client):
self.mqtt_client = mqtt_client self.mqtt_client = mqtt_client
self.pd = pd
self.vd = vd
def all_off(self, device=None, key=None, data=None): def all_off(self, device=None, key=None, data=None):
logger.info("Switching all off \"%s\"", type(self).__name__) logger.info("Switching all off \"%s\"", type(self).__name__)
for name, obj in inspect.getmembers(self): for name, obj in inspect.getmembers(self):
try: try:
if obj.__module__.startswith('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):
ALLOWED_CLASSES = ("room", "room_collection") ALLOWED_CLASSES = ("room", "room_collection")
def __init__(self, mqtt_client, pd, vd): def __init__(self, mqtt_client):
self.mqtt_client = mqtt_client self.mqtt_client = mqtt_client
self.pd = pd
self.vd = vd
def all_off(self, device=None, key=None, data=None): def all_off(self, device=None, key=None, data=None):
logger.info("Switching all off \"%s\"", type(self).__name__) logger.info("Switching all off \"%s\"", type(self).__name__)
@ -50,27 +40,3 @@ class room_collection(object):
sub = getattr(self, sub_name) sub = getattr(self, sub_name)
if sub.__class__.__bases__[0].__name__ in self.ALLOWED_CLASSES: if sub.__class__.__bases__[0].__name__ in self.ALLOWED_CLASSES:
sub.all_off() sub.all_off()
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 sub.__class__.__bases__[0].__name__ in self.ALLOWED_CLASSES:
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,7 +3,7 @@
# #
import config import config
from devdi import topic as props 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
@ -15,25 +15,23 @@ except ImportError:
ROOT_LOGGER_NAME = 'root' ROOT_LOGGER_NAME = 'root'
logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
loc = props.LOC_STW
class stairway(room_collection): class stairway(room_collection):
def __init__(self, mqtt_client, pd, vd): def __init__(self, mqtt_client):
super().__init__(mqtt_client, pd, vd) super().__init__(mqtt_client)
self.stairway = stairway_stairway(mqtt_client, pd, vd) self.stairway = stairway_stairway(mqtt_client)
class stairway_stairway(room): class stairway_stairway(room):
def __init__(self, mqtt_client, pd, vd): def __init__(self, mqtt_client):
# #
# Device initialisation # Device initialisation
# #
# http://shelly1-3494546A9364 # http://shelly1-3494546A9364
self.main_light_shelly = pd.get(props.STG_SHE, loc, props.ROO_STF, props.FUN_MAL) self.main_light_shelly = devices.shelly(mqtt_client, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_SHELLY)
self.motion_sensor_ff = pd.get(props.STG_ZFE, loc, props.ROO_STF, props.FUN_MSE) self.motion_sensor_gf = devices.silvercrest_motion_sensor(mqtt_client, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_MOTION_SENSOR_GF)
self.motion_sensor_gf = pd.get(props.STG_ZGW, loc, props.ROO_STG, props.FUN_MSE) self.motion_sensor_ff = devices.silvercrest_motion_sensor(mqtt_client, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_MOTION_SENSOR_FF)
super().__init__(mqtt_client, pd, vd) super().__init__(mqtt_client)
# #
# Functionality initialisation # Functionality initialisation
@ -49,6 +47,6 @@ class stairway_stairway(room):
# #
self.main_light_videv = videv_switching_motion( self.main_light_videv = videv_switching_motion(
mqtt_client, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_VIDEV, mqtt_client, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_VIDEV,
self.main_light_shelly, self.main_light_shelly.KEY_OUTPUT_0, self.main_light_shelly, devices.shelly.KEY_OUTPUT_0,
self.motion_sensor_light self.motion_sensor_light
) )

View File

@ -10,8 +10,9 @@ Targets:
* No functionality should be implemented here * No functionality should be implemented here
""" """
from base import videv_base from base import mqtt_base
from function.rooms import room, room_collection from function.rooms import room, room_collection
import json
import time import time
try: try:
@ -20,29 +21,115 @@ except ImportError:
ROOT_LOGGER_NAME = 'root' ROOT_LOGGER_NAME = 'root'
class videv_pure_switch(videv_base): class base(mqtt_base):
KEY_STATE = 'state' KEY_INFO = '__info__'
def __init__(self, mqtt_client, topic): def __init__(self, mqtt_client, topic, default_values=None):
super().__init__(mqtt_client, topic) super().__init__(mqtt_client, topic, default_values=default_values)
self[self.KEY_STATE] = False self.__display_dict__ = {}
# self.__control_dict__ = {}
self.mqtt_client.add_callback(self.topic + '/state/set', self.__state__) self.__capabilities__ = None
self.__active_tx__ = {}
def __state__(self, mqtt_client, userdata, message): def add_display(self, my_key, ext_device, ext_key, on_change_only=True):
self.set(self.KEY_STATE, message.payload == b'true') """
self.__tx__(self.KEY_STATE, message.payload == b'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(videv_base): class videv_switching(base):
KEY_STATE = 'state' KEY_STATE = 'state'
def __init__(self, mqtt_client, topic, sw_device, sw_key): def __init__(self, mqtt_client, topic, sw_device, sw_key):
super().__init__(mqtt_client, topic) super().__init__(mqtt_client, topic)
self.add_routing(self.KEY_STATE, sw_device, sw_key) self.add_routing(self.KEY_STATE, sw_device, sw_key)
#
self.__tx_capabilities__()
class videv_switching_timer(videv_base): class videv_switching_timer(base):
KEY_STATE = 'state' KEY_STATE = 'state'
KEY_TIMER = 'timer' KEY_TIMER = 'timer'
@ -50,9 +137,11 @@ class videv_switching_timer(videv_base):
super().__init__(mqtt_client, topic) super().__init__(mqtt_client, topic)
self.add_routing(self.KEY_STATE, sw_device, sw_key) self.add_routing(self.KEY_STATE, sw_device, sw_key)
self.add_display(self.KEY_TIMER, tm_device, tm_key) self.add_display(self.KEY_TIMER, tm_device, tm_key)
#
self.__tx_capabilities__()
class videv_switching_motion(videv_base): class videv_switching_motion(base):
KEY_STATE = 'state' KEY_STATE = 'state'
# #
KEY_TIMER = 'timer' KEY_TIMER = 'timer'
@ -67,9 +156,11 @@ class videv_switching_motion(videv_base):
# motion sensor state # motion sensor state
for index, motion_sensor in enumerate(self.motion_sensors): for index, motion_sensor in enumerate(self.motion_sensors):
self.add_display(self.KEY_MOTION_SENSOR % index, motion_sensor, motion_sensor.KEY_OCCUPANCY) self.add_display(self.KEY_MOTION_SENSOR % index, motion_sensor, motion_sensor.KEY_OCCUPANCY)
#
self.__tx_capabilities__()
class videv_switch_brightness(videv_base): class videv_switch_brightness(base):
KEY_STATE = 'state' KEY_STATE = 'state'
KEY_BRIGHTNESS = 'brightness' KEY_BRIGHTNESS = 'brightness'
@ -77,9 +168,11 @@ class videv_switch_brightness(videv_base):
super().__init__(mqtt_client, topic) super().__init__(mqtt_client, topic)
self.add_routing(self.KEY_STATE, sw_device, sw_key) 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_BRIGHTNESS, br_device, br_key)
#
self.__tx_capabilities__()
class videv_switch_brightness_color_temp(videv_base): class videv_switch_brightness_color_temp(base):
KEY_STATE = 'state' KEY_STATE = 'state'
KEY_BRIGHTNESS = 'brightness' KEY_BRIGHTNESS = 'brightness'
KEY_COLOR_TEMP = 'color_temp' KEY_COLOR_TEMP = 'color_temp'
@ -89,9 +182,11 @@ class videv_switch_brightness_color_temp(videv_base):
self.add_routing(self.KEY_STATE, sw_device, sw_key) 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_BRIGHTNESS, br_device, br_key)
self.add_routing(self.KEY_COLOR_TEMP, ct_device, ct_key) self.add_routing(self.KEY_COLOR_TEMP, ct_device, ct_key)
#
self.__tx_capabilities__()
class videv_heating(videv_base): class videv_heating(base):
KEY_USER_TEMPERATURE_SETPOINT = 'user_temperature_setpoint' KEY_USER_TEMPERATURE_SETPOINT = 'user_temperature_setpoint'
KEY_VALVE_TEMPERATURE_SETPOINT = 'valve_temperature_setpoint' KEY_VALVE_TEMPERATURE_SETPOINT = 'valve_temperature_setpoint'
KEY_AWAY_MODE = 'away_mode' KEY_AWAY_MODE = 'away_mode'
@ -115,9 +210,11 @@ class videv_heating(videv_base):
self.add_display(self.KEY_VALVE_TEMPERATURE_SETPOINT, heating_function, heating_function.KEY_TEMPERATURE_SETPOINT) 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_BOOST_TIMER, heating_function, heating_function.KEY_BOOST_TIMER)
self.add_display(self.KEY_TEMPERATURE, heating_function, heating_function.KEY_TEMPERATURE_CURRENT, False) self.add_display(self.KEY_TEMPERATURE, heating_function, heating_function.KEY_TEMPERATURE_CURRENT, False)
#
self.__tx_capabilities__()
class videv_multistate(videv_base): class videv_multistate(base):
KEY_STATE = 'state_%d' KEY_STATE = 'state_%d'
def __init__(self, mqtt_client, topic, key_for_device, device, num_states, default_values=None): def __init__(self, mqtt_client, topic, key_for_device, device, num_states, default_values=None):
@ -128,13 +225,17 @@ class videv_multistate(videv_base):
self.__tx__(self.KEY_STATE % i, False) self.__tx__(self.KEY_STATE % i, False)
# #
device.add_callback(key_for_device, None, self.__index_rx__, True) device.add_callback(key_for_device, None, self.__index_rx__, True)
#
self.__tx_capabilities__()
def __index_rx__(self, device, key, data): def __index_rx__(self, device, key, data):
for i in range(0, self.num_states): for i in range(0, self.num_states):
self.__tx__(self.KEY_STATE % i, i == data) self.__tx__(self.KEY_STATE % i, i == data)
#
self.__tx_capabilities__()
class videv_audio_player(videv_base): class videv_audio_player(base):
KEY_ACTIVE_PLAYER = 'player_%d' KEY_ACTIVE_PLAYER = 'player_%d'
KEY_TITLE = 'title' KEY_TITLE = 'title'
NO_TITLE = '---' NO_TITLE = '---'
@ -146,12 +247,20 @@ class videv_audio_player(videv_base):
# #
for audio_device in args: for audio_device in args:
audio_device.add_callback(audio_device.KEY_TITLE, None, self.__title_rx__, True) audio_device.add_callback(audio_device.KEY_TITLE, None, self.__title_rx__, True)
#
self.__tx_capabilities__()
def __title_rx__(self, device, key, data): def __title_rx__(self, device, key, data):
self.__tx__(self.KEY_TITLE, data or self.NO_TITLE) 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(videv_base):
class all_off(base):
ALLOWED_CLASSES = (room, room_collection, ) ALLOWED_CLASSES = (room, room_collection, )
def __init__(self, mqtt_client, topic, room_collection): def __init__(self, mqtt_client, topic, room_collection):
@ -163,6 +272,8 @@ class all_off(videv_base):
# register mqtt callbacks for all my keys # register mqtt callbacks for all my keys
for key in self.__inst_dict__: for key in self.__inst_dict__:
mqtt_client.add_callback(topic + "/" + key, self.all_off) mqtt_client.add_callback(topic + "/" + key, self.all_off)
#
self.__tx_capabilities__()
def __check_inst_capabilities__(self, name, inst): def __check_inst_capabilities__(self, name, inst):
# fits to specified classes # fits to specified classes
@ -193,3 +304,14 @@ class all_off(videv_base):
def all_off(self, client, userdata, message): def all_off(self, client, userdata, message):
key = message.topic[len(self.topic) + 1:] key = message.topic[len(self.topic) + 1:]
self.__inst_dict__[key].all_off() 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__

View File

@ -1,24 +0,0 @@
#!/bin/bash
#
BASEPATH=`realpath $(dirname $0)`
#
# Create venv
#
if [[ ! -e $BASEPATH/venv ]]; then
python3 -m venv $BASEPATH/venv > /dev/null 2>&1
# $BASEPATH/venv/bin/pip install --upgrade pip
find $BASEPATH -name requirements.txt | xargs -L 1 $BASEPATH/venv/bin/pip install -r > /dev/null 2>&1
echo "venv changed"
fi
#
# Update venv modules
#
for req_mod in $($BASEPATH/venv/bin/pip list --format=json | jq -r '.[] | .name'); do
$BASEPATH/venv/bin/pip install -U $req_mod | grep install > /dev/null 2>&1
if [[ "$?" -eq "0" ]]; then
echo $req_mod changed
fi
done
#|xargs -n1 $BASEPATH/venv/bin/pip install -U

2
mqtt

@ -1 +1 @@
Subproject commit 14e56ccdbf6594f699b4afcfb4acafe9b899e914 Subproject commit 1adfb0626e7777c6d29be65d4ad4ce2d57541301

2
report

@ -1 +1 @@
Subproject commit 7003c13ef8c7e7c3a55a545cbbad4039cc024a9f Subproject commit e2392c9f28d88ee54463681850acf95ae496c9a0

View File

@ -1,74 +1,28 @@
import config import config
import devdi.devices
import function import function
import json
import logging import logging
import mqtt import mqtt
import os
import report import report
import subprocess
import time import time
logger = logging.getLogger(config.APP_NAME) 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 = 3 # TODO: Implement handling of warnings (videv element to show in webapp?)
VERS_PATCH = 0 # 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__":
#
# Logging
#
if config.DEBUG: if config.DEBUG:
report.appLoggingConfigure(None, 'stdout', ((config.APP_NAME, logging.DEBUG), ), report.appLoggingConfigure(None, None, ((config.APP_NAME, logging.DEBUG), ), fmt=report.SHORT_FMT, host='localhost', port=19996)
target_level=logging.WARNING, fmt=report.SHORT_FMT, host='localhost', port=19996)
else: else:
report.stdoutLoggingConfigure(((config.APP_NAME, config.LOGLEVEL), ), report.SHORT_FMT) report.stdoutLoggingConfigure(((config.APP_NAME, logging.INFO), ), report.SHORT_FMT)
#
# MQTT Client
# #
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__)
# func = function.all_functions(mc)
# Smarthome physical Devices
#
pd = devdi.devices.physical_devices(mc)
#
# Smarthome physical Devices
#
vd = devdi.devices.videv_devices(mc)
#
# Smart Home Functionality
#
func = function.all_functions(mc, pd, vd)
while (True): while (True):
time.sleep(1) time.sleep(1)

View File

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