diff --git a/.gitignore b/.gitignore index c611444c4e..66eca40aed 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ data/log/* data/charge_log/* data/daily_log/* data/monthly_log/* +data/backup/*.tar.gz ramdisk/* web/lastcommit !.gitignore diff --git a/.htaccess b/.htaccess index 69e4da0200..6f220f45c4 100644 --- a/.htaccess +++ b/.htaccess @@ -1,9 +1,3 @@ - RedirectMatch 404 \.conf$ RedirectMatch 404 \.ini$ RedirectMatch 404 \.py$ -# -#Order allow,deny -#Deny from all - -# diff --git a/web/backup/.donotdelete b/data/backup/.donotdelete similarity index 100% rename from web/backup/.donotdelete rename to data/backup/.donotdelete diff --git a/data/config/000-default.conf b/data/config/000-default.conf index 6febdda5f2..00def235e1 100644 --- a/data/config/000-default.conf +++ b/data/config/000-default.conf @@ -1,4 +1,4 @@ -# openwb-version:4 +# openwb-version:5 # The ServerName directive sets the request scheme, hostname and port that # the server uses to identify itself. This is used when creating @@ -22,6 +22,13 @@ AllowOverride All Require all granted + Options -Indexes + + + Options +Indexes + + + Options +Indexes # For most configuration files from conf-available/, which are # enabled or disabled at a global level, it is possible to diff --git a/data/config/apache-openwb-ssl.conf b/data/config/apache-openwb-ssl.conf index 322d5cccb0..9603094810 100644 --- a/data/config/apache-openwb-ssl.conf +++ b/data/config/apache-openwb-ssl.conf @@ -1,4 +1,4 @@ -# openwb-version:5 +# openwb-version:6 ServerAdmin webmaster@localhost @@ -103,6 +103,13 @@ AllowOverride All Require all granted + Options -Indexes + + + Options +Indexes + + + Options +Indexes # SSL Protocol Adjustments: # The safe and default but still SSL/TLS standard compliant shutdown diff --git a/data/config/openwb_local.conf b/data/config/openwb_local.conf index a412672cfc..ee8d1b1732 100644 --- a/data/config/openwb_local.conf +++ b/data/config/openwb_local.conf @@ -1,4 +1,4 @@ -# openwb-version:1 +# openwb-version:2 listener 1886 localhost allow_anonymous true @@ -53,3 +53,4 @@ topic openWB/log/# out topic openWB/system/# out topic openWB/command/+/error both +topic openWB/command/+/messages/+ both diff --git a/packages/helpermodules/command.py b/packages/helpermodules/command.py index ee614707a0..3743e0bd70 100644 --- a/packages/helpermodules/command.py +++ b/packages/helpermodules/command.py @@ -26,7 +26,7 @@ class Command: ("mqtt_bridge", "system/mqtt/bridge", -1), ("charge_template", "vehicle/template/charge_template", 0), ("charge_template_scheduled_plan", - "vehicle/template/charge_template/+/chargemode/scheduled_charging/plans", + "vehicle/template/charge_template/+/chargemode/scheduled_charging/plans", -1), ("charge_template_time_charging_plan", "vehicle/template/charge_template/+/time_charging/plans", -1), ("chargepoint_template", "chargepoint/template", 0), @@ -66,7 +66,7 @@ def __get_max_id_by_json_object(self, id_topic: str, topic: str, default: int) - try: hierarchy = ProcessBrokerBranch(topic).get_payload() max_id = counter.get_max_id_in_hierarchy(hierarchy, default) - Pub().pub("openWB/set/command/max_id/"+id_topic, max_id) + Pub().pub(f'openWB/set/command/max_id/{id_topic}', max_id) except Exception: log.exception("Fehler im Command-Modul") @@ -98,15 +98,14 @@ def on_message(self, client, userdata, msg): if "todo" in msg.topic: payload = json.loads(str(msg.payload.decode("utf-8"))) connection_id = msg.topic.split("/")[2] - log.debug("Befehl: "+str(payload)+", Connection-ID: "+str(connection_id)) + log.debug(f'Befehl: {payload}, Connection-ID: {connection_id}') # Methoden-Name = Befehl try: func = getattr(self, payload["command"]) with ErrorHandlingContext(payload, connection_id): func(connection_id, payload) except Exception: - log.error("Zu dem Befehl wurde keine Methode gefunden.") - pub_error(payload, connection_id, "Zu dem Befehl wurde keine Methode gefunden.") + pub_error_user(payload, connection_id, "Zu dem Befehl wurde keine Methode gefunden.") Pub().pub(msg.topic, "") elif "max_id" in msg.topic: self.__process_max_id_topic(msg) @@ -121,8 +120,8 @@ def __process_max_id_topic(self, msg, no_log: bool = False) -> None: var = result.group(1) # Der Variablen-Name für die maximale ID setzt sich aus "max_id_" und dem Topic-Namen nach dem letzten / # zusammen. - setattr(self, "max_id_"+var, payload) - log.debug("Max ID "+var+" "+str(payload)) + setattr(self, f'max_id_{var}', payload) + log.debug(f'Max ID {var} {payload}') except Exception: log.exception("Fehler im Command-Modul") @@ -130,32 +129,32 @@ def addDevice(self, connection_id: str, payload: dict) -> None: """ sendet das Topic, zu dem ein neues Device erstellt werden soll. """ new_id = self.max_id_device + 1 - log.info( - "Neues Device vom Typ "+str(payload["data"]["type"])+" mit ID "+str(new_id)+" hinzugefügt.") dev = importlib.import_module("."+payload["data"]["type"]+".device", "modules") device_default = dataclass_utils.asdict(dev.device_descriptor.configuration_factory()) device_default["id"] = new_id - Pub().pub("openWB/set/system/device/" + - str(new_id)+"/config", device_default) + Pub().pub(f'openWB/set/system/device/{new_id}/config', device_default) self.max_id_device = self.max_id_device + 1 Pub().pub("openWB/set/command/max_id/device", self.max_id_device) + pub_success_user( + payload, connection_id, + f'Neues Device vom Typ \'{payload["data"]["type"]}\' mit ID \'{new_id}\' hinzugefügt.') def removeDevice(self, connection_id: str, payload: dict) -> None: """ löscht ein Device. """ if self.max_id_device >= payload["data"]["id"]: - log.info("Device mit ID " + - str(payload["data"]["id"])+" gelöscht.") ProcessBrokerBranch(f'system/device/{payload["data"]["id"]}/').remove_topics() + pub_success_user(payload, connection_id, f'Gerät mit ID \'{payload["data"]["id"]}\' gelöscht.') else: - pub_error(payload, connection_id, "Die ID ist größer als die maximal vergebene ID.") + pub_error_user( + payload, connection_id, + f'Die ID \'{payload["data"]["id"]}\' ist größer als die maximal vergebene ID \'{self.max_id_device}\'.') def addChargepoint(self, connection_id: str, payload: dict) -> None: """ sendet das Topic, zu dem ein neuer Chargepoint erstellt werden soll. """ new_id = self.max_id_hierarchy + 1 - log.info( - "Neuer Ladepunkt mit ID "+str(new_id)+" wird hinzugefügt.") + log.info(f'Neuer Ladepunkt mit ID \'{new_id}\' wird hinzugefügt.') chargepoint_default = chargepoint.get_chargepoint_default() # chargepoint_default["id"] = new_id module = importlib.import_module("." + payload["data"]["type"] + ".chargepoint_module", "modules") @@ -166,167 +165,190 @@ def addChargepoint(self, connection_id: str, payload: dict) -> None: evu_counter = data.data.counter_data["all"].get_id_evu_counter() data.data.counter_data["all"].hierarchy_add_item_below( new_id, ComponentType.CHARGEPOINT, evu_counter) - Pub().pub("openWB/set/chargepoint/"+str(new_id)+"/config", chargepoint_default) - Pub().pub("openWB/set/chargepoint/"+str(new_id)+"/set/manual_lock", False) + Pub().pub(f'openWB/set/chargepoint/{new_id}/config', chargepoint_default) + Pub().pub(f'openWB/set/chargepoint/{new_id}/set/manual_lock', False) self.max_id_hierarchy = self.max_id_hierarchy + 1 Pub().pub("openWB/set/command/max_id/hierarchy", self.max_id_hierarchy) if self.max_id_chargepoint_template == -1: self.addChargepointTemplate("addChargepoint", {}) if self.max_id_vehicle == -1: self.addVehicle("addChargepoint", {}) + pub_success_user(payload, connection_id, f'Neuer Ladepunkt mit ID \'{new_id}\' wurde erstellt.') except (TypeError, IndexError): - pub_error(payload, connection_id, "Bitte erst einen EVU-Zähler konfigurieren!") + pub_error_user(payload, connection_id, "Bitte zuerst einen EVU-Zähler konfigurieren!") def removeChargepoint(self, connection_id: str, payload: dict) -> None: """ löscht ein Chargepoint. """ if self.max_id_hierarchy >= payload["data"]["id"]: data.data.counter_data["all"].hierarchy_remove_item(payload["data"]["id"]) - log.info("Ladepunkt mit ID " + str(payload["data"]["id"])+" gelöscht.") ProcessBrokerBranch(f'chargepoint/{payload["data"]["id"]}/').remove_topics() + pub_success_user(payload, connection_id, f'Ladepunkt mit ID \'{payload["data"]["id"]}\' gelöscht.') else: - pub_error(payload, connection_id, "Die ID ist größer als die maximal vergebene ID.") + pub_error_user( + payload, connection_id, + f'Die ID \'{payload["data"]["id"]}\' ist größer als die maximal vergebene ' + f'ID \'{self.max_id_hierarchy}\'.') def addChargepointTemplate(self, connection_id: str, payload: dict) -> None: """ sendet das Topic, zu dem eine neue Ladepunkt-Vorlage erstellt werden soll. """ new_id = self.max_id_chargepoint_template + 1 - log.info("Neue Ladepunkt-Vorlage mit ID "+str(new_id)+" hinzugefügt.") default = chargepoint.get_chargepoint_template_default() default["id"] = new_id - Pub().pub("openWB/set/chargepoint/template/"+str(new_id), default) + Pub().pub(f'openWB/set/chargepoint/template/{new_id}', default) self.max_id_chargepoint_template = self.max_id_chargepoint_template + 1 Pub().pub("openWB/set/command/max_id/chargepoint_template", self.max_id_chargepoint_template) + pub_success_user( + payload, connection_id, + f'Neue Ladepunkt-Vorlage mit ID \'{new_id}\' hinzugefügt.') def removeChargepointTemplate(self, connection_id: str, payload: dict) -> None: """ löscht eine Ladepunkt-Vorlage. """ if self.max_id_chargepoint_template >= payload["data"]["id"]: if payload["data"]["id"] > 0: - log.info("Ladepunkt-Vorlage mit ID " + - str(payload["data"]["id"])+" gelöscht.") ProcessBrokerBranch(f'chargepoint/template/{payload["data"]["id"]}/').remove_topics() + pub_success_user(payload, connection_id, + f'Ladepunkt-Vorlage mit ID \'{payload["data"]["id"]}\' gelöscht.') else: - pub_error(payload, connection_id, "Ladepunkt-Vorlage mit ID 0 darf nicht gelöscht werden.") + pub_error_user(payload, connection_id, "Ladepunkt-Vorlage mit ID 0 darf nicht gelöscht werden.") else: - pub_error(payload, connection_id, "Die ID ist größer als die maximal vergebene ID.") + pub_error_user( + payload, connection_id, + f'Die ID \'{payload["data"]["id"]}\' ist größer als ' + f'die maximal vergebene ID \'{self.max_id_chargepoint_template}\'.') def addAutolockPlan(self, connection_id: str, payload: dict) -> None: """ sendet das Topic, zu dem ein neuer Zielladen-Plan erstellt werden soll. """ new_id = self.max_id_autolock_plan + 1 - log.info("Neuer Autolock-Plan mit ID " + str(new_id) + " zu Template " + - str(payload["data"]["template"]) + " hinzugefügt.") default = chargepoint.get_autolock_plan_default() - Pub().pub("openWB/set/chargepoint/template/"+str(payload["data"] - ["template"])+"/autolock/"+str(new_id), default) + Pub().pub(f'openWB/set/chargepoint/template/{payload["data"]["template"]}/autolock/{new_id}', + default) self.max_id_autolock_plan = new_id Pub().pub("openWB/set/command/max_id/autolock_plan", new_id) + pub_success_user( + payload, connection_id, + f'Neuer Autolock-Plan mit ID \'{new_id}\' zu Template ' + f'\'{payload["data"]["template"]}\' hinzugefügt.') def removeAutolockPlan(self, connection_id: str, payload: dict) -> None: """ löscht einen Zielladen-Plan. """ if self.max_id_autolock_plan >= payload["data"]["plan"]: - log.info( - "Autolock-Plan mit ID " + str(payload["data"]["plan"]) + " zu Template " + - str(payload["data"]["template"]) + " gelöscht.") Pub().pub( - "openWB/chargepoint/template/" + str(payload["data"]["template"]) + "/autolock/" + - str(payload["data"]["plan"]), + f'openWB/chargepoint/template/{payload["data"]["template"]}' + f'/autolock/{payload["data"]["plan"]}', "") + pub_success_user( + payload, connection_id, + f'Autolock-Plan mit ID \'{payload["data"]["plan"]}\' zu Template ' + f'\'{payload["data"]["template"]}\' gelöscht.') else: - pub_error(payload, connection_id, "Die ID ist größer als die maximal vergebene ID.") + pub_error_user( + payload, connection_id, + f'Die ID \'{payload["data"]["plan"]}\' ist größer als die ' + f'maximal vergebene ID \'{self.max_id_autolock_plan}\'.') def addChargeTemplate(self, connection_id: str, payload: dict) -> None: """ sendet das Topic, zu dem eine neue Lade-Vorlage erstellt werden soll. """ new_id = self.max_id_charge_template + 1 - log.info("Neues Lade-Template mit ID "+str(new_id)+" hinzugefügt.") charge_template_default = ev.get_charge_template_default() Pub().pub("openWB/set/vehicle/template/charge_template/" + str(new_id), charge_template_default) self.max_id_charge_template = new_id Pub().pub("openWB/set/command/max_id/charge_template", new_id) + pub_success_user(payload, connection_id, + f'Neues Lade-Template mit ID \'{new_id}\' hinzugefügt.') def removeChargeTemplate(self, connection_id: str, payload: dict) -> None: """ löscht eine Lade-Vorlage. """ if self.max_id_charge_template >= payload["data"]["id"]: if payload["data"]["id"] > 0: - log.info("Lade-Template mit ID " + - str(payload["data"]["id"])+" gelöscht.") - Pub().pub("openWB/vehicle/template/charge_template/" + - str(payload["data"]["id"]), "") + Pub().pub(f'openWB/vehicle/template/charge_template/{payload["data"]["id"]}', "") + pub_success_user( + payload, connection_id, + f'Lade-Template mit ID \'{payload["data"]["id"]}\' gelöscht.') else: - pub_error(payload, connection_id, "Ladevorlage mit ID 0 darf nicht gelöscht werden.") + pub_error_user(payload, connection_id, "Ladevorlage mit ID 0 darf nicht gelöscht werden.") else: - pub_error(payload, connection_id, "Die ID ist größer als die maximal vergebene ID.") + pub_error_user(payload, connection_id, "Die ID ist größer als die maximal vergebene ID.") def addChargeTemplateSchedulePlan(self, connection_id: str, payload: dict) -> None: """ sendet das Topic, zu dem ein neuer Zielladen-Plan erstellt werden soll. """ new_id = self.max_id_charge_template_scheduled_plan + 1 - log.info("Neues Zielladen-Template mit ID " + str(new_id) + " zu Template " + - str(payload["data"]["template"]) + " hinzugefügt.") charge_template_default = dataclass_utils.asdict(ev.ScheduledChargingPlan()) Pub().pub( - "openWB/set/vehicle/template/charge_template/" + str(payload["data"]["template"]) + - "/chargemode/scheduled_charging/plans/" + str(new_id), + f'openWB/set/vehicle/template/charge_template/{payload["data"]["template"]}' + f'/chargemode/scheduled_charging/plans/{new_id}', charge_template_default) self.max_id_charge_template_scheduled_plan = new_id Pub().pub( "openWB/set/command/max_id/charge_template_scheduled_plan", new_id) + pub_success_user( + payload, connection_id, + f'Neues Zielladen-Template mit ID \'{new_id}\' zu Template ' + f'\'{payload["data"]["template"]}\' hinzugefügt.') def removeChargeTemplateSchedulePlan(self, connection_id: str, payload: dict) -> None: """ löscht einen Zielladen-Plan. """ if self.max_id_charge_template_scheduled_plan >= payload["data"]["plan"]: - log.info( - "Zielladen-Template mit ID " + str(payload["data"]["plan"]) + " zu Template " + - str(payload["data"]["template"]) + " gelöscht.") Pub().pub( - "openWB/vehicle/template/charge_template/" + str(payload["data"]["template"]) + - "/chargemode/scheduled_charging/plans/" + str(payload["data"]["plan"]), + f'openWB/vehicle/template/charge_template/{payload["data"]["template"]}' + f'/chargemode/scheduled_charging/plans/{payload["data"]["plan"]}', "") + pub_success_user( + payload, connection_id, + f'Zielladen-Template mit ID \'{payload["data"]["plan"]}\' zu Template ' + f'{payload["data"]["template"]}\' gelöscht.') else: - pub_error(payload, connection_id, "Die ID ist größer als die maximal vergebene ID.") + pub_error_user( + payload, connection_id, + f'Die ID \'{payload["data"]["plan"]}\' ist größer als die maximal vergebene ' + f'ID \'{self.max_id_charge_template_scheduled_plan}\'.') def addChargeTemplateTimeChargingPlan(self, connection_id: str, payload: dict) -> None: """ sendet das Topic, zu dem ein neuer Zeitladen-Plan erstellt werden soll. """ new_id = self.max_id_charge_template_time_charging_plan + 1 - log.info("Neues Zeitladen-Template mit ID " + str(new_id) + " zu Template " + - str(payload["data"]["template"]) + " hinzugefügt.") time_charging_plan_default = dataclass_utils.asdict(ev.TimeChargingPlan()) Pub().pub( - "openWB/set/vehicle/template/charge_template/" + str(payload["data"]["template"]) + - "/time_charging/plans/" + str(new_id), + f'openWB/set/vehicle/template/charge_template/{payload["data"]["template"]}' + f'/time_charging/plans/{new_id}', time_charging_plan_default) self.max_id_charge_template_time_charging_plan = new_id Pub().pub( "openWB/set/command/max_id/charge_template_time_charging_plan", new_id) + pub_success_user( + payload, connection_id, + f'Neues Zeitladen-Template mit ID \'{new_id}\' zu Template ' + f'{payload["data"]["template"]}\' hinzugefügt.') def removeChargeTemplateTimeChargingPlan(self, connection_id: str, payload: dict) -> None: """ löscht einen Zeitladen-Plan. """ if self.max_id_charge_template_time_charging_plan >= payload["data"]["plan"]: - log.info( - "Zeitladen-Template mit ID " + str(payload["data"]["plan"]) + " zu Template " + - str(payload["data"]["template"]) + " gelöscht.") Pub().pub( - "openWB/vehicle/template/charge_template/" + str(payload["data"]["template"]) + - "/time_charging/plans/" + str(payload["data"]["plan"]), + f'openWB/vehicle/template/charge_template/{payload["data"]["template"]}' + f'/time_charging/plans/{payload["data"]["plan"]}', "") + pub_success_user( + payload, connection_id, + f'Zeitladen-Template mit ID \'{payload["data"]["plan"]}\' zu Template ' + f'\'{payload["data"]["template"]}\' gelöscht.') else: - pub_error(payload, connection_id, "Die ID ist größer als die maximal vergebene ID.") + pub_error_user(payload, connection_id, "Die ID ist größer als die maximal vergebene ID.") def addComponent(self, connection_id: str, payload: dict) -> None: """ sendet das Topic, zu dem eine neue Komponente erstellt werden soll. """ new_id = self.max_id_hierarchy + 1 - log.info( - "Neue Komponente vom Typ"+str(payload["data"]["type"])+" mit ID "+str(new_id)+" hinzugefügt.") component = importlib.import_module( "."+payload["data"]["deviceType"]+"."+payload["data"]["type"], "modules") component_default = dataclass_utils.asdict(component.component_descriptor.configuration_factory()) @@ -339,66 +361,75 @@ def addComponent(self, connection_id: str, payload: dict) -> None: if general_type == ComponentType.COUNTER: # es gibt noch keinen EVU-Zähler Pub().pub("openWB/set/counter/get/hierarchy", - [{"id": new_id, "type": ComponentType.COUNTER.value, "children": []}] + + [{ + "id": new_id, + "type": ComponentType.COUNTER.value, + "children": [] + }] + data.data.counter_data["all"].data["get"]["hierarchy"]) else: - pub_error(payload, connection_id, "Bitte erst einen EVU-Zähler konfigurieren!") + pub_error_user(payload, connection_id, "Bitte erst einen EVU-Zähler konfigurieren!") return # Bei Zählern müssen noch Standardwerte veröffentlicht werden. if general_type == ComponentType.COUNTER: default_config = counter.get_counter_default_config() for item in default_config: - Pub().pub("openWB/set/counter/"+str(new_id)+"/config/"+item, default_config[item]) + Pub().pub(f'openWB/set/counter/{new_id}/config/{item}', default_config[item]) elif general_type == ComponentType.INVERTER: default_config = pv.get_inverter_default_config() for item in default_config: - Pub().pub("openWB/set/pv/"+str(new_id)+"/config/"+item, default_config[item]) - Pub().pub("openWB/set/system/device/"+str(payload["data"]["deviceId"] - )+"/component/"+str(new_id)+"/config", component_default) + Pub().pub(f'openWB/set/pv/{new_id}/config/{item}', default_config[item]) + Pub().pub(f'openWB/set/system/device/{payload["data"]["deviceId"]}/component/{new_id}/config', + component_default) self.max_id_hierarchy = self.max_id_hierarchy + 1 Pub().pub("openWB/set/command/max_id/hierarchy", self.max_id_hierarchy) + pub_success_user( + payload, connection_id, + f'Neue Komponente vom Typ \'{payload["data"]["type"]}\' mit ID \'{new_id}\' hinzugefügt.') def removeComponent(self, connection_id: str, payload: dict) -> None: """ löscht eine Komponente. """ if self.max_id_hierarchy >= payload["data"]["id"]: - log.info("Komponente mit ID "+str(payload["data"]["id"])+" gelöscht.") branch = f'system/device/{payload["data"]["deviceId"]}/component/{payload["data"]["id"]}/' ProcessBrokerBranch(branch).remove_topics() + pub_success_user( + payload, connection_id, + f'Komponente mit ID \'{payload["data"]["id"]}\' gelöscht.') else: - pub_error(payload, connection_id, "Die ID ist größer als die maximal vergebene ID.") + pub_error_user(payload, connection_id, "Die ID ist größer als die maximal vergebene ID.") def addEvTemplate(self, connection_id: str, payload: dict) -> None: """ sendet das Topic, zu dem ein neues EV-Template erstellt werden soll. """ new_id = self.max_id_ev_template + 1 - log.info("Neues EV-Template mit ID "+str(new_id)+" hinzugefügt.") ev_template_default = dataclass_utils.asdict(ev.EvTemplateData()) - Pub().pub("openWB/set/vehicle/template/ev_template/" + - str(new_id), ev_template_default) + Pub().pub(f'openWB/set/vehicle/template/ev_template/{new_id}', ev_template_default) self.max_id_ev_template = new_id Pub().pub("openWB/set/command/max_id/ev_template", new_id) + pub_success_user( + payload, connection_id, + f'Neues EV-Template mit ID \'{new_id}\' hinzugefügt.') def removeEvTemplate(self, connection_id: str, payload: dict) -> None: """ löscht ein EV-Template. """ if self.max_id_ev_template >= payload["data"]["id"]: if payload["data"]["id"] > 0: - log.info("EV-Template mit ID " + - str(payload["data"]["id"])+" gelöscht.") - Pub().pub("openWB/vehicle/template/ev_template/" + - str(payload["data"]["id"]), "") + Pub().pub(f'openWB/vehicle/template/ev_template/{payload["data"]["id"]}', "") + pub_success_user( + payload, connection_id, + f'EV-Template mit ID \'{payload["data"]["id"]}\' gelöscht.') else: - pub_error(payload, connection_id, "EV-Vorlage mit ID 0 darf nicht gelöscht werden.") + pub_error_user(payload, connection_id, "EV-Vorlage mit ID 0 darf nicht gelöscht werden.") else: - pub_error(payload, connection_id, "Die ID ist größer als die maximal vergebene ID.") + pub_error_user(payload, connection_id, "Die ID ist größer als die maximal vergebene ID.") def addVehicle(self, connection_id: str, payload: dict) -> None: """ sendet das Topic, zu dem ein neues Vehicle erstellt werden soll. """ new_id = self.max_id_vehicle + 1 - log.info("Neues EV mit ID "+str(new_id)+" hinzugefügt.") vehicle_default = ev.get_vehicle_default() for default in vehicle_default: Pub().pub(f"openWB/set/vehicle/{new_id}/{default}", vehicle_default[default]) @@ -410,46 +441,43 @@ def addVehicle(self, connection_id: str, payload: dict) -> None: self.addChargeTemplate("addVehicle", {}) if self.max_id_ev_template == -1: self.addEvTemplate("addVehicle", {}) + pub_success_user(payload, connection_id, f'Neues EV mit ID \'{new_id}\' hinzugefügt.') def removeVehicle(self, connection_id: str, payload: dict) -> None: """ löscht ein Vehicle. """ if self.max_id_vehicle >= payload["data"]["id"]: if payload["data"]["id"] > 0: - log.info( - "EV mit ID "+str(payload["data"]["id"])+" gelöscht.") - Pub().pub("openWB/vehicle/"+str(payload["data"]["id"]), "") + Pub().pub(f'openWB/vehicle/{payload["data"]["id"]}', "") ProcessBrokerBranch(f'vehicle/{payload["data"]["id"]}/').remove_topics() + pub_success_user( + payload, connection_id, + f'EV mit ID \'{payload["data"]["id"]}\' gelöscht.') else: - pub_error(payload, connection_id, "Vehicle mit ID 0 darf nicht gelöscht werden.") + pub_error_user(payload, connection_id, "Vehicle mit ID 0 darf nicht gelöscht werden.") else: - pub_error(payload, connection_id, "Die ID ist größer als die maximal vergebene ID.") + pub_error_user(payload, connection_id, "Die ID ist größer als die maximal vergebene ID.") def sendDebug(self, connection_id: str, payload: dict) -> None: + pub_success_user(payload, connection_id, "Systembericht wird erstellt...") parent_file = Path(__file__).resolve().parents[2] previous_log_level = data.data.system_data["system"].data["debug_level"] Pub().pub("openWB/set/system/debug_level", 10) subprocess.run([str(parent_file / "runs" / "send_debug.sh"), json.dumps(payload["data"])]) Pub().pub("openWB/set/system/debug_level", previous_log_level) + pub_success_user(payload, connection_id, "Systembericht wurde versandt.") def getChargeLog(self, connection_id: str, payload: dict) -> None: - Pub().pub( - "openWB/set/log/"+connection_id+"/data", - chargelog.get_log_data(payload["data"]) - ) + Pub().pub(f'openWB/set/log/{connection_id}/data', chargelog.get_log_data(payload["data"])) def getDailyLog(self, connection_id: str, payload: dict) -> None: - Pub().pub( - "openWB/set/log/daily/"+payload["data"]["day"], - measurement_log.get_daily_log(payload["data"]["day"]) - ) + Pub().pub(f'openWB/set/log/daily/{payload["data"]["day"]}', + measurement_log.get_daily_log(payload["data"]["day"])) def getMonthlyLog(self, connection_id: str, payload: dict) -> None: - Pub().pub( - "openWB/set/log/monthly/"+payload["data"]["month"], - measurement_log.get_monthly_log(payload["data"]["month"]) - ) + Pub().pub(f'openWB/set/log/monthly/{payload["data"]["month"]}', + measurement_log.get_monthly_log(payload["data"]["month"])) def initCloud(self, connection_id: str, payload: dict) -> None: parent_file = Path(__file__).resolve().parents[2] @@ -463,9 +491,10 @@ def initCloud(self, connection_id: str, payload: dict) -> None: "data": result_dict } self.connectCloud(connection_id, connect_payload) + pub_success_user(payload, connection_id, "Verbindung zur Cloud wurde eingerichtet.") except subprocess.CalledProcessError as error: # exit status = 1 is failure, std_out contains error message - pub_error(payload, connection_id, error.output.decode("utf-8")) + pub_error_user(payload, connection_id, error.output.decode("utf-8")) def connectCloud(self, connection_id: str, payload: dict) -> None: cloud_config = bridge.get_cloud_config() @@ -473,29 +502,32 @@ def connectCloud(self, connection_id: str, payload: dict) -> None: cloud_config["remote"]["password"] = payload["data"]["password"] cloud_config["remote"]["prefix"] = payload["data"]["username"] + "/" self.addMqttBridge(connection_id, payload, cloud_config) + pub_success_user(payload, connection_id, "Verbindung zur Cloud wurde angelegt.") def addMqttBridge(self, connection_id: str, payload: dict, bridge_default: dict = bridge.get_default_config()) -> None: new_id = self.max_id_mqtt_bridge + 1 - log.info("Neue Bridge mit ID "+str(new_id)+" hinzugefügt.") - Pub().pub("openWB/set/system/mqtt/bridge/"+str(new_id), bridge_default) + Pub().pub(f'openWB/set/system/mqtt/bridge/{new_id}', bridge_default) self.max_id_mqtt_bridge = self.max_id_mqtt_bridge + 1 Pub().pub("openWB/set/command/max_id/mqtt_bridge", self.max_id_mqtt_bridge) + pub_success_user(payload, connection_id, f'Neue Bridge mit ID \'{new_id}\' hinzugefügt.') def removeMqttBridge(self, connection_id: str, payload: dict) -> None: if self.max_id_mqtt_bridge >= payload["data"]["bridge"]: - log.info("Bridge mit ID "+str(payload["data"]["bridge"])+" gelöscht.") - Pub().pub("openWB/system/mqtt/bridge/"+str(payload["data"]["bridge"]), "") + Pub().pub(f'openWB/system/mqtt/bridge/{payload["data"]["bridge"]}', "") + pub_success_user(payload, connection_id, f'Bridge mit ID \'{payload["data"]["bridge"]}\' gelöscht.') else: - pub_error(payload, connection_id, "Die ID ist größer als die maximal vergebene ID.") + pub_error_user(payload, connection_id, + f'Die ID \'{payload["data"]["bridge"]}\' ist größer als die maximal vergebene ' + f'ID \'{self.max_id_mqtt_bridge}\'.') def systemReboot(self, connection_id: str, payload: dict) -> None: - log.info("Reboot requested") + pub_success_user(payload, connection_id, "Neustart wird ausgeführt.") parent_file = Path(__file__).resolve().parents[2] subprocess.run([str(parent_file / "runs" / "reboot.sh")]) def systemShutdown(self, connection_id: str, payload: dict) -> None: - log.info("Shutdown requested") + pub_success_user(payload, connection_id, "OpenWB wird heruntergefahren.") parent_file = Path(__file__).resolve().parents[2] subprocess.run([str(parent_file / "runs" / "shutdown.sh")]) @@ -503,18 +535,43 @@ def systemUpdate(self, connection_id: str, payload: dict) -> None: log.info("Update requested") parent_file = Path(__file__).resolve().parents[2] if "branch" in payload["data"] and "tag" in payload["data"]: - log.warn("Update to branch '%s' tag '%s' requested", payload["data"]["branch"], payload["data"]["tag"]) + pub_success_user( + f'Wechsel auf Zweig \'{payload["data"]["branch"]}\' Tag \'{payload["data"]["tag"]}\' gestartet.') subprocess.run([ str(parent_file / "runs" / "update_self.sh"), str(payload["data"]["branch"]), str(payload["data"]["tag"])]) else: + pub_success_user("Update gestartet.") subprocess.run([str(parent_file / "runs" / "update_self.sh")]) def systemFetchVersions(self, connection_id: str, payload: dict) -> None: log.info("Fetch versions requested") parent_file = Path(__file__).resolve().parents[2] - subprocess.run([str(parent_file / "runs" / "update_available_versions.sh")]) + result = subprocess.run([str(parent_file / "runs" / "update_available_versions.sh")]) + if result.returncode == 0: + pub_success_user(payload, connection_id, "Versionsliste erfolgreich aktualisiert.") + else: + pub_error_user( + payload, connection_id, + f'Version-Status: {result.returncode}
Meldung: {result.stdout.decode("utf-8")}') + + def createBackup(self, connection_id: str, payload: dict) -> None: + pub_success_user(payload, connection_id, "Backup wird erstellt.") + parent_file = Path(__file__).resolve().parents[2] + result = subprocess.run( + [str(parent_file / "runs" / "backup.sh"), + "1" if "use_extended_filename" in payload["data"] and payload["data"]["use_extended_filename"] else "0"], + stdout=subprocess.PIPE) + if result.returncode == 0: + file_name = result.stdout.decode("utf-8").rstrip('\n') + file_link = "/openWB/data/backup/" + file_name + pub_success_user(payload, connection_id, + "Backup erfolgreich erstellt.
" + f'Jetzt herunterladen.') + else: + pub_error_user(payload, connection_id, + f'Backup-Status: {result.returncode}
Meldung: {result.stdout.decode("utf-8")}') class ErrorHandlingContext: @@ -527,14 +584,47 @@ def __enter__(self): def __exit__(self, exception_type, exception, exception_traceback) -> bool: if isinstance(exception, Exception): - pub_error(self.payload, self.connection_id, "Es ist ein interner Fehler aufgetreten: " + - traceback.format_exc()) + pub_error_global(self.payload, self.connection_id, + f'Es ist ein interner Fehler aufgetreten: {traceback.format_exc()}') return True else: return False -def pub_error(payload: dict, connection_id: str, error_str: str) -> None: +def pub_success_user(payload: dict, connection_id: str, message: str) -> None: + try: + log.info("pub_success: " + message) + now = time.time() + success_payload = { + "source": "command", + "type": "success", + "message": message, + "timestamp": int(now) + } + Pub().pub(f'openWB/set/command/{connection_id}/messages/{(now * 1000):.0f}', success_payload) + log.info(f'Befehl erfolgreich ausgeführt: {message}') + except Exception: + log.exception("Fehler im Command-Modul") + + +def pub_error_user(payload: dict, connection_id: str, error_str: str) -> None: + """ sendet ein Fehler-Topic, warum der Befehl nicht ausgeführt werden konnte. + """ + try: + now = time.time() + error_payload = { + "source": "command", + "type": "danger", + "message": error_str, + "timestamp": int(now) + } + Pub().pub(f'openWB/set/command/{connection_id}/messages/{(now * 1000):.0f}', error_payload) + log.error(f'Befehl konnte nicht ausgeführt werden: {error_payload}') + except Exception: + log.exception("Fehler im Command-Modul") + + +def pub_error_global(payload: dict, connection_id: str, error_str: str) -> None: """ sendet ein Fehler-Topic, warum der Befehl nicht ausgeführt werden konnte. """ try: @@ -543,9 +633,8 @@ def pub_error(payload: dict, connection_id: str, error_str: str) -> None: "data": payload["data"], "error": error_str } - Pub().pub("openWB/set/command/" + - str(connection_id)+"/error", error_payload) - log.error("Befehl konnte nicht ausgeführt werden: "+str(error_payload)) + Pub().pub(f'openWB/set/command/{connection_id}/error', error_payload) + log.error(f'Befehl konnte nicht ausgeführt werden: {error_payload}') except Exception: log.exception("Fehler im Command-Modul") @@ -576,18 +665,18 @@ def get_max_id(self) -> List[str]: def on_connect(self, client, userdata, flags, rc): """ connect to broker and subscribe to set topics """ - client.subscribe("openWB/"+self.topic_str+"#", 2) - client.subscribe("openWB/set/"+self.topic_str+"#", 2) + client.subscribe(f'openWB/{self.topic_str}#', 2) + client.subscribe(f'openWB/set/{self.topic_str}#', 2) def __on_message_rm(self, client, userdata, msg): if str(msg.payload.decode("utf-8")) != '': - log.debug("Gelöschtes Topic: "+str(msg.topic)) + log.debug(f'Gelöschtes Topic: {msg.topic}') Pub().pub(msg.topic, "") if "openWB/system/device/" in msg.topic and "component" in msg.topic and "config" in msg.topic: payload = json.loads(str(msg.payload.decode("utf-8"))) topic = type_to_topic_mapping(payload["type"]) data.data.counter_data["all"].hierarchy_remove_item(payload["id"]) - client.subscribe("openWB/"+topic+"/"+str(payload["id"])+"/#", 2) + client.subscribe(f'openWB/{topic}/{payload["id"]}/#', 2) def __on_message_max_id(self, client, userdata, msg): self.received_topics.append(msg.topic) diff --git a/packages/helpermodules/setdata.py b/packages/helpermodules/setdata.py index f0acbd5b46..4e5212dce1 100644 --- a/packages/helpermodules/setdata.py +++ b/packages/helpermodules/setdata.py @@ -923,6 +923,8 @@ def process_command_topic(self, msg): self._validate_value(msg, "json") elif "error" in msg.topic: self._validate_value(msg, "json") + elif "messages" in msg.topic: + self._validate_value(msg, "json") else: self.__unknown_topic(msg) except Exception: diff --git a/runs/backup.sh b/runs/backup.sh new file mode 100755 index 0000000000..bdfef8fee7 --- /dev/null +++ b/runs/backup.sh @@ -0,0 +1,44 @@ +#!/bin/bash +OPENWBBASEDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) +BACKUPDIR="$OPENWBBASEDIR/data/backup" +LOGFILE="$OPENWBBASEDIR/ramdisk/backup.log" + +useExtendedFilename=$1 +if ((useExtendedFilename == 1)); then + FILENAME="openWB_backup_$(date +"%Y-%m-%d_%H:%M:%S").tar.gz" +else + FILENAME="backup.tar.gz" +fi + +{ + echo "creating new backup: $FILENAME" + # remove old backup files + echo "deleting old backup files if present in '$BACKUPDIR'" + rm -v "$BACKUPDIR/"* + BACKUPFILE="$BACKUPDIR/$FILENAME" + + # tell mosquitto to store all retained topics in db now + echo "sending 'SIGUSR1' to mosquitto processes" + sudo pkill -e -SIGUSR1 mosquitto + # give mosquitto some time to finish + sleep 0.2 + + # create backup file + echo "creating new backup file: $BACKUPFILE" + sudo tar --verbose --create --gzip \ + --exclude="$BACKUPDIR" \ + --exclude="$OPENWBBASEDIR/.git" \ + --exclude "$OPENWBBASEDIR/ramdisk" \ + --exclude "$OPENWBBASEDIR/__pycache__" \ + --exclude "$OPENWBBASEDIR/.pytest_cache" \ + --file="$BACKUPFILE" \ + "$OPENWBBASEDIR/" "/var/lib/mosquitto/" "/var/lib/mosquitto_local/" + echo "setting permissions of new backup file" + sudo chown openwb:www-data "$BACKUPFILE" + sudo chmod 664 "$BACKUPFILE" + + echo "backup finished" +} >>"$LOGFILE" 2>&1 + +# return our filename for further processing +echo "$FILENAME"