Pronote dans HA - les notes, devoirs, absences et emploi du temps des enfants

merci @vingerha
J’ai corrigé aussi de mon coté (légèrement différemment) et j’ai mis à jour sur GitHub :

Et bien en fait ca serait possible en théorie mais il faudrait pouvoir mettre une 3eme balise différente pour le « modifié ».
Car vous aurez remarqué que je met un mark pour les cours standard et un span pour les cours annulés
Pourquoi car je ne peux pas ajouter un style à la balise pour faire :
<span class="normal"> ou <span class="annule">
Du coup, il faudrait trouver une 3eme balise html qui ne servent a rien pour gérer le modifié

@HollyFredD, Bravo ! c’est une super nouvelle ça !
pourrais-tu stp détailler en quelques point l’installation du script quand tu es sous HASS OS stp ?
Pour que je mette dans la doc pour tout ceux qui sont comme sur sur HASS OS
Merci d’avance

Voici le markdown du wiki que je me suis fait (il manque les étapes de créations des sensors, c’est du work in progress. Mais les étapes pour le script sont là)
Attention, j’ai vraiment pas mal bidouillé avant que ça ne « tombe en marche ». J’espère que sur une install « propre » ces étapes suffisent à rendre le script fonctionnel.

Je passe par un shell_command pour lancer le script, et j’execute de façon régulière (toutes les heures) ce shell_command via une time_based automation (non décris encore dans le doc)

Pronote

Configuration add-on SSH

Toute la configuration peut se faire en se connectant en SSH à HomeAssistant grâce à l’add-on suivant :

image.png

Dans l’onglet configuration, il faut penser à configurer le username et password

Puis ajouter dans la section init_commands la ligne suivante et bien penser à cliquer sur save

pip install pronepy

Cela aura pour effet d’installer le package pronotepy au démarrage de HomeAssistant.

Pour vous connecter en SSH à votre HassOS via votre terminal préféré :

ssh identifiant_ssh_hassos@ip_hassos

Puis saisir votre mot de passe

Installation des scripts et librairie Python

Créer un répertoire /config/python_scripts puis le sous-répertoire /config/python-scripts/local-packages

mkdir /config/python_scripts
mkdir /config/python_scripts/local-packages

Puis ajouter :

1/ Le script python pronote.py via la commande dans le répertoire python-scripts

curl https://raw.githubusercontent.com/dathosim/Pronote2Homeassistant/main/pronote.py --outpout /config/python_scripts/pronote.py

2/ Les librairies nécessaires dans le répertoire local-packages

Il faut ensuite récupérer les packages pronotepy qui est dans /usr/lib/pythonx.x/site-packages (remplacer « x.x » par la version python installée chez vous, au moment de la rédaction de ce wiki j’ai la 3.10) et copier cette librairie dans le répertoire /config/python_scripts/local-packages

cp -R /usr/lib/python3.10/site-packages/pronotepy /config/python_scripts/local-packages/

Il faut maintenant, dans le script pronote.py , récupéré plus tôt, préciser où se trouvent les « local-packages » (et donc pronotepy), pour cela il faut ajouter/modifier le code suivant tout en haut du fichier pronote.py (si vous n’ajouter pas cela en haut du fichier, cela ne marchera pas)

Création des shell_commands pour automatiser l’exécution de script

Dans /config/devices/shell_commands.yaml il faut ajouter :

pronote_update: python3 /config/python_scripts/pronote.py

pré-requis : il faut avoir la ligne suivante dans son configuration.yaml

shell_command: !include devices/shell_commands.yaml
1 « J'aime »

Bonjour à tous,

Petit soucis et besoin d’aide du coup.

Pour se connecter, je passe par :

Et ensuite je suis redirigé vers :
https://0340034c.index-education.net/pronote/eleve

Par conséquent, j’ai renseigné le fichier pronote.py :

prefix_url = « 0340034c »
type_compte = « eleve »
username=« user2mafille »
password=« password2mafille »
ent = None (pas compris cette variable)

Je un test avec le script seul mais ça ne parvient pas à se connecter j’ai l’impression.

root@hass:/usr/share/hassio/homeassistant/python_scripts# /usr/bin/python3 /usr/share/hassio/homeassistant/python_scripts/pronote.py
Erreur de connexion à Pronote (sans ENT) avec le compte élève - vérifier les paramètres
Traceback (most recent call last):
  File "/usr/share/hassio/homeassistant/python_scripts/pronote.py", line 60, in <module>
    if client.logged_in:
NameError: name 'client' is not defined
root@hass:/usr/share/hassio/homeassistant/python_scripts#

Merci

Bonsoir @noabeuh, (Benoît?)
En fait, tu te connecte via un cas (central authentification service) qui semble être celui de l’ent (espace numérique de travail) de l’occitannie
Du coup il faut que tu mettes la variable à 1
Mais aussi il faudrait vérifier que ce cas et cet ent est pris en charge par la lib pronotepy Que j’utilise dans cette intégration…

1 « J'aime »

Bonjour @Dathosim, (Damien?) :sweat_smile:

Merci pour ton retour, Je crois que je ne suis pas le seul.

Je essayer de creuser.

Merci

@Dathosim : une idée ?

Sinon j’ai aussi ceci dans mes logs HA

Logger: homeassistant.helpers.template_entity
Source: helpers/template_entity.py:356
First occurred: 22:53:37 (8 occurrences)
Last logged: 22:53:37

TemplateError('UndefinedError: None has no element 2') while processing template 'Template("{{ state_attr('sensor.pronote_edt_lou_prochain_jour', 'edt_prochainjour')[2]['annulation'] }}")' for attribute '_state' in entity 'binary_sensor.pronote_edt_lou_prochain_jour_cours_2_annulation'
TemplateError('UndefinedError: None has no element 3') while processing template 'Template("{{ state_attr('sensor.pronote_edt_lou_prochain_jour', 'edt_prochainjour')[3]['annulation'] }}")' for attribute '_state' in entity 'binary_sensor.pronote_edt_lou_prochain_jour_cours_3_annulation'
TemplateError('UndefinedError: None has no element 4') while processing template 'Template("{{ state_attr('sensor.pronote_edt_lou_prochain_jour', 'edt_prochainjour')[4]['annulation'] }}")' for attribute '_state' in entity 'binary_sensor.pronote_edt_lou_prochain_jour_cours_4_annulation'
TemplateError('UndefinedError: None has no element 5') while processing template 'Template("{{ state_attr('sensor.pronote_edt_lou_prochain_jour', 'edt_prochainjour')[5]['annulation'] }}")' for attribute '_state' in entity 'binary_sensor.pronote_edt_lou_prochain_jour_cours_5_annulation'
TemplateError('UndefinedError: None has no element 6') while processing template 'Template("{{ state_attr('sensor.pronote_edt_lou_prochain_jour', 'edt_prochainjour')[6]['annulation'] }}")' for attribute '_state' in entity 'binary_sensor.pronote_edt_lou_prochain_jour_cours_6_annulation'

quelqu’un as une solution pour régler ce soucis ? il me semble avoir déjà vu des annuls et deplacement de cours donc ça doit fonctionner …

@Plouf34

Hello, comment as tu rösolu ce point ? j’ai moi aussi une erreur comme tu avais si j’enlève les __ autour de file. Merci de ton aide ou de celui qui aura la réponse

J’ai commencé à travailler dessus.
J’ai créé un nouveau fichier json : pronote_last_notes_« +eleve_id+ ».json (vide au départ avec {})
Et modifié le fichier python comme ceci :

J’ai déplacé cette ligne tout en haut ou presque

location = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))

Puis modifié la partie des notes

#Lecture notes déjà transmises (créer le fichier vide avec {} la première fois)
    lastNotes = {}
    with open(os.path.join(location, "../www/pronote_last_notes_"+eleve_id+".json")) as outfile:
        lastNotes = json.load(outfile)

    #Transformation des notes en Json
    jsondata['note'] = []
    jsondata['noteSensor'] = []
    for grade in grades:
        index_note += 1
        if index_note == limit_note:
            break
        uniqid = ""
        commentaire = ""
        if grade.comment != '' :
            commentaire = ' ('+grade.comment+')'
        noteJson = {
            'date': grade.date.strftime("%d/%m/%Y"),
            'date_courte': grade.date.strftime("%d/%m"),            
            'cours': grade.subject.name+commentaire,
            'note': grade.grade,            
            'sur': grade.out_of,
            'note_sur': grade.grade+'\u00A0/\u00A0'+grade.out_of,
            'coeff': grade.coefficient,
            'moyenne_classe': grade.average,           
            'max': grade.max,
            'min': grade.min,
            'comment': grade.comment,
        }
        jsondata['note'].append(noteJson)
        uniqid = grade.date.strftime("%d/%m/%Y")+grade.subject.name+commentaire+grade.grade
        if uniqid not in lastNotes :
            jsondata['noteSensor'].append(noteJson)
        lastNotes[uniqid] = ""

Et à la fin :

with open(os.path.join(location, "../www/pronote_last_notes_"+eleve_id+".json"), "a") as outfile:
        outfile.truncate(0)
        json.dump(lastNotes, outfile, indent=4)

Et j’ai créé un nouveau sensor :

- platform: rest
  name: pronote_sensor_note_camille
  scan_interval: 10
  json_attributes:
    - noteSensor
  value_template: |
    {% if value_json.noteSensor.3 is defined %}
        Camille a 4 nouvelles notes :
        {{ value_json.noteSensor.0.cours }} : {{ value_json.noteSensor.0.note_sur }}, coefficient : {{ value_json.noteSensor.0.coeff }}, moyenne : {{ value_json.noteSensor.0.moyenne_classe }}, min : {{ value_json.noteSensor.0.min }}, max : {{ value_json.noteSensor.0.max }}
        {{ value_json.noteSensor.1.cours }} : {{ value_json.noteSensor.1.note_sur }}, coefficient : {{ value_json.noteSensor.1.coeff }}, moyenne : {{ value_json.noteSensor.1.moyenne_classe }}, min : {{ value_json.noteSensor.1.min }}, max : {{ value_json.noteSensor.1.max }}
        {{ value_json.noteSensor.2.cours }} : {{ value_json.noteSensor.2.note_sur }}, coefficient : {{ value_json.noteSensor.2.coeff }}, moyenne : {{ value_json.noteSensor.2.moyenne_classe }}, min : {{ value_json.noteSensor.2.min }}, max : {{ value_json.noteSensor.2.max }}
        {{ value_json.noteSensor.3.cours }} : {{ value_json.noteSensor.3.note_sur }}, coefficient : {{ value_json.noteSensor.3.coeff }}, moyenne : {{ value_json.noteSensor.3.moyenne_classe }}, min : {{ value_json.noteSensor.3.min }}, max : {{ value_json.noteSensor.3.max }}
    {% elif value_json.noteSensor.2 is defined %}
        Camille a 3 nouvelles notes :
        {{ value_json.noteSensor.0.cours }} : {{ value_json.noteSensor.0.note_sur }}, coefficient : {{ value_json.noteSensor.0.coeff }}, moyenne : {{ value_json.noteSensor.0.moyenne_classe }}, min : {{ value_json.noteSensor.0.min }}, max : {{ value_json.noteSensor.0.max }}
        {{ value_json.noteSensor.1.cours }} : {{ value_json.noteSensor.1.note_sur }}, coefficient : {{ value_json.noteSensor.1.coeff }}, moyenne : {{ value_json.noteSensor.1.moyenne_classe }}, min : {{ value_json.noteSensor.1.min }}, max : {{ value_json.noteSensor.1.max }}
        {{ value_json.noteSensor.2.cours }} : {{ value_json.noteSensor.2.note_sur }}, coefficient : {{ value_json.noteSensor.2.coeff }}, moyenne : {{ value_json.noteSensor.2.moyenne_classe }}, min : {{ value_json.noteSensor.2.min }}, max : {{ value_json.noteSensor.2.max }}
    {% elif value_json.noteSensor.1 is defined %}
        Camille a 2 nouvelles notes :
        {{ value_json.noteSensor.0.cours }} : {{ value_json.noteSensor.0.note_sur }}, coefficient : {{ value_json.noteSensor.0.coeff }}, moyenne : {{ value_json.noteSensor.0.moyenne_classe }}, min : {{ value_json.noteSensor.0.min }}, max : {{ value_json.noteSensor.0.max }}
        {{ value_json.noteSensor.1.cours }} : {{ value_json.noteSensor.1.note_sur }}, coefficient : {{ value_json.noteSensor.1.coeff }}, moyenne : {{ value_json.noteSensor.1.moyenne_classe }}, min : {{ value_json.noteSensor.1.min }}, max : {{ value_json.noteSensor.1.max }}
    {% elif value_json.noteSensor.0 is defined %}
        Camille a une nouvelle note :
        {{ value_json.noteSensor.0.cours }} : {{ value_json.noteSensor.0.note_sur }}, coefficient : {{ value_json.noteSensor.0.coeff }}, moyenne : {{ value_json.noteSensor.0.moyenne_classe }}, min : {{ value_json.noteSensor.0.min }}, max : {{ value_json.noteSensor.0.max }}
    {% else %}
        None
    {% endif %}
  resource: 'pronote_camille.json'

Voilà comme ça je ne suis plus dérangé si le prof change une note d’un autre élève ce qui change la moyenne.
Et je suis prévenu s’il y a plusieurs notes à la fois ou si plusieurs notes dans la journée, je n’ai plus à chaque fois que la première.
Si vous voyez une solution pour avoir une meilleure écriture pour le sensor et gérer x notes, je prends :slight_smile:

1 « J'aime »

Pour nous qui utilisent « pronote » dans HomeAssistent, je voudrais bien vous attender sur le fait que le tout est possible que avec le top travail de la part des devs de pronotepy.
Sans leurs efforts il n’y avait pas une interface qui permets de gratuitement connecter avec des dizaines de CAS/ENT spécifiques.
Même si leur travail n’est pas fait pour gagner de l’argent…je pense qu’une petite geste sera bien apprécié.

bain3/pronotepy (Raised $0.00) - Issuehunt

1 « J'aime »

@vingerha : tu as entièrement raison !
J’ai donc fait un geste ! :relaxed: (symbolique pour l’instant…)
Et ca me donne envie d’ouvrir la cagnotte pour mon intégration et reverser à pronotepy quand ça demande une correction/évolution de son côté

Salut.
j’aimerais que le sensor début de cours se mette à jours en cas d’annulation de cours.
est-ce possible ?

@jimsaye : la mise à jour du sensor « début de cours » en cas d’annulation est déjà prise en compte
(ou alors y’a un bug…)

@jimsaye : Désolé ! je viens de vérifier et j’avais fait une motification sur le script pour le gérer mais je ne l’avais jamais poussé la modification sur Github :frowning:
Et pourtant j’avais poussé le Lovelace associé (encore plus grave)
Dans ton cas tu mets à jour le script avec celui que je viens de pousser et ça va marcher !

@Specnaz :
je viens de corriger le problème que tu as sur les 2 premiers sensor qui affichent « pas cours »
En fait, j’avais fait une évolution du script pour gérer (dans le script) l’heure de début qui tient compte des cours annulé (pour pas le faire dans le template - mais elle n’avait pas été poussé sur Github
Je viens de le faire
tu peux reprendre la dernière version du script pronote.py
voila le dernier commit

NB : le lovelace associé est déjà bon :wink:

Merci je vais regarder ça

Un grand merci à toi !

@Dathosim

Dans le log j’ai encore celles là qui trainent :
Logger: homeassistant.helpers.template_entity
Source: helpers/template_entity.py:356
First occurred: 00:26:36 (12 occurrences)
Last logged: 00:26:36

Logger: homeassistant.helpers.template_entity
Source: helpers/template_entity.py:356
First occurred: 00:26:36 (12 occurrences)
Last logged: 00:26:36

   TemplateError('UndefinedError: None has no element 4') while processing template 'Template("{{ state_attr('sensor.pronote_edt_lou_prochain_jour', 'edt_prochainjour')[4]['annulation'] }}")' for attribute '_state' in entity 'binary_sensor.pronote_edt_lou_prochain_jour_cours_4_annulation'
   TemplateError('UndefinedError: None has no element 5') while processing template 'Template("{{ state_attr('sensor.pronote_edt_lou_prochain_jour', 'edt_prochainjour')[5]['annulation'] }}")' for attribute '_state' in entity 'binary_sensor.pronote_edt_lou_prochain_jour_cours_5_annulation'
   TemplateError('UndefinedError: None has no element 6') while processing template 'Template("{{ state_attr('sensor.pronote_edt_lou_prochain_jour', 'edt_prochainjour')[6]['annulation'] }}")' for attribute '_state' in entity 'binary_sensor.pronote_edt_lou_prochain_jour_cours_6_annulation'

@Specnaz :
Ça en fait ça peut arriver et c’est normal
Pour la recherche des annulations, le system créé des sensors pour chaque heure de la journée de cours - mais si l’élève n’a que 3 heures de cours alors les derniers sensor son crée en erreur…
Il faudrait que je trouve un moyen plus clean…