videv implementation for lights

This commit is contained in:
Dirk Alders 2023-01-20 08:03:06 +01:00
parent 0bf885e58a
commit 9233468a49
9 changed files with 342 additions and 2 deletions

45
base.py Normal file
View File

@ -0,0 +1,45 @@
import logging
try:
from config import APP_NAME as ROOT_LOGGER_NAME
except ImportError:
ROOT_LOGGER_NAME = 'root'
class common_base(dict):
DEFAULT_VALUES = {}
def __init__(self):
super().__init__(self.DEFAULT_VALUES)
self['__type__'] = self.__class__.__name__
#
self.__callback_list__ = []
self.logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
def add_callback(self, key, data, callback, on_change_only=True):
"""
key: key or None for all keys
data: data or None for all data
"""
cb_tup = (key, data, callback, on_change_only)
if cb_tup not in self.__callback_list__:
self.__callback_list__.append(cb_tup)
def set(self, key, data):
value_changed = self[key] != data
self[key] = data
for cb_key, cb_data, callback, on_change_only in self.__callback_list__:
if cb_key is None or key == cb_key: # key fits to callback definition
if cb_data is None or cb_data == self[key]: # data fits to callback definition
if value_changed or not on_change_only: # change status fits to callback definition
callback(self, key, self[key])
class mqtt_base(common_base):
def __init__(self, mqtt_client, topic):
super().__init__()
#
self.mqtt_client = mqtt_client
self.topic = topic
for entry in self.topic.split('/'):
self.logger = self.logger.getChild(entry)

View File

@ -26,6 +26,8 @@ devices (DEVICES)
"""
# TODO: Usage of mqtt_base for all devices
__DEPENDENCIES__ = []
import json
@ -506,6 +508,9 @@ class tradfri_light(base):
else:
return super().pack_filter(key, data)
def request_data(self):
self.mqtt_client.send(self.topic + "/get", '{%s: ""}' % self.KEY_OUTPUT_0)
#
# RX
#

View File

@ -7,6 +7,7 @@ import devices
from function.modules import brightness_choose_n_action, circulation_pump, radiator_function
import logging
from function.rooms import room_shelly, room_shelly_motion_sensor, room_shelly_tradfri_light
from function.videv import videv_switching, videv_switch_brightness, videv_switch_brightness_color_temp
try:
from config import APP_NAME as ROOT_LOGGER_NAME
except ImportError:
@ -18,6 +19,11 @@ class first_floor_east_floor(room_shelly):
def __init__(self, mqtt_client):
# http://shelly1l-3C6105E4E629
super().__init__(mqtt_client, config.TOPIC_FFE_FLOOR_MAIN_LIGHT_SHELLY, config.TOPIC_FFE_FLOOR_MAIN_LIGHT_GUI)
#
self.main_light = videv_switching(
mqtt_client, config.TOPIC_FFE_FLOOR_MAIN_LIGHT_VIDEV,
self.main_light_shelly, devices.shelly.KEY_OUTPUT_0
)
class first_floor_east_kitchen(room_shelly):
@ -27,6 +33,15 @@ class first_floor_east_kitchen(room_shelly):
#
self.circulation_pump = circulation_pump(mqtt_client)
self.circulation_pump.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.flash_main_light)
#
self.main_light_videv = videv_switching(
mqtt_client, config.TOPIC_FFE_KITCHEN_MAIN_LIGHT_VIDEV,
self.main_light_shelly, devices.shelly.KEY_OUTPUT_0
)
self.circulation_pump_videv = videv_switching(
mqtt_client, config.TOPIC_FFE_KITCHEN_CIRCULATION_PUMP_VIDEV,
self.circulation_pump.main_light_shelly, devices.shelly.KEY_OUTPUT_0
)
def all_off(self, device=None, key=None, data=None):
self.circulation_pump.all_off(device, key, data)
@ -48,6 +63,15 @@ class first_floor_east_dining(room_shelly):
self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.floorlamp_powerplug.set_output_0_mcb, True)
self.gui_floorlamp.add_callback(devices.nodered_gui_switch.KEY_STATE, None, self.floorlamp_powerplug.set_output_0_mcb)
self.floorlamp_powerplug.add_callback(devices.silvercrest_powerplug.KEY_OUTPUT_0, None, self.gui_floorlamp.set_state_mcb)
#
self.main_light_videv = videv_switching(
mqtt_client, config.TOPIC_FFE_DININGROOM_MAIN_LIGHT_VIDEV,
self.main_light_shelly, devices.shelly.KEY_OUTPUT_0
)
self.floorlamp_videv = videv_switching(
mqtt_client, config.TOPIC_FFE_DININGROOM_FLOOR_LAMP_VIDEV,
self.floorlamp_powerplug, devices.silvercrest_powerplug.KEY_OUTPUT_0
)
def all_off(self, device=None, key=None, data=None):
super().all_off(device, key, data)
@ -96,6 +120,22 @@ class first_floor_east_sleep(room_shelly_tradfri_light):
self.gui_bed_light_di.add_callback(devices.nodered_gui_light.KEY_BRIGHTNESS, None, self.bed_light_di_tradfri.set_brightness_mcb)
self.bed_light_di_tradfri.add_callback(devices.tradfri_light.KEY_OUTPUT_0, None, self.gui_bed_light_di.set_enable_mcb)
self.bed_light_di_tradfri.add_callback(devices.tradfri_light.KEY_BRIGHTNESS, None, self.gui_bed_light_di.set_brightness_mcb)
#
self.main_light_videv = videv_switch_brightness_color_temp(
mqtt_client, config.TOPIC_FFE_SLEEP_MAIN_LIGHT_VIDEV,
self.main_light_shelly, devices.shelly.KEY_OUTPUT_0,
self.main_light_tradfri, devices.tradfri_light.KEY_BRIGHTNESS,
self.main_light_tradfri, devices.tradfri_light.KEY_COLOR_TEMP
)
self.bed_light_di_videv = videv_switch_brightness(
mqtt_client, config.TOPIC_FFE_SLEEP_BED_LIGHT_DI_VIDEV,
self.bed_light_di_tradfri, devices.tradfri_light.KEY_OUTPUT_0,
self.bed_light_di_tradfri, devices.tradfri_light.KEY_BRIGHTNESS,
)
self.bed_light_ma_videv = videv_switching(
mqtt_client, config.TOPIC_FFE_SLEEP_BED_LIGHT_MA_VIDEV,
self.bed_light_ma_powerplug, devices.silvercrest_powerplug.KEY_OUTPUT_0
)
def all_off(self, device=None, key=None, data=None):
super().all_off(device, key, data)
@ -139,6 +179,24 @@ class first_floor_east_living(room_shelly_tradfri_light):
self.gui_xmas_tree.add_callback(devices.nodered_gui_switch.KEY_STATE, None, self.powerplug_xmas_tree.set_output_0_mcb)
#
self.powerplug_xmas_tree.add_callback(devices.silvercrest_powerplug.KEY_OUTPUT_0, None, self.powerplug_xmas_star.set_output_0_mcb)
#
self.main_light_videv = videv_switch_brightness_color_temp(
mqtt_client, config.TOPIC_FFE_LIVINGROOM_MAIN_LIGHT_VIDEV,
self.main_light_shelly, devices.shelly.KEY_OUTPUT_0,
self.main_light_tradfri, devices.tradfri_light.KEY_BRIGHTNESS,
self.main_light_tradfri, devices.tradfri_light.KEY_COLOR_TEMP
)
self.floorlamp_videv = videv_switch_brightness_color_temp(
mqtt_client, config.TOPIC_FFE_LIVINGROOM_FLOOR_LAMP_VIDEV,
self.floorlamp_tradfri_1, devices.tradfri_light.KEY_OUTPUT_0,
self.floorlamp_tradfri_1, devices.tradfri_light.KEY_BRIGHTNESS,
self.floorlamp_tradfri_1, devices.tradfri_light.KEY_COLOR_TEMP
)
if config.CHRISTMAS:
self.xmas_tree_videv = videv_switching(
mqtt_client, config.TOPIC_FFE_LIVINGROOM_XMAS_TREE_VIDEV,
self.powerplug_xmas_tree, devices.silvercrest_powerplug.KEY_OUTPUT_0
)
def all_off(self, device=None, key=None, data=None):
super().all_off(device, key, data)

View File

@ -3,9 +3,11 @@
#
import config
import devices
import logging
from function.modules import radiator_function
from function.rooms import room_shelly, room_shelly_tradfri_light
from function.videv import videv_switch_brightness, videv_switch_brightness_color_temp
try:
@ -19,6 +21,13 @@ class first_floor_west_julian(room_shelly_tradfri_light):
# http://shelly1l-3C6105E43452
def __init__(self, mqtt_client):
super().__init__(mqtt_client, config.TOPIC_FFW_JULIAN_MAIN_LIGHT_SHELLY, config.TOPIC_FFW_JULIAN_MAIN_LIGHT_GUI, config.TOPIC_FFW_JULIAN_MAIN_LIGHT_ZIGBEE)
#
self.main_light_videv = videv_switch_brightness_color_temp(
mqtt_client, config.TOPIC_FFW_JULIAN_MAIN_LIGHT_VIDEV,
self.main_light_shelly, devices.shelly.KEY_OUTPUT_0,
self.main_light_tradfri, devices.tradfri_light.KEY_BRIGHTNESS,
self.main_light_tradfri, devices.tradfri_light.KEY_COLOR_TEMP
)
class first_floor_west_bath(object):
@ -26,6 +35,7 @@ class first_floor_west_bath(object):
# radiator valve
self.radiator_function = radiator_function(mqtt_client, config.TOPIC_FFW_BATH_RADIATOR_VALVE_ZIGBEE,
config.TOPIC_FFW_BATH_RADIATOR_VALVE_GUI, config.DEFAULT_TEMPERATURE_FFW_BATH)
def all_off(self):
pass
@ -35,9 +45,22 @@ class first_floor_west_living(room_shelly_tradfri_light):
def __init__(self, mqtt_client):
super().__init__(mqtt_client, config.TOPIC_FFW_LIVINGROOM_MAIN_LIGHT_SHELLY,
config.TOPIC_FFW_LIVINGROOM_MAIN_LIGHT_GUI, config.TOPIC_FFW_LIVINGROOM_MAIN_LIGHT_ZIGBEE)
#
self.main_light_videv = videv_switch_brightness_color_temp(
mqtt_client, config.TOPIC_FFW_LIVINGROOM_MAIN_LIGHT_VIDEV,
self.main_light_shelly, devices.shelly.KEY_OUTPUT_0,
self.main_light_tradfri, devices.tradfri_light.KEY_BRIGHTNESS,
self.main_light_tradfri, devices.tradfri_light.KEY_COLOR_TEMP
)
class first_floor_west_sleep(room_shelly_tradfri_light):
# http://shelly1-3494546A51F2
def __init__(self, mqtt_client):
super().__init__(mqtt_client, config.TOPIC_FFW_SLEEP_MAIN_LIGHT_SHELLY, config.TOPIC_FFW_SLEEP_MAIN_LIGHT_GUI, config.TOPIC_FFW_SLEEP_MAIN_LIGHT_ZIGBEE)
#
self.main_light_videv = videv_switch_brightness(
mqtt_client, config.TOPIC_FFW_SLEEP_MAIN_LIGHT_VIDEV,
self.main_light_shelly, devices.shelly.KEY_OUTPUT_0,
self.main_light_tradfri, devices.tradfri_light.KEY_BRIGHTNESS
)

View File

@ -7,6 +7,7 @@ import devices
from function.modules import brightness_choose_n_action, radiator_function
import logging
from function.rooms import room_shelly, room_shelly_tradfri_light, room_shelly_silvercrest_light
from function.videv import videv_switching, videv_switch_brightness_color_temp
import task
try:
@ -26,10 +27,17 @@ class ground_floor_west_floor(room_shelly_silvercrest_light):
self.main_light_tradfri_2 = devices.tradfri_light(mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_2_ZIGBEE)
self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_BRIGHTNESS, None, self.main_light_tradfri_2.set_brightness_mcb)
self.main_light_tradfri.add_callback(devices.tradfri_light.KEY_COLOR_TEMP, None, self.main_light_tradfri_2.set_color_temp_mcb)
#
self.main_light_videv = videv_switch_brightness_color_temp(
mqtt_client, config.TOPIC_GFW_FLOOR_MAIN_LIGHT_VIDEV,
self.main_light_shelly, devices.shelly.KEY_OUTPUT_0,
self.main_light_tradfri, devices.tradfri_light.KEY_BRIGHTNESS,
self.main_light_tradfri, devices.tradfri_light.KEY_COLOR_TEMP
)
def send_init_message_main_light(self):
super().send_init_message_main_light()
self.main_light_tradfri_2.mqtt_client.send(self.main_light_tradfri_2.topic + "/get", '{"state": ""}')
self.main_light_tradfri_2.request_data()
class ground_floor_west_marion(room_shelly):
@ -39,6 +47,11 @@ class ground_floor_west_marion(room_shelly):
# radiator valve
self.radiator_function = radiator_function(mqtt_client, config.TOPIC_GFW_MARION_RADIATOR_VALVE_ZIGBEE,
config.TOPIC_GFW_MARION_RADIATOR_VALVE_GUI, config.DEFAULT_TEMPERATURE_GFW_MARION)
#
self.main_light_videv = videv_switching(
mqtt_client, config.TOPIC_GFW_MARION_MAIN_LIGHT_VIDEV,
self.main_light_shelly, devices.shelly.KEY_OUTPUT_0
)
class ground_floor_west_dirk(room_shelly_tradfri_light):
@ -136,6 +149,31 @@ class ground_floor_west_dirk(room_shelly_tradfri_light):
self.powerplug_common.toggle_output_3_mcb)
self.gui_pc_dock.add_callback(devices.nodered_gui_switch.KEY_STATE, None, self.powerplug_common.set_output_3_mcb)
self.powerplug_common.add_callback(self.KEY_POWERPLUG_PC_DOCK, None, self.gui_pc_dock.set_state_mcb)
#
self.main_light_videv = videv_switch_brightness_color_temp(
mqtt_client, config.TOPIC_GFW_DIRK_MAIN_LIGHT_VIDEV,
self.main_light_shelly, devices.shelly.KEY_OUTPUT_0,
self.main_light_tradfri, devices.tradfri_light.KEY_BRIGHTNESS,
self.main_light_tradfri, devices.tradfri_light.KEY_COLOR_TEMP
)
self.desk_light_videv = videv_switch_brightness_color_temp(
mqtt_client, config.TOPIC_GFW_DIRK_DESK_LIGHT_VIDEV,
self.powerplug_common, self.KEY_POWERPLUG_DESK_LIGHT,
self.desk_light_tradfri, devices.tradfri_light.KEY_BRIGHTNESS,
self.desk_light_tradfri, devices.tradfri_light.KEY_COLOR_TEMP
)
self.amplifier_videv = videv_switching(
mqtt_client, config.TOPIC_GFW_DIRK_AMPLIFIER_VIDEV,
self.powerplug_common, self.KEY_POWERPLUG_AMPLIFIER
)
self.cd_player_videv = videv_switching(
mqtt_client, config.TOPIC_GFW_DIRK_CD_PLAYER_VIDEV,
self.powerplug_common, self.KEY_POWERPLUG_CD_PLAYER
)
self.pc_dock_videv = videv_switching(
mqtt_client, config.TOPIC_GFW_DIRK_PC_DOCK_VIDEV,
self.powerplug_common, self.KEY_POWERPLUG_PC_DOCK
)
def all_off(self, device=None, key=None, data=None):
super().all_off(device, key, data)

View File

@ -1,6 +1,15 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
"""
Functional Modules
Targets:
* Device like structure to be compatible with videv
- KEY_* as part of the class for all parameters which needs to be accessed from videv
- Method *.set(key, data) to pass data from videv to Module
- Method .add_calback(key, data, callback, on_change_only=False) to register videv actualisation on changes
"""
import config
import devices
@ -16,6 +25,14 @@ except ImportError:
logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
class base(object):
def set(key, data):
pass
def add_calback(self, key, data, callback, on_change_only=False):
pass
class brightness_choose_n_action(object):
def __init__(self, mqtt_client, button_tradfri, topic_led):
self.gui_led_active_device = devices.nodered_gui_leds(mqtt_client, topic_led)

View File

@ -145,4 +145,4 @@ class room_shelly_silvercrest_light(room_shelly_tradfri_light):
self.main_light_shelly_last = data
def send_init_message_main_light(self):
self.main_light_tradfri.mqtt_client.send(self.main_light_tradfri.topic + "/get", '{"state": ""}')
self.main_light_tradfri.request_data()

View File

@ -3,9 +3,12 @@
#
import config
import devices
from function.modules import brightness_choose_n_action
import logging
from function.rooms import room_shelly_motion_sensor
from function.videv import videv_switching
try:
from config import APP_NAME as ROOT_LOGGER_NAME
except ImportError:
@ -19,3 +22,8 @@ class stairway(room_shelly_motion_sensor):
super().__init__(mqtt_client, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_SHELLY, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_GUI,
config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_MOTION_SENSOR_GF, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_MOTION_SENSOR_FF,
timer_value=config.USER_ON_TIME_STAIRWAYS)
#
self.main_light_videv = videv_switching(
mqtt_client, config.TOPIC_STW_STAIRWAY_MAIN_LIGHT_VIDEV,
self.main_light_shelly, devices.shelly.KEY_OUTPUT_0
)

146
function/videv.py Normal file
View File

@ -0,0 +1,146 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
"""
Virtual Device(s)
Targets:
* MQTT-Interface to control joined devices as one virtual device.
* Primary signal routing
* No functionality should be implemented here
"""
from base import mqtt_base
import devices
import json
import logging
BASETOPIC = "videv"
try:
from config import APP_NAME as ROOT_LOGGER_NAME
except ImportError:
ROOT_LOGGER_NAME = 'root'
class base(mqtt_base):
DEFAULT_VALUES = {}
def __init__(self, mqtt_client, topic, *args):
super().__init__(mqtt_client, topic)
self.__device_list__ = {}
for videv_key, device in [reduced[:2] for reduced in args]:
self.__device_list__[videv_key] = device
# send initial state
for key in self.keys():
self.__tx__(key, self[key])
# add receive topics
mqtt_client.add_callback(self.topic + "/#", self.__rx__)
def __tx__(self, key, data):
if type(data) not in (str, ):
data = json.dumps(data)
if key in self.keys():
self.mqtt_client.send(self.topic + '/' + key, data)
else:
self.logger.warning("Ignoring send request for key %s (not available for this class)", key)
def __rx__(self, client, userdata, message):
key = message.topic.split('/')[-1]
if key in self.keys():
try:
data = json.loads(message.payload)
except json.decoder.JSONDecodeError:
data = message.payload
if data != self[key]:
self.__rx_functionality__(key, data)
self.set(key, data)
else:
self.logger.info("Ignoring rx message with topic %s", message.topic)
def __rx_functionality__(self, key, data):
raise NotImplemented("Method __rx_functionality__ needs to be implemented in child class")
def __device_data__(self, device, key, data):
raise NotImplemented("Method __device_data__ needs to be implemented in child class")
class base_routing(base):
def __init__(self, mqtt_client, topic, *args):
super().__init__(mqtt_client, topic, *args)
#
self.__device_key__ = {}
index = 0
for videv_key, device, device_key in args:
if self.__device_list__[videv_key] != device:
raise ReferenceError("Parent class generated a deviating device list")
self.__device_key__[videv_key] = device_key
index += 1
# add callbacks
for key in self.__device_list__:
self.__device_list__[key].add_callback(self.__device_key__[key], None, self.__device_data__, True)
def __rx_functionality__(self, key, data):
try:
self.__device_list__[key].set(self.__device_key__[key], data)
except KeyError:
self.logger.warning("RX passthrough not possible for key %s", key)
def __device_data__(self, device, key, data):
l1 = [k for k, v in self.__device_list__.items() if v == device]
l2 = [k for k, v in self.__device_key__.items() if v == key]
try:
videv_key = [k for k in l1 if k in l2][0]
except IndexError:
self.logger.warning("videv_key not available for %s::%s", device.__class__.__name__, device.topic)
else:
self.set(videv_key, data)
self.__tx__(videv_key, data)
class videv_switching(base_routing):
KEY_STATE = 'state'
#
DEFAULT_VALUES = {
KEY_STATE: False,
}
def __init__(self, mqtt_client, topic, sw_device, sw_key):
#
super().__init__(mqtt_client, topic, (self.KEY_STATE, sw_device, sw_key))
class videv_switch_brightness(base_routing):
KEY_STATE = 'state'
KEY_BRIGHTNESS = 'brightness'
#
DEFAULT_VALUES = {
KEY_STATE: False,
KEY_BRIGHTNESS: 0
}
def __init__(self, mqtt_client, topic, sw_device, sw_key, br_device, br_key):
#
super().__init__(mqtt_client, topic, (self.KEY_STATE, sw_device, sw_key), (self.KEY_BRIGHTNESS, br_device, br_key))
class videv_switch_brightness_color_temp(base_routing):
KEY_STATE = 'state'
KEY_BRIGHTNESS = 'brightness'
KEY_COLOR_TEMP = 'color_temp'
#
DEFAULT_VALUES = {
KEY_STATE: False,
KEY_BRIGHTNESS: 0,
KEY_COLOR_TEMP: 0,
}
def __init__(self, mqtt_client, topic, sw_device, sw_key, br_device, br_key, ct_device, ct_key):
#
super().__init__(
mqtt_client, topic,
(self.KEY_STATE, sw_device, sw_key),
(self.KEY_BRIGHTNESS, br_device, br_key),
(self.KEY_COLOR_TEMP, ct_device, ct_key)
)