### Description
Scene formulas (the "Computed" value of **Set a variable**, **C…ontrol a device**, **Wait**, **Condition on variables** and the volume of **Play a notification**) run on a deliberately restricted `mathjs` instance. Until now that namespace only contained the four arithmetic operators, `%`, `^`, the comparison operators and `round()` / `random()`, so a formula like `sqrt(400)` or `min(15, x)` aborted the scene with "value is not a number".
This PR adds the functions asked for in the forum topic, and makes the whole supported surface discoverable directly in the scene editor.
**Functions added**
| Group | Functions |
|---|---|
| Rounding & sign | `abs`, `ceil`, `floor`, `fix`, `sign` |
| Powers & roots | `pow`, `sqrt`, `cbrt`, `square`, `cube`, `nthRoot`, `hypot` |
| Exponential & logarithm | `exp`, `log` (with optional base), `log2`, `log10` |
| Statistics | `min`, `max`, `mean`, `median`, `sum`, `prod` |
| Integer arithmetic | `gcd`, `lcm` |
| Random | `randomInt` |
| Trigonometry | `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `atan2` |
| Constants | `pi`, `e`, `tau` |
`abs`, `ceil` and `floor` already worked because `mathjs` pulled them in transitively through `round`. They are now listed explicitly like the rest, so a `mathjs` upgrade cannot silently remove them.
The use case from the forum topic now works as a single formula, with no HTTP request:
```
round(min(15, exp({{tankLevel}} / 25)) * 60)
```
**Technical changes**
- **`server/lib/scene/scene.formula.js`** (new): the restricted formula engine moves out of `scene.actions.js` into its own module, so the supported surface lives in one documented place. Every operator, function and constant is passed explicitly to `create()`. `mathjs` only guarantees the "math" namespace for what it is given, so anything only reachable transitively could disappear on an upgrade.
- **`config()` is no longer reachable from a formula.** `create()` always exposes it to the expression parser, and the engine is one instance shared by every scene. A formula calling `config({number: "BigNumber"})` therefore switched every formula evaluated afterwards to BigNumber results until the next restart, and the `Number.isFinite` guards then rejected them. This was already the case on `master`. The module now removes it from the parser namespace, and a test pins it.
- **`mathjs` 11.8.0 → 11.12.0** (latest 11.x, same API). 11.9.1 fixed a sandbox escape in the expression parser (`FunctionNode` / `SymbolNode` property lookup) that could lead to arbitrary code execution through `evaluate()`. Formulas are persisted and evaluated server-side, so the engine should not stay on the vulnerable version. The lockfile only moves `mathjs`, `fraction.js` and `typed-function`, which no other server package depends on.
- **`server/lib/scene/scene.actions.js`**: imports the shared `evaluate`.
- **Control a device** now aborts the scene with `ACTION_VALUE_NOT_A_NUMBER` when the formula cannot be evaluated. It was the only formula action that let a `mathjs` parse error escape.
- **Control a device**, **Wait** and the ordered operators of **Condition on variables** (`>`, `>=`, `<`, `<=`) now reject non-finite results (`log(0)`, `exp(1000)`), like **Set a variable** and the notification volume already did. Otherwise a device could be set to `Infinity`, a comparison could always be true, or Node could clamp an infinite wait to 1 ms and skip it. `=` / `!=` are unchanged, so text comparisons keep working.
- Out-of-domain results (`sqrt(-4)`, `log(-1)`, `asin(2)`) are `Complex` values. They coerce to `NaN`, so all five formula sites already fail closed on them.
- **Front**: a new `FormulaFunctionsHelp` component sits under every formula input: Set a variable, Control a device, Wait, Condition on variables, and the computed volume of Play a notification. It is a native `<details>`, collapsed by default to a single "Available functions and syntax" line. When opened, it lists the operators, functions and constants, gives a syntax example and warns about the surprising cases: trigonometry works in radians, `log` is the natural logarithm, `randomInt` excludes `max`, and constants need an explicit `*`. Function names are not translatable, so they live in the component and mirror the server module. The labels are translated in `en`, `fr`, `de` and `es`.
- **Tests**:
- `scene.action.formula.test.js` has one case per operator, function and constant, so a `mathjs` upgrade that drops one fails CI instead of failing silently in production. It also has fail-closed cases for parse errors and non-finite results.
- `scene.formula.test.js` probes the sandbox escape shapes from the `mathjs` security suite, plus `config()`.
- The `continue-only-if` fixture for "formula cannot be evaluated" now uses `unknownFunction(400)` instead of `sqrt(400)`, which is now valid.
**Not included**
`and` / `or` / `not` / `xor` / `equal` / `unequal` and `compareText` were mentioned in the forum thread but are left out on purpose:
- The formula string is whitespace-stripped before evaluation, so the operator form (`1 and 0` → `1and0`) cannot parse, and string arguments would be mangled.
- They return booleans rather than numbers. That does not fit a pipeline whose call sites all expect a number, and Gladys already has dedicated "Condition on variables" boxes with AND / OR semantics for that job.
Every function added here returns a number, so it behaves the same in every formula action.
### Related request
Forum: https://community.gladysassistant.com/t/scenes-supporter-quelques-fonctions-supplementaires-de-math-js/9509
### Checklist
- [x] If a forum topic or GitHub issue exists, the description links it (`Forum: https://community.gladysassistant.com/t/...` or `Closes #...`)
- [x] Tests pass: `cd server && npm run coverage` (Codecov requires 100% coverage on changed lines) and Cypress (`npm run cypress:run`) if the UI changed
- [x] Linter and prettier pass on both front and server (`npm run eslint`, `npm run prettier`)
- [x] No undocumented breaking change
Notes on the checklist: on Node 24, the server passes `prettier-check`, `eslint` and the full `npm run coverage` suite. The front passes `prettier-check`, `eslint`, `compare-translations` and `npm run build`. Cypress was not run locally. The UI change only adds a collapsed help block under existing inputs, and no Cypress spec targets those inputs.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01QDSknE2gk1P1nboSPRJYLg
## Summary by CodeRabbit
* **New Features**
* Added formula help to scene action editors, with supported operators, functions, constants, syntax guidance, and examples.
* Added formula-help translations in English, French, German, and Spanish.
* Expanded supported formula calculations, including comparisons, rounding, powers, statistics, trigonometry, and constants.
* **Bug Fixes**
* Invalid formulas and non-finite results now stop scene actions safely.
* Restricted unsupported formula operations to improve reliability.