diff --git a/devices/__init__.py b/devices/__init__.py index 26e104a..9660bc4 100644 --- a/devices/__init__.py +++ b/devices/__init__.py @@ -40,6 +40,10 @@ logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) BATTERY_WARN_LEVEL = 5 +def DEVICE_TYPE_LIST(): + return [shelly, silvercrest_powerplug, my_powerplug, tradfri_light, tradfri_button] + + def is_json(data): try: json.loads(data) @@ -163,7 +167,7 @@ class base(dict): def callback_caller(self, key, data): for cb_key, cb_data, callback in self.callback_list: - if (cb_key == key or cb_key is None) and (cb_data == data or cb_data is None): + if (cb_key == key or cb_key is None) and (cb_data == data or cb_data is None) and callback is not None: callback(self, key, data) def warning_caller(self): @@ -459,3 +463,34 @@ class tradfri_button(base): def warning_text(self): return "Low battery level detected for %s. Battery level was %.0f%%." % (self.topic, self.get(self.KEY_BATTERY)) + + +class nodered_gui(base): + KEY_FEEDBACK = "feedback" + KEY_STATE = "state" + # + TX_TOPIC = 'set' + TX_TYPE = base.TX_VALUE + TX_FILTER_DATA_KEYS = [] + # + RX_LOG_INFO_ALWAYS_KEYS = [] + RX_KEYS = [KEY_STATE] + RX_IGNORE_TOPICS = [KEY_FEEDBACK + '/' + TX_TOPIC] + RX_FILTER_DATA_KEYS = [] + + def __init__(self, mqtt_client, topic): + super().__init__(mqtt_client, topic) + + # + # RX + # + @property + def state(self): + """rv: [True, False]""" + return self.get(self.KEY_STATE) + + # + # TX + # + def set_feedback(self, data): + self.pack(self.KEY_FEEDBACK, data) diff --git a/function/__init__.py b/function/__init__.py index 17e5f43..76d45bb 100644 --- a/function/__init__.py +++ b/function/__init__.py @@ -1,19 +1,170 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # +import config +import devices +import inspect import logging -__all__ = ['all_functions', 'first_floor_dining'] - -from . import first_floor_dining - try: from config import APP_NAME as ROOT_LOGGER_NAME except ImportError: ROOT_LOGGER_NAME = 'root' logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) +# TODO: Add brightness and color temp including disable of gui elements. +# TODO: Add lamp sleep_di + + +class room(object): + def gui_switch_feedback(self, device, key, data): + self.gui_switch_main_light.set_feedback(data) + + +class room_shelly(room): + def __init__(self, mqtt_client, topic_shelly, topic_gui_switch): + self.main_light_shelly = devices.shelly(mqtt_client, topic=topic_shelly) + # + self.gui_switch_main_light = devices.nodered_gui(mqtt_client, topic=topic_gui_switch) + # + # Callback initialisation + # + self.gui_switch_main_light.add_callback(devices.nodered_gui.KEY_STATE, None, self.gui_switch_command) + self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.gui_switch_feedback) + + def all_off(self): + self.main_light_shelly.set_output_0(False) + self.main_light_shelly.set_output_1(False) + + def gui_switch_command(self, device, key, data): + logger.info("Switching \"%s\" main light: %s", type(self).__name__, str(data)) + self.main_light_shelly.set_output_0(data) + + +class first_floor_east_floor(room_shelly): + def __init__(self, mqtt_client): + # http://shelly1l-3C6105E4E629 + super().__init__(mqtt_client, "shellies/floor_madi", "gui/ffe_floor") + + +class first_floor_east_kitchen(room_shelly): + # TODO: add circulation pump (switch, time) + def __init__(self, mqtt_client): + # http://shelly1l-8CAAB5616C01 + super().__init__(mqtt_client, "shellies/kitchen", "gui/ffe_kitchen") + + +class first_floor_east_dining(room_shelly): + def __init__(self, mqtt_client): + # http://shelly1l-84CCA8ADD055 + super().__init__(mqtt_client, "shellies/diningroom", "gui/ffe_diningroom") + self.floorlamp_powerplug = devices.silvercrest_powerplug(mqtt_client, "zigbee_og_e/powerplug/dining_floorlamp") + if config.CHRISTMAS: + self.garland_powerplug = devices.silvercrest_powerplug(mqtt_client, topic="zigbee_og_e/powerplug/aux") + # + self.gui_switch_floorlamp = devices.nodered_gui(mqtt_client, topic="gui/ffe_dining_floorlamp") + # + # Callback initialisation + # + self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.floorlamp_synchronisation) + self.gui_switch_floorlamp.add_callback(devices.nodered_gui.KEY_STATE, None, self.gui_switch_command_floorlamp) + self.floorlamp_powerplug.add_callback(devices.silvercrest_powerplug.KEY_OUTPUT_0, + None, self.gui_switch_feedback_floorlamp) + # + self.main_light_shelly_last = None + + def floorlamp_synchronisation(self, device, key, data): + if data != self.main_light_shelly_last: + logger.info("Synching \"%s\" floorlamp with main light (%s)", type(self).__name__, str(data)) + self.floorlamp_powerplug.set_output_0(data) + self.main_light_shelly_last = data + + def gui_switch_command_floorlamp(self, device, key, data): + logger.info("Switching \"%s\" floorlamp: %s", type(self).__name__, str(data)) + self.floorlamp_powerplug.set_output_0(data) + + def gui_switch_feedback_floorlamp(self, device, key, data): + self.gui_switch_floorlamp.set_feedback(data) + + +class first_floor_east_sleep_madi(room_shelly): + def __init__(self, mqtt_client): + # http://shelly1l-E8DB84A254C7 + super().__init__(mqtt_client, "shellies/sleep_madi", "gui/ffe_sleep_madi") + self.main_light_tradfri = devices.tradfri_light(mqtt_client, topic="zigbee_og_e/light/sleep_madi") + self.bed_light_di_tradfri = devices.tradfri_light(mqtt_client, topic="zigbee_og_e/light/sleep_bed_di") + self.button_tradfri = devices.tradfri_button(mqtt_client, topic="zigbee_og_e/input_device/og_east") + + +class first_floor_east_living(room_shelly): + def __init__(self, mqtt_client): + # http://shelly1l-3C6105E3F910 + super().__init__(mqtt_client, "shellies/livingroom", "gui/ffe_livingroom") + self.main_light_tradfri = devices.tradfri_light(mqtt_client, topic="zigbee_og_e/light/livingroom") + for i in range(1, 7): + setattr(self, 'floorlamp_tradfri_%d' % i, + devices.tradfri_light(mqtt_client, topic="zigbee_og_e/light/living_floorlamp_%d" % i)) + # + self.gui_switch_floorlamp = devices.nodered_gui(mqtt_client, topic="gui/ffe_living_floorlamp") + # + # Callback initialisation + # + self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.floorlamp_synchronisation) + self.gui_switch_floorlamp.add_callback(devices.nodered_gui.KEY_STATE, None, self.gui_switch_command_floorlamp) + self.floorlamp_tradfri_1.add_callback(devices.tradfri_light.KEY_OUTPUT_0, + None, self.gui_switch_feedback_floorlamp) + # + self.main_light_shelly_last = None + + def __floorlamp_devices__(self): + rv = [] + for i in range(1, 7): + rv.append(getattr(self, 'floorlamp_tradfri_%d' % i)) + return rv + + def floorlamp_synchronisation(self, device, key, data): + if data != self.main_light_shelly_last: + logger.info("Synching \"%s\" floorlamp with main light (%s)", type(self).__name__, str(data)) + for device in self.__floorlamp_devices__(): + device.set_output_0(data) + self.main_light_shelly_last = data + + def gui_switch_command_floorlamp(self, device, key, data): + logger.info("Switching \"%s\" floorlamp: %s", type(self).__name__, str(data)) + for device in self.__floorlamp_devices__(): + device.set_output_0(data) + + def gui_switch_feedback_floorlamp(self, device, key, data): + self.gui_switch_floorlamp.set_feedback(data) + class all_functions(object): - def __init__(self, device_collection): - first_floor_dining.room_function(device_collection) + def __init__(self, mqtt_client): + self.rooms = {} + self.__devices__ = None + # + # first floor east + # + self.ffe_floor = first_floor_east_floor(mqtt_client) + self.ffe_kitchen = first_floor_east_kitchen(mqtt_client) + self.ffe_dining = first_floor_east_dining(mqtt_client) + self.ffe_sleep_madi = first_floor_east_sleep_madi(mqtt_client) + self.ffe_living = first_floor_east_living(mqtt_client) + + def ffe_off(self): + self.ffe_floor.off() + self.ffe_kitchen.off() + self.ffe_dining.off() + self.ffe_sleep_madi.off() + self.ffe_living.off() + + def devicelist(self): + raise Exception + # TODO: generate list by using getattr + if self.__devices__ is None: + self.__devices__ = [] + for room in self.rooms: + for name, obj in inspect.getmembers(room): + if type(obj) in devices.DEVICE_TYPE_LIST(): + self.__devices__.append(obj) + return self.__devices__ diff --git a/function/first_floor_dining.py b/function/first_floor_dining.py deleted file mode 100644 index 14d1a32..0000000 --- a/function/first_floor_dining.py +++ /dev/null @@ -1,24 +0,0 @@ -import devices -import logging - -try: - from config import APP_NAME as ROOT_LOGGER_NAME -except ImportError: - ROOT_LOGGER_NAME = 'root' -logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__) - - -class room_function(object): - def __init__(self, device_collection): - self.main_light_shelly = device_collection.shellies.dinigroom - self.floorlamp_powerplug = device_collection.powerplugs.dining_floorlamp - # - self.main_light_shelly.add_callback(devices.shelly.KEY_OUTPUT_0, None, self.main_light_shelly_callback) - # - self.main_light_shelly_last = None - - def main_light_shelly_callback(self, device, key, data): - if data != self.main_light_shelly_last: - logger.info("Test") - self.floorlamp_powerplug.set_output_0(data) - self.main_light_shelly_last diff --git a/smart_brain.py b/smart_brain.py index 29de7c3..cd6e097 100644 --- a/smart_brain.py +++ b/smart_brain.py @@ -11,66 +11,9 @@ import time logger = logging.getLogger(config.APP_NAME) -class shellies(object): - def __init__(self, mc): - self.dinigroom = devices.shelly(mc, topic="shellies/diningroom") # http://shelly1l-84CCA8ADD055 - self.sleep_madi = devices.shelly(mc, topic="shellies/sleep_madi") # http://shelly1l-E8DB84A254C7 - # self._ = devices.shelly(mc, topic="") # http:// - # self._ = devices.shelly(mc, topic="") # http:// - # self._ = devices.shelly(mc, topic="") # http:// - # self._ = devices.shelly(mc, topic="") # http:// - # self._ = devices.shelly(mc, topic="") # http:// - # self._ = devices.shelly(mc, topic="") # http:// - # self._ = devices.shelly(mc, topic="") # http:// - - -class powerplugs(object): - def __init__(self, mc): - self.dining_floorlamp = devices.silvercrest_powerplug(mc, "zigbee_og_e/powerplug/dining_floorlamp") - self.aux = devices.silvercrest_powerplug(mc, topic="zigbee_og_e/powerplug/aux") - self.dirk = devices.my_powerplug(mc, "powerplug/dirk") - - -class lights(object): - def __init__(self, mc): - self.sleep_madi = devices.tradfri_light(mc, topic="zigbee_og_e/light/sleep_madi") - self.sleep_bed_di = devices.tradfri_light(mc, topic="zigbee_og_e/light/sleep_bed_di") - # self._ = devices.tradfri_light(mc, topic="") - # self._ = devices.tradfri_light(mc, topic="") - # self._ = devices.tradfri_light(mc, topic="") - # self._ = devices.tradfri_light(mc, topic="") - # self._ = devices.tradfri_light(mc, topic="") - # self._ = devices.tradfri_light(mc, topic="") - # self._ = devices.tradfri_light(mc, topic="") - # self._ = devices.tradfri_light(mc, topic="") - # self._ = devices.tradfri_light(mc, topic="") - - -class input_devices(object): - def __init__(self, mc): - self.og_east = devices.tradfri_button(mc, topic="zigbee_og_e/input_device/og_east") - - -class all_devices(object): - def __init__(self, mc): - self.shellies = shellies(mc) - self.powerplugs = powerplugs(mc) - self.lights = lights(mc) - self.input_devices = input_devices(mc) - - def devicelist(self): - rv = [] - for name, obj in inspect.getmembers(self): - if not name.startswith('_') and name != inspect.stack()[0][3]: - for devicename, deviceobj in inspect.getmembers(obj): - if not devicename.startswith('_'): - rv.append(deviceobj) - return rv - - if __name__ == "__main__": if config.DEBUG: - report.stdoutLoggingConfigure(([config.APP_NAME, config.DEBUGLEVEL], ), report.LONG_FMT) + report.stdoutLoggingConfigure(([config.APP_NAME, logging.DEBUG], ), report.LONG_FMT) else: report.stdoutLoggingConfigure(((config.APP_NAME, logging.INFO), (config.APP_NAME+'.devices', logging.WARNING)), report.SHORT_FMT) @@ -78,17 +21,10 @@ if __name__ == "__main__": mc = mqtt.mqtt_client(host=config.MQTT_SERVER, port=config.MQTT_PORT, username=config.MQTT_USER, password=config.MQTT_PASSWORD, name=config.APP_NAME) - ad = all_devices(mc) - func = function.all_functions(ad) + func = function.all_functions(mc) - # def wcb(device, txt): - # logger.warning("%s: %s", device.topic, txt) - # for device in ad.devicelist(): - # device.add_warning_callback(wcb) - - # def cb(device, key, data): - # print("Callback: %s::%s" % (key, str(data))) - # ad.shellies.dinigroom.add_callback(devices.shelly.KEY_OUTPUT_0, None, cb) + # for device in func.devicelist(): + # device.add_warning_callback(None) while (True): time.sleep(1)