Skip to content

Conversation

fifeek0
Copy link

@fifeek0 fifeek0 commented Sep 16, 2025

Summary

This pull request introduces comprehensive support for Tech HVAC recuperation (ventilation) systems within the Home Assistant integration. It adds new binary sensors to monitor filter replacement and system status, introduces button entities for controlling recuperation features (filter reset, party mode, quick boost), and extends the integration's constants and platform support to handle these new capabilities.


Recuperation System Support

  • Added detection logic for recuperation systems in binary_sensor.py and button.py.
  • Enabled conditional creation of related entities when flow sensors are present in the system.

New Entities for Recuperation Control and Monitoring

  • Implemented new binary sensors:
    • FilterReplacementSensor for filter replacement needs.
    • RecuperationSystemStatusSensor for operational system status.
  • Added new button entities:
    • FilterResetButton
    • PartyModeButton
    • QuickBoostButton
  • These allow users to reset filter timers and activate party/boost modes directly from Home Assistant.

Platform and Constants Extension

  • Extended supported platforms to include:
    • Platform.FAN
    • Platform.NUMBER
    • Platform.SELECT
    • Platform.BUTTON
    • Platform.SWITCH
  • Defined new constants for:
    • Recuperation system types
    • Flow sensors
    • Temperature sensors
    • Speed controls
    • Party mode
    • Gear control
    • Filter management
    • Related configuration keys and ranges

Integration and State Management

  • Updated integration setup to store the config entry in the coordinator.
  • Added logic to load filter reset dates from persistent storage for accurate filter usage tracking.

These changes enhance the integration’s ability to monitor and control recuperation units, giving users improved diagnostics and direct control over key ventilation features.

fifeek0 and others added 4 commits September 16, 2025 17:29
…ch entities

- Implemented number controls for recuperation systems, including party mode, speed configurations, filter alarms, and ventilation parameters.
- Added select controls for fan modes and gear settings with Polish language support.
- Introduced a flow balancing switch for managing airflow in recuperation systems.
- Enhanced logging for better debugging and tracking of state changes.
…handling; update Tech class headers to include User-Agent
Enhance RecuperationEfficiencySensor with improved logging and error …
@fifeek0 fifeek0 marked this pull request as ready for review September 17, 2025 16:18
@anarion80
Copy link
Collaborator

anarion80 commented Sep 18, 2025

Thanks for the PR. Some big changes here. I had to update the main with some fixes and devenv updates to check the PR.
Tests are working, but when adding all controllers from demo account (test/test), there is one error:

2025-09-18 12:17:47.239 ERROR (MainThread) [homeassistant.components.fan] Error while setting up tech platform for fan: Can't instantiate abstract class TileFanEntity without an implementation for abstract method 'get_state'
Traceback (most recent call last):
  File "/home/anarion/Documents/Repos/tech-controllers/.devenv/state/venv/lib/python3.13/site-packages/homeassistant/helpers/entity_platform.py", line 451, in _async_setup_platform
    await asyncio.shield(awaitable)
  File "/home/anarion/Documents/Repos/tech-controllers/config/custom_components/tech/fan.py", line 75, in async_setup_entry
    entities.append(TileFanEntity(tile, coordinator, config_entry))
                    ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: Can't instantiate abstract class TileFanEntity without an implementation for abstract method 'get_state'

fifeek0 and others added 2 commits September 18, 2025 16:14
Refactor import statements for flow constants and add get_state metho…
@fifeek0
Copy link
Author

fifeek0 commented Sep 18, 2025

Thanks for the PR. Some big changes here. I had to update the main with some fixes and devenv updates to check the PR. Tests are working, but when adding all controllers from demo account (test/test), there is one error:

2025-09-18 12:17:47.239 ERROR (MainThread) [homeassistant.components.fan] Error while setting up tech platform for fan: Can't instantiate abstract class TileFanEntity without an implementation for abstract method 'get_state'
Traceback (most recent call last):
  File "/home/anarion/Documents/Repos/tech-controllers/.devenv/state/venv/lib/python3.13/site-packages/homeassistant/helpers/entity_platform.py", line 451, in _async_setup_platform
    await asyncio.shield(awaitable)
  File "/home/anarion/Documents/Repos/tech-controllers/config/custom_components/tech/fan.py", line 75, in async_setup_entry
    entities.append(TileFanEntity(tile, coordinator, config_entry))
                    ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: Can't instantiate abstract class TileFanEntity without an implementation for abstract method 'get_state'

Now should be ok

@anarion80
Copy link
Collaborator

anarion80 commented Sep 18, 2025

Now should be ok

Unfortunately, now there is a big problem with tiles.

Previously:

image image

And it was roughly matching the tiles in demo account (after much trial and error):
image

Now:
image

They are now devices with 1 entity, and all the other tiles are disconnected from the hub:
image

Also:

image image

while previously it was just a sensor showing fan rotations like in emodul dashboard:

Changing fan speed then results in an error for such sensors now:

2025-09-18 19:00:41.359 ERROR (MainThread) [homeassistant.components.websocket_api.http.connection] [140193446247648] Unexpected exception
Traceback (most recent call last):
  File "/home/anarion/Documents/Repos/tech-controllers/.devenv/state/venv/lib/python3.13/site-packages/homeassistant/components/websocket_api/commands.py", line 264, in handle_call_service
    response = await hass.services.async_call(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ...<7 lines>...
    )
    ^
  File "/home/anarion/Documents/Repos/tech-controllers/.devenv/state/venv/lib/python3.13/site-packages/homeassistant/core.py", line 2835, in async_call
    response_data = await coro
                    ^^^^^^^^^^
  File "/home/anarion/Documents/Repos/tech-controllers/.devenv/state/venv/lib/python3.13/site-packages/homeassistant/core.py", line 2878, in _execute_service
    return await target(service_call)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/anarion/Documents/Repos/tech-controllers/.devenv/state/venv/lib/python3.13/site-packages/homeassistant/helpers/service.py", line 850, in entity_service_call
    single_response = await _handle_entity_call(
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^
        hass, entity, func, data, call.context
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/anarion/Documents/Repos/tech-controllers/.devenv/state/venv/lib/python3.13/site-packages/homeassistant/helpers/service.py", line 922, in _handle_entity_call
    result = await task
             ^^^^^^^^^^
  File "/home/anarion/Documents/Repos/tech-controllers/config/custom_components/tech/fan.py", line 177, in async_set_percentage
    await self._coordinator.api.set_recuperation_speed(self._udid, speed_level, None)
  File "/home/anarion/Documents/Repos/tech-controllers/config/custom_components/tech/tech.py", line 460, in set_recuperation_speed
    raise TechError(400, f"Invalid speed level: {speed_level}")
custom_components.tech.tech.TechError: (400, 'Invalid speed level: 0.99')

(although this particular error might be a bug regardless).

Please try to debug and check with demo account as well.

@fifeek0 fifeek0 changed the title defo drx pluse defro drx pluse Sep 19, 2025
Copy link
Contributor

@daroga0002 daroga0002 left a comment

Choose a reason for hiding this comment

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

I have looked in fast way into this (without going into logic and deep review) so left few comments

Copy link
Contributor

Choose a reason for hiding this comment

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

this shouldnt be added

Copy link
Contributor

Choose a reason for hiding this comment

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

shouldnt be here

entities.append(TileFanSensor(tile, coordinator, config_entry))
# Check if this fan is handled by the fan platform (recuperation)
description = tile[CONF_PARAMS].get(CONF_DESCRIPTION, "").lower()
if not any(keyword in description for keyword in ["recuperation", "rekuperacja", "ventilation", "wentylacja"]):
Copy link
Contributor

Choose a reason for hiding this comment

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

this looks some bad assumption, tech api currently supports:

Available values : en, fr, it, es, nl, pl, de, cs, sk, hu, ro, lt, et, ru, si, hr

so assuming it will not work for other languages properly and we have a lot of users using those languages


# Direct gear control settings
GEAR_CONTROL_IDO_ID = 1833
GEAR_OPTIONS = {
Copy link
Contributor

Choose a reason for hiding this comment

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

be consistent and use english for comments everywhere

@property
def is_on(self) -> bool | None:
"""Return True if filter replacement is needed."""
# Calculate if filter replacement is needed based on usage days
Copy link
Contributor

Choose a reason for hiding this comment

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

What is source of max days and other constants?

Does device record them or what will happen when those will be passed?

for flow_sensor in [RECUPERATION_EXHAUST_FLOW, RECUPERATION_SUPPLY_FLOW, RECUPERATION_SUPPLY_FLOW_ALT]:
if flow_sensor["txt_id"] == widget_txt_id:
flow_value = widget_data.get("value", 0)
if flow_value and flow_value > 0:
Copy link
Contributor

Choose a reason for hiding this comment

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

we dont need to check flow_value exists as above you always make it using:
flow_value = widget_data.get("value", 0)


# Define options with Polish names
self._attr_options = [
"Zatrzymaj wentylator",
Copy link
Contributor

Choose a reason for hiding this comment

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

We should support all languages from tech API as minimum:

Options: 'en', 'fr', 'it', 'es', 'nl', 'pl', 'de', 'cs', 'sk', 'hu', 'ro', 'lt', 'et', 'ru', 'si', 'hr'

As we got a users from those countries also

Copy link
Collaborator

@anarion80 anarion80 Oct 1, 2025

Choose a reason for hiding this comment

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

Yes, this is all in the language file returned by the API:
https://emodul.eu/api/v1/i18n/en
https://emodul.eu/api/v1/i18n/pl
image
image
so you need to use the key/value from there


# Map option name to value
option_value_map = {
"Zatrzymaj wentylator": 0,
Copy link
Contributor

Choose a reason for hiding this comment

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

We should not use only Polish

async def async_select_option(self, option: str) -> None:
"""Change the selected gear."""
gear_mapping = {
"Zatrzymaj": 0,
Copy link
Contributor

Choose a reason for hiding this comment

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

Not only Polish

self.headers = {
"Accept": "application/json",
"Accept-Encoding": "gzip",
"User-Agent": "TechController/1.0 (Home Assistant Integration)"
Copy link
Contributor

Choose a reason for hiding this comment

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

it is hard to tell how tech company will react on that, this will give them chance to cut off this integration very easy by blocking this header

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