diff --git a/config_creation/__init__.py b/config_creation/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/config_creation/input.yaml b/config_creation/input.yaml new file mode 100644 index 0000000..e69de29 diff --git a/config_creation/knx_conf.py b/config_creation/knx_conf.py new file mode 100644 index 0000000..f3a8cac --- /dev/null +++ b/config_creation/knx_conf.py @@ -0,0 +1,180 @@ +from typing import List +from util import DeviceInfo + +__all__ = ['extent', 'import_ets5_csv_file', 'create_power_plug', 'create_lights', 'create_shutters', 'create_switches'] + + +def first_lower(s): + if len(s) == 0: + return s + else: + return s[0].lower() + s[1:] + + +def extent(result_dict, input_dict): + for k, v in input_dict.items(): + if k not in result_dict: + result_dict[k] = [] + for entry in v: + entry['platform'] = 'knx_data' + result_dict[k] += v + + +def import_ets5_csv_file(csv_file): + result = dict() + with open(csv_file, encoding='utf-8') as f: + for line in f: + splitted_line = line.split(",") + name = splitted_line[0].replace('"', "") + group_address = splitted_line[1].replace('"', "") + if '-' not in group_address: + result[name.strip()] = splitted_line[1].replace('"', "").strip() + return result + + +def create_power_plug(device_info: List[DeviceInfo], + csv_contents, + postfix_on_off_write=" Schalten", + postfix_on_off_read=" RM Schalten", + postfix_counter_value=" BSZ Wert", + postfix_consumption=" Verbrauch", + postfix_consumption_sum=" VerbrauchSumme", + postfix_consumption_reset=" Verbrauch Neustart", + postfix_counter_reset=" BSZ Neustart"): + result = {'switch': [], + 'sensor': []} + for entry in device_info: + try: + # Switching + on_off_write_addr = csv_contents.get(entry.csv_name + postfix_on_off_write, None) + on_off_read_addr = csv_contents.get(entry.csv_name + postfix_on_off_read, None) + if on_off_write_addr: + device_entry = { + 'name': entry.display_name, + 'address': on_off_write_addr, + } + if on_off_read_addr: + device_entry['state_address'] = on_off_read_addr + result['switch'].append(device_entry) + + # Counter for time (in hours) how long device was switched on + counter_value = csv_contents.get(entry.csv_name + postfix_counter_value, None) + if counter_value: + result['sensor'].append({ + 'name': entry.display_name + " Betriebsstunden", + 'address': counter_value, + 'type': '2byte_unsigned', + }) + counter_reset = csv_contents.get(entry.csv_name + postfix_counter_reset, None) + if counter_reset: + result['switch'].append({ + 'name': entry.display_name + " Betriebsstunden Reset", + 'address': counter_reset + }) + + # Consumption + consumption = csv_contents.get(entry.csv_name + postfix_consumption, None) + if consumption: + result['sensor'].append({ + 'name': entry.display_name + " Verbrauch", + 'address': consumption, + 'type': 'current', + }) + consumption_sum = csv_contents.get(entry.csv_name + postfix_consumption_sum, None) + if consumption_sum: + result['sensor'].append({ + 'name': entry.display_name + " Verbrauch Summe", + 'address': consumption_sum, + 'type': '4byte_unsigned', + }) + consumption_reset = csv_contents.get(entry.csv_name + postfix_consumption_reset, None) + if consumption_reset: + result['switch'].append({ + 'name': entry.display_name + " Verbrauch Reset", + 'address': consumption_reset + }) + + except KeyError as e: + raise ValueError(f"Skipping light {entry.csv_name} - Could not find CSV File entry: {e}") + return result + + +def create_lights(device_info: List[DeviceInfo], + csv_contents, + postfix_on_off_write=" Schalten", + postfix_on_off_read=" RM Schalten", + postfix_brightness_write=" Helligkeit", + postfix_brightness_read=" RM Helligkeit"): + result = [] + for entry in device_info: + try: + on_off_write_addr = csv_contents[entry.csv_name + postfix_on_off_write] + on_off_read_addr = csv_contents.get(entry.csv_name + postfix_on_off_read, None) + brightness_write_addr = csv_contents.get(entry.csv_name + postfix_brightness_write, None) + brightness_read_addr = csv_contents.get(entry.csv_name + postfix_brightness_read, None) + + entry = { + 'name': entry.display_name, + 'address': on_off_write_addr, + } + if on_off_read_addr: + entry['state_address'] = on_off_read_addr + if brightness_write_addr: + entry['brightness_address'] = brightness_write_addr + if brightness_read_addr: + entry['brightness_state_address'] = brightness_read_addr + result.append(entry) + except KeyError as e: + raise ValueError(f"Skipping light {entry.csv_name} - Could not find CSV File entry: {e}") + + return {'light': result} + + +def create_switches(device_info: List[DeviceInfo], csv_contents, + postfix_on_off_write=" Schalten", + postfix_on_off_read=" RM Schalten"): + result = [] + for entry in device_info: + try: + on_off_write_addr = csv_contents.get(entry.csv_name + postfix_on_off_write, None) + if on_off_write_addr is None: + on_off_write_addr = csv_contents[entry.csv_name] + on_off_read_addr = csv_contents.get(entry.csv_name + postfix_on_off_read, None) + + entry = { + 'name': entry.display_name, + 'address': on_off_write_addr, + } + if on_off_read_addr: + entry['state_address'] = on_off_read_addr + result.append(entry) + except KeyError as e: + raise ValueError(f"Skipping switch {entry.csv_name} - Could not find CSV File entry: {e}") + + return {'switch': result} + + +def create_shutters(device_info: List[DeviceInfo], + csv_contents, + postfix_long_addr=" Lang", postfix_short_addr=" Kurz", postfix_read_position=" RM Position", + postfix_set_position=" Position"): + result = [] + + for entry in device_info: + try: + long_addr = csv_contents[entry.csv_name + postfix_long_addr] + short_addr = csv_contents[entry.csv_name + postfix_short_addr] + read_pos_addr = csv_contents[entry.csv_name + postfix_read_position] + set_pos_addr = csv_contents[entry.csv_name + postfix_set_position] + + result.append({ + 'name': entry.display_name, + 'move_long_address': long_addr, + 'move_short_address': short_addr, + 'position_address': set_pos_addr, + 'position_state_address': read_pos_addr, + }) + except KeyError as e: + raise ValueError(f"Skipping shutter {entry.csv_name} - Could not find CSV File entry: {e}") + + return {'cover': result} diff --git a/knx_config/Nemsdorf_2019_06.knxproj b/config_creation/knx_data/Nemsdorf_2019_06.knxproj similarity index 100% rename from knx_config/Nemsdorf_2019_06.knxproj rename to config_creation/knx_data/Nemsdorf_2019_06.knxproj diff --git a/knx_config/Nemsdorf_2019_07.knxproj b/config_creation/knx_data/Nemsdorf_2019_07.knxproj similarity index 100% rename from knx_config/Nemsdorf_2019_07.knxproj rename to config_creation/knx_data/Nemsdorf_2019_07.knxproj diff --git a/knx_config/Nemsdorf_Bewegungsmelder_2019_06.knxproj b/config_creation/knx_data/Nemsdorf_Bewegungsmelder_2019_06.knxproj similarity index 100% rename from knx_config/Nemsdorf_Bewegungsmelder_2019_06.knxproj rename to config_creation/knx_data/Nemsdorf_Bewegungsmelder_2019_06.knxproj diff --git a/knx_config/Nemsdorf_Bewegungsmelder_2019_07.knxproj b/config_creation/knx_data/Nemsdorf_Bewegungsmelder_2019_07.knxproj similarity index 100% rename from knx_config/Nemsdorf_Bewegungsmelder_2019_07.knxproj rename to config_creation/knx_data/Nemsdorf_Bewegungsmelder_2019_07.knxproj diff --git a/knx_config/export_project1.csv b/config_creation/knx_data/export_project1.csv similarity index 100% rename from knx_config/export_project1.csv rename to config_creation/knx_data/export_project1.csv diff --git a/knx_config/export_project2.csv b/config_creation/knx_data/export_project2.csv similarity index 100% rename from knx_config/export_project2.csv rename to config_creation/knx_data/export_project2.csv diff --git a/config_creation/main.py b/config_creation/main.py new file mode 100644 index 0000000..534e5ce --- /dev/null +++ b/config_creation/main.py @@ -0,0 +1,86 @@ +from util import DeviceInfo +from ruamel.yaml import YAML +import knx_conf as knx + + +def create_config(): + lights = [ + # Dimmers + DeviceInfo('Wohnzimmerlampe', 'Wohnzimmer Deckenlampe'), + DeviceInfo('EsszimmerlampeWest', 'Esszimmer Deckenlampe West'), + DeviceInfo('EsszimmerlampeMitte', 'Esszimmer Deckenlampe Mitte'), + DeviceInfo("EsszimmerWandlampe", 'Esszimmer Schrankleuchte'), + DeviceInfo("Küchenlampe", "Küche Deckenlampe"), + DeviceInfo("AussenleuchteUntenSO", "Aussen Terassenlicht"), + DeviceInfo("Gang", "Gang Licht"), + DeviceInfo("Bad", "Bad Licht"), + DeviceInfo("GangWindfang", "Gang Einganglicht"), + DeviceInfo("LichtWaschküche", "Waschküche Licht"), + # Normal lights + DeviceInfo('AussenleuchteHaustüren', 'Haustür Licht'), + DeviceInfo('AussenleuchteObenNW', 'Haustür Licht NW'), + DeviceInfo('TreppenhausLicht', "Treppenhaus Licht"), + DeviceInfo('WCLicht', "WC Licht"), + DeviceInfo('LampeVorratsraum', "Vorratsraum Licht"), + ] + + shutters = [ + DeviceInfo('Wohnzimmer Fenster Rollo', 'Wohnzimmer Fenster Rollo'), + DeviceInfo('Terassentür Rollo', 'Wohnzimmer Terrassentür Rollo'), + DeviceInfo('Küchenfenster Rollo', 'Küche Fenster Rollo'), + DeviceInfo('Esszimmerfenster Rollo', 'Esszimmer Fenster Rollo'), + ] + + switches = [ + # Bells + DeviceInfo("KlingelOben", "Klingel Oben"), + DeviceInfo("Klingel Innen", "Klingel Innentür"), + DeviceInfo("Klingel Aussen", "Klingen Außentür"), + # Bewegungsmelder LEDs + DeviceInfo("BewegungsmelderMitte LED", "Bewegungsmelder Mitte LED"), + DeviceInfo("BewegungsmelderWest LED", "Bewegungsmelder West LED"), + DeviceInfo("BewegungsmelderOst LED", "Bewegungsmelder Ost LED"), + ] + scene_button_names = ['ObenLinks', 'ObenRechts', 'MitteLinks', 'MitteRechts', 'UntenLinks', 'UntenRechts'] + scene_button_names = [(i, e) for i, e in enumerate(scene_button_names)] + switches += [DeviceInfo(f"SzeneEsszimmer{n}", "Esszimmer Szene {i}") for i, n in scene_button_names] + switches += [DeviceInfo(f"SzeneWohnzimmer{n}", "Wohnzimmer Szene {i}") for i, n in scene_button_names] + switches += [DeviceInfo(f"SzeneEingang{n}", "Eingang Szene {i}") for i, n in scene_button_names[2:]] + switches += [DeviceInfo(f"SzeneTerrassentuer{n}", "Wohnzimmer Terrassentür Szene {i}") + for i, n in scene_button_names[2: 4]] + + power_plugs = [ + # Vorratsraum + DeviceInfo("VorratsraumSteckdose1", "Vorratsraum Steckdose"), + DeviceInfo("VorratsraumSteckdose2", "Vorratsraum Steckdose"), + DeviceInfo("VorratsraumSteckdose3", "Gefrierschrank"), + # Waschraum + DeviceInfo("Trockner", "Trockner"), + DeviceInfo("Waschmaschine", "Waschmaschine"), + # Küche + DeviceInfo("KücheSteckdose1", "Küche Steckdose 1"), + DeviceInfo("KücheSteckdose2", "Küche Steckdose 2"), + DeviceInfo("Spülmaschine", "Spülmaschine"), + DeviceInfo("Backofen", "Backofen"), + DeviceInfo("HerdP1", "Herd Phase 1"), + DeviceInfo("HerdP2", "Herd Phase 2"), + DeviceInfo("HerdP3", "Herd Phase 3"), + # Rest + DeviceInfo("ArbeitszimmerSteckdose", "Arbeitszimmer Steckdose"), + DeviceInfo("WohnzimmerSteckdose1", "Wohnzimmer Steckdose 1"), + DeviceInfo("WohnzimmerSteckdose2", "Wohnzimmer Steckdose 2"), + ] + + imported_csv = knx.import_ets5_csv_file('knx_data/export_project1.csv') + imported_csv.update(knx.import_ets5_csv_file('knx_data/export_project2.csv')) + + devices = {} + knx.extent(devices, knx.create_lights(lights, imported_csv)) + knx.extent(devices, knx.create_shutters(shutters, imported_csv)) + knx.extent(devices, knx.create_switches(switches, imported_csv)) + knx.extent(devices, knx.create_power_plug(power_plugs, imported_csv)) + YAML().dump(devices, open('knx_devices.yaml', 'w')) + + +if __name__ == '__main__': + create_config() diff --git a/config_creation/util.py b/config_creation/util.py new file mode 100644 index 0000000..2f5738f --- /dev/null +++ b/config_creation/util.py @@ -0,0 +1,3 @@ +from collections import namedtuple + +DeviceInfo = namedtuple("DeviceInfo", ['csv_name', 'display_name'])