feat(influxdb): export peak power, enriched Tempo, cost simulation, contract, address and collection health - #628
Open
Germwalker wants to merge 7 commits into
Conversation
Add ExportInfluxDB.max_power(), following the same pattern as tempo(): one point per day from the consumption_daily_max_power cache, with the peak power in VA, the exact timestamp of the peak, and the usage percentage against the contract's subscribed power when it is known. Wired conditionally into Job.run() (jobs.py), same pattern as the existing tempo export, and gated behind a new "power_max.enable" configuration key, disabled by default so existing installs are unaffected until opted in.
… day counts Extend ExportInfluxDB.tempo(): in addition to the color field already exported for every cached day, the current day's point now also gets color_tomorrow, the 6 configured Tempo prices (price_<color>) and the day-per-color counters (days_<color>), read from tempo_config. Since these extra fields share the same measurement/tags/timestamp as the existing color field, InfluxDB merges them into the same point instead of creating a new one.
Add ExportInfluxDB.cost_simulation(), sourced from the existing
statistic/price_consumption cache (already used by the cost simulation
page), a nested JSON {year: {month: {offer: {euro,kWh,Wh}}}} with
offer among BASE, HC, HP and TEMPO (itself split by sub-period).
One point is written per (year[, month], offer[, TEMPO period]), with
a tag model designed to let InfluxDB queries sum by offer and by year
without a cartesian product: `granularity` (year/month) separates the
yearly total from its monthly components, and `offer`/`period` avoid
mixing TEMPO's six sub-periods with the single BASE/HC/HP amounts.
Wired conditionally into Job.run() (jobs.py) and gated behind a new
"cost_simulation.enable" configuration key, disabled by default.
Add ExportInfluxDB.contract(), sourced from db.get_contract(), already used by ExportMqtt.contract(). Writes a single point in time (now), overwritten on every cycle, with the subscribed power, plan, meter type, segment, distribution tariff, contract status, last activation date and the 7 offpeak_hours_N fields. Subscribed power is additionally converted to VA (subscribed_power_va) for direct use in dashboards and alerting, alongside the original string. Wired conditionally into Job.run() (jobs.py) and gated behind a new "contract_export.enable" configuration key, disabled by default.
Add ExportInfluxDB.address(), sourced from db.get_addresse(), already used by ExportMqtt.address(). Writes a single point in time (now), overwritten on every cycle, with street, postal code, city and INSEE code as text fields; no tag beyond usage_point_id since none of these values has a cardinality worth indexing on. Wired conditionally into Job.run() (jobs.py) and gated behind a new "address_export.enable" configuration key, disabled by default.
Add ExportInfluxDB.health(), sourced from db.get_usage_point(), already used by ExportMqtt.status(). Writes a single point in time (now), overwritten on every cycle, with the API call quota used and its limit, last call timestamp, last error, and consentement_days_left: the number of days left before the Enedis consent expires (negative if already expired), computed here so Grafana/InfluxDB alerting does not need to recompute it. Wired conditionally into Job.run() (jobs.py) and gated behind a new "health_export.enable" configuration key, disabled by default.
contract(), address() and health() used self.tz.localize(datetime.now()): datetime.now() returns the container's local wall-clock time (e.g. Europe/Paris via the TZ env var), and localize() labels that value as-is in self.tz (UTC here) without converting it, so the point was written with a timestamp in the FUTURE (offset = the timezone, 2h in summer). Found during end-to-end verification: the points existed in InfluxDB but a `range(stop: now())` query without a future bound could not see them. Fix: use datetime.now(pytz.utc).astimezone(self.tz), which computes the correct current instant regardless of the configured timezone.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this adds
Six new, independently-gated InfluxDB exports, each following the existing patterns already
used by
ExportInfluxDB(for the data shape) andExportMqtt(for the source query), plus onebug fix discovered while validating the three "instant" exports end-to-end. All are disabled
by default — nothing changes for existing installs until a user opts in.
power_max— daily peak power. One point per day from theconsumption_daily_max_powercache: peak power in VA, exact timestamp of the peak, andusage percentage against the contract's subscribed power when known.
ExportInfluxDB.tempo()now also writes, on the current day's pointonly,
color_tomorrow, the 6 configured Tempo prices (price_<color>) and theday-per-color counters (
days_<color>). These share the same measurement/tags/timestamp asthe existing
colorfield, so InfluxDB merges them into the same point.cost_simulation— simulated costs by tariff offer (BASE/HC/HP/TEMPO with its 6sub-periods), sourced from the existing
statistic/price_consumptioncache. Tag model(
granularity= year/month,offer,period) is designed so asum(euro) group by (year) where granularity="year"query gives the yearly total per offerwithout a cartesian product against the monthly rows.
contract_export— contract details (subscribed power, also converted to VA; plan;meter type; segment; distribution tariff; status; last activation date; the 7
offpeak_hours_Nfields), sourced fromdb.get_contract(), already used byExportMqtt.contract().address_export— delivery point address (street, postal code, city, INSEE code),sourced from
db.get_addresse(), already used byExportMqtt.address().health_export— collection health (API quota used/limit, last call timestamp, lasterror, and
consentement_days_left: days left before the Enedis consent expires, negativeif already expired), sourced from
db.get_usage_point(), already used byExportMqtt.status().Each export adds: one method on
ExportInfluxDB(src/models/export_influxdb.py), oneconditional call in
Job.run()(src/models/jobs.py), and one configuration key withenable: Falseby default plus a<name>_config()accessor (src/models/config.py).Included fix: wrong timestamp on the three "instant" exports
contract(),address()andhealth()write a single point per cycle withself.tz.localize(datetime.now()).datetime.now()returns the container's local wall-clocktime (e.g. Europe/Paris via the
TZenv var);localize()then labels that value as-is inself.tz(UTC in the default config) without converting it — so the point lands in thefuture by exactly the timezone offset (2h in summer, observed end-to-end: the points existed
in InfluxDB but a
range(stop: now())query without a future bound would not surface them).Fixed by using
datetime.now(pytz.utc).astimezone(self.tz), which computes the correct instantregardless of the configured timezone. This only affects the three exports introduced in this
PR, so it is included here rather than filed as a separate fix.
Scope
3 files, 7 commits, +343/-2 lines total:
src/models/config.py(+65)src/models/export_influxdb.py(+249/-2, includes the timestamp fix)src/models/jobs.py(+31)Notes for reviewers
grouped them here since they share the same three files and the same small pattern.
jobs.pyis gated behind its own<name>.enableconfiguration flag, defaulting toFalse.