Skip to content

Conversation

@jesserockz
Copy link
Member

@jesserockz jesserockz commented Nov 27, 2025

What does this implement/fix?

Add client-side support for user-defined service responses. This allows ESPHome devices to send response data back to the client when executing user-defined services via the api.respond action.

Changes:

  • Added call_id and return_response fields to ExecuteServiceRequest
  • Added new ExecuteServiceResponse message (id=131) with call_id, success, error_message, and response_data fields
  • Added SupportsResponseType enum (NONE, OPTIONAL, ONLY, STATUS) to indicate service response capability
  • Added supports_response field to ListEntitiesServicesResponse / UserService model
  • Updated execute_service() to accept optional on_response callback and return_response parameters
  • Callback auto-unsubscribes after receiving the matching response

Usage:

def handle_response(response: ExecuteServiceResponse):
    print(f"Success: {response.success}, Data: {response.response_data}")

client.execute_service(
    service,
    data={"arg": value},
    on_response=handle_response,
    return_response=True,
)

Related PRs:

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Code quality improvements to existing code or addition of tests
  • Other

Related issue or feature (if applicable):

Pull request in esphome (if applicable):

  • esphome/esphome#XXXX

Checklist:

  • The code change is tested and works locally.
  • If api.proto was modified, a linked pull request has been made to esphome with the same changes.
  • Tests have been added to verify that the new code works (under tests/ folder).

@codecov
Copy link

codecov bot commented Nov 27, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (aa6db48) to head (99049c4).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main     #1436   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           22        22           
  Lines         3312      3335   +23     
=========================================
+ Hits          3312      3335   +23     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@codspeed-hq
Copy link

codspeed-hq bot commented Nov 27, 2025

CodSpeed Performance Report

Merging #1436 will not alter performance

Comparing jesserockz-2025-539 (99049c4) with main (aa6db48)1

Summary

✅ 11 untouched

Footnotes

  1. No successful run was found on main (6e4cd54) during the generation of this report, so aa6db48 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 27, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds protobuf enums/messages and client/model/core/test support to allow user-defined service actions to return server-sent ExecuteServiceResponse messages identified by call IDs and handled via optional response callbacks.

Changes

Cohort / File(s) Change Summary
Protobuf surface
aioesphomeapi/api.proto
Added SupportsResponseType enum; added supports_response to ListEntitiesServicesResponse; added call_id and return_response fields to ExecuteServiceRequest (ifdef guarded); added ExecuteServiceResponse message (ifdef guarded) and ListEntitiesCameraResponse (id 43).
Client code
aioesphomeapi/client.py
APIClient.execute_service signature extended with on_response and return_response; registers/unregisters response listeners, generates call IDs when on_response is provided, attaches call_id to request, and converts incoming proto responses to model before invoking callbacks; imports/proxies ExecuteServiceResponse.
Client base
aioesphomeapi/client_base.py
Added itertools import, new _call_id_counter slot and initialization (counter starting at 1) for call-id generation.
Core protocol mapping
aioesphomeapi/core.py
Imported ExecuteServiceResponse and added mapping entry (message type 131) to MESSAGE_TYPE_TO_PROTO.
Model layer
aioesphomeapi/model.py
Added SupportsResponseType enum, added supports_response field to UserService, and introduced ExecuteServiceResponse model (fields: call_id, success, error_message, response_data).
Tests
tests/test_client.py, tests/test_model.py
Added/updated tests for call_id generation and incrementing, on_response callback wiring/unsubscribe behavior, return_response propagation, proto/model aliases, and enum handling; adjusted imports and assertions accordingly.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant APIClient
    participant Server
    participant Callback

    User->>APIClient: execute_service(service, data, on_response=cb, return_response=true)
    APIClient->>APIClient: allocate call_id, register listener(call_id -> cb)
    APIClient->>Server: ExecuteServiceRequest(call_id, return_response=true, data)
    Note over Server: perform user-defined action (may emit response)
    Server->>APIClient: ExecuteServiceResponse(call_id, success, response_data)
    APIClient->>Callback: invoke cb(ExecuteServiceResponseModel)
    APIClient->>APIClient: unregister listener for call_id
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Inspect callback registration/unsubscription and race conditions in aioesphomeapi/client.py.
  • Verify _call_id_counter initialization and thread-safety in aioesphomeapi/client_base.py.
  • Confirm message number (131) mapping and proto import in aioesphomeapi/core.py.
  • Validate proto ifdef guards and generated code compatibility for ExecuteServiceResponse.
  • Check model conversion and enum mapping in aioesphomeapi/model.py.
  • Review added/updated tests in tests/test_client.py and tests/test_model.py for completeness.

Suggested labels

minor

Suggested reviewers

  • bdraco

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 43.48% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add device defined action responses' directly and clearly summarizes the main feature being introduced: support for device-defined service responses.
Description check ✅ Passed The description is well-detailed and directly related to the changeset, explaining the implementation, changes made, usage examples, and linking to related PRs.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch jesserockz-2025-539

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8f13eaf and 99049c4.

📒 Files selected for processing (2)
  • aioesphomeapi/client.py (4 hunks)
  • aioesphomeapi/client_base.py (3 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Use ruff check --fix to check and fix Python linting issues before committing
Use ruff format to format Python code before committing

Files:

  • aioesphomeapi/client_base.py
  • aioesphomeapi/client.py
**

⚙️ CodeRabbit configuration file

  • Do not generate or add any sequence diagrams

Files:

  • aioesphomeapi/client_base.py
  • aioesphomeapi/client.py
🧠 Learnings (1)
📚 Learning: 2025-04-16T01:35:06.073Z
Learnt from: bdraco
Repo: esphome/aioesphomeapi PR: 1156
File: tests/test_log_runner.py:187-187
Timestamp: 2025-04-16T01:35:06.073Z
Learning: In the aioesphomeapi project, the maintainers prefer using `asyncio.get_running_loop()` without fallbacks to `asyncio.get_event_loop()`, as this makes incorrect usage fail fast rather than having confusing outcomes. This was decided during a major version increment, making it an intentional breaking change.

Applied to files:

  • aioesphomeapi/client.py
🧬 Code graph analysis (1)
aioesphomeapi/client.py (3)
aioesphomeapi/model.py (2)
  • ExecuteServiceResponse (1256-1262)
  • UserService (1244-1252)
tests/test_client.py (1)
  • on_response (2273-2274)
aioesphomeapi/connection.py (1)
  • add_message_callback (817-822)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: py 3.11 on windows-latest (skip_cython)
🔇 Additional comments (2)
aioesphomeapi/client_base.py (1)

6-6: Per-client call ID counter wiring looks correct

Using itertools.count(1) and storing it in _call_id_counter via __slots__ gives you a simple, per-client monotonic ID generator with no extra state on APIConnection. This matches the “per device” expectation and is safe in the asyncio usage pattern here.

Also applies to: 220-229, 291-291

aioesphomeapi/client.py (1)

48-48: Public ExecuteServiceResponse imports are consistent

Importing ExecuteServiceResponse from api_pb2 and exposing ExecuteServiceResponseModel from .model aligns the client with the new proto/model surface and keeps the callback typing precise.

Also applies to: 138-138


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0d63de0 and 619c6be.

📒 Files selected for processing (6)
  • aioesphomeapi/api.proto (3 hunks)
  • aioesphomeapi/client.py (4 hunks)
  • aioesphomeapi/core.py (2 hunks)
  • aioesphomeapi/model.py (2 hunks)
  • tests/test_client.py (4 hunks)
  • tests/test_model.py (6 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Use ruff check --fix to check and fix Python linting issues before committing
Use ruff format to format Python code before committing

Files:

  • aioesphomeapi/core.py
  • aioesphomeapi/client.py
  • aioesphomeapi/model.py
  • tests/test_client.py
  • tests/test_model.py
**

⚙️ CodeRabbit configuration file

  • Do not generate or add any sequence diagrams

Files:

  • aioesphomeapi/core.py
  • aioesphomeapi/api.proto
  • aioesphomeapi/client.py
  • aioesphomeapi/model.py
  • tests/test_client.py
  • tests/test_model.py
🧬 Code graph analysis (4)
aioesphomeapi/core.py (1)
aioesphomeapi/model.py (1)
  • ExecuteServiceResponse (1255-1261)
aioesphomeapi/client.py (3)
aioesphomeapi/model.py (2)
  • ExecuteServiceResponse (1255-1261)
  • UserService (1243-1251)
tests/test_client.py (1)
  • on_response (2255-2256)
aioesphomeapi/connection.py (1)
  • add_message_callback (817-822)
tests/test_client.py (4)
aioesphomeapi/model.py (5)
  • ExecuteServiceResponse (1255-1261)
  • APIVersion (100-102)
  • UserService (1243-1251)
  • UserServiceArg (1223-1239)
  • UserServiceArgType (1204-1212)
tests/conftest.py (1)
  • api_client (209-238)
aioesphomeapi/client.py (2)
  • APIClient (201-1640)
  • execute_service (1316-1371)
tests/common.py (2)
  • mock_data_received (65-76)
  • generate_plaintext_packet (109-110)
tests/test_model.py (1)
aioesphomeapi/model.py (10)
  • ExecuteServiceResponse (1255-1261)
  • SupportsResponseType (1215-1219)
  • convert (39-43)
  • convert (178-181)
  • UserService (1243-1251)
  • from_pb (86-87)
  • from_pb (1319-1362)
  • from_pb (1408-1414)
  • from_pb (1448-1459)
  • from_pb (1491-1503)
🪛 GitHub Actions: CI
aioesphomeapi/client.py

[error] 1360-1360: flake8: F824 'nonlocal unsub' is unused: name is never assigned in scope

🪛 GitHub Check: py 3.14 on ubuntu-latest (skip_cython)
aioesphomeapi/client.py

[failure] 1360-1360:
F824 nonlocal unsub is unused: name is never assigned in scope

🔇 Additional comments (13)
aioesphomeapi/core.py (2)

8-55: ExecuteServiceResponse import wiring looks correct

Importing ExecuteServiceResponse alongside ExecuteServiceRequest keeps the core surface in sync with the proto definitions; usage below in MESSAGE_TYPE_TO_PROTO ensures it is not unused.


364-496: Protocol mapping for ExecuteServiceResponse is consistent

Adding 131: ExecuteServiceResponse at the end of MESSAGE_TYPE_TO_PROTO maintains the sequential message-number mapping and ensures ExecuteServiceResponse frames are decoded to the right proto type. No functional issues detected.

aioesphomeapi/model.py (2)

1215-1219: SupportsResponseType enum integrates cleanly with APIIntEnum

The SupportsResponseType enum follows the existing APIIntEnum pattern, uses protobuf-default values (0,1,2,100), and will gracefully map unknown integers to None via convert(). This is compatible with the converter_field mechanism and back-compat expectations.


1243-1261: UserService.supports_response and ExecuteServiceResponse model are well-structured

  • UserService.supports_response uses converter_field with default SupportsResponseType.NONE, so older payloads deserialize correctly and unknown values degrade to None.
  • ExecuteServiceResponse mirrors the proto shape (call_id/success/error_message/response_data) and respects protobuf defaults via appropriate dataclass defaults.

No behavioral or compatibility issues spotted here.

tests/test_model.py (3)

8-69: Import surface updates for new response types are consistent

Aliases ExecuteServiceResponsePb and SupportsResponseTypePb from api_pb2, and their model counterparts, are introduced in line with existing naming patterns. They are used later in tests and do not introduce unused-symbol issues.

Also applies to: 70-151


272-327: Including ExecuteServiceResponse in generic pb conversion test is appropriate

Extending test_basic_pb_conversions with (ExecuteServiceResponse, ExecuteServiceResponsePb) ensures the new model participates in the same from_pb sanity checks as other models, which is a good safety net for future changes.


1780-1910: New tests thoroughly cover SupportsResponseType and ExecuteServiceResponse behavior

  • test_supports_response_type_convert and test_supports_response_type_values validate enum mapping, including unknown values returning None.
  • test_user_service_with_supports_response checks protobuf, dict, and to_dict paths for UserService.supports_response, confirming converter behavior and defaulting to NONE.
  • test_execute_service_response exercises from_pb (success and error cases), defaults, from_dict, and to_dict for ExecuteServiceResponse.

These tests accurately reflect the model semantics and should catch common regression modes.

tests/test_client.py (3)

16-56: New ExecuteServiceResponse imports are consistent and correctly aliased

Using ExecuteServiceResponsePb from api_pb2 and ExecuteServiceResponseModel from model matches the proto/model aliasing pattern used elsewhere in the tests and lines up with the client’s execute_service implementation.

Also applies to: 107-146


946-1092: Tests around call_id and inferred return_response cover key combinations

  • test_execute_service_with_call_id asserts that specifying a non-zero call_id without on_response leaves return_response=False, and that the default call_id is 0, both reflected in the outgoing ExecuteServiceRequest.
  • test_execute_service_return_response_combinations systematically covers the matrix of call_id {0, non-zero} × on_response {None, callback}, ensuring the return_response inference logic in APIClient.execute_service behaves as intended.

These tests tightly couple to the intended API contract without over-constraining implementation details.


2245-2331: Callback-based ExecuteService response handling is well-validated

test_execute_service_with_response_callback exercises the full callback path:

  • Verifies that a matching ExecuteServiceResponsePb produces a single ExecuteServiceResponseModel instance with the expected data.
  • Confirms auto-unsubscribe by asserting that a second response with the same call_id is ignored.
  • Ensures responses with a different call_id are ignored, while a subsequent correct call_id delivers exactly one callback.

This matches the implementation in aioesphomeapi.client.execute_service and guards against regressions in the call_id filtering/unsubscribe logic.

aioesphomeapi/api.proto (2)

857-864: LGTM - Well-designed enum with future-proofing.

The SUPPORTS_RESPONSE_STATUS = 100 value is appropriately set high to avoid conflicts with potential future Home Assistant values, and the comment explains this design decision clearly.


901-916: New request/response fields and message are well-structured.

The ExecuteServiceRequest extensions and new ExecuteServiceResponse message follow existing patterns in the codebase (similar to HomeassistantActionRequest/HomeassistantActionResponse). The field_ifdef guards ensure proper conditional compilation.

aioesphomeapi/client.py (1)

48-48: New imports properly aligned with proto and model changes.

The ExecuteServiceResponse proto message and ExecuteServiceResponseModel model class are correctly imported to support the new response handling functionality.

Also applies to: 138-138

@jesserockz jesserockz requested a review from Copilot November 27, 2025 04:20
Copilot finished reviewing on behalf of jesserockz November 27, 2025 04:21
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds client-side support for user-defined service responses from ESPHome devices. It enables ESPHome devices to send response data back to the client when executing user-defined services via the api.respond action.

Key changes:

  • Added ExecuteServiceResponse message (id=131) with call_id matching, success/error status, and response data
  • Added SupportsResponseType enum to indicate service response capabilities (NONE, OPTIONAL, ONLY, STATUS)
  • Extended execute_service() method to accept optional call_id and on_response callback parameters with auto-unsubscribe functionality

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
aioesphomeapi/api.proto Added SupportsResponseType enum, ExecuteServiceResponse message, and new fields to ExecuteServiceRequest and ListEntitiesServicesResponse
aioesphomeapi/api_pb2.py Auto-generated protobuf Python code reflecting the proto changes
aioesphomeapi/model.py Added Python models for SupportsResponseType enum and ExecuteServiceResponse dataclass
aioesphomeapi/core.py Registered ExecuteServiceResponse message type (id=131)
aioesphomeapi/client.py Extended execute_service() with callback support and auto-unsubscribe logic
tests/test_model.py Added comprehensive tests for new enum conversions, model serialization, and UserService updates
tests/test_client.py Added tests for execute_service with call_id, return_response logic, and callback behavior

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
aioesphomeapi/model.py (1)

1255-1262: Add pylint disable comment for consistency.

The response_data field is missing the # pylint: disable=invalid-field-call comment that is consistently used throughout this file for all other bytes fields with default_factory (see lines 149, 155, 600, 1199, 1400, 1616, 1686).

Apply this diff:

 @_frozen_dataclass_decorator
 class ExecuteServiceResponse(APIModelBase):
     call_id: int = 0  # Call ID that matches the original request
     success: bool = False  # Whether the service execution succeeded
     error_message: str = ""  # Error message if success = false
     response_data: bytes = field(
         default_factory=bytes
-    )  # JSON response data from ESPHome
+    )  # pylint: disable=invalid-field-call
+    # JSON response data from ESPHome
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 62063cc and 14ecfb0.

📒 Files selected for processing (2)
  • aioesphomeapi/api.proto (3 hunks)
  • aioesphomeapi/model.py (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Use ruff check --fix to check and fix Python linting issues before committing
Use ruff format to format Python code before committing

Files:

  • aioesphomeapi/model.py
**

⚙️ CodeRabbit configuration file

  • Do not generate or add any sequence diagrams

Files:

  • aioesphomeapi/model.py
  • aioesphomeapi/api.proto
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: py 3.11 on windows-latest (skip_cython)
🔇 Additional comments (6)
aioesphomeapi/model.py (2)

1216-1221: LGTM!

The enum definition is correct and matches the protobuf specification. The STATUS=100 value is intentionally set higher to avoid conflicts with future values, as documented in the proto file.


1250-1252: LGTM!

The supports_response field is properly configured with the correct default value and converter, matching the protobuf specification.

aioesphomeapi/api.proto (4)

858-865: LGTM!

The enum definition is well-structured with a clear comment explaining the STATUS value placement. The values correctly map to the Python model.


879-879: LGTM!

The field is properly positioned with a sequential field number and correctly typed to match the new enum.


902-904: LGTM!

The new fields are properly guarded and follow the established pattern from HomeassistantActionRequest. Field numbering is sequential and appropriate.


906-917: LGTM!

The message definition is well-structured and mirrors the established pattern from HomeassistantActionResponse. The ifdef guards and field options are appropriate, and the message ID (131) is unique and sequential.

service: UserService,
data: ExecuteServiceDataType,
*,
call_id: int = 0,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be a bit safer to make this a sentinel value so its harder to forget/mis-use the api

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not 100% on the flow here, but does the caller need to know about the call_id?

      if on_response is not None:
          self._call_id_counter += 1
          call_id = self._call_id_counter
          return_response = True
      else:
          call_id = 0
          return_response = False

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm.. I guess you could have multiple HA instances controlling the device.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either way you have collisions. Probably still a cleaner API to have aioesphomeapi have the counter logic instead of HA having to know about it

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
aioesphomeapi/client.py (1)

1319-1371: execute_service callback behavior is correct; consider documenting semantics

The updated execute_service:

  • Auto‑generates a non‑zero call_id whenever on_response is provided, leaving it 0 otherwise.
  • Always passes the provided return_response flag through as‑is.
  • Registers a temporary ExecuteServiceResponse handler that:
    • Filters by call_id,
    • Converts to ExecuteServiceResponseModel,
    • Unsubscribes itself after the first matching response.

This matches the new tests and avoids race conditions by registering the callback before sending the request.

As a small improvement, it may be worth adding a brief docstring or API comment clarifying:

  • That call_id is managed internally and monotonically increasing.
  • That on_response can be used for status‑only responses even when return_response is False.
  • That if the device never responds, the callback remains registered until the connection is torn down.

That extra documentation would make the new behavior easier for external callers to reason about.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 14ecfb0 and 8f13eaf.

📒 Files selected for processing (2)
  • aioesphomeapi/client.py (6 hunks)
  • tests/test_client.py (4 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Use ruff check --fix to check and fix Python linting issues before committing
Use ruff format to format Python code before committing

Files:

  • tests/test_client.py
  • aioesphomeapi/client.py
**

⚙️ CodeRabbit configuration file

  • Do not generate or add any sequence diagrams

Files:

  • tests/test_client.py
  • aioesphomeapi/client.py
🧠 Learnings (1)
📚 Learning: 2025-04-16T01:35:06.073Z
Learnt from: bdraco
Repo: esphome/aioesphomeapi PR: 1156
File: tests/test_log_runner.py:187-187
Timestamp: 2025-04-16T01:35:06.073Z
Learning: In the aioesphomeapi project, the maintainers prefer using `asyncio.get_running_loop()` without fallbacks to `asyncio.get_event_loop()`, as this makes incorrect usage fail fast rather than having confusing outcomes. This was decided during a major version increment, making it an intentional breaking change.

Applied to files:

  • aioesphomeapi/client.py
🧬 Code graph analysis (2)
tests/test_client.py (2)
aioesphomeapi/model.py (5)
  • ExecuteServiceResponse (1256-1262)
  • APIVersion (100-102)
  • UserService (1244-1252)
  • UserServiceArg (1224-1240)
  • UserServiceArgType (1205-1213)
aioesphomeapi/client.py (2)
  • APIClient (203-1641)
  • execute_service (1318-1372)
aioesphomeapi/client.py (4)
aioesphomeapi/model.py (2)
  • ExecuteServiceResponse (1256-1262)
  • UserService (1244-1252)
tests/test_client.py (1)
  • on_response (2273-2274)
aioesphomeapi/client_base.py (1)
  • _get_connection (363-371)
aioesphomeapi/connection.py (1)
  • add_message_callback (817-822)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: py 3.11 on windows-latest (skip_cython)
🔇 Additional comments (3)
tests/test_client.py (2)

55-55: ExecuteService call_id and return_response tests match client semantics

The new imports and tests around execute_service correctly exercise the updated API:

  • test_execute_service_with_call_id verifies:
    • call_id == 0 and return_response is False when on_response is not provided.
    • Non-zero, monotonically increasing call_id values when on_response is set, without assuming a specific starting value.
  • test_execute_service_return_response_combinations cleanly covers the four important combinations of on_response and return_response, asserting that return_response is passed through unchanged and call_id is only generated when a callback is present.

This gives good confidence that the client-side behavior for request wiring is stable and backward compatible for existing callers.

Also applies to: 125-125, 1047-1141


2252-2358: End‑to‑end ExecuteServiceResponse callback behavior is well covered

test_execute_service_with_response_callback thoroughly validates the callback flow:

  • Captures the auto-generated call_id from the actual ExecuteServiceRequest sent on the connection.
  • Confirms that a matching ExecuteServiceResponsePb is converted to ExecuteServiceResponseModel and delivered once.
  • Asserts that the callback auto-unsubscribes after the first matching response.
  • Verifies that responses with incorrect call_id are ignored and that a second request increments call_id.

No issues found; this is a solid regression test for the new response-handling path.

aioesphomeapi/client.py (1)

7-7: Call‑ID counter and ExecuteServiceResponse wiring are consistent

Using a module‑level _CALL_ID_COUNTER = itertools.count(1) and reserving call_id == 0 for calls without a response callback matches the new tests and keeps the pb/model imports (ExecuteServiceResponse / ExecuteServiceResponseModel) cleanly separated. No functional or style issues stand out here.

Also applies to: 49-49, 139-139, 170-170

from .util import create_eager_task

_LOGGER = logging.getLogger(__name__)
_CALL_ID_COUNTER = itertools.count(1)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be per device ?

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.

3 participants