Skip to content

Release v4.0.0

Choose a tag to compare

@github-actions github-actions released this 25 Aug 08:34
  • (theimo1221) BREAKING: this release changes the public surface in four ways. iPersist gains five mandatory members (getBatteryLevelHistory, getActuatorHistory, getEnergyConsumptionHistory, getWeatherDaySummaries, persistWeatherDaySummary) - an own implementation of that interface no longer compiles until it carries them · iEnergyManager gains morningOutlook and morningReserveVerdict, both answered by delegating to EnergyManagerUtils, so an own manager needs two one line getters · HTTPSService.request now always calls its callback exactly once, where an exhausted retry chain used to call it never - a caller that only handled success now sees a failure carrying HTTPSService.failureStatusCode and has to tell the two apart · and three dachs settings moved to the energy manager, see below. Everything an installation configures keeps its meaning; what breaks is code that implements these interfaces or relied on the callback never arriving
  • (theimo1221) Fix settings silently not being saved when any text in them contains an apostrophe. The persistence pasted free text straight into its statements, so a room called Anna's Zimmer or a device named with an inch mark closed the string literal and broke the insert; the failure only showed as Postgres Query failed in the log, and the setting was gone after the next restart. Every free text on the write path is now bound as a parameter (addRoom, addDevice, persistSwitchInput, persistSettings), and so is every device id on the read path (getActuatorHistory, getTemperatureHistory, getLastDesiredPosition, motionSensorTodayCount, loadSettings). Timestamps are deliberately left as they were: a bound Date and a pasted toISOString() can date differently, and the stored history must keep one time base
  • (theimo1221) Stop the OpenWeather key from being written to the log on every forecast call. HTTPSOptions logs the path it is constructed with, and the live forecast handed it the full query including appid; the same key also reached an error message. Both now build a redacted path and set the real query afterwards. Rotate your key and check existing logs - this only stops new lines, it cannot unwrite the old ones
  • (theimo1221) Check the answer of the weather endpoint before parsing it. Anything outside 200-299 is now a failure that keeps the previous forecast and writes one warning; a 401 or 429 whose body happens to parse as JSON was previously processed as a weather situation
  • (theimo1221) Make HTTPSService.request always call its callback exactly once. An exhausted retry chain used to log and return without calling it at all, leaving a promise that never settled and a caller stuck until restart, and a socket error arriving after a finished answer could start a second retry chain and call it twice. There is also a request timeout now (HTTPSService.requestTimeoutMs, 30s per attempt): an endpoint that accepts the connection and then stays silent emits no error event, so nothing used to bound it. downloadFile gained the matching handling - a write error rejects and removes the torso, and a stream error after a finished download no longer deletes the finished file
  • (theimo1221) Fix getTemperatureHistory shifting every timestamp by the local offset, the last reader carrying that defect, and reading a NULL measurement as 0°C instead of discarding the row
  • (theimo1221) Coarsen the fallback coordinates in settings-service-instance.ts to one decimal. They are the default when weather.lattitude is not configured; fifteen decimals suggested a precision that a fallback cannot have
  • (theimo1221) Fix TimeCallbackService.hoursTilSunset() returning minutes instead of hours. This raises the battery share of the victron excess calculation by a factor of 60 and turns its > 4 check from four minutes into four hours, so excess energy consumers now switch on noticeably less often - no configuration change involved
  • (theimo1221) Fix the dachs three hour block release firing after eleven minutes
  • (theimo1221) Cap the dachs warm water charge at summerWarmWaterDesiredMaxTemp (default 58°C, was 75°C) outside the heating season, so the warm water storage keeps headroom while it doubles as a heat sink
  • (theimo1221) Fill the previously empty VictronDevice.getReport() with production, grid draw, grid feed-in, self consumption and battery level
  • (theimo1221) Decide the electricity driven dachs start from the plant's own history instead of from the state of charge alone: estimate how the charge develops until the next morning's low, and suppress a start while the pessimistic edge of that estimate still holds the reserve, or ask for one while even the optimistic edge misses it. No setting turns this on - the recorded data decides. Without persistence, without a battery capacity, without enough consumption windows or without the daily weather the gate says nothing at all and the previous behaviour applies, which is what an installation that carries none of that sees. The two model-free stages act as soon as the data is there, because they are arithmetic on measured consumption. The two stages that rest on the fitted model do not act yet: they run alongside and record what they would have decided and what a trivial state-of-charge threshold would have decided, so the question whether the model beats that rule is answered from a week of operation rather than from synthetic data
  • (theimo1221) Add getBatteryLevelHistory, getActuatorHistory, getEnergyConsumptionHistory and getWeatherDaySummaries plus persistWeatherDaySummary to iPersist - breaking for third party implementers of that interface
  • (theimo1221) Add a WeatherDaySummary table and a throttled backfill over the OpenWeather day summary endpoint, bounded by the fitting window rather than by the whole history. It runs hourly while the gate is on and refetches the running day every time, because that day's aggregate is a forecast that moves and the gate reads its own two weather figures out of it - so expect one paid call per hour on top of a one-off fill of the window, not one per day. A past day is attempted at most once per calendar day, whether the fetch or the store failed - without an "One Call by Call" subscription the endpoint answers 401 for every past day, and retrying those hourly would drain a day's quota in a morning
  • (theimo1221) Add dachs settings for the history gate. historyWindowDays 90 is a reasoned judgement rather than a measurement, and only its effect is under test, not its height: the backtest gets monotonically worse with window length on synthetic data, so treat it as unverified until a run against the real history produces a number - which is what the model shadow above is there to deliver. The dials that describe the quality bar of the recording rather than the shape of one question - the coverage a counted day must reach, the consumption quantile and the number of windows below which that quantile means nothing - live on the energy manager, see below
  • (theimo1221) Add jest-dst, a second test pass under a daylight saving timezone selected through jest.dst.config.js, and run it from full-test - a guard that only runs in UTC cannot catch a calendar arithmetic mistake
  • (theimo1221) Move the history arithmetic off the dachs name: DachsHistoryUtils becomes EnergyHistoryUtils and iDachsHistory* becomes iEnergyHistory*, because where the battery bottoms out tomorrow morning is a property of the plant rather than of one device. Only the dachs' own decision, iDachsHistoryGateResult, keeps the device name. correctForDachs becomes correctForFossilGeneration and takes a list of iFossilGeneratorRun whose shares are summed, so a second block heat and power unit or a standby generator is one more list entry instead of one more branch. No change in behaviour, and never released under the old names
  • (theimo1221) Let the victron energy manager answer the same question the dachs asks, instead of guessing at it twice more. Its battery reservation aimed at reaching 100% by sunset - the goal is that tomorrow morning's low clears a reserve, and on a hot sunny morning the old aim held back exactly the surplus the air conditioning should have used. It now decides in three rungs: if the recorded consumption alone shows the charge clears the reserve even with no further sun, nothing is held back and nothing is blocked; otherwise the deficit is measured against the pessimistic edge of the projected morning band; otherwise the previous formula applies unchanged. acBlocked reads the same decision rather than its own hour ladder, so the morning exception no longer depends on clouds being a day mean - a day that is overcast until noon and clear after averages above the old threshold and could never trigger it. No setting turns this on either - the first rung acts as soon as the recorded consumption is there, the second one only records for now, and without the data the hour ladder decides exactly as before. minimumMorningSocReserve, historyWindowDays, historyMinimumDays and historyBandSigma are the manager's own: an air conditioner switched off is taken back the moment the outlook turns, a burnt generator run is not
  • (theimo1221) Let fuel burning generators announce themselves instead of being wired in: a device that carries the new DeviceCapability.fossilGenerator and implements iFossilGeneratorSource is picked up by Devices.fossilGenerators and subtracted from the recorded photovoltaic yield. Which generators the plant has is a property of the plant; what a generator is rated at and how well it converts stays a property of that device, so dachsRatedElectricalWattage and dachsConversionFactor remain dachs settings. A second block heat and power unit or a standby generator now needs nothing but the capability. The list is read on every history read rather than cached, so a device built after the first read still counts, and it does not hang off the energy manager - a plant can have generators and no energy manager
  • (theimo1221) Move the history orchestration off the dachs as well: reading the window, the throttles, the weather backfill and the features of the running day now live in EnergyHistoryService, which answers one question - outlook(soc, moment) returns the remaining sun hours, the model-free worst case, the projected morning band and the basis behind them. It names no threshold: the reserve, the warm water limits, the season and the decision order stay with the dachs, which turns that outlook into a start or a suppression. The service is passive - it reads nothing until a caller asks - and one instance belongs to one caller, so a caller that never asks costs nothing. This moves 378 lines out of the device class and adds EnergyHistoryService, iEnergyHistoryOptions, iEnergyHistoryOutlook, iEnergyHistoryBasis, iProjectedSocBand and iFossilGeneratorSource to the public surface
  • (theimo1221) Move the three dials that describe how strictly the plant's own recorded data is read - historyMinimumDayCoverage, the consumption quantile and historyMinimumConsumptionDays - from the dachs settings to the energy manager settings, where the battery capacity already lives. They state the quality bar of the recording, not the shape of one caller's question, and two callers disagreeing on what counts as a covered day is not a feature. If you had set any of these three on the dachs, set them on the energy manager instead - the old keys are no longer read and fall back to their defaults without an error. The defaults themselves are unchanged (0.9 / 0.9 / 10), and an installation without an energy manager keeps working: a missing dial takes its default, while a missing battery capacity still makes the history say nothing at all - a default for a strictness is a judgement, a default for installed hardware would be a guess