Integrieren Sie PRONOTE in Ihren Home Assistant

Pronote ist eine geniale App des Unternehmens Index Education, die eine Mehrheit der Schulen ausstattet.

Wenn Sie es nicht wissen, gehen Sie Ihren Weg, weil Sie es nicht in Ihre Hausautomation integrieren müssen.

 

Achtung, dieser Retex ist überholt, in der Tat seit Juni 2023, eine Integration (Danke kommt Delphiki) sich um alles zu kümmern, was im Folgenden beschrieben wird. Einige Links oder Dateien in meinem HA stimmen möglicherweise nicht mehr mit dem Rest dieses Textes überein. Ich empfehle natürlich, die Integration zu nutzen.

Hier ist der letzte Retex: Ich arbeite an den HA-Karten von Pronote

 

Diese Integration basiert auf dem Dathosim-Skript (Vielen Dank !!)
https://github.com/dathosim/Pronote2Homeassistant

Ich habe einige Zeilen des Codes geändert, entweder um sie an meine Verwendung anzupassen oder um sie an die neuen HA-Standards anzupassen (Plattform: Template/Sensoren: zum Beispiel template:/sensor)

Installation der PronotePy-Bibliothek

Es handelt sich um eine Python-Bibliothek, die als Open Source entwickelt wurde: Pronotepy

Um diese Bibliothek zu installieren, starten Sie die Befehlszeile:

pip install pronotepy

oder um die neueste Version zu erhalten (nicht empfohlen):

pip install https://github.com/bain3/pronotepy/archive/refs/heads/master.zip

Von Zeit zu Zeit muss daran gedacht werden, einen kleinen Schlag zu machen:

pip install –upgrade pronotepy

Installation des Python-Skripts von Dathosim

Gehen Sie in den /config-Ordner Ihres HA und erstellen Sie dort einen neuen Ordner, der als /config-Ordner bezeichnet werden kann. python_scripts und das Ihre benutzerdefinierten Skripte enthalten wird.

In diesem Ordner /config/python_scripts die Dateien pronote.py und config.ini des Vertriebs Pronote2HomeAssistent.

Einrichten des Python-Skripts

Datei muss bearbeitet werden config.ini und spezifische Informationen hinzuzufügen.

Spezifische Sigalou-Konfiguration

Ich habe Probleme bei der Identifizierung mit dem ENT atrium_sud. Aus einem Grund, den ich nicht kenne, musste ich das Python-Skript ändern. Ich werde diese Informationen an Dathosim weiterleiten, um zu sehen, was passiert. Ich gebe hier meine Änderungen an, sie können nützlich sein. Ich habe Zeile 57 geändert.

# client = (pronotepy.ParentClient if type_konto == 'parent' else pronotepy.Client)(url, username, password, ent) client = (pronotepy.ParentClient if type_konto == 'parent' else pronotepy.Client)('https://0xx0xxxx.index-education.net/pronote/mobile.parent.html', username = 'xxxxx', password='yyyyy', ent=atrium_sud)
  • Um die URL zu erhalten, ging ich auf pronote, um mir beim Flashen des QR-Codes die vorgeschlagene URL anzusehen.
  • Bei xxxx muss der Benutzername und yyyy das Passwort eingegeben werden.

Damit funktioniert das Skript perfekt. Wenn Sie ein bisschen mehr Glück haben als ich, sollte alles gut mit dem bereitgestellten Skript funktionieren, indem Sie die Konfigurationsdatei mit Ihren Anmeldeinformationen ausfüllen.

Skript starten

Hier muss man die Daumen drücken, um sicherzustellen, dass alles gut läuft.

Sie müssen sich in die SSH-Befehlszeile einloggen und in den Ordner phyton_scripts gehen, der zuvor mit einem erstellt wurde:

cd /config/python_scripts

cd /usr/share/hassio/homeAssistant/python_scripts/ (bei Dathosim)

Dann starten Sie das Skript mit:

python3 pronote.py

Wenn nichts Besonderes passiert, wird keine Fehlermeldung angezeigt, es ist OK, das Skript wurde erfolgreich ausgeführt.

Wenn alles gut gelaufen ist, eine Datei pronote_prenomenfant.json wird in /config/www erstellt

Diese Datei wird von HA abgefragt, um die Informationen abzurufen.

Automatisieren Sie den Start des Skripts

Es gibt die klassische Methode, die wahrscheinlich von Linux-Puristen bevorzugt wird, d. h. das Hinzufügen eines Crontabs. 

*/10 * * * * /usr/bin/python3 /usr/share/hassio/homeAssistant/python_scripts/pronote.py > /tmp/pronote.log 2>&1

oder je nach System:

*/10 * * * * /config/python_scripts/pronote.py > /tmp/pronote.log 2>&1

Dies ist nicht die Methode, die ich gewählt habe, ich möchte 100 verwenden.% Home Assistant und in meiner Lernphase habe ich versucht, einen Cron zu starten und ein Python-Skript zu starten.

Hinzufügen einer Automatisierung zum Starten eines Shell-Befehls

HA nennt dies ein Timer Pattern

Ich füge meiner configuration.yaml-Datei eine _command.yaml-Shell hinzu 

In dieser Shell-Datei _command.yaml füge ich hinzu:

update_pronote: python3 /config/python_scripts/pronote.py

Unter Automatisierungen füge ich eine neue Automatisierung hinzu, mit der das update_pronote-Skript gestartet wird. 

Der Auslöser ist ein Auslöser alle 10 Minuten

und die Aktion ist ein Shell-Befehl, mit dem update_pronote gestartet wird

Im YAML-Modus bedeutet dies:

Alle 10 Minuten wird daher die Datei /config/www/pronote_enfant.json generiert.

Daten wiederherstellen

Anschließend wird HA REST-Abfragen durchführen, um die Daten abzurufen.

Dathosim gibt alle zu startenden Anfragen ab, sie befinden sich auf:
https://github.com/dathosim/Pronote2Homeassistant/blob/main/configuration.yaml

Es muss natürlich ersetzt werden:

  • Demo durch den Vornamen des Kindes
  • 192.168.XX.XX über die IP-Adresse von HA

Ich habe einige Syntaxen oder Sensoren (Templates) geändert, siehe meine Datei templates.yaml

Pronote-Bildschirm erstellen

Der letzte Schritt besteht darin, die Sensoren, die mit pronote… beginnen, auf Karten anzuzeigen, um einen schönen Bildschirm zu erstellen.

Hier der Bildschirmcode von Dathosim: https://github.com/dathosim/Pronote2Homeassistant/blob/main/lovelace.yaml

Hier mein Code:

Typ: horizontal-stack columns: 2 Plätze: False Cards: - Typ: vertikale Stack-Karten: - Typ: Markdown Content: |-
          <div>Nutzung der Zeit von heute </div>
          <table>
              <tbody>
                  {%-for attr in states.sensor.pronote_edt_coralie_heute.attributes.edt_heute -%}
                  <tr style="background-color:#FF0000"><td>
                      {%- if state_attr('sensor.pronote_edt_coralie_heute', 'edt_heute')[loop.index-1]['Annullierung'] == false -%}
                          <mark>
                          {{state_attr('sensor.pronote_edt_coralie_heute', 'edt_heute')[loop.index-1][zeit']}}
                          </mark>
                      {%- else -%}
                          <span>
                          {{state_attr('sensor.pronote_edt_coralie_heute', 'edt_heute')[loop.index-1][zeit']}}
                          </span>
                      {% endif %}</td>
                      <td>{{state_attr('sensor.pronote_edt_coralie_heute', 'edt_heute')[loop.index-1]['_fin'-Zeit']}}</td>
                      <td>
                      {{state_attr('sensor.pronote_edt_coralie_heute', 'edt_heute')[loop.index-1]['cours']} {% if not state_attr('sensor.pronote_edt_coralie_heute', 'edt_heute')[loop.index-1]['status'] == None %}<span>{{state_attr('sensor.pronote_edt_coralie_heute', 'edt_heute')[loop.index-1]['status']}}</span>
                      {% endif %}</td>
                      <td>{{state_attr('sensor.pronote_edt_coralie_heute', 'edt_heute')[loop.index-1]['salle']}}</td>
                  </tr>
                  {% endfor %}
            </tbody>
          </table>
        card_mod: Stil: .: | ha-card ha-markdown { padding:0px } ha-card ha-markdown.no-header { padding:0px } ha-markdown$: | div { background-color:rgb(100, 100, 100); Padding: 12px 12px; color:white; font-weight:normal; font-size:1.2em; border-top-left-radius: 5px; border-top-right-radius: 5px; } table{ Border-Collapse: Kollaps; font-size: 0.9em; font-family: Roboto; width: 100%, Outline: 0px solid #393c3d; margin-top:5px; } caption { text-align: Zentrum; font-weight: bold; font-size: 1.2em; } td { padding: 5px 10px 5px 10px; text-align: left; Border-Bottom: 0px solid #1c2020; } tr { border-bottom: 0px solid #1c2020; } tr:nth-of-type(even) { background-color: rgb(54, 54, 54, 0.3); } tr:last-of-type { border-bottom: transparent; }* mark { background: #009767; color: #222627; Border-Radius: 5px; Padding: 5px; } span { background: #EC4B34; color: #222627; Border-Radius: 5px; Padding: 5px; } span { padding: 5px; } tr:nth-child(n+2) &gt; td:nth-child(2) { text-align: left; } - Typ: Markdown Content: &gt;-
          <div>Zeitaufwand für: {{state_attr('sensor.pronote_edt_coralie_nächster Tag', 'edt_nächster Tag')[0]['date']}</div>

          <table>
              <tbody>
                  {%-for attr in states.sensor.pronote_edt_coralie_nächster Tag.attributes.edt_nächster Tag -%}
                  <tr style="background-color:#FF0000"><td>
                      {%- if state_attr('sensor.pronote_edt_coralie_nächster Tag', 'edt_nächster Tag')[loop.index-1]['Annullierung'] == false -%}
                          <mark>
                          {{state_attr('sensor.pronote_edt_coralie_nächster Tag', 'edt_nächster Tag')[loop.index-1][Uhrzeit']}}
                          </mark>
                      {%- else -%}
                          <span>
                          {{state_attr('sensor.pronote_edt_coralie_nächster Tag', 'edt_nächster Tag')[loop.index-1][Uhrzeit']}}
                          </span>
                      {% endif %}</td>
                      <td>{{state_attr('sensor.pronote_edt_coralie_nächster Tag', 'edt_nächster Tag')[loop.index-1][Uhrzeit_fin']}}</td>
                      <td>
                      {{state_attr('sensor.pronote_edt_coralie_nächster Tag', 'edt_nächster Tag')[loop.index-1]['Kurs']} {% if not state_attr('sensor.pronote_edt_coralie_nächster Tag', 'edt_nächster Tag')[loop.index-1]['status'] == None %}<span>{{state_attr('sensor.pronote_edt_coralie_nächster Tag', 'edt_nächster Tag')[loop.index-1]['status']}}</span>
                      {% endif %}</td>
                      <td>{{state_attr('sensor.pronote_edt_coralie_nächster Tag', 'edt_nächster Tag')[loop.index-1]['salle']}}</td>
                  </tr>
                  {% endfor %}
            </tbody>
          </table>
        card_mod: Stil: .: | ha-card ha-markdown { padding:0px } ha-card ha-markdown.no-header { padding:0px } ha-markdown$: | div { background-color:rgb(100, 100, 100); Padding: 12px 12px; color:white; font-weight:normal; font-size:1.2em; border-top-left-radius: 5px; border-top-right-radius: 5px; } table{ Border-Collapse: Kollaps; font-size: 0.9em; font-family: Roboto; width: 100%, Outline: 0px solid #393c3d; margin-top:5px; } caption { text-align: Zentrum; font-weight: bold; font-size: 1.2em; } td { padding: 5px 10px 5px 10px; text-align: left; Border-Bottom: 0px solid #1c2020; } tr { border-bottom: 0px solid #1c2020; } tr:nth-of-type(even) { background-color: rgb(54, 54, 54, 0.3); } tr:last-of-type { border-bottom: transparent; }* mark { background: #009767; color: #222627; Border-Radius: 5px; Padding: 5px; } span { background: #EC4B34; color: #222627; Border-Radius: 5px; Padding: 5px; } span { padding: 5px; } tr:nth-child(n+2) &gt; td:nth-child(2) { text-align: left; } - Typ: Markdown Content: |-
      <div>Pflichten</div>
      <table>
          <tbody>
              {%-for attr in states.sensor.pronote_devoir_coralie.attributes.devoir -%}
              <tr>
                  <td>
                  {%- if state_attr('sensor.pronote_devoir_coralie', 'müssen')[loop.index-1]['done'] == true -%}
                      <mark>
                      {{state_attr('sensor.pronote_devoir_coralie', 'müssen')[loop.index-1]['date']}}
                      </mark>
                  {%- else -%}
                      <span>
                      {{state_attr('sensor.pronote_devoir_coralie', 'müssen')[loop.index-1]['date']}}
                      </span>
                  {% endif %}</td>              
                  <td>{{state_attr('sensor.pronote_devoir_coralie', 'müssen')[loop.index-1]['title']}}</td>
                  <td>{{state_attr('sensor.pronote_devoir_coralie', 'müssen')[loop.index-1]['description']}}</td>
              </tr>
              {% endfor %}
        </tbody>
      </table>
    card_mod: Stil: .: | ha-card ha-markdown { padding:0px } ha-card ha-markdown.no-header { padding:0px } ha-markdown$: | h1 { font-weight: normal; font-size: 24px; } div { background-color:rgb(100, 100, 100); Padding: 12px 12px; color:white; font-weight:normal; font-size:1.2em; border-top-left-radius: 5px; border-top-right-radius: 5px; } table{ Border-Collapse: Kollaps; font-size: 0.9em; font-family: Roboto; width: Auto; Outline: 0px solid #393c3d; Margin-Top: 10px; } caption { text-align: Zentrum; font-weight: bold; font-size: 1.2em; } td { padding: 5px 5px 5px 5px; text-align: left; Border-Bottom: 0px solid #1c2020; } tr { border-bottom: 0px solid #1c2020; } tr:nth-of-type(even) { background-color: rgb(54, 54, 54, 0.3); } tr:last-of-type { border-bottom: transparent; } mark { background: #009767; color: #222627; Border-Radius: 5px; Padding: 5px; } span { background: #EC4B34; color: #222627; Border-Radius: 5px; Padding: 5px; } span { padding: 5px; } tr:nth-child(n+2) &gt; td:nth-child(2) { text-align: left; } - Typ: Markdown Content: |-
      <div>Bewertungen</div>
      <table width='100%'>
        <tbody>
        {%-for attr in states.sensor.pronote_eval_coralie.attributes.evaluation -%} {% if loop.index < 40  %}
        <tr>
        <td width='10%'>{{attr['date_courte']}}</td>
        <td width='70%'>{{attr['eval']}}</td>
        <td width='20%'>
        {%foder attr2 in attr.achats-%}{% if attr2[Akquisition_level'] == "A+" %} 💚 {% elif attr2[Akquisition_level'] == "A" %} 🟢 {% elif attr2[Acquisition_level'] == "B+" %} ⚪▼+ {% elif attr2[Akquisition_level'] == "B" %} ⚪・ {% elif attr2[Acquisition_level'] == "C+" %} 🟡+ {% elif attr2[Akquisition_level'] == "C" %} 🟡 {% elif attr2[Acquisition_level'] == "D+" %} ⚪▼+ {% elif attr2[Akquisition_level'] == "D" %} ⚪・ {% elif attr2[Acquisition_level'] == "E" %} 🔴 {% else %}{{ attr2[Erwerb _level']}} {% endif %} {% endfor %}
        </td>
        </tr>
        {% endif %} {% endfor %}
      </tbody>
      </table>
    card_mod: Stil: .: | ha-card ha-markdown { padding:0px } ha-card ha-markdown.no-header { padding:0px } ha-markdown$: | h1 { font-weight: normal; font-size: 24px; } div { background-color:rgb(100, 100, 100); Padding: 12px 12px; color:white; font-weight:normal; font-size:1.2em; border-top-left-radius: 5px; border-top-right-radius: 5px; } table{ Border-Collapse: Kollaps; font-size: 0.9em; font-family: Roboto; width: Auto; Outline: 0px solid #393c3d; Margin-Top: 10px; } caption { text-align: Zentrum; font-weight: bold; font-size: 1.2em; } td { padding: 5px 5px 5px 5px; text-align: left; Border-Bottom: 0px solid #1c2020; } tr { border-bottom: 0px solid #1c2020; } tr:nth-of-type(even) { background-color: rgb(54, 54, 54, 0.3); } tr:last-of-type { border-bottom: transparent; } mark { background: #009767; color: #222627; Border-Radius: 10px; Padding: 5px; } span { background: #EC4B34; color: #222627; Border-Radius: 10px; Padding: 5px; } span { padding: 5px; } tr:nth-child(n+2) &gt; td:nth-child(2) { text-align: left; }

Hinweis:

Ich habe die Farbe der Punkte geändert, da die Übereinstimmung mit der Pronote-Konfiguration der Einrichtung meiner Tochter nicht gut war. Für mehr Grün habe ich ein grünes Herz gesetzt.

Bonus

Aktualisierungskarte hinzugefügt

Um die Aktualisierung von alle 10 Minuten auf alle 4 Stunden zu reduzieren, habe ich die Karte aus dem obigen Bild hinzugefügt.

So haben wir die Zeit seit dem letzten Update und wenn wir ein sofortiges Update wollen, drücken wir es und starten EXECUT

Kartencode:

- Typ: custom:auto-entities card: Typ: entities filter: include: - entity_id: automation.mise_a_jour_pronote options: secondary_info: last-triggered

Beschränken Sie das Update auf Werktage

Nachdem ich das Aktualisierungstempo von 10 Minuten auf 4 Stunden gewechselt hatte, versuchte ich, dieses Update auf Arbeitstage zu beschränken.

Es gibt eine Integration, die einen binary_sensor erstellt, der uns diese Informationen gibt: Workday.

Ich gehe nicht in ihre Einrichtung, die Doc ist einfach.

Sodann habe ich in die Automatisierung nur eine Bedingung aufgenommen:

Hier der vollständige Automatisierungscode:

alias: Aktualisiert Pronote Beschreibung: "" Trigger: - platform: time_pattern hours: /4 Bedingung: - Typ: is_on Bedingung: device device_id: 64bbc75616d4864596e1fd861c865904 entity_id: binary_sensor.workday domain: binary_sensor action: - Dienststelle: shell_command.update_pronote data: {} Modus: Single

Edit : ein Neues Tutorial Das Pronote-Update kann nur an Werktagen und außerhalb der Schulferien ausgelöst werden.

Verknüpfen Sie Pronote mit dem Auslösen des Alexa-Weckrufs

Das wird der nächste Schritt sein

Aber ich werde dies an einem Sonntag planen, um morgen eine Unterrichtsbeginnzeit zu haben