Skip to content

Add support for Tapo H110 IR Air Conditioners#1716

Open
Andrei965 wants to merge 69 commits into
python-kasa:masterfrom
Andrei965:feature/tapo-h110-ac-support
Open

Add support for Tapo H110 IR Air Conditioners#1716
Andrei965 wants to merge 69 commits into
python-kasa:masterfrom
Andrei965:feature/tapo-h110-ac-support

Conversation

@Andrei965

Copy link
Copy Markdown

Based on #1590

Description:
This PR introduces support for Air Conditioner virtual remotes connected via the Tapo H110 Hub using the TPAP/KLAP protocol.

Because IR remotes are stateless and require sending the entire state payload in a single IR blast, this PR implements a dedicated SmartIrAC class subclassing SmartDevice that overrides standard behavior. Instead of updating properties individually, it uses the hub's control_child method to send a sendIrCmdByStatus request containing all AC properties (power, on, mode, temp, wind_speed, wind_direct).

Key Changes:

  • Added SmartIrAC class in kasa/smart/smartirac.py.
  • Added Climate to the DeviceType enum.
  • Updated SmartDevice factory logic to instantiate SmartIrAC when sys_info.get("category") == "ir.remote".
  • Added corresponding unit tests in tests/smart/test_smartirac.py.

Testing Performed:

  • Manually tested against a physical Tapo H110 Hub with a registered Air Conditioner virtual remote.
  • Successfully verified bidirectional state tracking (via ac_status strings) and successful IR blasting for Power, Modes, Fan Speeds, and Target Temperature.
  • Verified that it handles missing properties in the ac_status string by gracefully falling back to defaults or previously known states.


@staticmethod
def _sha256_hex_upper(value: str) -> str:
return hashlib.sha256(value.encode()).hexdigest().upper() # noqa: S324
encoding=serialization.Encoding.X962,
format=serialization.PublicFormat.UncompressedPoint,
)

cls, password: str, prefix: str, rounds_from_params: int | None = None
) -> str | None:
if not prefix:
return None
return "default_userpw"
if 3 in pake:
return "shared_token"
if 1 in pake or 2 in pake or 5 in pake:

@rytilahti rytilahti left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hey @Andrei965, really nice to see new contributors to the project! I left few comments inline, feel free to take a look. As this requires a new comms protocol that has not been merged, I ignored the parts related to that for now.

We might create a separate feature branch for TPAP soon to make it easier to review PRs that require it, but until then, feel free to take a look at my comments and let me know what you think. And please create a PR to add a fixture file for testing (or include it to this PR), as seeing the data makes it easier to review the code.

Comment thread kasa/smart/__init__.py
from .smartirac import SmartIrAC

__all__ = ["SmartDevice", "SmartChildDevice"]
__all__ = ["SmartDevice", "SmartChildDevice", "SmartIrAC"]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

As this is a child device, there should be no need to export this.

Comment thread kasa/smart/smartdevice.py
async def _try_create_child(
self, info: dict, child_components: dict
) -> SmartDevice | None:
if info.get("category") == "ir.remote" and info.get("model") == "AC":

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

As this is a child device, please move the initialization to happen inside SmartChildDevice.create().

Comment thread kasa/smart/smartirac.py
Comment on lines +22 to +27
def device_type(self) -> DeviceType:
"""Return the device type."""
try:
return DeviceType.Climate
except AttributeError:
return DeviceType.Thermostat

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

When can this happen?

Comment thread kasa/smart/smartirac.py
"""Update the internal info state."""
super()._update_internal_state(info)

ac_status = info.get("ac_status")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We want to have a less fragile parsing of the status. I cannot off the bat recommend how it should look like (could you create a PR to add a fixture?), but we want to make it more robust to avoid breaking if the device responses change suddenly.

If this is a regular smart device, maybe it is only necessary to implement a module to handle this case? Let me know what do you think & if you need any help with deciphering how to bolt modules in!

@rytilahti rytilahti added enhancement New feature or request new device New device supported due to fixture being added labels Jul 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request new device New device supported due to fixture being added

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants