### Description
Supersedes #2707, rebuilt on the current `master` (363 commits …later) instead of merged: the mobile scene editor, `condition.while`, "Set a variable", select features and a fourth language all landed in between.
Until now the "only continue if" box only offered the variables created by previous steps, so testing the state of a device needed a "Get device value" step first — a common stumbling block when building a first scene. A condition now compares **either** a scene variable (`variable`) **or the current state of a device** (`device_feature`), read when the condition runs. Scenes saved with a variable are untouched. As the If / Then / Else and While blocks use the same step for their conditions, they get it too.
**Server**
- `scene.actions.js`: the feature is read from the state manager. A feature that no longer exists aborts the scene with `DEVICE_FEATURE_NOT_FOUND`, like a value that cannot be computed: a condition that cannot be evaluated is never read as true or false.
- `models/scene.js`: exactly one of `variable` / `device_feature` per condition (`xor`).
- `services/mcp/lib/sceneSchemas.js`: the two forms are two strict objects (same pattern as the `device.new-state` trigger), and the feature must be a known selector. The `scene.create` example now uses the direct form.
- `device.migrate.js`: the selectors held by `conditions[]` are rewritten by a device migration.
**Front**
- The select, now "Device or variable", lists the variables available at this step, then every feature of the house grouped by room (devices without a room last). The search matches the room and ignores accents. A feature holding constants offers its list of values.
- The collapsed step (desktop card, mobile row) names the device and its value: `Front door (Opening) = Opened`.
- Label and explanation updated in en / fr / de / es.
**What changed compared to #2707**
- **Select and music features were compared against the wrong column** (open review thread): the value now comes from `isStringStateFeature`, so text, select and the music string types all read `last_value_string`, exactly what "Get device value" exposes.
- **No extra request per condition box**: #2707 fetched the rooms and the devices again in every box; the options are now built from the devices the editor already loads.
- **The AI tool schema kept its size**: the list of feature selectors stays one shared `$defs` entry in the tool sent to the AI chat. A description on the selector would have copied the whole list inline — caught by `mcpToolsToChatApiFormat.test.js`, which now also asserts the shape of the two condition forms.
- **A deleted device no longer leaves an empty field**: the stored selector is shown, and kept when the scene is saved.
- Conflicts resolved on the migration spec, the `rewriteItems` JSDoc and the translations, keeping what `master` added (trigger `device_features[]`, `condition.while`, "Set a variable").
- Specs updated in the same diff: `scene-editor-mobile.md` (new C.6, C.1, E.10, tests) and `device-migration.md` B.3.
**Not changed, on purpose**
- The step is still named "Condition on variables". It now reads a bit narrow, but the name is quoted in other explanations and in the documentation: renaming it is a wording decision of its own.
- Conditions of one box are still all evaluated (no short-circuit on the first true one), so a deleted device in one OR arm stops the scene even if another arm is true. Same behavior as an uncomputable value today.
- On a phone the field keeps its select rather than the device picker sheet, which cannot list variables (noted in E.10).
**Tests**
- Server: 11 new cases on the condition (numeric, text, select, music, computed value, state read at run time, OR with a variable, missing feature, inside an If block), 5 on the Joi model, 6 on the MCP schema, the migration test extended with a condition at the root and one inside an If block. Every changed line is covered (checked against `coverage.lcov`).
- Cypress: new `SceneConditionDevice.cy.js` (6 tests) — devices listed by room with no step before, search, saved payload, summary, replacing a variable by a device, deleted device.
- Checked by hand in the built app, desktop and phone, and through the API on a running server (condition true → scene continues, false → `CONDITION_NOT_VERIFIED`, missing feature → `DEVICE_FEATURE_NOT_FOUND`, both attributes → 422).
Two things about the local runs, so CI results are read correctly:
- `npm run coverage`: everything passes except 26 tests that cannot run in my sandbox — the backup / restore tests need the `sqlite3` binary, which is not installed there, and the Gladys Plus controller tests get a 405 from the sandbox proxy. None of them touch this diff.
- Cypress: the 7 scene specs pass except `SceneMobileDevicePicker.cy.js`, which fails with `ResizeObserver loop limit exceeded` in my sandbox on a clean `master` build as well. The other spec folders were not run locally.
Not tested with real devices.
### Related request
Forum: `` https://community.gladysassistant.com/t/la-box-continuer-seulement-si-devrait-proposer-les-valeurs-instantanee-des-appareils-par-defaut ``/6502
### Checklist
- [x] If a forum topic or GitHub issue exists, the description links it (`Forum: https://community.gladysassistant.com/t/...` or `Closes #...`)
- [ ] Tests pass: `cd server && npm run coverage` (Codecov requires 100% coverage on changed lines) and Cypress (`npm run cypress:run`) if the UI changed — see the two notes above: left to CI for the tests my sandbox cannot run
- [x] Linter and prettier pass on both front and server (`npm run eslint`, `npm run prettier`)
- [x] No undocumented breaking change
---
_Generated by [Claude Code](https://claude.ai/code/session_01MNjB33gTEDGscAVMq3H2Rv)_