Add Device Pairing Enabled binary sensor to Lyric integration - #177062
Add Device Pairing Enabled binary sensor to Lyric integration#177062clutch2sft wants to merge 13 commits into
Conversation
Resideo's priority endpoint reports per-accessory motion detection (detectMotion) for paired IndoorAirSensor room accessories, which the integration parsed but never surfaced as an entity. Adds a new binary_sensor platform following the same per-accessory pattern already used for Room Temperature/Humidity in sensor.py.
Resideo's device response includes vacationHold.enabled, which was parsed but never surfaced. Adds a device-level binary sensor alongside the existing per-accessory Room Motion sensor, introducing a DEVICE_BINARY_SENSORS list mirroring the DEVICE_SENSORS pattern already used in sensor.py.
Resideo's device response includes settings.devicePairingEnabled, which was parsed but never surfaced. Adds it as a diagnostic binary sensor alongside Vacation Hold, using the same DEVICE_BINARY_SENSORS list.
|
Hey there @timmo001, mind taking a look at this pull request as it has been labeled with an integration ( Code owner commandsCode owners of
|
There was a problem hiding this comment.
Pull request overview
Adds Lyric binary sensors for room motion, vacation hold, and device pairing status.
Changes:
- Registers the binary sensor platform.
- Adds device and accessory binary sensor entities.
- Adds translated entity names.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
homeassistant/components/lyric/__init__.py |
Registers the platform. |
homeassistant/components/lyric/binary_sensor.py |
Implements three binary sensors. |
homeassistant/components/lyric/strings.json |
Adds entity names. |
| LyricBinarySensorEntityDescription( | ||
| key="vacation_hold", | ||
| translation_key="vacation_hold", | ||
| value_fn=lambda device: device.vacation_hold.enabled, |
There was a problem hiding this comment.
Confirmed — good catch. vacationHold.Enabled (capital E) is what Resideo actually returns; I have a live captured payload showing it. Fixed in timmo001/aiolyric here: clutch2sft/aiolyric#170 (branch fix-vacation-hold-key).
| translation_key="room_motion", | ||
| device_class=BinarySensorDeviceClass.MOTION, | ||
| value_fn=lambda _, accessory: accessory.detect_motion, | ||
| suitable_fn=lambda _, accessory: accessory.type == "IndoorAirSensor", |
There was a problem hiding this comment.
Correct, and already tracked — this needs timmo001/aiolyric#165 (the currentPriority/priority and type/sensorType field fix) merged and released before Room Motion populates for real accounts. Filing it as a dependency rather than blocking on it here, since #165 is a separate project on its own release cycle, and this PR is still correct/complete for what core can control. Once #165 releases, the follow-up is a one-line manifest.json version bump — tracked separately.
| async def async_setup_entry( | ||
| hass: HomeAssistant, | ||
| entry: LyricConfigEntry, | ||
| async_add_entities: AddConfigEntryEntitiesCallback, | ||
| ) -> None: |
| } | ||
| }, | ||
| "entity": { | ||
| "binary_sensor": { |
There was a problem hiding this comment.
Link to documentation pull request: home-assistant/home-assistant.io#46986
Covers entity creation (async_setup_entry), the accessory-type filter that gates Room Motion to IndoorAirSensor accessories only, the Vacation Hold/Device Pairing Enabled value mappings, and diagnostic entity_category metadata. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (2)
homeassistant/components/lyric/binary_sensor.py:44
- Fix the upstream vacation-hold key mapping and bump
aiolyricbefore exposing this entity. The pinned 2.1.1 readsvacationHold["enabled"], but the live/locationspayload in #177010 uses{"Enabled": ...}andLyricBaseObjectperforms no normalization, so this value always falls back toFalseand the entity cannot report an active vacation hold.
value_fn=lambda device: device.vacation_hold.enabled,
homeassistant/components/lyric/binary_sensor.py:62
- Depend on an
aiolyricrelease that mapssensorTypebefore filtering room accessories. In the pinned 2.1.1,LyricAccessory.typereads the nonexistent live-response keytype; live priority responses usesensorType(timmo001/aiolyric#165), so.typeis empty and every room-motion entity is filtered out.
suitable_fn=lambda _, accessory: accessory.type == "IndoorAirSensor",
| side_effect=lambda entities: added.append(list(entities)) | ||
| ) | ||
|
|
||
| await async_setup_entry(MagicMock(), entry, async_add_entities) |
There was a problem hiding this comment.
Fair — the previous version built entities from MagicMocks with already-parsed properties set directly, so it would pass regardless of real payload/key mismatches. Reworked to construct real LyricDevice/LyricRoom/LyricAccessory objects from realistic payloads and assert through entity.is_on, exercising the actual aiolyric parsing boundary. For vacation_hold and room_motion specifically, the currently-pinned aiolyric==2.1.1 still has the exact key mismatches this PR's description already discloses (vacationHold.Enabled vs .enabled, sensorType vs type) — so those two are now xfail(strict=True) against the real live payload shape: they fail today for the same reason the real entities don't populate correctly yet, and strict=True means they'll turn into hard failures (forcing the marker's removal) the moment the companion aiolyric fixes are released and the manifest pin is bumped, rather than silently staying green. device_pairing_enabled has no known mismatch, so it gets a real end-to-end passing test today.
Addresses two review comments on the previous version of this test: - Copilot: tests built entities from MagicMocks with pre-set already- parsed properties, so they'd pass even with the exact live payload/ key mismatches this session found (e.g. vacationHold.Enabled vs .enabled). Now builds real LyricDevice/LyricRoom/LyricAccessory objects from realistic payloads and asserts through entity.is_on, exercising the actual parsing boundary. - Maintainer (@Samielakkad): setup test only checked entity_description.key, not the generated unique_id - the part most likely to collide/drift as more room/accessory sensors are added. Added a dedicated test asserting exact unique_id values across multiple rooms/accessories. Since aiolyric 2.1.1 (the currently pinned release) still has the vacationHold.Enabled and accessory sensorType mismatches, two new tests use xfail(strict=True) against the real live payload shape: they currently fail for the same reason the real entities don't work yet, and will start passing (forcing marker removal, since strict=True turns an unexpected pass into a failure) once the companion aiolyric fixes are released and the manifest pin is bumped. This documents the known gap explicitly instead of hiding it behind mocks. device_pairing_enabled has no known aiolyric mismatch, so it gets a real end-to-end pass today, proving the pattern isn't blanket broken. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Good catch, added. There's now a dedicated test (test_async_setup_entry_generates_correct_unique_ids) using real LyricDevice/LyricRoom/LyricAccessory objects across two rooms (one with a non-qualifying Thermostat-type accessory mixed in) that asserts the exact unique_id set for both device-level and accessory-level entities — confirming the room-id/accessory-id components of the accessory unique_id don't collide. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (3)
tests/components/lyric/test_binary_sensor.py:74
- Set up the config entry through
hass.config_entries.async_setup(...)instead of invoking the platform directly. The tests pylint job rejects this call withhome-assistant-tests-direct-platform-async-setup-entry, so the current branch cannot pass CI.
await async_setup_entry(MagicMock(), entry, async_add_entities)
tests/components/lyric/test_binary_sensor.py:134
- Shorten this docstring and avoid describing this mocked platform-level test as end-to-end. The parsing distinction is already evident from the payload and assertions.
Unlike vacation_hold and room_motion, this field isn't affected by any
known aiolyric field-name mismatch, so this exercises the full
integration boundary (real LyricDevice -> entity.is_on) end-to-end.
tests/components/lyric/test_binary_sensor.py:206
- Remove the duplicated failure narrative from this docstring. The
xfailreason immediately above already documents the upstream schema mismatch.
Built from the actual /priority response shape captured from a live
T9-T10 account - currently fails because accessory.type never matches
"IndoorAirSensor" under the pending key-name fix, so no entity is
created at all.
| """Entity unique_ids are correctly formed and don't collide across rooms. | ||
|
|
||
| Uses real LyricDevice/LyricRoom/LyricAccessory objects (constructed with | ||
| field names aiolyric 2.1.1 already parses correctly) to verify the ID | ||
| formula itself: device-level IDs key off the device MAC, accessory-level | ||
| IDs additionally key off room id and accessory id - the parts most | ||
| likely to collide when more room/accessory sensors are added later. | ||
| """ |
| """Vacation Hold should resolve True given a live-shaped payload. | ||
|
|
||
| Built from the actual API response shape captured from a live account, | ||
| not a synthetic/pre-parsed mock - currently fails because of the | ||
| pending key-name fix, by design. | ||
| """ |
CI enforces this as a hard rule (pylint plugin check home-assistant-tests-direct-platform-async-setup-entry, W7420): a platform's async_setup_entry must not be called directly in tests; use hass.config_entries.async_setup(entry.entry_id) instead. The previous version called binary_sensor.async_setup_entry directly with mocked hass/entry/async_add_entities, which this now-enforced rule flags. Adds tests/components/lyric/conftest.py with fixtures for a fully authenticated config entry (registered via application_credentials, matching test_config_flow.py's proven pattern) and HTTP-level mocks for the /locations and /priority endpoints using the actual live payload shape captured from a real T9-T10 account this session - including the real key names aiolyric 2.1.1 gets wrong (vacationHold .Enabled, accessories[].sensorType, currentPriority vs priority). Entities are looked up via entity_registry.async_get_entity_id() with their known unique_id format rather than guessing generated entity_id slugs, which is deterministic regardless of naming/translation quirks. device_pairing_enabled has no known field-name mismatch, so its test passes for real against the currently-pinned aiolyric release. vacation_hold and room_motion are xfail(strict=True) against the real live payload shape for the same reasons already disclosed in this PR's description; strict=True means they'll turn into hard failures (forcing marker removal) once the companion aiolyric fixes are released and the manifest pin is bumped, rather than silently staying green. Note: I could not run this against a real pytest+hass fixture harness locally (blocked by a Windows/fcntl limitation unrelated to this integration) - built carefully against the proven patterns in this repo's own test_config_flow.py and a structurally similar OAuth2 + DataUpdateCoordinator integration (iotty), but this needs a real pytest run to confirm before considering it fully verified. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
| # Deliberately "LCC-"-prefixed: this branch is intentionally cut from a | ||
| # clean base without the coordinator fix from home-assistant/core#177022 | ||
| # (which removes a device-ID-prefix heuristic gating the /priority fetch). | ||
| # Using a non-"LCC-" ID here would make room-level entities fail to be | ||
| # created for that unrelated, separately-tracked reason, muddying what | ||
| # these tests are actually checking. |
Only the LCC- prefix requirement matters to the fixture; the branch/PR context it referenced would rot as the codebase evolves. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
joostlek flagged the same conftest.py pattern on PR home-assistant#177067 (this branch's conftest.py was cut from the same base): a bespoke "cred" credential name instead of the DOMAIN default, setting up the lyric domain directly instead of application_credentials, and mocking HTTP responses instead of the aiolyric client itself. Bringing this branch in line proactively rather than waiting for the same comments here. - Register the test credential under DOMAIN, matching every other OAuth2 integration's conftest. - Set up application_credentials directly instead of relying on it being pulled in as a side effect of lyric's manifest dependency. - Patch Lyric.get_locations/get_thermostat_rooms to build real LyricLocation/LyricPriority objects instead of mocking HTTP at the aiohttp transport level. Field-name parsing still runs for real (the properties read straight off the same attributes dicts). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (2)
tests/components/lyric/test_binary_sensor.py:88
- Remove the duplicated upstream-failure narrative from this docstring. The
xfailreason immediately above already documents why the test currently fails.
"""Vacation Hold should read "on" via a real config entry setup.
The fixture location has vacationHold.Enabled = True (the real live
shape), so this documents the currently-pinned aiolyric bug rather
than hiding it - it fails today for the same reason the real entity
tests/components/lyric/test_binary_sensor.py:124
- Remove the duplicated upstream-failure narrative from this docstring. The
xfailreason immediately above already documents why the test currently fails.
"""Room Motion should be created via a real config entry setup.
The fixture priority data uses the real live shape ("priority",
"sensorType"), so under the currently-pinned aiolyric this entity
doesn't get created at all - documents the gap instead of hiding it.
| """Device Pairing Enabled is created via a real config entry setup. | ||
|
|
||
| Exercises the full boundary: real aiolyric parsing of the actual live | ||
| payload shape -> real coordinator -> real entity setup -> registered | ||
| state. devicePairingEnabled has no known aiolyric field-name mismatch, | ||
| so this passes against the currently-pinned release. | ||
| """ |
Same issue as the earlier DEVICE_ID comment fix: these docstrings restated the fixture/xfail-reason narrative rather than just the behavior under test, which would go stale as aiolyric changes. The xfail reason above each xfail test already documents the field-name mismatch, so the docstring doesn't need to repeat it.
| LOCATIONS_RESPONSE = [ | ||
| { | ||
| "locationID": LOCATION_ID, | ||
| "name": "Ocala P01", | ||
| "devices": [ | ||
| { | ||
| "vacationHold": {"Enabled": True}, | ||
| "scheduleStatus": "Resume", | ||
| "settings": {"devicePairingEnabled": True}, | ||
| "deviceClass": "Thermostat", | ||
| "deviceType": "Thermostat", | ||
| "deviceID": DEVICE_ID, | ||
| "name": "Ocala", | ||
| "macID": MAC_ID, | ||
| "units": "Fahrenheit", | ||
| "indoorTemperature": 79, | ||
| "deviceModel": "T9-T10", | ||
| } | ||
| ], | ||
| "users": [], | ||
| } | ||
| ] | ||
|
|
||
| PRIORITY_RESPONSE = { |
There was a problem hiding this comment.
Put these in a json file in fixtures
There was a problem hiding this comment.
Done — LOCATIONS_RESPONSE (and the priority payload, before it moved out entirely, see below) now live in tests/components/lyric/fixtures/*.json, loaded via load_json_array_fixture.
| @pytest.fixture | ||
| def mock_lyric_api() -> Generator[None]: | ||
| """Patch the aiolyric client to build real Location/Priority objects. | ||
|
|
||
| Patches Lyric.get_locations/get_thermostat_rooms directly rather than | ||
| mocking HTTP responses, so tests exercise real aiolyric parsing (the | ||
| same LyricLocation/LyricPriority code reading the actual field names | ||
| Resideo returns) without depending on network-mocking machinery. | ||
| """ | ||
|
|
||
| async def get_locations(self: Lyric) -> None: | ||
| self._locations = [ | ||
| LyricLocation(self._client, location) for location in LOCATIONS_RESPONSE | ||
| ] | ||
| self._locations_dict = { | ||
| location.location_id: location for location in self._locations | ||
| } | ||
|
|
||
| async def get_thermostat_rooms( | ||
| self: Lyric, location_id: str, device_id: str | ||
| ) -> None: | ||
| priority = LyricPriority(PRIORITY_RESPONSE) | ||
| self._priorities_dict[priority.device_id] = priority | ||
| self._rooms_dict[priority.device_id] = { | ||
| room.id: room for room in priority.current_priority.rooms | ||
| } | ||
|
|
||
| with ( | ||
| patch.object(Lyric, "get_locations", get_locations), | ||
| patch.object(Lyric, "get_thermostat_rooms", get_thermostat_rooms), |
There was a problem hiding this comment.
Instead check mealie how we patch the object there. This is already better than just patching http request so that's an improvement :)
There was a problem hiding this comment.
Good call, thanks — switched to patching Lyric where the integration imports it, with autospec=True, same as mealie's MealieClient pattern. Real LyricLocation/LyricPriority objects are still built from the fixture JSON and assigned directly onto the mock (locations, rooms_dict, etc.), so field-name parsing is still exercised for real — this just changes how the client gets substituted, not what's actually under test. Bonus: since nothing touches the network anymore, I could drop the whole "give it a valid unexpired token" dance too.
| async def async_setup_lyric_entry( | ||
| hass: HomeAssistant, | ||
| mock_config_entry: MockConfigEntry, | ||
| ) -> None: | ||
| """Set up the mock config entry and wait for it to settle.""" | ||
| mock_config_entry.add_to_hass(hass) | ||
| await hass.config_entries.async_setup(mock_config_entry.entry_id) | ||
| await hass.async_block_till_done() |
There was a problem hiding this comment.
this is more a method for __init__.py as it's not a fixture
There was a problem hiding this comment.
Right — moved it to tests/components/lyric/init.py as setup_integration, matching what dev's newer tests already do there.
| @pytest.mark.xfail( | ||
| strict=True, | ||
| reason=( | ||
| "aiolyric 2.1.1's VacationHold.enabled reads JSON key 'enabled', but " | ||
| "Resideo's live API returns 'Enabled' (capital E). Fixed upstream in " | ||
| "clutch2sft/aiolyric#fix-vacation-hold-key; once that's released and " | ||
| "the manifest pin is bumped, this will start passing for real and " | ||
| "this marker must be removed." | ||
| ), | ||
| ) | ||
| async def test_vacation_hold_created( |
There was a problem hiding this comment.
Let's not add failing tests directly IMO
There was a problem hiding this comment.
Fair, and it lines up with what abmantis said on the Priority Status sensor PR too — don't ship an entity that can't work yet. Rather than keep arguing that case per-entity, I split this PR down to just device_pairing_enabled (works today against the currently-pinned aiolyric) and moved vacation_hold/room_motion to a held-back branch to pick back up once aiolyric#165 and the vacation-hold-key fix actually release.
That also resolved the second half of this comment for free: with only one entity left, and its suitable_fn always True, there was nothing left for a direct value_fn/suitable_fn test to usefully cover beyond what a single snapshot_platform test already asserts — so that's what's here now instead.
| def test_vacation_hold_value_fn() -> None: | ||
| """Vacation Hold reflects device.vacation_hold.enabled.""" | ||
| description = next(d for d in DEVICE_BINARY_SENSORS if d.key == "vacation_hold") | ||
| assert description.value_fn(_mock_device(vacation_enabled=True)) is True | ||
| assert description.value_fn(_mock_device(vacation_enabled=False)) is False | ||
|
|
||
|
|
||
| def test_device_pairing_enabled_value_fn() -> None: | ||
| """Device Pairing Enabled reflects device.settings.device_pairing_enabled.""" | ||
| description = next( | ||
| d for d in DEVICE_BINARY_SENSORS if d.key == "device_pairing_enabled" | ||
| ) | ||
| assert description.value_fn(_mock_device(pairing_enabled=True)) is True | ||
| assert description.value_fn(_mock_device(pairing_enabled=False)) is False | ||
|
|
||
|
|
||
| def test_device_pairing_enabled_is_diagnostic() -> None: | ||
| """Device Pairing Enabled is diagnostic; Vacation Hold is not.""" | ||
| pairing = next( | ||
| d for d in DEVICE_BINARY_SENSORS if d.key == "device_pairing_enabled" | ||
| ) | ||
| vacation = next(d for d in DEVICE_BINARY_SENSORS if d.key == "vacation_hold") | ||
| assert pairing.entity_category is EntityCategory.DIAGNOSTIC | ||
| assert vacation.entity_category is None | ||
|
|
||
|
|
||
| def test_room_motion_suitable_fn_filters_by_accessory_type() -> None: | ||
| """Room Motion only applies to IndoorAirSensor accessories.""" | ||
| description = ACCESSORY_BINARY_SENSORS[0] | ||
| sensor_accessory = _mock_accessory(1, "IndoorAirSensor") | ||
| thermostat_accessory = _mock_accessory(0, "Thermostat") |
There was a problem hiding this comment.
We don't test all these things directly. Instead use snapshot_platform to fixate all the entities at least once, and for things like suitable_fn, you can change the source data and see that it's not created
|
Please take a look at the requested changes, and use the Ready for review button when you are done, thanks 👍 |
…helper - Move LOCATIONS_RESPONSE/PRIORITY_RESPONSE from inline dicts into fixtures/locations.json and fixtures/priority.json. - Patch Lyric where the integration imports it (autospec, matching mealie's client-mocking pattern) instead of patching individual methods on the aiolyric class directly. Real LyricLocation/LyricPriority objects are still built from the fixture JSON and assigned directly to the mock's locations/rooms_dict/priorities_dict, so field-name parsing is still exercised for real - this only changes how the client is substituted, not what gets tested. Bonus: no longer needs a valid OAuth token dance to avoid HTTP, since nothing touches the network at all now. - Move async_setup_lyric_entry out of conftest.py into tests/components/lyric/__init__.py as setup_integration, matching the convention already used by this integration's other test module (it's a helper, not a fixture).
joostlek pushed back on shipping xfail-marked entities directly (vacation_hold, room_motion), consistent with abmantis's separate "keep as draft until the dependency lands" stance on the Priority Status sensor PR. Rather than argue two reviewers down independently, split scope: this PR now ships only device_pairing_enabled, which works today against the currently-pinned aiolyric. vacation_hold and room_motion move to a held-back branch (binary-sensor-vacation-hold-room-motion) to resume once timmo001/aiolyric#165 and the vacation-hold-key fix release. - Remove ACCESSORY_BINARY_SENSORS/LyricAccessoryBinarySensor and the vacation_hold DEVICE_BINARY_SENSORS entry from binary_sensor.py, and their strings.json entries - dead code with only one entity left. - Drop the now-unused priority.json fixture and the rooms_dict/ priorities_dict setup in mock_lyric_api; nothing left needs room data. - Replace the three hand-written entity tests (one xfail, two working) with a single snapshot_platform test, and drop the direct value_fn/ suitable_fn unit tests - joostlek's other point on this PR. With only one entity and a suitable_fn that's always True, there's nothing left for those to usefully cover beyond what the snapshot already asserts.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
homeassistant/components/lyric/binary_sensor.py:37
- Align the implementation with the advertised binary sensors. The PR description and linked documentation say this platform adds Room Motion, Vacation Hold, and Device Pairing Enabled, but
DEVICE_BINARY_SENSORSnow defines only Device Pairing Enabled, so the first two entities are never created.
DEVICE_BINARY_SENSORS: list[LyricBinarySensorEntityDescription] = [
LyricBinarySensorEntityDescription(
key="device_pairing_enabled",
translation_key="device_pairing_enabled",
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=lambda device: device.settings.device_pairing_enabled,
suitable_fn=lambda device: True,
),
tests/components/lyric/conftest.py:63
- Shorten this fixture docstring to describe only its behavior. The autospec/Mealie implementation narrative duplicates the body and will become stale when the mock setup changes.
"""Mock the aiolyric client, backed by a real Location parsed from a live-shaped fixture.
Patches Lyric where the integration imports it (autospec, like the
mealie client mock) rather than mocking HTTP responses, so tests
exercise real aiolyric parsing - the same LyricLocation code reading
…ract Resideo's own API docs document locationID as an Integer, and aiolyric's get_thermostat_rooms already types it int, but this fixture quoted it as a string - inherited from early in this session before checking the real docs. No behavior change (the value is only ever interpolated into a URL), but the fixture now matches reality.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (3)
homeassistant/components/lyric/binary_sensor.py:34
- Implement the advertised Room Motion and Vacation Hold entities, or narrow the PR title and description to the pairing diagnostic.
DEVICE_BINARY_SENSORScurrently defines only Device Pairing Enabled, so the code does not deliver two of the three entities described as this PR's purpose.
DEVICE_BINARY_SENSORS: list[LyricBinarySensorEntityDescription] = [
LyricBinarySensorEntityDescription(
key="device_pairing_enabled",
translation_key="device_pairing_enabled",
entity_category=EntityCategory.DIAGNOSTIC,
tests/components/lyric/conftest.py:65
- Condense this fixture docstring to a single purpose-focused sentence; the patch-location and Mealie comparison narrate implementation details that callers do not need.
Patches Lyric where the integration imports it (autospec, like the
mealie client mock) rather than mocking HTTP responses, so tests
exercise real aiolyric parsing - the same LyricLocation code reading
the actual field names Resideo returns.
"""
tests/components/lyric/test_binary_sensor.py:21
- Activate the unused fixtures with
pytest.mark.usefixturesinstead of injecting them as unused parameters, following the repository's test guidance.
setup_credentials: None,
mock_lyric_api: MagicMock,
- Condense mock_lyric_api's docstring to a single purpose-focused sentence; drop the patch-location/Mealie-comparison narration. - Apply setup_credentials/mock_lyric_api via usefixtures in test_binary_sensor instead of injecting them as unused parameters.
Proposed change
Adds a new
binary_sensorplatform to thelyricintegration, exposing afield that was already being parsed by the coordinator but never surfaced
as an entity:
device.settings.device_pairing_enabledDevice Pairing Enabled introduces a
DEVICE_BINARY_SENSORSlist mirroringthe existing
DEVICE_SENSORSpattern.Room Motion and Vacation Hold were originally part of this PR too, but both
depend on upstream aiolyric fixes that haven't released yet (field-name
mismatches tracked in timmo001/aiolyric#165 and the vacation-hold-key fix),
so they've been split out to a held-back branch to avoid shipping entities
that can't work correctly yet. They'll come back as their own PR once the
dependency chain lands.
IndoorAirSensorroom accessory) —accessory.detect_motiondevice.vacation_hold.enableddevice.settings.device_pairing_enabledRoom Motion follows the same per-accessory pattern already used for Room
Temperature/Humidity in
sensor.py. Vacation Hold and Device Pairing Enabledintroduce a
DEVICE_BINARY_SENSORSlist mirroring the existingDEVICE_SENSORSpattern.
Dependency note: Room Motion only populates once room/accessory data is being
fetched at all, which depends on #177022 (fixes the coordinator silently skipping
this fetch for some thermostats). Vacation Hold and Device Pairing Enabled are
device-level fields from the base
/locationsresponse, which is always fetched —they work independently of #177022.
Type of change
Additional information
Checklist
ruff format homeassistant tests)If user exposed functionality or configuration variables are added/changed:
Documentation added/updated for www.home-assistant.io
If the code communicates with devices, web services, or third-party tools:
Updated and included derived files by running:
python3 -m script.hassfest.requirements_all.txt.Updated by running
python3 -m script.gen_requirements_all.To help with the load of incoming pull requests: