[Article] Intégration Pronote : cours, devoirs, notes, etc (archive)

Hello @mael,

Merci pour ton retour !
Du coup quelques infos qui m’intéressent :

  • est-ce que tu utilises le login via compte parent ou enfant ?
  • est-ce que tu utilises le login via user / password ou QRCode ?

De mon côté j’ai retesté encore pas mal de choses, et même avec la récupération uniquement des infos basiques sur l’élève (donc pas les cours, les devoirs, etc.), et en ajoutant uniquement ce sensor, le phénomène se produit encore.
L’avantage de tout ça c’est que ça m’a permet d’optimiser pas mal de choses au passage, voyons le bon côté des choses en attendant !

Bonjour, moi aussi, j’en ai presque autant d’indisponibles :

  • delays

  • evaluations

  • information_and_surveys

  • menus

  • punishments

  • timetable_ical_url

Bonjour @Dathosim , superbe card…
de quel côté dois-je chercher pour corriger ces 2 problèmes que sont fonts blancs et l’absence des trais de couleurs sur la gauche?

Hello,
installé en octobre il me semble. Avec login parent avec password.

J’ai activé tous les sensors dispos dès le début pour voir mais je n’affiche que timetable_today, devoirs, moyennes, notes, absences et évaluations.

Côté négatif j’ai aussi ce message dans les logs:

State attributes for sensor.pronote_xxx_xxx_timetable_period exceed maximum size of 16384 bytes.
This can cause database performance issues; Attributes will not be stored

et des sondes unavailables:
delays, menus, punishments, timetable_ical_url, timetable_tomorrow

Bizarre ça … je suis en parents / mot de passe pour 3 enfants et c’est impressionnant de réactivité pour toutes les infos…!

Salut,

As-tu ces infos dans Pronote ? Car il est possible que tu ais des enfants modèle, jamais punis et jamais en retard ce qui expliquerait delays et punishments. Hélas, moi je les ai bien.
Pour le menu de cantine idem. Dispo dans Pronotes ? Parce que ça dépend si l’école ne le founi pas, il n’y aura pas de sensor dispo.
Pour timetable_ical_url et timetable_tomorrow, je ne les ai pas non plus.

Tout le monde a ce message également. @delphiki y travail d’arrache pieds mais je ne crois pas que ce soit bloquant.

Donc en résumé rien d’anormal à priori dans ta config (sous réserve que tu ais vraiment des enfants modèles…)

salut @delphiki sur le carte que tu a faite pour t’on intergation, on peut mettre que les cours pas les notes et évaluations?
je parle de ce hacs la

Hello !

Pour faire un retour sur les différents points :

  • le sujet des sensors indisponibles, je vais essayer de faire en sorte que ça soit moins perturbant, parce qu’effectivement aujourd’hui, si par exemple, il n’y a pas de notes, le sensor est indisponible, je vais regarder pour plutôt mettre le sensor mais vide
  • pour les sensors avec le message state attributes for ... exceed maximum size of 16384 bytes. c’est un souci connu, pas impactant normalement, j’avais commencé à essayer de faire un truc en auto, mais je pense que le plus cohérent sera de donner la main sur le nombre de jours / infos max à récupérer pour pas dépasser cette limite
  • @titof2375 pour les cartes, pour le moment effectivement, il n’y a que l’emploi du temps de dispo, je vais essayer de trouver du temps ce week-end pour en rajouter d’autres
3 « J'aime »

Bonjour @delphiki ,
D’abord merci pour tout ce travail d’intégration qui est fait et ces cards, c’est vraiment formidable!
Dans mon cas, je ne pense malheureusement pas que le sensor sensor.pronote_balac_samuel_information_and_surveys soit indisponible à cause de l’absence d’évènement…
Cela n’est pas pour me vanter… mais mon fils arrive à avoir jusqu’à 6 observations ou retards par jour! :imp: Peut-être que cela congestionne les serveurs! :crazy_face:
Les observations sont bien dans sensor.pronote_balac_samuel_information_and_surveys?
Les Encouragements (on en sait jamais!) aussi?

Le trimestre est reset depuis début janvier pour moi les unavailables sont vide pour punishments (celui la je sais qu’il a déja fonctionné…grr).

La cantine n’a jamais été visible et n’est pas renseignée dans pronote.

par contre timetable_ical_url et timetable_tomorrow n’ont jamais fonctionné:
pour timetable_ical_url c’est supposé être accessible depuis l’interface de pronote? que j’aille vérifier si il existe (pas que je m’en serve, juste pour troubleshoot).

timetable_tomorrow est le seul que je vois pas comment troubleshoot

@delphiki +1 pour la distinction entre sensor indispo et vide. l’état pourrait servir en automatisation

Hello tout le monde,

petit retour d’expérience pour ma part:

  • je n’ai pas réussi à mettre en place les cours qui se grisent au fur et à mesure de la journée. Le jour J, je vois J+1.
  • bug sur l’emploi du temps lorsqu’il n’y a pas cours le lendemain (avec des professeurs absents par exemple). Ca m’affiche une erreur ValueError: template error: as_timestamp got invalid input 'None' .... qui est due aux heures de début et de fin qui forcément créer le problème car les cours ont été annulés.
    Sinon que du bonheur, j’ai réussi à afficher les cartes des moyennes, notes, etc… avec un peu de persévérance :sweat_smile:
    Ce qui m’a pris du temps, c’est d’arriver à comprendre qu’il fallait installer pronotepy afin de pouvoir automatiser la mise à jour de pronote et de m’afficher la notification voulue :slight_smile:

salut, essai avec ça:

type: custom:pronote-timetable-card
entity: sensor.pronote_XXXXXX_timetable_next_day
display_lunch_break: true
display_classroom: true
display_teacher: true
display_day_hours: true
dim_ended_lessons: true
max_days: null
current_week_only: false

Moi, c’est les ‹ notes › impossible à trouver le bon code. et la partie ‹ evaluation › qui n’a pas le bon format
image
ou
image
j’y travail…

Ca doit être du côté du fichier pronote.jinja que l’erreur vient je pense (surtout la partie styles)

Si ça peut t’aider, mon fichier pronote.jinja

{%- macro averages_markdown(firstname, sensor_id) -%}
{% set items = state_attr(sensor_id,'averages') %}
<div>{{ firstname }} - Moyennes</div>
<table>
        {%- for item in items -%}
            <tr>
                <td><span></span></td>
                <td ><b>{{ item.subject}}</b></td>
                <td width="50%">
                    <b>{{ item.average }} </b>
                    <ul>
                    <li>Moy. cl.: {{ item.class}}</li>
                    <li>Min / max : {{ item.min }} / {{ item.max }}</li>
                    </ul>
                </td>
                <td><span></span></td>
           </tr>
        {% endfor %}
</table>
{% endmacro %}

{%- macro absences_markdown(firstname, sensor_id) -%}
{% set items = state_attr(sensor_id,'absences') %}
<div>{{ firstname }} - Absences</div>
<table>
        {%- for item in items -%}
        <tr>
            <td width="50%">
                Le {{as_timestamp(item.from) | int | timestamp_custom('%d %b à %Hh%M', true)}}
                <br>{{item.hours}} de cours manqué(s)</td>
            <td>
            {%- if item.reason == '' -%}
                Motif : inconnu<br/>
            {%- else -%}
                Motif : {{item.reason}}<br/>
            {% endif %}
            {%- if item.justified == false -%}
                <span>Absence non justifiée</span>
            {%- else -%}
                <mark>Absence justifiée</mark>
            {% endif %}</td>               
        </tr>
        {% endfor %}
</table>
{% endmacro %}

{%- macro homework_markdown(firstname, sensor_id) -%}
{% set days = ["Lun.", "Mar.", "Mer.", "Jeu.", "Ven.", "Sam.", "Dim."] %}
{% set items = state_attr(sensor_id,'homework') %}
<div>{{ firstname }} - Devoirs</div>
<table>
        {%- for item in items -%}
            <tr>
                <td>{{ days[item.date.strftime('%w')| int - 1] }} {{as_timestamp(item.date) | int | timestamp_custom('%d/%m', true)}}</td>
                <td><span></span></td>
                <td><b>{{ item.subject}}</b><br>{{ item.short_description }}</td>
                <td>{%- if item.done -%}<mark>fait</mark>{%- else -%}<span>non fait</span>{%- endif -%}</td>
           </tr>
        {% endfor %}
</table>
{% endmacro %}

{%- macro grades_markdown(firstname, sensor_id) -%}
{% set items = state_attr(sensor_id,'grades') %}
<div>{{ firstname }} - Notes</div>
<table>
        {%- for item in items -%}
            <tr>
                <td width="20%">{{as_timestamp(item.date) | int | timestamp_custom('%d/%m', true)}}</td>
                <td><span></span></td>
                <td><b>{{ item.subject}}</b><br>{{ item.comment }}</td>
                <td width="30%"><b>{{ item.grade_out_of }} </b>
                {%- if item.coefficient != '1' -%} <i>(Coeff. : {{ item.coefficient }})</i>{% endif %}
                <ul>
                <li>Moy. cl.: {{ item.class_average }}</li>
                <li>Min-max : {{ item.min }}-{{ item.max }}</li>
                </ul>
                </td>
           </tr>
        {% endfor %}
</table>
{% endmacro %}

{%- macro timetable_markdown(firstname, sensor_id) -%}
    {%- if as_timestamp(now())|int <= as_timestamp((state_attr(sensor_id, 'lessons')|last).end_at)|int + (3*3600) -%}
        {% set days = ["Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche"] %}
        {% set items = state_attr(sensor_id,'lessons') %}
        <div>
            {{ firstname }} - {{ days[items[0].start_at.strftime('%w')| int - 1] }} {{as_timestamp(items[0].start_at) | int | timestamp_custom('%d/%m', true)}}
            - Début à {{ as_timestamp(state_attr(sensor_id,'day_start_at'))| int | timestamp_custom('%-Hh%M', true) }}
        </div>
        <table>
            {%- for item in items -%}
                {%- set canceled = item.canceled == True -%}
                <tr>
                    <td>
                        {{ item.start_time }}<br />
                        {{ item.end_time }}
                    </td>
                    <td>
                        <span></span>
                    </td>
                    <td>
                        {%- if canceled -%}<del>{%- endif -%}
                        <b>{{ item.lesson }}</b>
                        {%- if canceled -%}</del>{%- endif -%}
                        <br />
                        Salle {{ item.classroom }}
                    </td>
                    <td>
                        {%- if canceled -%}
                            <mark>{{ item.status }}</mark>  
                        {%- elif item.status != None -%}
                            <span>{{ item.status }}</span>
                        {%- endif -%}
                    </td>
                </tr>
            {%- endfor -%}
        </table>
    {%- endif -%}
{% endmacro %}

{%- macro pronote_styles(sensor_id) -%}
    div {
        background-color:rgb(50, 50, 50);
        padding: 12px;
        color:white;
        font-weight:normal;
        font-size:1em;
        border-top-left-radius: 5px; 
        border-top-right-radius: 5px;
    }
    table{
        font-size: 0.9em;
        font-family: Roboto;
        width: 100%;
        outline: 0px solid #393c3d;
        margin-top:5px;
        border-collapse: collapse;
    }
    td {
        vertical-align: middle;
    }
    tr td:last-child span {
        background-color: #EC4B34;
        padding: 4px;
        border-radius: 4px;
        white-space: nowrap;
    }
    mark {
        background-color: #009767;
        padding: 4px;
        border-radius: 4px;
        white-space: nowrap;
        margin-top:3px
    }
    @media (prefers-color-scheme: dark) {
        tr:nth-child(even) {background-color: var(--divider-color);}
    }
    @media (prefers-color-scheme: light) {
        tr:nth-child(odd) {background-color: #e9e9e9;}
    }    
    tr td:nth-child(2) {
        width: 4px;
        padding: 5px 0;
    }

    {%- if sensor_id == "sensor.pronote_XXX_XXX_timetable_next_day" -%}
     
        tr td:nth-child(2) > span {
            display:inline-block;
            width: 4px;
            height: 3rem;
            border-radius:4px;
        }
    
        tr td:first-child {
            width: 13%;
            text-align:right;
        }
        {%- set items = state_attr(sensor_id, 'lessons') -%}
        {%- for i in range(0, items | count, 1) -%}
            {%- if items[i].canceled and not loop.last and items[i].start_at == items[i+1].start_at -%}
                tr:nth-child({{ loop.index }}) {
                    display:none;
                }
            {%- else -%}
                tr:nth-child({{ loop.index }}) td:nth-child(2) > span {
                    background-color: {{items[i].background_color}};
                }
                {% if as_timestamp(now()) > as_timestamp(items[i].end_at) %}
                    tr:nth-child({{ loop.index }}) {
                        opacity: 0.3;
                    }
                {% endif %}
            {%- endif -%}
        {%- endfor -%}
    {% endif %}
    
    {%- if sensor_id == "sensor.pronote_XXX_XXX_homework" -%}
        tr td:nth-child(2) > span {
            display:inline-block;
            width: 4px;
            height: 3rem;
            border-radius:4px;
        }
        
        tr td:first-child {
            width: 13%;
            text-align:center;
            white-space: nowrap;
        }

        {%- set items = state_attr(sensor_id, 'homework') -%}
        {%- for i in range(0, items | count, 1) -%}
            tr:nth-child({{ loop.index }}) td:nth-child(2) > span {
                background-color: {{items[i].background_color}};
            }

        {%- endfor -%}
    {% endif %}


    {%- if sensor_id == "sensor.pronote_XXX_XXX_grades" -%}
        tr td:nth-child(2) > span {
            display:inline-block;
            width: 4px;
            height: 3rem;
            border-radius:4px;
        }
        tr td:first-child {
            width: 15%;
            text-align:center;
            white-space: nowrap;
        }
        tr td:last-child {
            width: 30%;
            white-space: nowrap;
        }
        
        {%- set items = state_attr(sensor_id, 'grades') -%}
        {%- for i in range(0, items | count, 1) -%}
            {%- if items[i].grade >= items[i].class_average -%}
                tr:nth-child({{ loop.index }}) td:nth-child(2) > span {
                    background-color: #009767;
                }
            {%- else -%}    
                tr:nth-child({{ loop.index }}) td:nth-child(2) > span {
                    background-color: #EC4B34;
                }
            {%- endif -%}    
        {%- endfor -%}
    {% endif %}

    ul{
        padding: 0px 0px 0px 0px;
        margin: 0px;
        list-style-type:none;
    }
    li{
        padding: 0px 0px 0px 0px;
        font-style: italic;
        color: grey;
    }
    tr.canceled td {
        color: orange;
    }
    td {
        padding: 5px 10px 5px 10px;
        text-align: left;
        vertical-align: top;
    }
    {%- if sensor_id == "sensor.pronote_XXX_XXX_averages" -%}
        tr td:last-child > span {
            display: inline-block;
            width: 4px;
            max-width: 4px;
            height: 3rem;
            border-radius: 4px;
        }
        tr td:nth-child(1) > span {
            display:inline-block;
            width: 4px;
            height: 3rem;
            border-radius:4px;
        }

        
        {%- set items = state_attr(sensor_id, 'averages') -%}
        {%- for i in range(0, items | count, 1) -%}
            tr:nth-child({{ loop.index }}) td:nth-child(1) > span {
                background-color: {{items[i].background_color}};
            }
            {%- if items[i].average >= items[i].class -%}
                tr:nth-child({{ loop.index }}) td:last-child > span{
                    background-color: #009767;

                    padding: 0px;
                }
            {%- else -%}    
                tr:nth-child({{ loop.index }}) td:last-child > span {
                    background-color: #EC4B34;
                    padding: 0px;
                }
                tr:nth-child({{ loop.index }}) td:last-child {
                    color: #EC4B34;
                }
                tr:nth-child({{ loop.index }}) td:last-child > ul {
                    color: var(--primary-text-color);
                }
                
            {%- endif -%}
            
            
        {%- endfor -%}
    {% endif %}
{%- endmacro -%}

tu le trouves où ton fichier ‹ pronote.jinja › ?

Je l’ai créé et mis dans config/custom_templates

C’est bon. Concernant aux heures qui ne se grisaient pas, j’ai trouvé la solution.
Plutôt que de mettre entity: sensor.pronote_xxxx_xxxx_timetable_next_day
J’ai mis entity: sensor.pronote_xxxx_xxxx_timetable_today

plutôt logique puisque le next day n’est pas passé donc les cours ne peuvent pas être grisés.

Effectivement j’avais bêtement recopié le code un peu plus haut tel quel :sweat_smile:

Bonjour, j’ai intégré pas mal de carte de Pronote, mais je n’arrive pas à intégrer une pour les notes qui fonctionne, pourriez vous me partage un exemple de code? merci :slight_smile: