Skip to content
  • Home
  • Recent
  • Tags
  • 0 Unread 0
  • Categories
  • Unreplied
  • Popular
  • GitHub
  • Docu
  • Hilfe
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
ioBroker Logo

Community Forum

donate donate
  1. ioBroker Community Home
  2. Deutsch
  3. Tester
  4. TESTER: Neuer Adapter Webuntis

NEWS

  • Neuer ioBroker-Blog online: Monatsrückblick März/April 2026
    BluefoxB
    Bluefox
    8
    1
    735

  • Verwendung von KI bitte immer deutlich kennzeichnen
    HomoranH
    Homoran
    10
    1
    565

  • Monatsrückblick Januar/Februar 2026 ist online!
    BluefoxB
    Bluefox
    18
    1
    1.1k

TESTER: Neuer Adapter Webuntis

Scheduled Pinned Locked Moved Tester
200 Posts 52 Posters 45.6k Views 45 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • Walter WhiteW Walter White

    @xdelta das plus durch ein Leerzeichen ersetzen!

    X Offline
    X Offline
    xdelta
    wrote on last edited by xdelta
    #181

    @walter-white said in TESTER: Neuer Adapter Webuntis:

    @xdelta das plus durch ein Leerzeichen ersetzen!

    gymn-benedictum meschede
    

    Gleiche Fehlermeldung > invalid schoolname ?!

    EDIT: K.A. warum, aber nachdem ich den Adapter x mal neu gestartet habe, gehts nun. Danke!

    1 Reply Last reply
    0
    • C chuck2941

      @tugsi danke. Ich glaube nicht das die Schule sowas macht aber fragen kann man ja.
      So wie ich das lese hast du den Zugriff nicht bekommen?

      tugsiT Offline
      tugsiT Offline
      tugsi
      wrote on last edited by
      #182

      @chuck2941 Richtig, die gewähren keiner "externen" App etc einen Zugriff, so hatte ich den Administrator der Schule verstanden.
      Man kann wohl beschränken, ob nur die offizielle App "UNTIS" Zugriff bekommt oder auch Drittanbeiter.

      Master ioBroker in Proxmox als LXC auf 1. Tiny-Lenovo, Proxmox-VM als Slave auf 2. Tinyi-Lenovo mit Zigbee und Monitor zur Visualisierung, Hauptautomatisierung über S7 auf Beckhoff CX, dazu noch Shelly, Homematic...

      1 Reply Last reply
      0
      • Ronny GerndtR Offline
        Ronny GerndtR Offline
        Ronny Gerndt
        wrote on last edited by
        #183

        Da nun wieder das neue Schuljahr gestartet ist versuch ich mal für meinen Sohn und auch mich die wichtigsten Infos aus Webunits in meiner Visualisierung darzustellen. Was aktuell noch fehlt wären die Hausaufgaben. Kann man die noch abfragen und bereitstellen?

        1 Reply Last reply
        1
        • B bishop

          hat hier vielleicht jemand ein Script oder Blockly mit dem ich die Ausfallende Stunden per Telegram rausschicken kann?

          Walter WhiteW Offline
          Walter WhiteW Offline
          Walter White
          wrote on last edited by Walter White
          #184

          @bishop der Post ist zwar alt aber ich habe auch nach so etwas gesucht, habe hier Alexa Meldungen fürs Kinderzimmer und Telegramm Meldungen für uns Erwachsene gebaut.
          Ich hoffe das funktioniert alles korrekt, habe das Skript nämlich noch umgebaut um auch vertretungsunterricht anzeigen zu lassen, einfach nur die ausfallstunden hat auf jeden Fall funktioniert das andere ist gerade noch neu.

          https://forum.iobroker.net/post/1040116

          1 Reply Last reply
          0
          • Walter WhiteW Offline
            Walter WhiteW Offline
            Walter White
            wrote on last edited by
            #185

            Kann ich die gemeldete Abwesenheit irgendwo sehen?
            Wollte das in meinem Licht Script einbauen, damit das Licht morgens nicht an geht bei Abwesen gemeldet zb wenn Krank usw.

            1 Reply Last reply
            0
            • Walter WhiteW Offline
              Walter WhiteW Offline
              Walter White
              wrote on last edited by
              #186

              Oh mein Gott, der IT "vollprofi" der Schule blockiert jetzt den Webuntis Zugang!?

              Muss man jetzt echt die Schule anschreiben?

              Da machen sie einen auf Digitalisierung, und zwingen ein jetzt noch die App zu benutzen, ein Schritt vorwärts zwei Schritte zurück!

              1 Reply Last reply
              0
              • K Offline
                K Offline
                Kasti_76
                wrote on last edited by
                #187

                Hallo zusammen,
                ich wollte den Adapter installieren, aber die URL im Browser bei mir lautet "https://thalia.webuntis.com/today" Da gibt es kein Schoolsecret. Wie melde ich mich dann an?

                Danke

                Jürgen

                G 1 Reply Last reply
                0
                • inbuxI inbux

                  Hallo,

                  danke erstmal für den Adapter.

                  Leider überträgt unsere Schule die Daten wohl auch abweichend von "Standard" - zumindest konnte ich den Adapter so nicht zufrieden stellend zum Laufen bekommen.
                  Bei der anonymen Abfrage kommen ja alle Kurse, die es gibt an dem Tag - auch jene, die vielleicht gar nicht gewählt wurden.

                  Login mit Passwort klappt nicht, da unsere Kinder sich über IServ bei Webuntis anmelden. Diese Login Daten kann man nicht verwenden. Alternativ kann man sich in Webuntis einen QR mit Secret anzeigen lassen, den man prinzipiell zum Login benutzen könnte. Ein Issue habe ich dafür bereits aufgemacht (könnte für andere ja vielleicht auch interessant sein)

                  Ich habe mir den Adapter geforkt, um die teilweise recht speziellen Anforderungen für mich zu implementieren - aber vielleicht ist es ja auch noch hilfreich für andere...

                  Ich habe jetzt folgendes eingebaut:

                  • Login mit Username und Secret unterstützt
                  • doppeltes Login (anonym und der Username), um alle relevanten Daten zu erhalten und nicht gewählte Kurse ausfiltern zu können
                  • zusätzliche Daten, wie ursprünglicher Lehrer/Raum bei Vertretung oder Raumwechsel
                  • unsere Schule liefert nur den Code "cancelled" bei Ausfall, sonst gar nichts.
                    Aber der ursprünglicher Lehrer oder Raum wird übertragen, den benutze ich dann für einen "irregular" Code

                  Um die Daten in der Visualisierung anzuzeigen, habe ich ein kleines Script geschrieben, das die Daten dann in einen Datenpunkt schreibt. Das Ergebnis in JSON ist dann kompatibel zum Tabellen Widget aus den Material Design Widgets.
                  Das sieht dann bei mir so aus:
                  WebuntisTabelle.jpg

                  Ich benutze die zusätzlichen Datenpunkte von meiner Adapterversion. Das Script läuft aber auch mit der Standardversion von Webuntis Adapter, soweit ich das probiert habe.
                  Das Script kann man leicht anpassen (ein paar Variablen zur Einstellung habe ich vorgesehen) oder als Vorlage für eine eigene Variante verwenden.

                  Meine Spezialversion vom Adapter und auch das Script findet man hier:
                  https://github.com/inbux/ioBroker.webuntis

                  direkter Link zum Script

                  S Offline
                  S Offline
                  SaiBot1981
                  wrote on last edited by
                  #188

                  @inbux sagte in TESTER: Neuer Adapter Webuntis:

                  Hallo,

                  danke erstmal für den Adapter.

                  Leider überträgt unsere Schule die Daten wohl auch abweichend von "Standard" - zumindest konnte ich den Adapter so nicht zufrieden stellend zum Laufen bekommen.
                  Bei der anonymen Abfrage kommen ja alle Kurse, die es gibt an dem Tag - auch jene, die vielleicht gar nicht gewählt wurden.

                  Login mit Passwort klappt nicht, da unsere Kinder sich über IServ bei Webuntis anmelden. Diese Login Daten kann man nicht verwenden. Alternativ kann man sich in Webuntis einen QR mit Secret anzeigen lassen, den man prinzipiell zum Login benutzen könnte. Ein Issue habe ich dafür bereits aufgemacht (könnte für andere ja vielleicht auch interessant sein)

                  Ich habe mir den Adapter geforkt, um die teilweise recht speziellen Anforderungen für mich zu implementieren - aber vielleicht ist es ja auch noch hilfreich für andere...

                  Ich habe jetzt folgendes eingebaut:

                  • Login mit Username und Secret unterstützt
                  • doppeltes Login (anonym und der Username), um alle relevanten Daten zu erhalten und nicht gewählte Kurse ausfiltern zu können
                  • zusätzliche Daten, wie ursprünglicher Lehrer/Raum bei Vertretung oder Raumwechsel
                  • unsere Schule liefert nur den Code "cancelled" bei Ausfall, sonst gar nichts.
                    Aber der ursprünglicher Lehrer oder Raum wird übertragen, den benutze ich dann für einen "irregular" Code

                  Um die Daten in der Visualisierung anzuzeigen, habe ich ein kleines Script geschrieben, das die Daten dann in einen Datenpunkt schreibt. Das Ergebnis in JSON ist dann kompatibel zum Tabellen Widget aus den Material Design Widgets.
                  Das sieht dann bei mir so aus:
                  WebuntisTabelle.jpg

                  Ich benutze die zusätzlichen Datenpunkte von meiner Adapterversion. Das Script läuft aber auch mit der Standardversion von Webuntis Adapter, soweit ich das probiert habe.
                  Das Script kann man leicht anpassen (ein paar Variablen zur Einstellung habe ich vorgesehen) oder als Vorlage für eine eigene Variante verwenden.

                  Meine Spezialversion vom Adapter und auch das Script findet man hier:
                  https://github.com/inbux/ioBroker.webuntis

                  direkter Link zum Script

                  moin, mal ne blöde frage. wie hast du das rot bekommen? denke das ist dann irregulärer unterricht?

                  1 Reply Last reply
                  -1
                  • K Kasti_76

                    Hallo zusammen,
                    ich wollte den Adapter installieren, aber die URL im Browser bei mir lautet "https://thalia.webuntis.com/today" Da gibt es kein Schoolsecret. Wie melde ich mich dann an?

                    Danke

                    Jürgen

                    G Offline
                    G Offline
                    Georgius
                    wrote on last edited by
                    #189

                    @kasti_76 Auch wenn etwas späte Antwort-

                    Das Problem hatte ich auch, du bist aber schon zu weit. Du musst die Schule auf webunits.com suchen, dort ist das Geheimnis.

                    G 1 Reply Last reply
                    0
                    • G Georgius

                      @kasti_76 Auch wenn etwas späte Antwort-

                      Das Problem hatte ich auch, du bist aber schon zu weit. Du musst die Schule auf webunits.com suchen, dort ist das Geheimnis.

                      G Offline
                      G Offline
                      Georgius
                      wrote on last edited by
                      #190

                      Ich habe den Adapter gerade eingerichtet, aber nur inbox,info und newsfeed zu sehen. Weit und breit nichts vom Stundenplan

                      EisbaeeerE 1 Reply Last reply
                      0
                      • G Georgius

                        Ich habe den Adapter gerade eingerichtet, aber nur inbox,info und newsfeed zu sehen. Weit und breit nichts vom Stundenplan

                        EisbaeeerE Offline
                        EisbaeeerE Offline
                        Eisbaeeer
                        Developer
                        wrote on last edited by Eisbaeeer
                        #191

                        Ich habe eben mal den Adapter installiert und bekomme sehr wenig Infos:
                        e9fca9c2-878b-487d-ac10-a50860af6670-image.png

                        Im Log steht, dass er den Kalender nicht abrufen kann. In der Weboberfläche sehe ich den Kalender aber.

                        --- Edit ---
                        Ok, mit dem Account vom Schüler bekomme ich den Stundenplan (macht irgendwie auch Sinn).
                        Leider sind im Adapter die Klassenarbeiten nicht ersichtlich. Hat da schon jemand eine Lösung?
                        VG Lars

                        Kein support per PM. Bitte im Forum Fragen stellen!

                        Tg-71T 1 Reply Last reply
                        0
                        • madingM Offline
                          madingM Offline
                          mading
                          wrote on last edited by mading
                          #192

                          Edit

                          Der Adapter ruft bei mir nur die nächsten zwei Tage ab, über die App sehe ich zb die gesamte Woche. Kann man das erweitern?

                          1 Reply Last reply
                          0
                          • T Offline
                            T Offline
                            tobi_kieninger
                            wrote on last edited by
                            #193

                            Hallo zusammen,

                            ich bin auch Grade neu zum Adapter hinzugekommen.
                            Leider hat Untis die Serveradressen und vermutlich auch die API geändert.
                            Weiss da jemand mehr?

                            madingM 1 Reply Last reply
                            0
                            • T tobi_kieninger

                              Hallo zusammen,

                              ich bin auch Grade neu zum Adapter hinzugekommen.
                              Leider hat Untis die Serveradressen und vermutlich auch die API geändert.
                              Weiss da jemand mehr?

                              madingM Offline
                              madingM Offline
                              mading
                              wrote on last edited by
                              #194

                              @tobi_kieninger sagte in TESTER: Neuer Adapter Webuntis:

                              Hallo zusammen,

                              ich bin auch Grade neu zum Adapter hinzugekommen.
                              Leider hat Untis die Serveradressen und vermutlich auch die API geändert.
                              Weiss da jemand mehr?

                              Bei mir hat sich seit Einrichtung letzten Sept die Base URL und das Secret geändert. Die Daten kommen aber an

                              T 1 Reply Last reply
                              0
                              • madingM mading

                                @tobi_kieninger sagte in TESTER: Neuer Adapter Webuntis:

                                Hallo zusammen,

                                ich bin auch Grade neu zum Adapter hinzugekommen.
                                Leider hat Untis die Serveradressen und vermutlich auch die API geändert.
                                Weiss da jemand mehr?

                                Bei mir hat sich seit Einrichtung letzten Sept die Base URL und das Secret geändert. Die Daten kommen aber an

                                T Offline
                                T Offline
                                tobi_kieninger
                                wrote on last edited by
                                #195

                                @mading sagte in TESTER: Neuer Adapter Webuntis:

                                @tobi_kieninger sagte in TESTER: Neuer Adapter Webuntis:

                                Hallo zusammen,

                                ich bin auch Grade neu zum Adapter hinzugekommen.
                                Leider hat Untis die Serveradressen und vermutlich auch die API geändert.
                                Weiss da jemand mehr?

                                Bei mir hat sich seit Einrichtung letzten Sept die Base URL und das Secret geändert. Die Daten kommen aber an

                                In wieweit hat sich das Secret geändert?

                                Ich. In seit 7.12. abgeschnitten..

                                Die neue BaseUrl enthält jetzt den Schulnamen, das hab ich geschafft, aber wie ändert sich das Secret?

                                1 Reply Last reply
                                0
                                • madingM Offline
                                  madingM Offline
                                  mading
                                  wrote on last edited by
                                  #196

                                  Ist auf der git-Seite erklärt was was ist

                                  1 Reply Last reply
                                  0
                                  • EisbaeeerE Eisbaeeer

                                    Ich habe eben mal den Adapter installiert und bekomme sehr wenig Infos:
                                    e9fca9c2-878b-487d-ac10-a50860af6670-image.png

                                    Im Log steht, dass er den Kalender nicht abrufen kann. In der Weboberfläche sehe ich den Kalender aber.

                                    --- Edit ---
                                    Ok, mit dem Account vom Schüler bekomme ich den Stundenplan (macht irgendwie auch Sinn).
                                    Leider sind im Adapter die Klassenarbeiten nicht ersichtlich. Hat da schon jemand eine Lösung?
                                    VG Lars

                                    Tg-71T Offline
                                    Tg-71T Offline
                                    Tg-71
                                    Forum Testing
                                    wrote on last edited by
                                    #197

                                    @Eisbaeeer
                                    Das mit dem Schüler Account hat geholfen - Danke - Hatte es erst mit dem Elternaccount probiert

                                    1 Reply Last reply
                                    0
                                    • M Offline
                                      M Offline
                                      mrMuppet
                                      wrote on last edited by
                                      #198

                                      Bei uns benutzen alle Schüler und Eltern(!) den gleichen Login und man sucht sich unter "weitere Stundenpläne" die entsprechende Klasse(n) aus.
                                      Lässt sich das auch abfragen?
                                      Aktuell bekomm ich nur "Cannot read Timetable for today - possible block by scool"

                                      ioBroker auf NUC (Celeron mit Ubuntu-Server)

                                      Homematic, HMIP, Hue, Unifi, Plex, Nest, Roborock, Google Assistant

                                      1 Reply Last reply
                                      0
                                      • Bass-TB Offline
                                        Bass-TB Offline
                                        Bass-T
                                        wrote on last edited by
                                        #199

                                        Hi alle zusammen,
                                        nutze den Adapter nun auch, nachdem mit die Schule mitgeteilt hat, dass ical export ein kostenpflichtiger zusatz ist, und die Schule dass daher nicht anbieten kann.

                                        Adapter installiert und mit Schüler Account Verbunden.

                                        Alles super.

                                        Wünsche:
                                        Wie wäre ein Datenpunkt mit komplett input als rohdaten?

                                        Hintergrund:
                                        Ich möchte gerne den Stundenplan als export für JARVIS nutzen. Mache das aktuell schon mit Edupage meiner Grundschüler.
                                        Weiter hätte ich gerne die gesammte aktuelle Woche inkl. Wichtiger news (wenn möglich als separaten datenpunkt)

                                        Gerne unterstütze ich auch dabei ;)

                                        Grüße

                                        Proxmox Cluster

                                        • NUC7i5 ioBroker Server (Proxmox)
                                        • HP Elitedesk 800 G4 SSF (Proxmox)
                                        • HP Elitedesk 800 G2 DM (Proxmox)
                                          mein Proxmox-Updater für euch

                                        KNX / Broadlink / Tasmota ESPs / Zigbee
                                        Vis: 15" Touchmonitor mit Raspi4 / 10" Tablet (Dauerstrom umbau) / Sonoff NSPanel (alle)

                                        1 Reply Last reply
                                        0
                                        • madingM Offline
                                          madingM Offline
                                          mading
                                          wrote on last edited by mading
                                          #200

                                          Hi zusammen,

                                          der Adapter war mir zu unzuverlässig bzw. hatte ich ein Javascript gefunden, dass mir alles für vis in einer Tabelle aufarbeitet. Das war aber nicht immer richtig. Ich habe mit Hilfe von copilot ein Skript gebaut, das mir Screenshots des Stundenplans macht.

                                          
                                          # Create a new folder for the script
                                          mkdir webuntis-shot && cd webuntis-shot
                                          
                                          # Initialize and install dependencies
                                          npm init -y
                                          npm i playwright dotenv
                                          
                                          # Install browsers for Playwright (first time only)
                                          npx playwright install
                                          
                                          

                                          dann .env Datei erstellen

                                          
                                          # .env
                                          WEBUNTIS_USERNAME="user"
                                          WEBUNTIS_PASSWORD="xyz"
                                          HEADLESS="1"
                                          OUTPUT_DIR="/opt/iobroker"
                                          
                                          

                                          dann die Datei z.B. webuntis.js anlegen

                                          // webuntis-screenshot.js
                                          // Usage: node webuntis-screenshot.js
                                          // Optional env: WEBUNTIS_USERNAME, WEBUNTIS_PASSWORD, HEADLESS, OUTPUT_DIR, TARGET_HASH_FILE
                                          
                                          import { chromium } from 'playwright';
                                          import fs from 'fs';
                                          import fsp from 'fs/promises';
                                          import path from 'path';
                                          import 'dotenv/config';
                                          
                                          const USERNAME = process.env.WEBUNTIS_USERNAME || 'user';
                                          const PASSWORD = process.env.WEBUNTIS_PASSWORD || 'pass';
                                          const HEADLESS = (process.env.HEADLESS || '1') !== '0';
                                          const OUTPUT_DIR = process.env.OUTPUT_DIR || '/opt/iobroker/iobroker-data/files/vis.0/main/img/';
                                          
                                          const BASE_URL = 'https://gss-realschule.webuntis.com/timetable/my-student?date=';
                                          
                                          // === NEW: read TARGET_HASH from external file ===
                                          const TARGET_HASH_FILE = process.env.TARGET_HASH_FILE || path.resolve(process.cwd(), 'target-hash.txt');
                                          
                                          async function loadTargetHash(filePath) {
                                            const exists = fs.existsSync(filePath);
                                            if (!exists) {
                                              throw new Error(`Target-hash file not found: ${filePath}`);
                                            }
                                          
                                            const ext = path.extname(filePath).toLowerCase();
                                          
                                            if (ext === '.json') {
                                              const raw = await fsp.readFile(filePath, 'utf-8');
                                              let data;
                                              try {
                                                data = JSON.parse(raw);
                                              } catch (e) {
                                                throw new Error(`Invalid JSON in ${filePath}: ${e.message}`);
                                              }
                                              const hash = (data.TARGET_HASH || '').trim();
                                              if (!hash) throw new Error(`Missing "TARGET_HASH" property in ${filePath}`);
                                              validateHash(hash);
                                              return hash;
                                            } else {
                                              // treat as plain text
                                              const hash = (await fsp.readFile(filePath, 'utf-8')).trim();
                                              validateHash(hash);
                                              return hash;
                                            }
                                          }
                                          
                                          function validateHash(hash) {
                                            if (!hash.startsWith('#/')) {
                                              throw new Error(`TARGET_HASH must start with "#/". Got: "${hash}"`);
                                            }
                                          }
                                          
                                          // Utility: ensure directory exists (try to create if not)
                                          function ensureDir(dir) {
                                            try {
                                              if (!fs.existsSync(dir)) {
                                                fs.mkdirSync(dir, { recursive: true });
                                              }
                                              const testFile = path.join(dir, '.test_write.tmp');
                                              fs.writeFileSync(testFile, 'ok');
                                              fs.unlinkSync(testFile);
                                              return true;
                                            } catch (e) {
                                              console.error(`[WARN] Cannot write to ${dir}: ${e.message}`);
                                              return false;
                                            }
                                          }
                                          
                                          async function tryFill(page, selectors, value, opts = {}) {
                                            for (const sel of selectors) {
                                              try {
                                                const el = await page.$(sel);
                                                if (el) {
                                                  await el.fill(value, { timeout: opts.timeout || 3000 });
                                                  return true;
                                                }
                                              } catch { /* continue */ }
                                            }
                                            return false;
                                          }
                                          
                                          async function tryClick(page, candidates, opts = {}) {
                                            for (const c of candidates) {
                                              try {
                                                if (c.selector) {
                                                  await page.click(c.selector, { timeout: opts.timeout || 3000 });
                                                  return true;
                                                }
                                                if (c.text) {
                                                  const el = page.getByText(c.text, { exact: false });
                                                  await el.first().click({ timeout: opts.timeout || 3000 });
                                                  return true;
                                                }
                                              } catch { /* continue */ }
                                            }
                                            return false;
                                          }
                                          
                                          async function acceptCookies(page) {
                                            const candidates = [
                                              { text: 'Accept all' }, { text: 'Accept All' }, { text: 'Accept' },
                                              { text: 'Alle akzeptieren' }, { text: 'Akzeptieren' }, { text: 'Einverstanden' },
                                              { selector: 'button#onetrust-accept-btn-handler' },
                                              { selector: 'button[aria-label*="accept"]' },
                                            ];
                                            await tryClick(page, candidates, { timeout: 2000 });
                                          }
                                          
                                          async function loginIfNeeded(page, username, password) {
                                            const usernameSelectors = [
                                              'input[name="username"]',
                                              'input#username',
                                              'input[name="user"]',
                                              'input[type="text"]',
                                              'input[autocomplete="username"]'
                                            ];
                                            const passwordSelectors = [
                                              'input[name="password"]',
                                              'input#password',
                                              'input[type="password"]',
                                              'input[autocomplete="current-password"]'
                                            ];
                                            const loginButtonCandidates = [
                                              { text: 'Log in' }, { text: 'Login' }, { text: 'Anmelden' }, { text: 'Sign in' },
                                              { selector: 'button[type="submit"]' }
                                            ];
                                          
                                            let needsLogin = false;
                                            for (const sel of passwordSelectors) {
                                              const el = await page.$(sel);
                                              if (el) { needsLogin = true; break; }
                                            }
                                          
                                            if (!needsLogin) {
                                              const clicked = await tryClick(page, [{ text: 'Login' }, { text: 'Anmelden' }, { text: 'Log in' }], { timeout: 2000 });
                                              if (clicked) {
                                                await page.waitForTimeout(1200);
                                              }
                                              for (const sel of passwordSelectors) {
                                                const el = await page.$(sel);
                                                if (el) { needsLogin = true; break; }
                                              }
                                            }
                                          
                                            if (!needsLogin) return false;
                                          
                                            const uFilled = await tryFill(page, usernameSelectors, username);
                                            const pFilled = await tryFill(page, passwordSelectors, password);
                                            if (!uFilled || !pFilled) {
                                              throw new Error('Could not locate username/password fields to perform login.');
                                            }
                                          
                                            const clickedSubmit = await tryClick(page, loginButtonCandidates, { timeout: 3000 });
                                            if (!clickedSubmit) {
                                              for (const sel of passwordSelectors) {
                                                const el = await page.$(sel);
                                                if (el) {
                                                  await el.press('Enter');
                                                  break;
                                                }
                                              }
                                            }
                                          
                                            await page.waitForLoadState('domcontentloaded');
                                            await page.waitForTimeout(2000);
                                            return true;
                                          }
                                          
                                          function timestamp() {
                                            const d = new Date();
                                            const pad = (n) => String(n).padStart(2, '0');
                                            return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}_${pad(d.getHours())}-${pad(d.getMinutes())}-${pad(d.getSeconds())}`;
                                          }
                                          
                                          (async () => {
                                            console.log('[INFO] Starting WebUntis screenshot task...');
                                            const canWrite = ensureDir(OUTPUT_DIR);
                                            const outDir = canWrite ? OUTPUT_DIR : process.cwd();
                                            if (!canWrite) {
                                              console.warn(`[WARN] Falling back to current directory: ${outDir}`);
                                            }
                                          
                                            // Load external hash
                                            let TARGET_HASH = '';
                                            try {
                                              TARGET_HASH = await loadTargetHash(TARGET_HASH_FILE);
                                              console.log(`[INFO] Loaded TARGET_HASH from ${TARGET_HASH_FILE}: ${TARGET_HASH}`);
                                            } catch (e) {
                                              console.error('[ERROR] Failed to load TARGET_HASH:', e.message);
                                              process.exit(1);
                                            }
                                          
                                            const TARGET_URL = `${BASE_URL}${TARGET_HASH}`;
                                          
                                            const browser = await chromium.launch({ headless: HEADLESS });
                                            const context = await browser.newContext({ viewport: { width: 1400, height: 900 } });
                                            const page = await context.newPage();
                                          
                                            try {
                                              console.log('[INFO] Navigating to base URL…');
                                              await page.goto(BASE_URL, { waitUntil: 'domcontentloaded', timeout: 60000 });
                                              await acceptCookies(page);
                                          
                                              await loginIfNeeded(page, USERNAME, PASSWORD);
                                          
                                              console.log('[INFO] Navigating to target timetable URL…');
                                              await page.goto(TARGET_URL, { waitUntil: 'domcontentloaded', timeout: 60000 });
                                          
                                              await page.waitForTimeout(4000);
                                              await acceptCookies(page);
                                          
                                              const filePath = path.join(outDir, `webuntis-screenshot.png`);
                                              await page.screenshot({ path: filePath, fullPage: true });
                                              console.log(`[SUCCESS] Screenshot saved to: ${filePath}`);
                                            } catch (err) {
                                              console.error('[ERROR]', err.message);
                                              process.exitCode = 1;
                                            } finally {
                                              await context.close();
                                              await browser.close();
                                            }
                                          })();
                                          
                                          

                                          eigentlich dachte ich, ich muss die URL noch wöchentlich auf die richtige Woche ändern, das macht aber ein redirect. die target-hash.txt Datei ist aber geblieben (gleicher Ordner) - steht "nichts" drin, könnte geändert werden

                                          #/
                                          
                                          

                                          dann das Skript laufen lassen

                                          node webuntis.js
                                          

                                          Bei mir kamen noch ein paar Fehler, ich musste ein paar libraries nachinstallieren.

                                          Somit läuft es bei mir, ich muss jetzt nur noch schauen, ob ich es mit einem blockly und cron triggere oder von debian aus.

                                          494470b8-8e71-4663-8202-0716112cdf72-grafik.png

                                          Damit läuft das Skript stündlich
                                          0 * * * * /usr/bin/node /home/mading/webuntis-shot/webuntis.js >> /home/mading/webuntis-shot/cron.log 2>&1

                                          Damit vis das Bild kennt bzw. lesen kann, ist ein Javascript notwendig, dass die Datei in vis.0 schreibt:

                                          var fs = require('fs'); 
                                           
                                           const picture= fs.readFileSync('/opt/iobroker/iobroker-data/files/vis.0/main/img/webuntis-screenshot.png'); //liest linux-datei-system
                                          writeFile('vis.0','/main/img/webuntis-screenshot.png', picture, function (error) { });   //schreibt in iobroker system
                                          

                                          Blockly:

                                          <xml xmlns="https://developers.google.com/blockly/xml">
                                            <block type="procedures_defcustomnoreturn" id="FuE%6{wZt7R{vL]0sp=:" x="-37" y="38">
                                              <mutation statements="false"></mutation>
                                              <field name="NAME">etwas tun</field>
                                              <field name="SCRIPT">dmFyIGZzID0gcmVxdWlyZSgnZnMnKTsgDQogDQogY29uc3QgcGljdHVyZT0gZnMucmVhZEZpbGVTeW5jKCcvb3B0L2lvYnJva2VyL2lvYnJva2VyLWRhdGEvZmlsZXMvdmlzLjAvbWFpbi9pbWcvd2VidW50aXMtc2NyZWVuc2hvdC5wbmcnKTsgLy9saWVzdCBsaW51eC1kYXRlaS1zeXN0ZW0NCndyaXRlRmlsZSgndmlzLjAnLCcvbWFpbi9pbWcvd2VidW50aXMtc2NyZWVuc2hvdC5wbmcnLCBwaWN0dXJlLCBmdW5jdGlvbiAoZXJyb3IpIHsgfSk7ICAgLy9zY2hyZWlidCBpbiBpb2Jyb2tlciBzeXN0ZW0=</field>
                                              <comment pinned="false" h="80" w="160">Beschreibe diese Funktion …</comment>
                                            </block>
                                            <block type="schedule" id="9DD.a-@UNvoRp8i:Yode" x="63" y="113">
                                              <field name="SCHEDULE">{"time":{"start":"06:00","end":"23:00","mode":"minutes","interval":61},"period":{"days":1}}</field>
                                              <statement name="STATEMENT">
                                                <block type="procedures_callcustomnoreturn" id="esc^G_dpBegT:H7{mULt">
                                                  <mutation name="etwas tun"></mutation>
                                                </block>
                                              </statement>
                                            </block>
                                          </xml>
                                          
                                          1 Reply Last reply
                                          0
                                          • HomoranH Homoran verschob dieses Thema von Tester am
                                          • HomoranH Homoran verschob dieses Thema von ...nicht in offiziellem Repo am

                                          Hello! It looks like you're interested in this conversation, but you don't have an account yet.

                                          Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.

                                          With your input, this post could be even better 💗

                                          Register Login
                                          Reply
                                          • Reply as topic
                                          Log in to reply
                                          • Oldest to Newest
                                          • Newest to Oldest
                                          • Most Votes


                                          Support us

                                          ioBroker
                                          Community Adapters
                                          Donate

                                          334

                                          Online

                                          32.8k

                                          Users

                                          82.9k

                                          Topics

                                          1.3m

                                          Posts
                                          Community
                                          Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen | Einwilligungseinstellungen
                                          ioBroker Community 2014-2025
                                          logo
                                          • Login

                                          • Don't have an account? Register

                                          • Login or register to search.
                                          • First post
                                            Last post
                                          0
                                          • Home
                                          • Recent
                                          • Tags
                                          • Unread 0
                                          • Categories
                                          • Unreplied
                                          • Popular
                                          • GitHub
                                          • Docu
                                          • Hilfe