bosch_shc: add Shutter Contact II bypass timeout number (#183638)

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Erwin Douna <e.douna@gmail.com>
This commit is contained in:
Thomas
2026-09-30 09:18:22 +02:00
committed by GitHub
co-authored by Claude Sonnet 5.5 Erwin Douna
parent 9341a09dfd
commit 659eba7659
4 changed files with 80 additions and 2 deletions
+27 -1
View File
@@ -4,7 +4,7 @@ from collections.abc import Callable, Coroutine
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any, override
from boschshcpy import SHCMicromoduleRelay
from boschshcpy import SHCMicromoduleRelay, SHCShutterContact2
from boschshcpy.device import SHCDevice
from homeassistant.components.number import (
@@ -66,6 +66,21 @@ NUMBER_TYPES: dict[str, SHCNumberEntityDescription] = {
value_fn=_impulse_length_value_fn,
set_value_fn=_impulse_length_set_value_fn,
),
"bypass_timeout": SHCNumberEntityDescription[SHCShutterContact2](
key="bypass_timeout",
translation_key="bypass_timeout",
entity_category=EntityCategory.CONFIG,
device_class=NumberDeviceClass.DURATION,
native_unit_of_measurement=UnitOfTime.MINUTES,
native_min_value=1.0,
native_max_value=15.0,
native_step=1.0,
mode=NumberMode.BOX,
value_fn=lambda device: float(device.bypass_timeout),
set_value_fn=lambda device, value: device.async_set_bypass_timeout(
round(value)
),
),
}
@@ -101,6 +116,17 @@ async def async_setup_entry(
)
)
entities.extend(
SHCNumber(
hass=hass,
device=device,
parent_id=shc_info.unique_id,
entry_id=config_entry.entry_id,
description=NUMBER_TYPES["bypass_timeout"],
)
for device in session.device_helper.shutter_contacts2
)
async_add_entities(entities)
@@ -60,6 +60,9 @@
}
},
"number": {
"bypass_timeout": {
"name": "Break function timeout"
},
"impulse_length": {
"name": "Pulse length"
}
+2
View File
@@ -463,6 +463,7 @@ def shutter_contact2_device(
name: str = "Shutter contact",
bypass: BypassService.State = BypassService.State.BYPASS_INACTIVE,
bypass_infinite: bool = False,
bypass_timeout: int = 5,
) -> SHCShutterContact2:
"""Build a minimal device double for the shutter_contacts2 bucket."""
device = create_autospec(SHCShutterContact2, instance=True, spec_set=True)
@@ -477,6 +478,7 @@ def shutter_contact2_device(
device.status = "AVAILABLE"
device.bypass = bypass
device.bypass_infinite = bypass_infinite
device.bypass_timeout = bypass_timeout
return device
+48 -1
View File
@@ -12,11 +12,16 @@ from homeassistant.components.number import (
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.core import HomeAssistant
from .conftest import micromodule_relay_device, setup_integration
from .conftest import (
micromodule_relay_device,
setup_integration,
shutter_contact2_device,
)
from tests.common import MockConfigEntry
IMPULSE_LENGTH_ENTITY_ID = "number.relay_pulse_length"
BYPASS_TIMEOUT_ENTITY_ID = "number.shutter_contact_break_function_timeout"
@pytest.mark.parametrize(
@@ -99,3 +104,45 @@ async def test_micromodule_impulse_relay_impulse_length_partial_poll(
state = hass.states.get(IMPULSE_LENGTH_ENTITY_ID)
assert state is not None
assert state.state == "unknown"
@pytest.mark.parametrize(
"device_buckets",
[{"shutter_contacts2": [shutter_contact2_device(bypass_timeout=7)]}],
indirect=True,
)
@pytest.mark.usefixtures("mock_session")
async def test_shutter_contact2_bypass_timeout_value(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
) -> None:
"""The bypass timeout is reported in minutes."""
await setup_integration(hass, mock_config_entry)
state = hass.states.get(BYPASS_TIMEOUT_ENTITY_ID)
assert state is not None
assert state.state == "7.0"
@pytest.mark.parametrize(
"device_buckets",
[{"shutter_contacts2": [shutter_contact2_device()]}],
indirect=True,
)
@pytest.mark.usefixtures("mock_session")
async def test_shutter_contact2_bypass_timeout_set_value(
hass: HomeAssistant,
mock_session: MagicMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Setting a value writes the rounded minutes to the device."""
await setup_integration(hass, mock_config_entry)
device = mock_session.device_helper.shutter_contacts2[0]
await hass.services.async_call(
NUMBER_DOMAIN,
SERVICE_SET_VALUE,
{ATTR_ENTITY_ID: BYPASS_TIMEOUT_ENTITY_ID, ATTR_VALUE: 10.6},
blocking=True,
)
device.async_set_bypass_timeout.assert_awaited_once_with(11)