diff --git a/bridge_endpoints.py b/bridge_endpoints.py index d511d60..251ea5a 100644 --- a/bridge_endpoints.py +++ b/bridge_endpoints.py @@ -679,6 +679,7 @@ class EndpointsMixin: (getattr(br._args, "power_on_url", "") or "").strip() or (getattr(br._args, "power_off_url", "") or "").strip() ), + "power_status_inverted": bool(getattr(br._args, "power_status_inverted", 0)), }) return self._json_cors({"result": out}) @@ -2018,6 +2019,7 @@ class EndpointsMixin: "power_on_url": getattr(self._args, "power_on_url", "") or "", "power_off_url": getattr(self._args, "power_off_url", "") or "", "power_status_url": getattr(self._args, "power_status_url", "") or "", + "power_status_inverted": getattr(self._args, "power_status_inverted", 0), "default_ams_slot": getattr(self._args, "default_ams_slot", "auto"), "auto_leveling": getattr(self._args, "auto_leveling", 1), "vibration_compensation": getattr(self._args, "vibration_compensation", 0), @@ -2067,6 +2069,7 @@ class EndpointsMixin: cfg.set("connection", "power_on_url", str(data.get("power_on_url", getattr(self._args, "power_on_url", "") or "")).strip()) cfg.set("connection", "power_off_url", str(data.get("power_off_url", getattr(self._args, "power_off_url", "") or "")).strip()) cfg.set("connection", "power_status_url", str(data.get("power_status_url", getattr(self._args, "power_status_url", "") or "")).strip()) + cfg.set("connection", "power_status_inverted", str(int(bool(data.get("power_status_inverted", getattr(self._args, "power_status_inverted", 0)))))) cfg.set("print", "default_ams_slot", str(data.get("default_ams_slot", getattr(self._args, "default_ams_slot", "auto")))) cfg.set("print", "auto_leveling", str(data.get("auto_leveling", getattr(self._args, "auto_leveling", 1)))) cfg.set("print", "vibration_compensation", str(int(bool(data.get("vibration_compensation", getattr(self._args, "vibration_compensation", 0)))))) diff --git a/config_loader.py b/config_loader.py index ab2c92c..1ab7599 100644 --- a/config_loader.py +++ b/config_loader.py @@ -66,6 +66,7 @@ CONFIG_ENV_MAPPING = { "POWER_ON_URL": (CONFIG_SECTION_CONNECTION, "power_on_url"), "POWER_OFF_URL": (CONFIG_SECTION_CONNECTION, "power_off_url"), "POWER_STATUS_URL": (CONFIG_SECTION_CONNECTION, "power_status_url"), + "POWER_STATUS_INVERTED": (CONFIG_SECTION_CONNECTION, "power_status_inverted"), "DEFAULT_AMS_SLOT": (CONFIG_SECTION_PRINT, "default_ams_slot"), "AUTO_LEVELING": (CONFIG_SECTION_PRINT, "auto_leveling"), "VIBRATION_COMPENSATION": (CONFIG_SECTION_PRINT, "vibration_compensation"), @@ -487,6 +488,7 @@ DEVICE_ID = get("DEVICE_ID", "") POWER_ON_URL = get("POWER_ON_URL", "") POWER_OFF_URL = get("POWER_OFF_URL", "") POWER_STATUS_URL = get("POWER_STATUS_URL", "") +POWER_STATUS_INVERTED = _safe_int(get("POWER_STATUS_INVERTED", "0"), 0) DEFAULT_AMS_SLOT = get("DEFAULT_AMS_SLOT", "auto") AUTO_LEVELING = _safe_int(get("AUTO_LEVELING", "1"), 1) VIBRATION_COMPENSATION = _safe_int(get("VIBRATION_COMPENSATION", "0"), 0) diff --git a/kobrax_moonraker_bridge.py b/kobrax_moonraker_bridge.py index ba851e2..3246976 100644 --- a/kobrax_moonraker_bridge.py +++ b/kobrax_moonraker_bridge.py @@ -1177,6 +1177,7 @@ def _build_per_printer_args(base_args, p: dict): a.power_on_url = p.get("power_on_url") or getattr(base_args, "power_on_url", "") or "" a.power_off_url = p.get("power_off_url") or getattr(base_args, "power_off_url", "") or "" a.power_status_url = p.get("power_status_url") or getattr(base_args, "power_status_url", "") or "" + a.power_status_inverted = int(p.get("power_status_inverted") or getattr(base_args, "power_status_inverted", 0) or 0) a.kxgauge_url = p.get("kxgauge_url") or getattr(base_args, "kxgauge_url", "") or "" a.kxgauge_enabled = p.get("kxgauge_enabled") or getattr(base_args, "kxgauge_enabled", 0) or 0 a.kxgauge_heat_peak = p.get("kxgauge_heat_peak") or getattr(base_args, "kxgauge_heat_peak", 250) or 250 @@ -1329,6 +1330,8 @@ def main(): help="HTTP GET URL to power the printer off") parser.add_argument("--power-status-url", default=env_loader.POWER_STATUS_URL, help="HTTP GET URL returning the smart plug's current on/off state") + parser.add_argument("--power-status-inverted", type=int, default=env_loader.POWER_STATUS_INVERTED, + help="Show the power switch's current state instead of the toggle action") parser.add_argument("--default-ams-slot",default=env_loader.DEFAULT_AMS_SLOT) parser.add_argument("--auto-leveling", type=int, default=env_loader.AUTO_LEVELING) parser.add_argument("--vibration-compensation", type=int, default=env_loader.VIBRATION_COMPENSATION) diff --git a/web/themes/default/app.js b/web/themes/default/app.js index 494f1d7..87642f6 100644 --- a/web/themes/default/app.js +++ b/web/themes/default/app.js @@ -355,10 +355,17 @@ function _applyHeaderPowerState(state){ _headerPowerState=state; var btn=document.getElementById('h-power-btn'), lbl=document.getElementById('h-power-lbl'); if(!btn||!lbl)return; - if(state==='on'){ + // Default: show the action a click would perform (printer on -> offer + // "Aus"). With power_status_inverted set (Settings -> Power Switch), show + // the actual current state instead (printer on -> show "An") - some users + // expect the button to mirror reality, not the available action. + var inverted=!!(_activePrinter&&_activePrinter.power_status_inverted); + var showOn=inverted?state==='on':state==='off'; + var showOff=inverted?state==='off':state==='on'; + if(showOff){ btn.classList.remove('h-power-can-on');btn.classList.add('h-power-can-off'); lbl.textContent=T.printers_power_off_short||'Aus'; - }else if(state==='off'){ + }else if(showOn){ btn.classList.remove('h-power-can-off');btn.classList.add('h-power-can-on'); lbl.textContent=T.printers_power_on_short||'An'; }else{ @@ -572,6 +579,7 @@ function applyLang(){ setText('lbl-power-off-url',T.settings_power_off_url||'Power-Off URL'); setText('lbl-power-status-url',T.settings_power_status_url||'Status URL'); setText('lbl-power-hint',T.settings_power_hint||'Optional: plain HTTP GET URLs for a smart plug (e.g. Tasmota) controlling the printer\'s mains power. Leave empty to hide the power button.'); + setText('lbl-power-status-inverted',T.settings_power_status_inverted||'Show current status instead of action'); setText('lbl-default-slot',T.settings_default_slot); setText('opt-slot-auto',T.settings_slot_auto); setText('lbl-auto-leveling',T.settings_auto_leveling); @@ -1254,6 +1262,7 @@ function openSettings(){ var pon=document.getElementById('s-power-on-url');if(pon)pon.value=d.power_on_url||''; var poff=document.getElementById('s-power-off-url');if(poff)poff.value=d.power_off_url||''; var pstat=document.getElementById('s-power-status-url');if(pstat)pstat.value=d.power_status_url||''; + var pinv=document.getElementById('s-power-status-inverted');if(pinv)pinv.checked=!!d.power_status_inverted; document.getElementById('s-default-slot').value=d.default_ams_slot||'auto'; document.getElementById('s-auto-leveling').checked=(d.auto_leveling===undefined?true:!!d.auto_leveling); var vc=document.getElementById('s-vibration-compensation');if(vc)vc.checked=!!d.vibration_compensation; @@ -2033,6 +2042,7 @@ function saveSettings(){ power_on_url: (document.getElementById('s-power-on-url')||{}).value||'', power_off_url: (document.getElementById('s-power-off-url')||{}).value||'', power_status_url: (document.getElementById('s-power-status-url')||{}).value||'', + power_status_inverted: (document.getElementById('s-power-status-inverted')||{}).checked?1:0, default_ams_slot: document.getElementById('s-default-slot').value, auto_leveling: document.getElementById('s-auto-leveling').checked?1:0, vibration_compensation: (document.getElementById('s-vibration-compensation')||{}).checked?1:0, diff --git a/web/themes/default/index.html b/web/themes/default/index.html index da31eb5..274f7b5 100644 --- a/web/themes/default/index.html +++ b/web/themes/default/index.html @@ -566,6 +566,10 @@ +