Skip to content

Silence ERROR log on override_properties recovery path#491

Open
pszypowicz wants to merge 1 commit into
ikalchev:devfrom
pszypowicz:fix/473-quiet-override-properties
Open

Silence ERROR log on override_properties recovery path#491
pszypowicz wants to merge 1 commit into
ikalchev:devfrom
pszypowicz:fix/473-quiet-override-properties

Conversation

@pszypowicz
Copy link
Copy Markdown

@pszypowicz pszypowicz commented May 22, 2026

Fixes #473

Summary

Service.configure_char calls Characteristic.override_properties to install a restricted valid_values set, then set_value to seed an initial value. override_properties is designed to recover gracefully when the characteristic's existing _value (the format default, set in __init__) falls outside the newly-restricted valid_values - it catches the ValueError and falls back via _get_default_value().

The validator it uses, valid_value_or_raise, calls logger.error() before raising. So even though the recovery path is the intended behavior, every consumer that restricts valid_values for a characteristic whose format-default is excluded leaks a spurious ERROR log line.

This is what users hit at every HomeKit accessory build for alarm panels that do not advertise ARM_HOME: the SecuritySystemCurrentState and SecuritySystemTargetState default to StayArmed=0, but the configured valid_values excludes 0. The existing recovery still kicks in correctly (HA's HomeKit accessory works fine), but the log fills with red entries that look like real failures.

Root cause

# pyhap/characteristic.py - override_properties (before)
try:
    self.value = self.to_valid_value(self._value)
    self.valid_value_or_raise(self._value)   # logs ERROR before raising
except ValueError:
    self.value = self._get_default_value()    # silent recovery (intended)

valid_value_or_raise is appropriate when the caller wants the validation error surfaced (e.g. set_value), but inside override_properties the exception is by design caught and ignored - so the ERROR log is misleading.

Fix

Replace the try/except-around-validator pattern with an explicit silent membership check. valid_value_or_raise itself is left unchanged so other callers retain ERROR-level logging.

# after
try:
    candidate = self.to_valid_value(self._value)
except ValueError:
    self.value = self._get_default_value()
    return

valid_values_dict = self._properties.get(PROP_VALID_VALUES)
if valid_values_dict and candidate not in valid_values_dict.values():
    self.value = self._get_default_value()
else:
    self.value = candidate

Behavior matrix is unchanged:

to_valid_value candidate in restricted valid_values result
raises - fall back to default
ok no fall back to default
ok yes adopt candidate

Verification

  • pytest tests/ passes the relevant suite (one unrelated pre-existing failure on Python 3.14 in test_accessory_driver.py::test_start_from_sync, from upstream's use of the removed asyncio.SafeChildWatcher).
  • Smoke-tested the patched characteristic.py against a live Home Assistant install with a HomeKit-bridged alarm panel: the two pyhap.characteristic ERROR lines that appeared on every restart are gone, and the accessory still works (state changes propagate, target-state changes from the Home app reach the panel).

Related

Happy to add a CHANGELOG entry if you'd like - I kept the diff to the fix itself.

override_properties is designed to recover gracefully when the
characteristic's existing value falls outside a newly-restricted
valid_values set (e.g. SecuritySystemCurrentState defaults to
StayArmed=0, but a consumer configures the characteristic for an
alarm panel that does not advertise ARM_HOME and so removes 0 from
valid_values). The recovery is silent from the caller's perspective:
ValueError is caught and self.value is reset via _get_default_value().

The validator it relied on, valid_value_or_raise, still calls
logger.error() before raising, leaking an "is an invalid value" line
into the application log even though the situation is expected and
recovered. Downstream this manifests in Home Assistant as spurious
pyhap.characteristic ERRORs at every HomeKit accessory build for
alarm panels that restrict StayArmed (issue ikalchev#473
and home-assistant/core#130564, #156142).

Replace the try/except-around-validator pattern in override_properties
with an explicit silent membership check. valid_value_or_raise itself
is unchanged so callers like set_value (where ERROR-level logging is
appropriate) keep their behavior.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Default characterisic value is generating errors in HA logs when unsupported

1 participant