release: 1.15.2+etm64 — dégel de la courbe, clé de contenu, et la règle de fusion écrite

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015F7G5VeaPVSMeVNjiGj36p
This commit is contained in:
Patrick Schurig 2026-09-09 07:56:31 +02:00
parent ee6f1732bc
commit a8947579fa
3 changed files with 82 additions and 1 deletions

View File

@ -1,3 +1,25 @@
powersync-energy-plugin-nymea (1.15.2+etm64) trixie; urgency=medium
* DÉGEL. +etm63 refusait toute réécriture sous un issuedAt inchangé et gelait
donc la courbe sur les valeurs du premier cycle — un écran aurait montré une
prévision périmée sans le dire. La clé de révision devient LE CONTENU :
(targetAt, siteId, empreinte de la courbe). issuedAt et model deviennent des
attributs, et issuedAt peut être NUL.
* Établi par le producteur, mesuré : issuedAt vient d'un domaine AROME qui ne
fournit AUCUNE valeur de la courbe, et le domaine qui les fournit n'expose
pas de meta.json. Le seamless fusionne deux domaines avec une rampe sur les
trois dernières heures du domaine 15 min — les premiers créneaux sont donc
plus FRAIS que le reste, et un seul instant ne peut pas dater la courbe.
* issuedAt == 0 n'est plus un refus : refuser une courbe exploitable parce
qu'un ATTRIBUT manque reviendrait à jeter la donnée pour son étiquette.
* Les instants d'observation passent en millisecondes : deux révisions écrites
dans la même seconde n'étaient pas ordonnables, et la courbe rendait des
doublons.
* Migration v1 -> v2 : la table est REFAITE. Les lignes v1 sont clés sur un run
qui n'identifie pas leur contenu et ont pu être écrasées.
-- Patrick Schurig <etm.schurig@gmail.com> Wed, 09 Sep 2026 07:56:31 +0200
powersync-energy-plugin-nymea (1.15.2+etm63) trixie; urgency=medium
* UN RUN EST IMMUABLE. Des valeurs neuves sous une identité de run connue

View File

@ -951,3 +951,55 @@ satellite, mélange court terme, ou deux domaines sur des runs différents. **Il
diverge et sur quelles heures.** L'explication n'est pas acquise, et rien ici n'en dépend : la clé
de contenu est correcte quelle qu'en soit la cause. C'est précisément pourquoi elle est
préférable — elle n'a pas besoin qu'on comprenne le producteur pour être juste.
---
## 21. Pourquoi les créneaux proches bougent — la règle de fusion du `seamless`
Le producteur a établi le mécanisme, **mesuré et non documenté par Open-Meteo** :
```
seamless = w · arome_france_15min + (1 − w) · arome_france
```
Relevé le 2026-09-09 à 07:45, `w` déduit valeur par valeur : `w = 1,00` de 08:00 à 10:00,
`0,75` à 11:00, `0,50` à 12:00, `0,25` à 13:00, `0` à partir de 14:00. **Rampe linéaire sur les
trois dernières heures de l'horizon du domaine 15 min.**
**Ce n'est pas un nowcast satellite** — le produit satellite ne sert que le passé, vérifié. Les
premières heures **égalent exactement** `arome_france_15min` : c'est le **même modèle, initialisé
plus récemment**.
### 21.1 L'ÂGE EST LOCAL AU CRÉNEAU, pas global à la courbe
Le domaine 15 min se rafraîchit **toutes les heures**, l'horaire **toutes les 3 h**. Les ~5
premiers créneaux sont donc plus frais que le reste — **et c'est l'unique raison pour laquelle
ils bougent sous un `issuedAt` stable.**
> **Un seul `issuedAt` ne peut pas dater une courbe dont les parties n'ont pas le même âge.**
Ce n'est pas une limite de notre modèle : c'est une propriété de la donnée. La clé de contenu s'en
accommode sans rien savoir du mécanisme — **c'est précisément pourquoi elle est préférable.**
**Conséquence à tenir** : `issuedAt` est stocké comme attribut de la RÉVISION et n'est **pas**
publié par créneau. Il ne doit pas le devenir : le publier point par point affirmerait un âge
uniforme que la fusion dément. Si une indication de fraîcheur est un jour affichée, elle est
**dégradée avec l'horizon**, jamais uniforme.
### 21.2 L'avertissement qui compte pour l'écran
L'erreur **météorologique** décroît sur le proche. **Le biais de CONVERSION, lui, ne bouge pas** :
ombrage, encrassement, température de module et écrêtage onduleur se trompent **autant à trois
heures qu'à trente-six**, et tous dans le sens optimiste.
> **Un horizon proche améliore la météo, jamais la conversion.** Il ne faut pas laisser
> « prévision proche plus fiable » se lire comme « chiffre proche plus juste ».
C'est le §3 sous un angle neuf, et le plus dangereux : le §3 disait qu'un plafond systématique se
lit comme une contre-performance. Ici s'ajoute qu'une **confiance croissante sur le proche**
donnerait à ce plafond l'apparence d'une mesure. Deux erreurs qui se composent — l'une fait
douter de l'installation, l'autre fait croire au chiffre qui la fait douter.
Écrit dans la description de `GetPvForecast`, pas seulement ici : c'est un client qui construira
l'écran, et c'est le contrat qu'il lira.

View File

@ -319,7 +319,14 @@ NymeaEnergyJsonHandler::NymeaEnergyJsonHandler(SpotMarketManager *spotMarketMana
"none of the curve's values), so revisions are keyed on the content itself. "
"'firstSeenAt' is when this content first appeared - it ORDERS revisions; "
"'lastSeenAt' is the last time it was republished unchanged - FRESHNESS. "
"Neither is an emission time, and no horizon may be computed from them.";
"Neither is an emission time, and no horizon may be computed from them. "
"The provider blends two model domains, so the FIRST HOURS ARE FRESHER THAN "
"THE REST - a single age cannot date the whole curve, which is why none is "
"published per slot. And beware what a near horizon buys: it reduces the "
"METEOROLOGICAL error, never the CONVERSION bias - shading, soiling, module "
"temperature and inverter clipping are as wrong at three hours as at "
"thirty-six, and all in the optimistic direction. 'Nearer forecast is more "
"reliable' must NOT be shown as 'nearer figure is more accurate'.";
registerMethod("GetPvForecast", description, params, returns, Types::PermissionScopeControlThings);
// Désignation GLOBALE, sur le modèle d'Energy.SetRootMeter — même nature : une source unique