Fix new-group defaults + tidy section collapse defaults

Thread 2 (bug): runtime range number entities seeded from native_min on a
fresh profile (only a name stored at setup), so new groups ran at
max_brightness=1% and a 1000-1000K color-temp range until the options
dialog was saved once. Each RANGE_ENTITIES row now carries a sensible
DEFAULT_* and _options_value() falls back to it. Adds a regression test.

Thread 3: collapse 'Sun schedule' and 'Light control' by default
(now 2 open / 5 closed): Targets + Daytime curve expanded, the rest
collapsed.

Existing profiles unaffected (their options are already saved); only
newly-created groups get the corrected seed. Bump 2.3.0 -> 2.3.1-cdit.1.
This commit is contained in:
Casey 2026-05-31 21:41:57 +02:00
commit 61be07c179
5 changed files with 55 additions and 5 deletions

View file

@ -234,7 +234,7 @@ def _build_options_schema(
): _sun_event_selector(),
},
),
{"collapsed": False},
{"collapsed": True},
)
# An illuminance EntitySelector rejects an empty string, so we must NOT
@ -278,7 +278,7 @@ def _build_options_schema(
): BooleanSelector(),
},
),
{"collapsed": False},
{"collapsed": True},
)
advanced_schema: dict[Any, Any] = {

View file

@ -169,6 +169,7 @@ RANGE_ENTITIES: list[dict[str, Any]] = [
{
"field_key": "min_brightness",
"conf_key": CONF_MIN_BRIGHTNESS,
"default": DEFAULT_MIN_BRIGHTNESS,
"name": "Min brightness",
"native_min": 1,
"native_max": 100,
@ -179,6 +180,7 @@ RANGE_ENTITIES: list[dict[str, Any]] = [
{
"field_key": "max_brightness",
"conf_key": CONF_MAX_BRIGHTNESS,
"default": DEFAULT_MAX_BRIGHTNESS,
"name": "Max brightness",
"native_min": 1,
"native_max": 100,
@ -189,6 +191,7 @@ RANGE_ENTITIES: list[dict[str, Any]] = [
{
"field_key": "min_color_temp",
"conf_key": CONF_MIN_COLOR_TEMP,
"default": DEFAULT_MIN_COLOR_TEMP,
"name": "Min color temp",
"native_min": 1000,
"native_max": 10000,
@ -199,6 +202,7 @@ RANGE_ENTITIES: list[dict[str, Any]] = [
{
"field_key": "max_color_temp",
"conf_key": CONF_MAX_COLOR_TEMP,
"default": DEFAULT_MAX_COLOR_TEMP,
"name": "Max color temp",
"native_min": 1000,
"native_max": 10000,

View file

@ -9,5 +9,5 @@
"iot_class": "calculated",
"issue_tracker": "https://github.com/CaseyRo/adaptive-lighting/issues",
"requirements": ["ulid-transform"],
"version": "2.3.0-cdit.1"
"version": "2.3.1-cdit.1"
}

View file

@ -49,6 +49,7 @@ async def async_setup_entry(
entry=config_entry,
field_key=row["field_key"],
conf_key=row["conf_key"],
default=row["default"],
display_name=row["name"],
native_min=row["native_min"],
native_max=row["native_max"],
@ -74,6 +75,7 @@ class AdaptiveRangeNumber(RestoreNumber):
entry: ConfigEntry,
field_key: str,
conf_key: str,
default: float,
display_name: str,
native_min: float,
native_max: float,
@ -85,6 +87,7 @@ class AdaptiveRangeNumber(RestoreNumber):
self._entry = entry
self._field_key = field_key
self._conf_key = conf_key
self._default = default
self._attr_name = display_name
self._attr_translation_key = field_key
self._attr_unique_id = f"{entry.entry_id}_{field_key}"
@ -108,10 +111,17 @@ class AdaptiveRangeNumber(RestoreNumber):
)
def _options_value(self) -> float:
"""Read the seed value from entry.options (typed cast)."""
"""Read the seed value from entry.options (typed cast).
On a freshly-created profile neither ``entry.options`` nor
``entry.data`` carries the range keys (setup only stores the name),
so fall back to the field's sensible ``DEFAULT_*`` rather than the
slider's ``native_min`` — otherwise a new group would come up at
max_brightness=1% and a 1000-1000K color-temp range.
"""
raw = self._entry.options.get(self._conf_key)
if raw is None:
raw = self._entry.data.get(self._conf_key, self._attr_native_min_value)
raw = self._entry.data.get(self._conf_key, self._default)
return float(raw)
async def async_added_to_hass(self) -> None:

View file

@ -25,6 +25,10 @@ from custom_components.adaptive_lighting.const import (
CONF_MIN_BRIGHTNESS,
CONF_MIN_COLOR_TEMP,
CONFIG_ENTRY_VERSION,
DEFAULT_MAX_BRIGHTNESS,
DEFAULT_MAX_COLOR_TEMP,
DEFAULT_MIN_BRIGHTNESS,
DEFAULT_MIN_COLOR_TEMP,
DOMAIN,
)
@ -126,6 +130,38 @@ async def test_number_entity_attributes(hass, field_key, expected) -> None:
assert attrs["mode"] == NumberMode.SLIDER
# ---------------------------------------------------------------------------
# 7.4b — Fresh profile (only a name) seeds sensible DEFAULT_*, not native_min
# ---------------------------------------------------------------------------
@pytest.mark.parametrize(
("field_key", "expected_default"),
[
("min_brightness", DEFAULT_MIN_BRIGHTNESS),
("max_brightness", DEFAULT_MAX_BRIGHTNESS),
("min_color_temp", DEFAULT_MIN_COLOR_TEMP),
("max_color_temp", DEFAULT_MAX_COLOR_TEMP),
],
)
async def test_new_profile_seeds_sensible_defaults(
hass,
field_key,
expected_default,
) -> None:
"""A profile created with only a name must come up at DEFAULT_*.
Regression: the seed previously fell back to the slider's native_min,
so a brand-new group ran at max_brightness=1% and a collapsed
1000-1000K color-temp range until the options dialog was saved once.
"""
entry = await _setup_entry(hass) # data={name}, options={}
eid = _resolve_entity_id(hass, entry, field_key)
state = hass.states.get(eid)
assert state is not None
assert float(state.state) == float(expected_default)
# ---------------------------------------------------------------------------
# 7.5 — async_set_native_value does NOT write to entry.options
# ---------------------------------------------------------------------------