diff --git a/fpga_device_manager/Config.py b/fpga_device_manager/Config.py index 50b9ee380c6e0ba91abf03d1219736f1084284d3..71f9167f3c060f563881651cfd0126edab83c9fc 100644 --- a/fpga_device_manager/Config.py +++ b/fpga_device_manager/Config.py @@ -13,6 +13,7 @@ from fpga_device_manager.Devices import Output from fpga_device_manager.Inputs import Input from fpga_device_manager.device_manager import DeviceManager from fpga_device_manager.exceptions import DeviceInvalidError, InvalidConfigError +from fpga_device_manager.hass import Action, Automation _output_mgr = DeviceManager(device_class=Output) _input_mgr = DeviceManager(device_class=Input) @@ -188,3 +189,38 @@ def export(out_path: str) -> None: print(f"Writing to {filepath}...") with open(filepath, "w") as file: file.write(text) + + +def export_yaml(out_file_path: str) -> None: + """ + Exports the current device configuration to a Home Assistant YAML file. + :param out_file_path: Path to write the YAML file to + """ + + x = { + 'alias': 'Rule 1 Light on in the evening', + 'trigger': [ + {'platform': 'sun', 'event': 'sunset', 'offset': '-01:00:00'}, + {'platform': 'state', 'entity_id': 'all', 'to': 'home'} + ], + 'condition': [ + {'condition': 'state', 'entity_id': 'all', 'state': 'home'}, + {'condition': 'time', 'after': '16:00:00', 'before': '23:00:00'} + ], + 'action': {'service': 'homeassistant.turn_on', 'entity_id': 'group.living_room'} + } + output_devices = list(_output_mgr.all_devices()) + input_devices = list(_input_mgr.all_devices()) + + for device in input_devices: + input_ = device.dev_id + target = device.associated_device.dev_id + + if device.type is None: + pass + + # for filename, text in exports.items(): + # filepath = os.file_path.join(out_file_path, filename) + # print(f"Writing to {filepath}...") + # with open(filepath, "w") as file: + # file.write(text) diff --git a/fpga_device_manager/hass.py b/fpga_device_manager/hass.py new file mode 100644 index 0000000000000000000000000000000000000000..257f6ff160f7704d5061428fad9c2a579a4a7fbc --- /dev/null +++ b/fpga_device_manager/hass.py @@ -0,0 +1,32 @@ +from typing import Iterable, Dict +from dataclasses import dataclass + + +@dataclass +class Action: + service: str + entity_id: str + + +@dataclass +class Automation: + alias: str + triggers: Iterable[Dict] + actions: Iterable[Action] + conditions: Iterable[Dict] = None + description: str = "" + + @property + def dict(self): + dict_ = {"alias": self.alias, + "triggers": self.triggers, + "actions": [action.__dict__ for action in self.actions] + } + + if self.conditions: + dict_["conditions"] = self.conditions + + if self.description: + dict_["description"] = self.description + + return dict_ diff --git a/fpga_device_manager/res/data/device_types.json b/fpga_device_manager/res/data/device_types.json index 3083245b247c53a923260b977bb8b9b21b5d5431..e2c09c07ab661e4ac3bf4aada4228b262083317d 100755 --- a/fpga_device_manager/res/data/device_types.json +++ b/fpga_device_manager/res/data/device_types.json @@ -7,7 +7,8 @@ "pins": { "signal": { "requires_pwm": false, - "active_low": false + "active_low": false, + "action": } } }, diff --git a/fpga_device_manager/res/ui/main.ui b/fpga_device_manager/res/ui/main.ui index 6c153099fcadb45a897b25ba0cfcace07fcf3eec..4714cace1ccd9b088ff98849b216a46b41286850 100755 --- a/fpga_device_manager/res/ui/main.ui +++ b/fpga_device_manager/res/ui/main.ui @@ -340,6 +340,13 @@ </property> </widget> </item> + <item row="1" column="1"> + <widget class="QPushButton" name="btn_yaml"> + <property name="text"> + <string>Export YAML…</string> + </property> + </widget> + </item> </layout> </widget> </item> diff --git a/fpga_device_manager/windows/main_window.py b/fpga_device_manager/windows/main_window.py index 7854b8259e17b067262786f2f0760c45c9d81a70..c923ac7ac9ecaeb5dfe06e266b1ef44c41f863c8 100755 --- a/fpga_device_manager/windows/main_window.py +++ b/fpga_device_manager/windows/main_window.py @@ -90,6 +90,7 @@ class MainWindow(BaseWindow): """Updates the window's buttons.""" self.btn_save.setEnabled(Config.outputs().has_devices() or Config.inputs().has_devices()) self.btn_export.setEnabled(Config.outputs().has_devices() and Config.inputs().has_devices()) + self.btn_yaml.setEnabled(Config.outputs().has_devices() and Config.inputs().has_devices()) self.btn_clear.setEnabled(Config.outputs().has_devices() or Config.inputs().has_devices()) self.btn_add_device.setEnabled(not Config.outputs().has_max_devices()) self.btn_add_input.setEnabled(not Config.inputs().has_max_devices()) @@ -152,6 +153,21 @@ class MainWindow(BaseWindow): Popup.alert(title=f"Failed to export", message=str(e)) + def export_yaml(self, file_path: str) -> None: + """ + Exports the current device configuration to a YAML Home Assistant configuration. + :param file_path: Path to output the YAML file to + """ + + try: + Config.export_yaml(file_path) + Popup.info(title="Successfully generated", + message=f"The YAML configuration file has been successfully written to {file_path}.") + + except InvalidConfigError as e: + Popup.alert(title=f"YAML generation failed", + message=str(e)) + def reset(self) -> None: """Resets the configuration and window.""" Config.clear() @@ -310,3 +326,26 @@ class MainWindow(BaseWindow): message="\n".join(e.errors), additional_text="Please check your configuration and try again.") return + + @Slot() + def on_btn_yaml_clicked(self): + """Handler for clicking the YAML button. This performs some sanity checks on the current configuration, + and if passed opens a directory selection dialog for exporting the .yaml file for Home Assistant. + """ + # Verify device settings + try: + Config.check() + file_path = QFileDialog.getSaveFileName(parent=self, + caption="Generate YAML", + dir="./generated/smarthome.yaml", + filter="YAML files (*.yaml)" + ) + + if file_path != '': + self.export_yaml(file_path) + + except InvalidConfigError as e: + Popup.alert(title="YAML generation failed", + message="\n".join(e.errors), + additional_text="Please check your configuration and try again.") + return