# Verbesserte Nachberechnung der Energieverfolgung

**URL:** <https://community.gladysassistant.com/t/amelioration-re-calcul-suivi-de-lenergie/10069>\
**Category:** Développement\
**Created:** [27. Februar 2026 um 13:24 UTC](https://community.gladysassistant.com/t/amelioration-re-calcul-suivi-de-lenergie/10069 "2026-02-27T13:24:58Z")\
**Posts on this page:** 1\
**Page:** 2

<div class="post-metadata">

**Author:** ![pierre-gilles](https://gladys-assistant-community.b-cdn.net/user_avatar/community.gladysassistant.com/pierre-gilles/32/5886_2.png) [@pierre-gilles](https://community.gladysassistant.com/u/pierre-gilles)\
**Post date:** [22. Mai 2026 um 11:41 UTC](https://community.gladysassistant.com/t/amelioration-re-calcul-suivi-de-lenergie/10069/21 "2026-05-22T11:41:34Z")

</div>

Ich habe den PR hier angesehen und geantwortet:

> <https://github.com/GladysAssistant/Gladys/pull/2528#pullrequestreview-4345086049>
>
> \### Pull Request check-list
> 
> To ensure your Pull Request can be accepted as fa…st as possible, make sure to review and check all of these items:
> 
> \- \[x\] If your changes affect the code, did you write the tests?
> \- \[x\] Are tests passing? (\`npm test\` on both front/server)
> \- \[x\] Is the linter passing? (\`npm run eslint\` on both front/server)
> \- \[x\] Did you run prettier? (\`npm run prettier\` on both front/server)
> \- \[\] Did you test this pull request in real life? With real devices? If this development is a big feature or a new service, we recommend that you provide a Docker image to the community (\[forum\](https://community.gladysassistant.com/)) for testing before merging.
> \- \[\] If your changes modify the API (REST or Node.js), did you modify the API documentation? (Documentation is based on comments in code)
> 
> 
> NOTE: these things are not required to open a PR and can be done afterwards / while the PR is open.
> 
> \### Description of change
> 
> \## Summary
> 
> \> This PR is the first independent slice extracted from #2413 as discussed
> \> on https://community.gladysassistant.com/t/amelioration-re-calcul-suivi-de-lenergie/10069 ;
> \> the rest of #2413 will follow in subsequent PRs only if this one is merged.
> 
> Extends two existing core primitives with an optional parameter, in preparation for upcoming energy-monitoring features (selective recalculation, date-range recalculation). The change is intentionally \*\*incremental\*\* rather than introducing sibling functions: every pre-existing caller keeps the same effective behavior, no new file is added, no new public surface area is exposed.
> 
> \- \`device.destroyStatesFrom(selector, from, to = new Date())\` — adds an optional \`to\` upper bound (inclusive) defaulting to \`now\`.
> \- \`job.wrapper(type, func, { detached = false } = {})\` — adds an optional \`{ detached }\` option flag. When false (default), the wrapper body is byte-for-byte identical to master; when true, the wrapper starts the job, runs \`func\` in the background, returns the started job immediately and never re-throws to the caller.
> 
> \## Details
> 
> \### \`device.destroyStatesFrom(selector, from, to = new Date())\`
> 
> \- The existing function previously had the signature \`(selector, from)\`.
> \- The new optional \`to\` parameter defaults to \`new Date()\`, so every pre-existing caller keeps the same effective behavior: the SQL goes from \`WHERE created\_at \>= from\` to \`WHERE created\_at \>= from AND created\_at \<= now\`, which is identical in practice.
> \- When \`to\` is explicitly provided, the function deletes only states inside \`\[from, to\]\` (both inclusive).
> \- When \`from \> to\`, the SQL is naturally empty and the function silently no-ops — documented in the JSDoc and covered by an explicit test.
> \- Continues to throw \`NotFoundError('DeviceFeature not found')\` for unknown selectors. Continues to use parameterized DuckDB DELETE (no string interpolation in SQL).
> 
> \### \`job.wrapper(type, func, { detached = false } = {})\`
> 
> \- The existing function previously had the signature \`(type, func)\`.
> \- When \`detached === false\` (default), the wrapper performs the exact same SQL sequence as on master (\`start\` → \`await func\` → \`finish(SUCCESS)\` / on error \`finish(FAILED)\` then re-throw) and returns the same value (\`res\`). The shared body is now extracted into a local IIFE (\`runAndFinish\`) so the legacy and detached modes share it instead of duplicating it.
> \- When \`detached === true\`, the wrapper calls \`this.start(type)\`, fires \`runAndFinish()\` without awaiting it, and returns the started \`job\` immediately. Errors thrown by \`func\` are reported on the job (status=FAILED) and never re-thrown to the caller; a secondary \`try/catch\` falls back to \`logger.error('job.wrapper: failed to finish job ...')\` if \`finish(FAILED)\` itself rejects.
> \- This is the fire-and-forget mode needed by long-running "from beginning" recalculations that would otherwise hold the HTTP request open until completion.
> 
> \### Tests
> 
> \- \`server/test/lib/device/device.destroyStatesFrom.test.js\`:
> - the 5 pre-existing master tests are unchanged,
> - 2 new tests cover the bounded form: a happy path with a range cut-out (states inside \`\[from, to\]\` deleted, states outside kept) and an inverted-range silent no-op.
> \- \`server/test/lib/job/job.test.js\`:
> - the existing \`describe('job.wrapper')\` legacy tests are unchanged,
> - the new \`describe('job.wrapper with { detached: true }')\` block carries 3 tests covering success path, failure path, and the secondary log path when \`job.finish\` itself rejects,
> - a small additional test on \`updateProgress\` asserts it rejects when the job id is unknown — easy win for coverage of a pre-existing branch.
> 
> \### Coverage
> 
> \- \`device.destroyStatesFrom.js\`: \*\*100%\*\* statements / lines / functions / branches.
> \- \`job.wrapper.js\`: \*\*100%\*\* statements / lines / functions; branch coverage at 75% (one defensive guard \`if (error && error.toString)\` is not exercised — identical to the pre-existing situation on master, no new uncovered branch introduced).
> 
> \### Tested manually
> 
> \- A long-running "from beginning" recalculation on my own production instance now returns the HTTP response immediately and runs as a background job (progress visible in the jobs page) — this is the actual functional motivation for the \`{ detached }\` mode and the reason this PR is the prerequisite for the follow-ups.
> 
> \## Scope
> 
> Diff vs base branch (\`master\`):
> 
> | Area | Insertions | Deletions |
> |---|---|---|
> | server (production) | 67 | 13 |
> | server (tests) | 148 | 6 |
> | \*\*Total\*\* | \*\*215\*\* | \*\*19\*\* |
> 
> Files touched:
> 
> \- \`server/lib/device/device.destroyStatesFrom.js\` — \`+23 / -4\` (add optional \`to\` parameter)
> \- \`server/lib/job/job.wrapper.js\` — \`+25 / -13\` (factor out shared body, add \`{ detached }\` option)
> \- \`server/test/lib/device/device.destroyStatesFrom.test.js\` — \`+87 / 0\` (2 new tests for the bounded form)
> \- \`server/test/lib/job/job.test.js\` — \`+80 / -6\` (3 new tests for the detached mode + 1 on \`updateProgress\`)
> 
> No new file, no new public function, no new prototype binding on \`DeviceManager\` or \`Job\`.
> 
> 
> 
> \## Summary by CodeRabbit
> 
> \* \*\*New Features\*\*
> \* Jobs can now run in detached/background mode, enabling non-blocking job execution
> \* Device state deletion now supports specifying an optional date range (from and to) for more precise cleanup
> 
> \* \*\*Tests\*\*
> \* Added comprehensive test coverage for detached job execution and error handling
> \* Added test cases for bounded date range state deletion, including edge cases
> 
> 
> 
> \[!\[Review Change Stack\](https://storage.googleapis.com/coderabbit\_public\_assets/review-stack-in-coderabbit-ui.svg)\](https://app.coderabbit.ai/change-stack/GladysAssistant/Gladys/pull/2528?utm\_source=github\_walkthrough&utm\_medium=github&utm\_campaign=change\_stack)

[Vorherige Seite](https://community.gladysassistant.com/t/amelioration-re-calcul-suivi-de-lenergie/10069.md?page=1)
